1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "cvundoes.h"
31 #include "fontforgeui.h"
32 #include "spiro.h"
33 #include "splinefont.h"
34 #include "splineorder2.h"
35 #include "splineutil.h"
36 #include "splineutil2.h"
37 #include "ustring.h"
38 
39 #include <math.h>
40 
CVOneThingSel(CharView * cv,SplinePoint ** sp,SplinePointList ** _spl,RefChar ** ref,ImageList ** img,AnchorPoint ** ap,spiro_cp ** scp)41 int CVOneThingSel(CharView *cv, SplinePoint **sp, SplinePointList **_spl,
42 	RefChar **ref, ImageList **img, AnchorPoint **ap, spiro_cp **scp) {
43     /* if there is exactly one thing selected return it */
44     SplinePointList *spl, *found=NULL;
45     Spline *spline;
46     SplinePoint *foundsp=NULL;
47     RefChar *refs, *foundref=NULL;
48     ImageList *imgs, *foundimg=NULL;
49     AnchorPoint *aps, *foundap=NULL;
50     spiro_cp *foundcp = NULL;
51     int i;
52 
53     *sp = NULL; *_spl=NULL; *ref=NULL; *img = NULL; *scp = NULL;
54     if ( ap ) *ap = NULL;
55     for ( spl= cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl=spl->next ) {
56 	if ( !cv->b.sc->inspiro || !hasspiro()) {
57 	    if ( spl->first->selected ) {
58 		if ( found!=NULL )
59 return( 0 );			/* At least two points */
60 		found = spl; foundsp = spl->first;
61 	    }
62 	    for ( spline = spl->first->next; spline!=NULL ; spline=spline->to->next ) {
63 		if ( spline->to==spl->first )
64 	    break;
65 		if ( spline->to->selected ) {
66 		    if ( found!=NULL )
67 return( 0 );
68 		    found = spl; foundsp = spline->to;
69 		}
70 	    }
71 	} else {
72 	    for ( i=0; i<spl->spiro_cnt-1; ++i ) {
73 		if ( SPIRO_SELECTED(&spl->spiros[i])) {
74 		    if ( found )
75 return( 0 );
76 		    found = spl;
77 		    foundcp = &spl->spiros[i];
78 		}
79 	    }
80 	}
81     }
82     *sp = foundsp; *scp = foundcp; *_spl = found;
83 
84     if ( cv->b.drawmode!=dm_grid ) {
85 	for ( refs=cv->b.layerheads[cv->b.drawmode]->refs; refs!=NULL; refs = refs->next ) {
86 	    if ( refs->selected ) {
87 		if ( found!=NULL || foundref!=NULL )
88 return( 0 );
89 		foundref = refs;
90 	    }
91 	}
92 	*ref = foundref;
93 	if ( cv->showanchor && ap!=NULL ) {
94 	    for ( aps=cv->b.sc->anchor; aps!=NULL; aps=aps->next ) {
95 		if ( aps->selected ) {
96 		    if ( found!=NULL || foundref!=NULL || foundap!=NULL )
97 return( 0 );
98 		    foundap = aps;
99 		}
100 	    }
101 	    *ap = foundap;
102 	}
103     }
104 
105     for ( imgs=cv->b.layerheads[cv->b.drawmode]->images; imgs!=NULL; imgs = imgs->next ) {
106 	if ( imgs->selected ) {
107 	    if ( found!=NULL || foundimg!=NULL )
108 return( 0 );
109 	    foundimg = imgs;
110 	}
111     }
112     *img = foundimg;
113 
114     if ( found )
115 return( foundimg==NULL && foundref==NULL && foundap==NULL );
116     else if ( foundref || foundimg || foundap )
117 return( true );
118 
119 return( false );
120 }
121 
CVOneContourSel(CharView * cv,SplinePointList ** _spl,RefChar ** ref,ImageList ** img)122 int CVOneContourSel(CharView *cv, SplinePointList **_spl,
123 	RefChar **ref, ImageList **img) {
124     /* if there is exactly one contour/image/reg selected return it */
125     SplinePointList *spl, *found=NULL;
126     Spline *spline;
127     RefChar *refs, *foundref=NULL;
128     ImageList *imgs, *foundimg=NULL;
129     int i;
130 
131     *_spl=NULL; *ref=NULL; *img = NULL;
132     for ( spl= cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl=spl->next ) {
133 	if ( !cv->b.sc->inspiro || !hasspiro()) {
134 	    if ( spl->first->selected ) {
135 		if ( found!=NULL && found!=spl )
136 return( 0 );			/* At least two contours */
137 		found = spl;
138 	    }
139 	    for ( spline = spl->first->next; spline!=NULL ; spline=spline->to->next ) {
140 		if ( spline->to==spl->first )
141 	    break;
142 		if ( spline->to->selected ) {
143 		    if ( found!=NULL && found!=spl )
144 return( 0 );
145 		    found = spl;
146 		}
147 	    }
148 	} else {
149 	    for ( i=0; i<spl->spiro_cnt-1; ++i ) {
150 		if ( SPIRO_SELECTED(&spl->spiros[i])) {
151 		    if ( found!=NULL && found!=spl )
152 return( 0 );
153 		    found = spl;
154 		}
155 	    }
156 	}
157     }
158     *_spl = found;
159 
160     if ( cv->b.drawmode==dm_fore ) {
161 	for ( refs=cv->b.layerheads[cv->b.drawmode]->refs; refs!=NULL; refs = refs->next ) {
162 	    if ( refs->selected ) {
163 		if ( found!=NULL || foundref!=NULL )
164 return( 0 );
165 		foundref = refs;
166 	    }
167 	}
168 	*ref = foundref;
169     }
170 
171     for ( imgs=cv->b.layerheads[cv->b.drawmode]->images; imgs!=NULL; imgs = imgs->next ) {
172 	if ( imgs->selected ) {
173 	    if ( found!=NULL || foundimg!=NULL )
174 return( 0 );
175 	    foundimg = imgs;
176 	}
177     }
178     *img = foundimg;
179 
180     if ( found )
181 return( foundimg==NULL && foundref==NULL );
182     else if ( foundref || foundimg )
183 return( true );
184 
185 return( false );
186 }
187 
CVAnySelPointList(CharView * cv)188 SplinePointList *CVAnySelPointList(CharView *cv) {
189     /* if there is exactly one point selected and it is on an open splineset */
190     /*  and it is one of the endpoints of the splineset, then return that */
191     /*  splineset */
192     SplinePointList *spl, *found=NULL;
193     Spline *spline, *first;
194     int i;
195 
196     for ( spl= cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl=spl->next ) {
197 	if ( cv->b.sc->inspiro && hasspiro()) {
198 	    for ( i = 0; i<spl->spiro_cnt-1; ++i ) {
199 		if ( SPIRO_SELECTED(&spl->spiros[i])) {
200 		    /* Only interesting if the single selection is at the */
201 		    /* start/end of an open contour */
202 		    if (( i!=0 && i!=spl->spiro_cnt-2 ) ||
203 			    !SPIRO_SPL_OPEN(spl))
204 return( NULL );
205 		    else if ( found==NULL )
206 			found = spl;
207 		    else
208 return( NULL );
209 		}
210 	    }
211 	} else {
212 	    if ( spl->first->selected ) {
213 		if ( found!=NULL )
214 return( NULL );			/* At least two points */
215 		if ( spl->first->prev!=NULL )
216 return( NULL );			/* Not an open splineset */
217 		found = spl;
218 	    }
219 	    first = NULL;
220 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
221 		if ( spline->to->selected ) {
222 		    if ( found!=NULL )
223 return( NULL );
224 		    if ( spline->to->next!=NULL )
225 return( NULL );			/* Selected point is not at end of a splineset */
226 		    found = spl;
227 		}
228 		if ( first==NULL ) first = spline;
229 	    }
230 	}
231     }
232 return( found );
233 }
234 
CVAnySelPoint(CharView * cv,SplinePoint ** sp,spiro_cp ** cp)235 int CVAnySelPoint(CharView *cv,SplinePoint **sp, spiro_cp **cp) {
236     /* if there is exactly one point selected */
237     SplinePointList *spl;
238     Spline *spline, *first; SplinePoint *found = NULL;
239     spiro_cp *foundcp = NULL;
240     int i;
241 
242     *sp = NULL; *cp = NULL;
243     if ( cv->b.sc->inspiro && hasspiro()) {
244 	for ( spl= cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl=spl->next ) {
245 	    for ( i=0; i<spl->spiro_cnt-1; ++i )
246 		if ( SPIRO_SELECTED( &spl->spiros[i]) ) {
247 		    if ( foundcp )
248 return( false );
249 		    foundcp = &spl->spiros[i];
250 		}
251 	}
252 	*cp = foundcp;
253 return( foundcp!=NULL );
254     } else {
255 	for ( spl= cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl=spl->next ) {
256 	    if ( spl->first->selected ) {
257 		if ( found!=NULL )
258 return( false );			/* At least two points */
259 		found = spl->first;
260 	    }
261 	    first = NULL;
262 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
263 		if ( spline->to->selected ) {
264 		    if ( found!=NULL )
265 return( false );
266 		    found = spline->to;
267 		}
268 		if ( first==NULL ) first = spline;
269 	    }
270 	}
271 	*sp = found;
272 return( found!=NULL );
273     }
274 }
275 
276 // Return type: index into spiro_cp array, or -1 if not found.
CVSpiroSelected(CharView * cv,SplineSet * ss)277 static int CVSpiroSelected(CharView *cv, SplineSet *ss) {
278     int i, j = 0;
279     bool found = false;
280     for (i = 0; i < ss->spiro_cnt; i++) {
281         if (SPIRO_SELECTED(&ss->spiros[i])) {
282             j = i;
283             found = true;
284         }
285     }
286     if (!found) {
287         TRACE("CVSpiroSelected: We expected to find a selected spiro, but failed to.\n");
288         return -1;
289     }
290     TRACE("CVSpiroSelected: %d\n", j);
291     return j;
292 }
293 
294 // If needed, reset the selected spiro.
CVResetSelSpiro(CharView * cv,SplineSet * ss)295 static void CVResetSelSpiro(CharView* cv, SplineSet *ss) {
296     if (CVInSpiro(cv)) {
297         int sel = CVSpiroSelected(cv, ss);
298         cv->p.spiro = cv->lastselcp = cv->active_cp = sel == -1 ? NULL : &ss->spiros[sel];
299     }
300 }
301 
CVMergeSPLS(CharView * cv,SplineSet * ss,SplinePoint * base,SplinePoint * sp)302 static void CVMergeSPLS(CharView *cv,SplineSet *ss, SplinePoint *base,SplinePoint *sp) {
303     int order2 = cv->b.layerheads[cv->b.drawmode]->order2;
304 
305     cv->joinvalid = true;
306     cv->joinpos = *sp; cv->joinpos.selected = false;
307     if ( sp->prev!=NULL )
308 	SplineSetReverse(cv->p.spl);
309     if ( sp->prev!=NULL )
310 	IError("Base point not at start of splineset in CVMouseDownPoint");
311     /* remove the old spl entry from the chain */
312     if ( cv->p.spl==cv->b.layerheads[cv->b.drawmode]->splines )
313 	cv->b.layerheads[cv->b.drawmode]->splines = cv->p.spl->next;
314     else { SplineSet *temp;
315 	for ( temp = cv->b.layerheads[cv->b.drawmode]->splines; temp->next!=cv->p.spl; temp = temp->next );
316 	temp->next = cv->p.spl->next;
317     }
318     if ( order2 && (!RealNear(base->nextcp.x,sp->prevcp.x) ||
319 	    !RealNear(base->nextcp.y,sp->prevcp.y)) ) {
320 	base->nonextcp = sp->noprevcp = true;
321 	base->nextcp = base->me;
322 	sp->prevcp = sp->me;
323     }
324     SplineMake(base,sp,order2);
325     SplineCharDefaultNextCP(base);
326     SplineCharDefaultPrevCP(sp);
327     if ( sp->pointtype==pt_tangent ) {
328 	SplineCharTangentNextCP(sp);
329 	if ( sp->next ) SplineRefigure(sp->next );
330     }
331     ss->last = cv->p.spl->last;
332     if ( ss->spiros && cv->p.spl->spiros ) {
333 	if ( ss->spiro_cnt+cv->p.spl->spiro_cnt > ss->spiro_max )
334 	    ss->spiros = realloc(ss->spiros,
335 		    (ss->spiro_max = ss->spiro_cnt+cv->p.spl->spiro_cnt)*sizeof(spiro_cp));
336 	memcpy(ss->spiros+ss->spiro_cnt-1,
337 		cv->p.spl->spiros+1, (cv->p.spl->spiro_cnt-1)*sizeof(spiro_cp));
338 	ss->spiro_cnt += cv->p.spl->spiro_cnt-2;
339     } else
340 	SplineSetSpirosClear(ss);
341     cv->p.spl->last = cv->p.spl->first = NULL;
342     cv->p.spl->spiros = 0;
343     SplinePointListFree(cv->p.spl);
344     cv->p.spl = NULL;
345 }
346 
CVMouseDownSpiroPoint(CharView * cv,GEvent * event)347 static void CVMouseDownSpiroPoint(CharView *cv, GEvent *event) {
348     SplineSet *sel, *ss;
349     SplineChar *sc = cv->b.sc;
350     spiro_cp *base, *cp;
351     int base_index, cp_index, i;
352     char ty = (cv->active_tool==cvt_curve?SPIRO_G4:
353 			    cv->active_tool==cvt_hvcurve?SPIRO_G2:
354 			    cv->active_tool==cvt_corner?SPIRO_CORNER:
355 			    cv->active_tool==cvt_tangent?SPIRO_LEFT:
356 			    /*cv->active_tool==cvt_pen?*/SPIRO_RIGHT);
357 
358     cv->active_spl = NULL;
359     cv->active_sp = NULL;
360 
361     sel = CVAnySelPointList(cv);
362     if ( sel!=NULL ) {
363 	if ( SPIRO_SELECTED(&sel->spiros[0]) ) base_index = 0;
364 	else base_index = sel->spiro_cnt-2;
365 	base = &sel->spiros[base_index];
366 	if ( base==cv->p.spiro )
367 return;			/* We clicked on the active point, that's a no-op */
368     }
369     CVPreserveState(&cv->b);
370     CVClearSel(cv);
371     if ( sel!=NULL ) {
372 	if ( (cp = cv->p.spiro)!=NULL )
373 	    cp_index = cp-cv->p.spl->spiros;
374 	cv->lastselcp = base;
375 	ss = sel;
376 	if ( base_index!=sel->spiro_cnt-2 ) {
377 	    SplineSetReverse(sel);
378 	    base = &sel->spiros[sel->spiro_cnt-2];
379 	    if ( cv->p.spl==sel ) {
380 		cp_index = sel->spiro_cnt-2-cp_index;
381 		cp = &sel->spiros[cp_index];
382 	    }
383 	}
384 	if ( cp==NULL || (cp_index!=0 && cp_index!=cv->p.spl->spiro_cnt-2) ||
385 		cp==base || !SPIRO_SPL_OPEN(cv->p.spl)) {
386 	    /* Add a new point */
387 	    if ( sel->spiro_cnt>=sel->spiro_max )
388 		sel->spiros = realloc(sel->spiros,(sel->spiro_max += 10)*sizeof(spiro_cp));
389 	    cp = &sel->spiros[sel->spiro_cnt-1];
390 	    cp[1] = cp[0];		/* Move the final 'z' */
391 	    cp->x = cv->p.cx;
392 	    cp->y = cv->p.cy;
393 	    cp->ty = ty;
394 	    SPIRO_DESELECT(cp-1);
395 	    ++sel->spiro_cnt;
396 	} else if ( cv->p.spl==sel ) {
397 	    /* Close the current spline set */
398 	    sel->spiros[0].ty = ty;
399 	    cv->joinvalid = true;
400 	    cv->joincp = *cp; SPIRO_DESELECT(&cv->joincp);
401 	} else {
402 	    /* Merge two spline sets */
403 	    SplinePoint *sp = cp_index==0 ? cv->p.spl->first : cv->p.spl->last;
404 	    SplinePoint *basesp = base_index==0 ? sel->first : sel->last;
405 	    cv->joincp = *cp; SPIRO_DESELECT(&cv->joincp);
406 	    CVMergeSPLS(cv,ss,basesp,sp);
407 	}
408     } else if ( cv->p.spline!=NULL ) {
409 	/* Add an intermediate point on an already existing spline */
410 	ss = cv->p.spl;
411 	if ( ss->spiro_cnt>=ss->spiro_max )
412 	    ss->spiros = realloc(ss->spiros,(ss->spiro_max += 10)*sizeof(spiro_cp));
413 	for ( i=ss->spiro_cnt-1; i>cv->p.spiro_index; --i )
414 	    ss->spiros[i+1] = ss->spiros[i];
415 	++ss->spiro_cnt;
416 	cp = &ss->spiros[cv->p.spiro_index+1];
417 	cp->x = cv->p.cx;
418 	cp->y = cv->p.cy;
419 	cp->ty = ty;
420 	ss = cv->p.spl;
421 	cv->joinvalid = true;
422 	cv->joincp = *cp; SPIRO_DESELECT(&cv->joincp);
423     } else {
424 	/* A new point on a new (open) contour */
425 	ss = chunkalloc(sizeof(SplineSet));
426 	ss->next = cv->b.layerheads[cv->b.drawmode]->splines;
427 	cv->b.layerheads[cv->b.drawmode]->splines = ss;
428 	ss->spiros = malloc((ss->spiro_max=10)*sizeof(spiro_cp));
429 	cp = &ss->spiros[0];
430 	cp->x = cv->p.cx;
431 	cp->y = cv->p.cy;
432 	cp->ty = SPIRO_OPEN_CONTOUR;
433 	cp[1].x = cp[1].y = 0; cp[1].ty = 'z';
434 	ss->spiro_cnt = 2;
435     }
436     SPIRO_SELECT(cp);
437 
438     SSRegenerateFromSpiros(ss);
439 
440     cv->active_spl = ss;
441     cv->active_cp = cp;
442     // We may have reallocated the spiros (above), so we need to reset the selected spiro.
443     CVResetSelSpiro(cv, ss);
444     CVSetCharChanged(cv,true);
445     CVInfoDraw(cv,cv->gw);
446     SCUpdateAll(sc);
447 }
448 
449 /* When the user tries to add a point (by doing a mouse down with a point tool
450   selected) there are several cases to be looked at:
451 	If there is a single point selected and it is at the begining/end of an open spline set
452 	    if we clicked on another point which is the begining/end of an open splineset
453 		draw a spline connecting the two spline sets and merge them
454 			(or if it's the same spline set, then close it)
455 	    else
456 		create a new point where we clicked
457 		draw a spline between the selected point and the new one
458 		deselect the old point
459 		select the new one
460 	    endif
461 	else if they clicked on a spline
462 	    split the spline into two bits at the point where they clicked
463 	else
464 	    create a new point where they clicked
465 	    put it on a new splineset
466 	    select it
467 	endif
468 
469 	and, if the old point is a tangent, we may need to adjust its control pt
470 
471     With the introduction of spiro mode (Raph Levien's clothoid splines)
472       we've got to worry about all the above cases for spiro points too.
473 */
CVMouseDownPoint(CharView * cv,GEvent * event)474 void CVMouseDownPoint(CharView *cv, GEvent *event) {
475     SplineSet *sel, *ss;
476     SplinePoint *sp, *base = NULL;
477     SplineChar *sc = cv->b.sc;
478     enum pointtype ptype = (cv->active_tool==cvt_curve?pt_curve:
479 			    cv->active_tool==cvt_hvcurve?pt_hvcurve:
480 			    cv->active_tool==cvt_corner?pt_corner:
481 			    cv->active_tool==cvt_tangent?pt_tangent:
482 			    /*cv->active_tool==cvt_pen?*/pt_corner);
483     int order2 = cv->b.layerheads[cv->b.drawmode]->order2;
484     int order2_style = (order2 && !(event->u.mouse.state&ksm_meta)) ||
485 		    (!order2 && (event->u.mouse.state&ksm_meta));
486 
487     cv->active_spl = NULL;
488     cv->active_sp = NULL;
489 
490     if ( cv->b.sc->inspiro && hasspiro()) {
491 	CVMouseDownSpiroPoint(cv, event);
492 return;
493     }
494 
495     sel = CVAnySelPointList(cv);
496     if ( sel!=NULL ) {
497 	if ( sel->first->selected ) base = sel->first;
498 	else base = sel->last;
499 	if ( base==cv->p.sp )
500 return;			/* We clicked on the active point, that's a no-op */
501     }
502     CVPreserveState(&cv->b);
503     CVClearSel(cv);
504     if ( sel!=NULL ) {
505 	sp = cv->p.sp;
506 	cv->lastselpt = base;
507 	ss = sel;
508 	if ( base->next!=NULL )
509 	    SplineSetReverse(sel);
510 	if ( base->next!=NULL )
511 	    IError("Base point not at end of splineset in CVMouseDownPoint");
512 	if ( sp==NULL || (sp->next!=NULL && sp->prev!=NULL) || sp==base ) {
513 	    /* Add a new point */
514 	    SplineSetSpirosClear(sel);
515 	    sp = SplinePointCreate( cv->p.cx, cv->p.cy );
516 	    sp->noprevcp = sp->nonextcp = 1;
517 	    sp->nextcpdef = sp->prevcpdef = 1;
518 	    sp->pointtype = ptype;
519 	    sp->selected = true;
520 	    if ( !base->nonextcp && order2_style &&
521 		    cv->active_tool==cvt_pen ) {
522 		sp->prevcp = base->nextcp;
523 		sp->noprevcp = false;
524 		sp->me.x = ( sp->prevcp.x + sp->nextcp.x )/2;
525 		sp->me.y = ( sp->prevcp.y + sp->nextcp.y )/2;
526 		sp->nonextcp = false;
527 		sp->pointtype = pt_curve;
528 	    } else if ( order2 && !base->nonextcp ) {
529 		sp->prevcp = base->nextcp;
530 		sp->noprevcp = false;
531 		if (  cv->active_tool==cvt_pen ) {
532 		    sp->nextcp.x = sp->me.x - (sp->prevcp.x-sp->me.x);
533 		    sp->nextcp.y = sp->me.y - (sp->prevcp.y-sp->me.y);
534 		    sp->nonextcp = false;
535 		    sp->pointtype = pt_curve;
536 		}
537 	    }
538 	    if ( base->nonextcp )
539 		base->nextcpdef = true;
540 	    SplineMake(base,sp,order2);
541 	    if ( cv->active_tool!=cvt_pen ) {
542 		SplineCharDefaultNextCP(base);
543 		SplineCharDefaultPrevCP(sp);
544 	    }
545 	    ss->last = sp;
546 	} else if ( cv->p.spl==sel ) {
547 	    /* Close the current spline set */
548 	    SplineSetSpirosClear(sel);
549 	    cv->joinvalid = true;
550 	    cv->joinpos = *sp; cv->joinpos.selected = false;
551 	    if ( order2 ) {
552 		if ( base->nonextcp || sp->noprevcp ) {
553 		    base->nonextcp = sp->noprevcp = true;
554 		    base->nextcp = base->me;
555 		    sp->prevcp = sp->me;
556 		} else {
557 		    base->nextcp.x = sp->prevcp.x = (base->nextcp.x+sp->prevcp.x)/2;
558 		    base->nextcp.y = sp->prevcp.y = (base->nextcp.y+sp->prevcp.y)/2;
559 		}
560 		base->nextcpdef = sp->prevcpdef = true;
561 	    }
562 	    SplineMake(base,sp,order2);
563 	    if ( cv->active_tool!=cvt_pen )
564 		SplineCharDefaultNextCP(base);
565 	    SplineCharDefaultPrevCP(sp);
566 	    ss->last = sp;
567 	    if ( sp->pointtype==pt_tangent ) {
568 		SplineCharTangentNextCP(sp);
569 		if ( sp->next ) SplineRefigure(sp->next );
570 	    }
571 	} else {
572 	    /* Merge two spline sets */
573 	    SplineSetSpirosClear(sel);
574 	    CVMergeSPLS(cv,sel, base,sp);
575 	}
576 	sp->selected = true;
577 	if ( base->pointtype==pt_tangent ) {
578 	    SplineCharTangentPrevCP(base);
579 	    if ( base->prev!=NULL )
580 		SplineRefigure(base->prev);
581 	}
582     } else if ( cv->p.spline!=NULL ) {
583 	sp = SplineBisect(cv->p.spline,cv->p.t);
584 	cv->joinvalid = true;
585 	cv->joinpos = *sp; cv->joinpos.selected = false;
586 	if (  cv->active_tool==cvt_pen )
587 	    ptype = pt_curve;
588 	sp->pointtype = ptype;
589 	if ( ptype==pt_hvcurve ) {
590 	    SPHVCurveForce(sp);
591 	}
592 	sp->selected = true;
593 	ss = cv->p.spl;
594     } else {
595 	ss = chunkalloc(sizeof(SplineSet));
596 	sp = SplinePointCreate( cv->p.cx, cv->p.cy );
597 
598 	ss->first = ss->last = sp;
599 	ss->next = cv->b.layerheads[cv->b.drawmode]->splines;
600 	cv->b.layerheads[cv->b.drawmode]->splines = ss;
601 	sp->nonextcp = sp->noprevcp = 1;
602 	sp->nextcpdef = sp->prevcpdef = 1;
603 	sp->pointtype = ptype;
604 	sp->selected = true;
605     }
606 
607     cv->active_spl = ss;
608     cv->active_sp = sp;
609     CVSetCharChanged(cv,true);
610     CVInfoDraw(cv,cv->gw);
611     SCUpdateAll(sc);
612 
613     if ( cv->active_tool == cvt_pen )
614 	cv->p.constrain = sp->me;
615 }
616 
AdjustControls(SplinePoint * sp)617 void AdjustControls(SplinePoint *sp) {
618     if ( sp->next!=NULL ) {
619 	SplineCharDefaultNextCP(sp);	/* also fixes up tangents */
620 	SplineCharDefaultPrevCP(sp->next->to);
621 	SplineRefigure(sp->next);
622 	if ( sp->next->to->pointtype==pt_tangent && sp->next->to->next!=NULL ) {
623 	    SplineCharTangentNextCP(sp->next->to);
624 	    SplineRefigure(sp->next->to->next);
625 	}
626     }
627     if ( sp->prev!=NULL ) {
628 	SplineCharDefaultPrevCP(sp);
629 	SplineCharDefaultNextCP(sp->prev->from);
630 	SplineRefigure(sp->prev);
631 	if ( sp->prev->from->pointtype==pt_tangent && sp->prev->from->prev!=NULL ) {
632 	    SplineCharTangentPrevCP(sp->prev->from);
633 	    SplineRefigure(sp->prev->from->prev);
634 	}
635     }
636 }
637 
CVAdjustPoint(CharView * cv,SplinePoint * sp)638 void CVAdjustPoint(CharView *cv, SplinePoint *sp) {
639 
640     if ( cv->info.x==sp->me.x && cv->info.y==sp->me.y )
641 return;
642     sp->nextcp.x += (cv->info.x-sp->me.x);
643     sp->nextcp.y += (cv->info.y-sp->me.y);
644     sp->prevcp.x += (cv->info.x-sp->me.x);
645     sp->prevcp.y += (cv->info.y-sp->me.y);
646     sp->me.x = cv->info.x;
647     sp->me.y = cv->info.y;
648     AdjustControls(sp);
649     CVSetCharChanged(cv,true);
650 }
651 
CVMergeSplineSets(CharView * cv,SplinePoint * active,SplineSet * activess,SplinePoint * merge,SplineSet * mergess)652 void CVMergeSplineSets(CharView *cv, SplinePoint *active, SplineSet *activess,
653 	SplinePoint *merge, SplineSet *mergess) {
654     SplinePointList *spl;
655 
656     cv->joinvalid = true;
657     cv->joinpos = *merge; cv->joinpos.selected = false;
658 
659     if ( active->prev==NULL )
660 	SplineSetReverse(activess);
661     if ( merge->next==NULL )
662 	SplineSetReverse(mergess);
663     active->nextcp = merge->nextcp;
664     active->nonextcp = merge->nonextcp;
665     active->nextcpdef = merge->nextcpdef;
666     active->next = merge->next;
667     if ( merge->next!= NULL ) {
668 	active->next->from = active;
669 	activess->last = mergess->last;
670     }
671     merge->next = NULL;
672     if ( mergess==activess ) {
673 	activess->first = activess->last = active;
674 	SplinePointMDFree(cv->b.sc,merge);
675 	if ( activess->spiro_cnt!=0 ) {
676 	    activess->spiros[0].ty = activess->spiros[activess->spiro_cnt-2].ty;
677 	    activess->spiros[activess->spiro_cnt-2] = activess->spiros[activess->spiro_cnt-1];
678 	    --activess->spiro_cnt;
679 	}
680     } else {
681 	mergess->last = merge;
682 	if ( mergess==cv->b.layerheads[cv->b.drawmode]->splines )
683 	    cv->b.layerheads[cv->b.drawmode]->splines = mergess->next;
684 	else {
685 	    for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl->next!=mergess; spl=spl->next );
686 	    spl->next = mergess->next;
687 	}
688 	if ( activess->spiros && mergess->spiros ) {
689 	    if ( activess->spiro_cnt+mergess->spiro_cnt > activess->spiro_max ) {
690 	        activess->spiros = realloc(activess->spiros, (activess->spiro_max = activess->spiro_cnt+mergess->spiro_cnt)*sizeof(spiro_cp));
691 	    }
692 	    memcpy(activess->spiros+activess->spiro_cnt-1,
693 		    mergess->spiros+1, (mergess->spiro_cnt-1)*sizeof(spiro_cp));
694 	    activess->spiro_cnt += mergess->spiro_cnt-2;
695 	} else {
696 	    SplineSetSpirosClear(activess);
697 	}
698 	SplinePointListMDFree(cv->b.sc,mergess); // frees mergess
699     }
700     if (( active->pointtype==pt_curve || active->pointtype==pt_hvcurve ) &&
701 	    !active->nonextcp && !active->noprevcp &&
702 	    !active->nextcpdef && !active->prevcpdef &&
703 	    !BpColinear(&active->prevcp,&active->me,&active->nextcp))
704 	active->nextcpdef = active->prevcpdef = true;
705     SplineSetJoinCpFixup(active);
706     // We may have reallocated the spiros, so we need to reset the selected spiro.
707     CVResetSelSpiro(cv, activess);
708 }
709 
CVMouseMoveSpiroPoint(CharView * cv,PressedOn * p)710 static void CVMouseMoveSpiroPoint(CharView *cv, PressedOn *p) {
711     spiro_cp *active = cv->active_cp, *merge = p->spiro;
712     SplineSet *activess = cv->active_spl;
713     int active_index;
714 
715     if ( active==NULL )
716 return;
717     if ( cv->info.x==active->x && cv->info.y==active->y )
718 return;
719 
720     if ( !cv->recentchange ) CVPreserveState(&cv->b);
721 
722     active->x = cv->info.x;
723     active->y = cv->info.y;
724     CVSetCharChanged(cv,true);
725 
726     active_index = active-activess->spiros;
727 
728     if ( active!=merge && merge!=NULL && p->spl!=NULL &&
729 	    SPIRO_SPL_OPEN(activess) &&
730 	    SPIRO_SPL_OPEN(p->spl) &&
731 	    (active_index==0 || active_index==activess->spiro_cnt-2) &&
732 	    ((merge-p->spl->spiros)==0 || (merge-p->spl->spiros)==p->spl->spiro_cnt-2) ) {
733 	SplinePoint *activesp = active_index==0 ? activess->first : activess->last;
734 	SplinePoint *mergesp = (merge-p->spl->spiros)==0 ? p->spl->first : p->spl->last;
735 	CVMergeSplineSets(cv,activesp,activess,mergesp,p->spl);
736     }
737     SSRegenerateFromSpiros(activess);
738     SCUpdateAll(cv->b.sc);
739 }
740 
741 /* We move the active point around following the mouse. */
742 /*  There's one special case. If the active point is an end point on its splineset */
743 /*  and we've just moved on top of another splineset end-point, then join the */
744 /*  two splinesets at the active point. Of course we might close up our own */
745 /*  spline set... */
CVMouseMovePoint(CharView * cv,PressedOn * p)746 void CVMouseMovePoint(CharView *cv, PressedOn *p) {
747     SplinePoint *active = cv->active_sp, *merge = p->sp;
748     SplineSet *activess = cv->active_spl;
749 
750     if ( cv->b.sc->inspiro && hasspiro()) {
751 	CVMouseMoveSpiroPoint(cv,p);
752 return;
753     }
754 
755     if ( active==NULL )
756 return;
757     if ( cv->info.x==active->me.x && cv->info.y==active->me.y )
758 return;
759 
760     if ( !cv->recentchange ) CVPreserveState(&cv->b);
761 
762     CVAdjustPoint(cv,active);
763     SplineSetSpirosClear(activess);
764     if (( active->next==NULL || active->prev==NULL ) && merge!=NULL && p->spl!=NULL &&
765 	    merge!=active &&
766 	    (merge->next==NULL || merge->prev==NULL )) {
767 	CVMergeSplineSets(cv,active,activess,merge,p->spl);
768     }
769     SCUpdateAll(cv->b.sc);
770 }
771 
CVMouseMovePen(CharView * cv,PressedOn * p,GEvent * event)772 void CVMouseMovePen(CharView *cv, PressedOn *p, GEvent *event) {
773     SplinePoint *active = cv->active_sp;
774     int order2 = cv->b.layerheads[cv->b.drawmode]->order2;
775     int order2_style = (order2 && !(event->u.mouse.state&ksm_meta)) ||
776 		    (!order2 && (event->u.mouse.state&ksm_meta));
777 
778     if ( cv->b.sc->inspiro && hasspiro()) {
779 	CVMouseMoveSpiroPoint(cv,p);
780 return;
781     }
782 
783     if ( active==NULL )
784 return;
785     if ( cv->info.x==active->nextcp.x && cv->info.y==active->nextcp.y )
786 return;
787     /* In order2 fonts when the user clicks with the pen tool we'd like to */
788     /*  leave it with the default cp (ie. the cp which makes the current point*/
789     /*  implicit) rather than moving the cp to the base point and losing the */
790     /*  curve */
791     if ( cv->info.x==active->me.x && cv->info.y==active->me.y &&
792 	    event->type==et_mouseup && cv->b.layerheads[cv->b.drawmode]->order2 )
793 return;
794     SplineSetSpirosClear(cv->active_spl);
795     cv->lastselpt = cv->active_sp;
796 
797     active->nextcp.x = cv->info.x;
798     active->nextcp.y = cv->info.y;
799     if ( order2_style && active->next==NULL ) {
800 	active->me.x = (active->nextcp.x + active->prevcp.x)/2;
801 	active->me.y = (active->nextcp.y + active->prevcp.y)/2;
802 	if ( active->me.x == active->nextcp.x && active->me.y == active->nextcp.y ) {
803 	    active->nonextcp = active->noprevcp = true;
804 	} else {
805 	    active->nonextcp = active->noprevcp = false;
806 	    active->pointtype = pt_curve;
807 	}
808 	if ( active->prev!=NULL )
809 	    SplineRefigure(active->prev);
810 	SCUpdateAll(cv->b.sc);
811 return;
812     } else if ( active->nextcp.x==active->me.x && active->nextcp.y==active->me.y ) {
813 	active->prevcp = active->me;
814 	active->nonextcp = active->noprevcp = true;
815 	active->pointtype = pt_corner;
816     } else {
817 	active->prevcp.x = active->me.x - (active->nextcp.x-active->me.x);
818 	active->prevcp.y = active->me.y - (active->nextcp.y-active->me.y);
819 	active->nonextcp = active->noprevcp = false;
820 	active->nextcpdef = active->prevcpdef = false;
821 	active->pointtype = pt_curve;
822     }
823     if ( cv->b.layerheads[cv->b.drawmode]->order2 ) {
824 	if ( active->prev!=NULL ) {
825 	    if ( active->noprevcp )
826 		active->prev->from->nonextcp = true;
827 	    else {
828 		active->prev->from->nextcp = active->prevcp;
829 		active->prev->from->nonextcp = false;
830 	    }
831 	    SplinePointNextCPChanged2(active->prev->from);
832 	    SplineRefigureFixup(active->prev);
833 	}
834 	if ( active->next!=NULL ) {
835 	    if ( active->nonextcp )
836 		active->next->to->noprevcp = true;
837 	    else {
838 		active->next->to->prevcp = active->nextcp;
839 		active->next->to->noprevcp = false;
840 	    }
841 	    SplineRefigureFixup(active->next);
842 	}
843     } else {
844 	if ( active->prev!=NULL )
845 	    SplineRefigure(active->prev);
846 	if ( active->next!=NULL )
847 	    SplineRefigure(active->next);
848     }
849     CPUpdateInfo(cv,event);
850     SCUpdateAll(cv->b.sc);
851 }
852 
CVMouseUpPoint(CharView * cv,GEvent * event)853 void CVMouseUpPoint(CharView *cv,GEvent *event) {
854     SplinePoint *active = cv->active_sp;
855     cv->lastselpt = active;
856     cv->active_spl = NULL;
857     cv->active_sp = NULL;
858     cv->active_cp = NULL;
859     cv->joinvalid = false;
860     CVInfoDraw(cv,cv->gw);
861     CPEndInfo(cv);
862     if ( event->u.mouse.clicks>1 )
863 	CVGetInfo(cv);
864 }
865