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