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