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 "splineorder2.h"
30 : #include "splinestroke.h"
31 : #include "splineutil.h"
32 : #include "splineutil2.h"
33 : #include <math.h>
34 :
35 : #undef DEBUG_FREEHAND
36 :
37 : const int min_line_cnt = 10; /* line segments must be at least this many datapoints to be distinguished */
38 : const int min_line_len = 20; /* line segments must be at least this many pixels to be distinguished */
39 : const double line_wobble = 1.0; /* data must not stray more than this many pixels from the true line */
40 : const int extremum_locale = 7; /* for something to be an extremum in x it must be an extremum for at least this many y pixels. Or vice versa */
41 : const int extremum_cnt = 4; /* for something to be an extremum in x it must be an extremum for at least this data samples. Or for y */
42 :
43 : enum extreme { e_none=0, e_xmin, e_xmax, e_xflat, e_ymin, e_ymax, e_yflat };
44 :
45 : typedef struct tracedata {
46 : /* First the data */
47 : int x,y;
48 : BasePoint here;
49 : uint32 time;
50 : int32 pressure, xtilt, ytilt, separation;
51 :
52 : /* Then overhead */
53 : struct tracedata *next, *prev;
54 :
55 : unsigned int extremum: 3;
56 : unsigned int use_as_pt: 1;
57 : unsigned int online: 1;
58 : unsigned int wasconstrained: 1;
59 : unsigned int constrained_corner: 1;
60 : uint16 num;
61 : } TraceData;
62 :
63 0 : static void TraceDataFree(TraceData *td) {
64 0 : TraceData *next, *first=td;
65 :
66 0 : while ( td!=NULL ) {
67 0 : next = td->next;
68 0 : chunkfree(td,sizeof(TraceData));
69 0 : td = next;
70 0 : if ( td==first )
71 0 : break;
72 : }
73 0 : }
74 :
75 0 : static void TraceDataFromEvent(CharView *cv, GEvent *event) {
76 : TraceData *new;
77 : int skiplast;
78 :
79 0 : if ( cv->freehand.head!=NULL &&
80 0 : cv->freehand.last->here.x==(event->u.mouse.x-cv->xoff)/cv->scale &&
81 0 : cv->freehand.last->here.y==(cv->height-event->u.mouse.y-cv->yoff)/cv->scale ) {
82 : /* Has not moved */
83 0 : int constrained = (event->u.mouse.state&ksm_shift)?1:0;
84 0 : if ( constrained != cv->freehand.last->wasconstrained )
85 0 : cv->freehand.last->constrained_corner = true;
86 0 : return;
87 : }
88 :
89 : /* I sometimes seem to get events out of order on the wacom */
90 0 : skiplast = false;
91 0 : if ( cv->freehand.last!=NULL && cv->freehand.last->prev!=NULL ) {
92 0 : int x = (event->u.mouse.x-cv->xoff)/cv->scale;
93 0 : int y = (cv->height-event->u.mouse.y-cv->yoff)/cv->scale;
94 0 : TraceData *bad = cv->freehand.last, *base = bad->prev;
95 :
96 0 : if ( ((bad->here.x < x-15 && bad->here.x < base->here.x-15) ||
97 0 : (bad->here.x > x+15 && bad->here.x > base->here.x+15)) &&
98 0 : ((bad->here.y < y-15 && bad->here.y < base->here.y-15) ||
99 0 : (bad->here.y > y+15 && bad->here.y > base->here.y+15)) )
100 0 : skiplast = true;
101 : }
102 :
103 0 : if ( skiplast )
104 0 : new = cv->freehand.last;
105 : else {
106 0 : new = chunkalloc(sizeof(TraceData));
107 :
108 0 : if ( cv->freehand.head==NULL )
109 0 : cv->freehand.head = cv->freehand.last = new;
110 : else {
111 0 : cv->freehand.last->next = new;
112 0 : new->prev = cv->freehand.last;
113 0 : cv->freehand.last = new;
114 : }
115 : }
116 :
117 0 : new->here.x = (event->u.mouse.x-cv->xoff)/cv->scale;
118 0 : new->here.y = (cv->height-event->u.mouse.y-cv->yoff)/cv->scale;
119 0 : new->time = event->u.mouse.time;
120 0 : new->pressure = event->u.mouse.pressure;
121 0 : new->xtilt = event->u.mouse.xtilt;
122 0 : new->ytilt = event->u.mouse.ytilt;
123 0 : new->wasconstrained = (event->u.mouse.state&ksm_shift)?1:0;
124 : #ifdef DEBUG_FREEHAND
125 : printf( "(%d,%d) (%g,%g) %d %d\n", event->u.mouse.x, event->u.mouse.y,
126 : new->here.x, new->here.y, new->pressure, new->time );
127 : if ( new->prev!=NULL && new->time<new->prev->time )
128 : printf( "\tAh ha!\n" );
129 : #endif
130 0 : if ( new->wasconstrained &&
131 0 : ( new->prev==NULL || !new->prev->wasconstrained))
132 0 : new->constrained_corner = true;
133 0 : else if ( new->prev!=NULL && !new->wasconstrained && new->prev->wasconstrained )
134 0 : new->prev->constrained_corner = true;
135 : }
136 :
137 0 : static int TraceGoodLine(TraceData *base,TraceData *end) {
138 : /* Make sure that we don't stray more than line_wobble pixels */
139 : int dx, dy;
140 : double diff;
141 : TraceData *pt;
142 :
143 0 : if (( dx = end->x-base->x )<0 ) dx = -dx;
144 0 : if (( dy = end->y-base->y )<0 ) dy = -dy;
145 :
146 0 : if ( dy>dx ) {
147 0 : dx = end->x-base->x; dy = end->y-base->y;
148 0 : for ( pt=base->next; pt!=end; pt=pt->next ) {
149 0 : diff = (pt->y-base->y)*dx/(double) dy + base->x - pt->x;
150 0 : if ( diff>line_wobble || diff<-line_wobble )
151 0 : return( false );
152 : }
153 : } else {
154 0 : dx = end->x-base->x; dy = end->y-base->y;
155 0 : for ( pt=base->next; pt!=end; pt=pt->next ) {
156 0 : diff = (pt->x-base->x)*dy/(double) dx + base->y - pt->y;
157 0 : if ( diff>line_wobble || diff<-line_wobble )
158 0 : return( false );
159 : }
160 : }
161 0 : return( true );
162 : }
163 :
164 0 : static TraceData *TraceLineCheck(TraceData *base) {
165 : /* Look for a line. To be a line it must be at least min_line_len pixels */
166 : /* long (or reach the end of data with no real bends) */
167 : /* and we can't have strayed more than line_wobble pixels from the center */
168 : /* of the line */
169 : TraceData *pt, *end, *last_good;
170 : int cnt;
171 :
172 0 : if ( base->next==NULL || base->next->next==NULL || base->next->next->next==NULL ||
173 : base->wasconstrained )
174 0 : return( base );
175 :
176 0 : for ( end=base->next, cnt=0; end->next!=NULL; end=end->next, ++cnt ) {
177 0 : if ( (end->x-base->x)*(end->x-base->x) + (end->y-base->y)*(end->y-base->y)>=
178 0 : min_line_len*min_line_len &&
179 0 : cnt>=min_line_cnt )
180 0 : break;
181 0 : if ( end->wasconstrained )
182 0 : return( base );
183 : }
184 :
185 0 : last_good = NULL;
186 0 : while ( TraceGoodLine(base,end) ) {
187 0 : last_good = end;
188 0 : if ( end->wasconstrained )
189 0 : break;
190 0 : end = end->next;
191 0 : if ( end==NULL )
192 0 : break;
193 : }
194 0 : if ( last_good==NULL )
195 0 : return( base ); /* No good line */
196 0 : base->use_as_pt = last_good->use_as_pt = true;
197 0 : for ( pt = base; pt!=last_good; pt=pt->next )
198 0 : pt->online = true;
199 0 : last_good->online = true;
200 0 : return( last_good );
201 : }
202 :
203 0 : static enum extreme TraceIsExtremum(TraceData *base) {
204 : TraceData *pt;
205 : enum extreme type;
206 : int i;
207 :
208 0 : if ( base->online || base->use_as_pt )
209 0 : return( e_none );
210 :
211 0 : type = e_xflat;
212 0 : for ( pt = base->next, i=0; pt!=NULL && type!=e_none; pt=pt->next, ++i ) {
213 0 : if ( i>=extremum_cnt && ( pt->y-base->y > extremum_locale || pt->y-base->y < -extremum_locale ))
214 : break;
215 0 : if ( pt->x>base->x ) {
216 0 : if ( type==e_xmax )
217 0 : type = e_none;
218 : else
219 0 : type = e_xmin;
220 0 : } else if ( pt->x<base->x ) {
221 0 : if ( type==e_xmin )
222 0 : type = e_none;
223 : else
224 0 : type = e_xmax;
225 : }
226 : }
227 0 : for ( pt = base->prev, i=0; pt!=NULL && type!=e_none; pt=pt->prev, ++i ) {
228 0 : if ( i>=extremum_cnt && ( pt->y-base->y > extremum_locale || pt->y-base->y < -extremum_locale ))
229 : break;
230 0 : if ( pt->x>base->x ) {
231 0 : if ( type==e_xmax )
232 0 : type = e_none;
233 : else
234 0 : type = e_xmin;
235 0 : } else if ( pt->x<base->x ) {
236 0 : if ( type==e_xmin )
237 0 : type = e_none;
238 : else
239 0 : type = e_xmax;
240 : }
241 : }
242 0 : if ( type!=e_none )
243 0 : return( type );
244 :
245 0 : type = e_yflat;
246 0 : for ( pt = base->next, i=0; pt!=NULL && type!=e_none; pt=pt->next, ++i ) {
247 0 : if ( i>=extremum_cnt && ( pt->x-base->x > extremum_locale || pt->x-base->x < -extremum_locale ))
248 : break;
249 0 : if ( pt->y>base->y ) {
250 0 : if ( type==e_ymax )
251 0 : type = e_none;
252 : else
253 0 : type = e_ymin;
254 0 : } else if ( pt->y<base->y ) {
255 0 : if ( type==e_ymin )
256 0 : type = e_none;
257 : else
258 0 : type = e_ymax;
259 : }
260 : }
261 0 : for ( pt = base->prev, i=0; pt!=NULL && type!=e_none; pt=pt->prev, ++i ) {
262 0 : if ( i>=extremum_cnt && ( pt->x-base->x > extremum_locale || pt->x-base->x < -extremum_locale ))
263 : break;
264 0 : if ( pt->y>base->y ) {
265 0 : if ( type==e_ymax )
266 0 : type = e_none;
267 : else
268 0 : type = e_ymin;
269 0 : } else if ( pt->y<base->y ) {
270 0 : if ( type==e_ymin )
271 0 : type = e_none;
272 : else
273 0 : type = e_ymax;
274 : }
275 : }
276 :
277 0 : return( type );
278 : }
279 :
280 0 : static TraceData *TraceNextPoint(TraceData *pt) {
281 0 : if ( pt==NULL )
282 0 : return( NULL );
283 0 : for ( pt=pt->next; pt!=NULL && !pt->use_as_pt ; pt=pt->next );
284 0 : return( pt );
285 : }
286 :
287 0 : static void TraceMassage(TraceData *head, TraceData *end) {
288 : TraceData *pt, *npt, *last, *nnpt;
289 : double nangle, pangle;
290 : /* Look for corners that are too close together */
291 : /* Paired tangents that might better be a single curve */
292 : /* tangent corner tangent that might better be a single curve */
293 :
294 0 : for ( pt=head ; pt!=NULL ; pt=npt ) {
295 0 : npt = TraceNextPoint(pt);
296 : for (;;) {
297 0 : nnpt = TraceNextPoint(npt);
298 0 : if ( nnpt!=NULL &&
299 0 : ((pt->x==npt->x && npt->x==nnpt->x) ||
300 0 : (pt->y==npt->y && npt->y==nnpt->y)) &&
301 0 : (pt->next == npt || pt->next->online) ) {
302 : /*npt->online = true;*/
303 0 : npt->use_as_pt = false;
304 0 : } else if ( nnpt==NULL && npt!=NULL && (pt->x==npt->x || pt->y==npt->y) &&
305 0 : (pt->next == npt || pt->next->online) ) {
306 0 : pt->use_as_pt = false;
307 : } else
308 : break;
309 0 : npt = nnpt;
310 0 : }
311 : }
312 :
313 : /* Remove very short segments */
314 0 : if ( head && head->next ) {
315 0 : for ( pt=head->next; pt!=NULL && pt->use_as_pt && !pt->constrained_corner; pt = pt->next )
316 0 : pt->use_as_pt = false;
317 0 : for ( pt=head->next ; pt->next!=NULL ; pt=pt->next ) {
318 0 : if ( pt->use_as_pt && pt->next->use_as_pt && !pt->constrained_corner )
319 0 : pt->use_as_pt = false;
320 : }
321 : }
322 0 : for ( pt=head ; pt!=NULL ; pt=npt ) {
323 0 : npt = pt;
324 : for (;;) {
325 0 : npt = TraceNextPoint(pt);
326 0 : if ( npt == NULL || npt->constrained_corner )
327 : break;
328 0 : if ( (npt->x-pt->x)*(npt->x-pt->x) + (npt->y-pt->y)*(npt->y-pt->y)<4 )
329 0 : npt->use_as_pt = false;
330 : else
331 0 : break;
332 0 : }
333 : }
334 :
335 0 : last = NULL;
336 0 : for ( pt=head ; pt!=NULL ; pt=pt->next ) {
337 0 : if ( pt->use_as_pt && pt->next!=NULL && pt->next->online && !pt->next->wasconstrained &&
338 0 : pt->prev!=NULL && !pt->prev->online ) {
339 0 : npt = TraceNextPoint(pt);
340 0 : if ( npt==NULL )
341 0 : break;
342 0 : if ( npt->wasconstrained )
343 : /* Leave it */;
344 0 : else if ( npt->next!=NULL && !npt->next->online ) {
345 : /* We've got two adjacent tangents */
346 0 : nnpt = TraceNextPoint(npt);
347 0 : if ( last!=NULL && nnpt!=NULL ) {
348 0 : int lencur = (npt->x-pt->x)*(npt->x-pt->x) + (npt->y-pt->y)*(npt->y-pt->y);
349 0 : int lennext = (nnpt->x-npt->x)*(nnpt->x-npt->x) + (nnpt->y-npt->y)*(nnpt->y-npt->y);
350 0 : int lenprev = (npt->x-pt->x)*(npt->x-pt->x) + (npt->y-pt->y)*(npt->y-pt->y);
351 0 : if ( lencur<16*lennext || lencur<16*lenprev ) {
352 : /* Probably better done as one point */
353 0 : int which = (pt->num+npt->num)/2;
354 : for (;;) {
355 0 : pt->online = pt->use_as_pt = false;
356 0 : if ( pt->num==which )
357 0 : pt->use_as_pt = true;
358 0 : if ( pt==npt )
359 0 : break;
360 0 : pt = pt->next;
361 0 : }
362 : }
363 : }
364 0 : } else if ( (nnpt = TraceNextPoint(npt))!=NULL &&
365 0 : nnpt->next && !nnpt->next->online ) {
366 : /* We've got tangent corner tangent */
367 0 : pangle = atan2(npt->y-pt->y,npt->x-pt->x);
368 0 : nangle = atan2(nnpt->y-npt->y,nnpt->x-npt->x);
369 0 : if ( pangle<0 && nangle>0 && nangle-pangle>=3.1415926 )
370 0 : pangle += 2*3.1415926535897932;
371 0 : else if ( pangle>0 && nangle<0 && pangle-nangle>=3.1415926 )
372 0 : nangle += 2*3.1415926535897932;
373 0 : if ( nangle-pangle<1 && nangle-pangle>-1 ) {
374 : for (;;) {
375 0 : pt->online = pt->use_as_pt = false;
376 0 : if ( pt==nnpt )
377 0 : break;
378 0 : pt = pt->next;
379 0 : }
380 0 : pt = npt;
381 0 : pt->use_as_pt = true;
382 : }
383 : }
384 : }
385 0 : if ( pt->use_as_pt )
386 0 : last = pt;
387 : }
388 0 : head->use_as_pt = end->use_as_pt = true;
389 0 : }
390 :
391 0 : static bigreal Trace_Factor(void *_cv,Spline *spline, real t) {
392 0 : CharView *cv = (CharView *) _cv;
393 0 : TraceData *head = cv->freehand.head, *pt, *from=NULL, *to=NULL;
394 0 : int fromnum = spline->from->ptindex, tonum = spline->to->ptindex;
395 0 : StrokeInfo *si = CVFreeHandInfo();
396 : int p;
397 :
398 0 : if ( si->radius<=0 || si->pressure1==si->pressure2 )
399 0 : return( 1.0 );
400 :
401 0 : for ( pt = head; pt!=NULL; pt=pt->next ) {
402 0 : if ( fromnum == pt->num ) {
403 0 : from = pt;
404 0 : if ( to!=NULL )
405 0 : break;
406 : }
407 0 : if ( tonum == pt->num ) {
408 0 : to = pt;
409 0 : if ( from!=NULL )
410 0 : break;
411 : }
412 : }
413 0 : if ( from==NULL || to==NULL ) {
414 0 : fprintf( stderr, "Not found\n" );
415 0 : return( 1.0 );
416 : }
417 0 : p = from->pressure*t + to->pressure*(1-t);
418 :
419 0 : if ( p<=si->pressure1 && p<=si->pressure2 ) {
420 0 : if ( si->pressure1<si->pressure2 )
421 0 : return( 1.0 );
422 : else
423 0 : return( si->radius2/si->radius );
424 0 : } else if ( p>=si->pressure1 && p>=si->pressure2 ) {
425 0 : if ( si->pressure1<si->pressure2 )
426 0 : return( si->radius2/si->radius );
427 : else
428 0 : return( 1.0 );
429 : } else
430 0 : return( ((p-si->pressure1)*si->radius2 + (si->pressure2-p)*si->radius)/
431 0 : (si->radius*(si->pressure2-si->pressure1)) );
432 : }
433 :
434 0 : static void TraceFigureCPs(SplinePoint *last,SplinePoint *cur,TraceData *tlast,TraceData *tcur) {
435 : TraceData *pt;
436 :
437 0 : last->nonextcp = false; cur->noprevcp=false;
438 :
439 0 : for ( pt=tlast; !pt->use_as_pt ; pt=pt->prev );
440 0 : if ( pt->online ) {
441 0 : last->nextcp.x = last->me.x + ( tlast->x-pt->x );
442 0 : last->nextcp.y = last->me.y + ( tlast->y-pt->y );
443 0 : } else if ( tlast->extremum==e_xmin || tlast->extremum==e_xmax || tlast->extremum==e_xflat ) {
444 0 : last->nextcp.x = last->me.x;
445 0 : last->nextcp.y = cur->me.y>last->me.y ? last->me.y+1 : last->me.y-1;
446 0 : } else if ( tlast->extremum==e_ymin || tlast->extremum==e_ymax || tlast->extremum==e_yflat ) {
447 0 : last->nextcp.y = last->me.y;
448 0 : last->nextcp.x = cur->me.x>last->me.x ? last->me.x+1 : last->me.x-1;
449 0 : } else if ( !last->noprevcp && !tlast->constrained_corner ) {
450 0 : last->nextcp.x = last->me.x + (last->me.x - last->prevcp.x);
451 0 : last->nextcp.y = last->me.y + (last->me.y - last->prevcp.y);
452 : } else
453 0 : last->nonextcp = true;
454 :
455 0 : if ( tcur->online ) {
456 0 : for ( pt=tcur; !pt->use_as_pt ; pt=pt->next );
457 0 : cur->prevcp.x = cur->me.x + ( tcur->x-pt->x );
458 0 : cur->prevcp.y = cur->me.y + ( tcur->y-pt->y );
459 0 : } else if ( tcur->extremum==e_xmin || tcur->extremum==e_xmax || tcur->extremum==e_xflat ) {
460 0 : cur->prevcp.x = cur->me.x;
461 0 : cur->prevcp.y = last->me.y>cur->me.y ? cur->me.y+1 : cur->me.y-1;
462 0 : } else if ( tcur->extremum==e_ymin || tcur->extremum==e_ymax || tcur->extremum==e_yflat ) {
463 0 : cur->prevcp.y = cur->me.y;
464 0 : cur->prevcp.x = last->me.x>cur->me.x ? cur->me.x+1 : cur->me.x-1;
465 0 : } else if ( !cur->nonextcp && !tcur->constrained_corner ) { /* Only happens at end of a closed contour */
466 0 : cur->prevcp.x = cur->me.x + (cur->me.x - cur->nextcp.x);
467 0 : cur->prevcp.y = cur->me.y + (cur->me.y - cur->nextcp.y);
468 : } else
469 0 : cur->noprevcp = true;
470 0 : }
471 :
472 0 : static SplineSet *TraceCurve(CharView *cv) {
473 0 : TraceData *head = cv->freehand.head, *pt, *base, *e;
474 : SplineSet *spl;
475 : SplinePoint *last, *cur;
476 : int cnt, i, tot;
477 : TPoint *mids;
478 : double len,sofar;
479 : StrokeInfo *si;
480 :
481 : /* First we look for straight lines in the data. We will put SplinePoints */
482 : /* at their endpoints */
483 : /* Then we find places that are local extrema, or just flat in x,y */
484 : /* SplinePoints here too. */
485 : /* Then approximate splines between */
486 :
487 0 : cnt = 0;
488 0 : for ( pt=head; pt!=NULL; pt = pt->next ) {
489 0 : pt->extremum = e_none;
490 0 : pt->online = pt->wasconstrained;
491 0 : pt->use_as_pt = pt->constrained_corner;
492 : /* We recalculate x,y because we might have autoscrolled the window */
493 0 : pt->x = cv->xoff + rint(pt->here.x*cv->scale);
494 0 : pt->y = -cv->yoff + cv->height - rint(pt->here.y*cv->scale);
495 0 : pt->num = cnt++;
496 : }
497 0 : head->use_as_pt = cv->freehand.last->use_as_pt = true;
498 :
499 : /* Look for lines */
500 0 : for ( pt=head->next ; pt!=NULL; pt=pt->next )
501 0 : pt = TraceLineCheck(pt);
502 :
503 : /* Look for extremum */
504 0 : for ( pt=head->next ; pt!=NULL; pt=pt->next )
505 0 : pt->extremum = TraceIsExtremum(pt);
506 : /* Find the middle of a range of extremum points, that'll be the one we want */
507 0 : for ( base=head->next; base!=NULL; base = base->next ) {
508 0 : if ( base->extremum>=e_xmin ) {
509 0 : tot = 1;
510 0 : if ( base->extremum>=e_ymin ) {
511 0 : for ( pt=base->next ; pt->extremum>=e_ymin ; pt = pt->next ) ++tot;
512 : } else {
513 0 : for ( pt=base->next ; pt->extremum>=e_xmin && pt->extremum<e_ymin ; pt = pt->next ) ++tot;
514 : }
515 0 : tot /= 2;
516 0 : e = pt;
517 0 : for ( pt=base, i=0 ; i<tot ; pt = pt->next, ++i );
518 0 : pt->use_as_pt = true;
519 0 : base = e;
520 : }
521 : }
522 :
523 0 : TraceMassage(head,cv->freehand.last);
524 :
525 : /* Calculate the mids array */
526 0 : mids = malloc(cnt*sizeof(TPoint));
527 0 : for ( base=head; base!=NULL && base->next!=NULL; base = pt ) {
528 0 : mids[base->num].x = base->here.x;
529 0 : mids[base->num].y = base->here.y;
530 0 : mids[base->num].t = 0;
531 0 : len = 0;
532 0 : if ( base->next->online ) {
533 0 : pt = base->next; /* Don't bother to calculate the length, we won't use the data */
534 0 : continue;
535 : }
536 0 : for ( pt=base->next; ; pt=pt->next ) {
537 0 : len += sqrt((double) (
538 0 : (pt->x-pt->prev->x)*(pt->x-pt->prev->x) +
539 0 : (pt->y-pt->prev->y)*(pt->y-pt->prev->y) ));
540 0 : if ( pt->use_as_pt )
541 0 : break;
542 0 : }
543 0 : sofar = 0;
544 0 : for ( pt=base->next; ; pt=pt->next ) {
545 0 : sofar += sqrt((double) (
546 0 : (pt->x-pt->prev->x)*(pt->x-pt->prev->x) +
547 0 : (pt->y-pt->prev->y)*(pt->y-pt->prev->y) ));
548 0 : mids[pt->num].x = pt->here.x;
549 0 : mids[pt->num].y = pt->here.y;
550 0 : mids[pt->num].t = sofar/len;
551 0 : if ( pt->use_as_pt )
552 0 : break;
553 0 : }
554 : }
555 :
556 : /* Splice things together */
557 0 : spl = chunkalloc(sizeof(SplineSet));
558 0 : spl->first = last = SplinePointCreate(rint(head->here.x),rint(head->here.y));
559 0 : last->ptindex = 0;
560 :
561 0 : for ( base=head; base!=NULL && base->next!=NULL; base = pt ) {
562 0 : for ( pt=base->next; !pt->use_as_pt ; pt=pt->next );
563 0 : cur = SplinePointCreate(rint(pt->here.x),rint(pt->here.y));
564 0 : cur->ptindex = pt->num;
565 : /* even if we are order2, do everything in order3, and then convert */
566 : /* when they raise the pen */
567 0 : if ( base->next->online || base->next==pt )
568 0 : SplineMake(last,cur,false);
569 : else {
570 0 : TraceFigureCPs(last,cur,base,pt);
571 0 : if ( !last->nonextcp && !cur->noprevcp )
572 0 : ApproximateSplineFromPointsSlopes(last,cur,mids+base->num+1,pt->num-base->num-1,false);
573 : else {
574 0 : last->nonextcp = false; cur->noprevcp=false;
575 0 : ApproximateSplineFromPoints(last,cur,mids+base->num+1,pt->num-base->num-1,false);
576 : }
577 : }
578 0 : last = cur;
579 : }
580 0 : spl->last = last;
581 :
582 : /* Now we've got a rough approximation to the contour, but the joins are */
583 : /* probably not smooth. Clean things up a bit... */
584 0 : if ( spl->first->nonextcp )
585 0 : spl->first->pointtype = pt_corner;
586 : else
587 0 : spl->first->pointtype = pt_curve;
588 0 : if ( spl->last->noprevcp )
589 0 : spl->last->pointtype = pt_corner;
590 : else
591 0 : spl->last->pointtype = pt_curve;
592 :
593 0 : free(mids);
594 :
595 0 : si = CVFreeHandInfo();
596 0 : if ( si->stroke_type!=si_centerline ) {
597 0 : si->factor = ( si->pressure1==si->pressure2 ) ? NULL : Trace_Factor;
598 0 : si->data = cv;
599 0 : spl->next = SplineSetStroke(spl,si,false);
600 : }
601 0 : return( spl );
602 : }
603 :
604 0 : static void TraceDataClose(CharView *cv,GEvent *event) {
605 : TraceData *new;
606 : SplineSet *trace;
607 : double langle, hangle, llen, hlen;
608 : double dx,dy;
609 : BasePoint oldp, oldn;
610 :
611 0 : if ( cv->freehand.head==NULL )
612 0 : return; /* Eh? No points? How did that happen? */
613 0 : if ( cv->freehand.head->here.x!=cv->freehand.last->here.x ||
614 0 : cv->freehand.head->here.y!=cv->freehand.last->here.y ) {
615 0 : new = chunkalloc(sizeof(TraceData));
616 0 : *new = *cv->freehand.head;
617 0 : new->time = event->u.mouse.time;
618 0 : new->wasconstrained = (event->u.mouse.state&ksm_shift)?1:0;
619 :
620 0 : new->prev = cv->freehand.last;
621 0 : new->next = NULL;
622 0 : cv->freehand.last->next = new;
623 0 : cv->freehand.last = new;
624 0 : SplinePointListsFree(cv->freehand.current_trace);
625 0 : cv->freehand.current_trace = TraceCurve(cv);
626 0 : } else if ( cv->freehand.head == cv->freehand.last )
627 0 : return; /* Only one point, no good way to close it */
628 :
629 : /* Now the first and last points are at the same location. We need to: */
630 : /* average their control points (so that they form a smooth curve) */
631 : /* merge the two SplinePoints into one */
632 :
633 0 : trace = cv->freehand.current_trace;
634 0 : trace->first->prevcp = trace->last->prevcp;
635 0 : trace->first->noprevcp = trace->last->noprevcp;
636 0 : trace->first->prevcpdef = trace->last->prevcpdef;
637 0 : trace->first->prev = trace->last->prev;
638 0 : trace->first->prev->to = trace->first;
639 0 : SplinePointFree(trace->last);
640 0 : trace->last = trace->first;
641 :
642 0 : if ( cv->freehand.head->wasconstrained || cv->freehand.last->wasconstrained )
643 0 : return;
644 :
645 0 : trace->first->pointtype = pt_curve;
646 :
647 0 : if ( trace->first->nonextcp && trace->first->noprevcp ) {
648 0 : SplineCharDefaultPrevCP(trace->first);
649 0 : SplineCharDefaultNextCP(trace->first);
650 0 : return;
651 : }
652 :
653 0 : dx = trace->first->nextcp.x-trace->first->me.x;
654 0 : dy = trace->first->nextcp.y-trace->first->me.y;
655 0 : hlen = sqrt(dx*dx+dy*dy);
656 0 : hangle = atan2(dy,dx);
657 0 : oldn = trace->first->nextcp;
658 0 : dx = trace->first->me.x-trace->first->prevcp.x;
659 0 : dy = trace->first->me.y-trace->first->prevcp.y;
660 0 : llen = sqrt(dx*dx+dy*dy);
661 0 : langle = atan2(dy,dx);
662 0 : oldp = trace->first->prevcp;
663 :
664 0 : if ( trace->first->nonextcp ) {
665 0 : SplineCharDefaultPrevCP(trace->first);
666 0 : dx = trace->first->me.x-trace->first->prevcp.x;
667 0 : dy = trace->first->me.y-trace->first->prevcp.y;
668 0 : llen = sqrt(dx*dx+dy*dy);
669 0 : trace->first->prevcp.x = trace->first->me.x -
670 0 : cos(hangle)*llen;
671 0 : trace->first->prevcp.y = trace->first->me.y -
672 0 : sin(hangle)*llen;
673 0 : trace->first->nextcp = oldn;
674 0 : } else if ( trace->first->noprevcp ) {
675 0 : SplineCharDefaultPrevCP(trace->first);
676 0 : dx = trace->first->me.x-trace->first->nextcp.x;
677 0 : dy = trace->first->me.y-trace->first->nextcp.y;
678 0 : hlen = sqrt(dx*dx+dy*dy);
679 0 : trace->first->nextcp.x = trace->first->me.x +
680 0 : cos(langle)*hlen;
681 0 : trace->first->nextcp.y = trace->first->me.y +
682 0 : sin(langle)*hlen;
683 0 : trace->first->prevcp = oldp;
684 : } else {
685 0 : if ( hangle>3.1415926535897932/2 && langle<-3.1415926535897932/2 )
686 0 : langle += 2*3.1415926535897932;
687 0 : if ( hangle<-3.1415926535897932/2 && langle>3.1415926535897932/2 )
688 0 : hangle += 2*3.1415926535897932;
689 0 : hangle = (hangle+langle)/2;
690 0 : dx = cos(hangle);
691 0 : dy = sin(hangle);
692 0 : trace->first->prevcp.x = trace->first->me.x - dx*llen;
693 0 : trace->first->prevcp.y = trace->first->me.y - dy*llen;
694 0 : trace->first->nextcp.x = trace->first->me.x + dx*hlen;
695 0 : trace->first->nextcp.y = trace->first->me.y + dy*hlen;
696 : }
697 0 : SplineRefigure(trace->first->next);
698 0 : SplineRefigure(trace->first->prev);
699 : }
700 :
701 0 : static int TraceDataCleanup(CharView *cv) {
702 : TraceData *mid, *next;
703 0 : int cnt=0;
704 :
705 0 : for ( mid = cv->freehand.head; mid!=NULL; mid=next ) {
706 0 : ++cnt;
707 0 : next = mid->next;
708 : }
709 0 : return( cnt );
710 : }
711 :
712 0 : void CVMouseDownFreeHand(CharView *cv, GEvent *event) {
713 :
714 0 : TraceDataFree(cv->freehand.head);
715 0 : cv->freehand.head = cv->freehand.last = NULL;
716 0 : cv->freehand.current_trace = NULL;
717 0 : TraceDataFromEvent(cv,event);
718 :
719 0 : cv->freehand.current_trace = chunkalloc(sizeof(SplinePointList));
720 0 : cv->freehand.current_trace->first = cv->freehand.current_trace->last =
721 0 : SplinePointCreate(rint(cv->freehand.head->here.x),rint(cv->freehand.head->here.y));
722 0 : }
723 :
724 0 : void CVMouseMoveFreeHand(CharView *cv, GEvent *event) {
725 : double dx, dy;
726 : SplinePoint *last;
727 : BasePoint *here;
728 :
729 0 : TraceDataFromEvent(cv,event);
730 : /* I used to do the full processing here to get a path. But that took */
731 : /* too long to process them and we appeared to get events out of order */
732 : /* from the wacom tablet */
733 0 : last = cv->freehand.current_trace->last;
734 0 : here = &cv->freehand.last->here;
735 0 : if ( (dx=here->x-last->me.x)<0 ) dx = -dx;
736 0 : if ( (dy=here->y-last->me.y)<0 ) dy = -dy;
737 0 : if ( (dx+dy)*cv->scale > 4 ) {
738 0 : SplineMake3(last,SplinePointCreate(rint(here->x),rint(here->y)));
739 0 : cv->freehand.current_trace->last = last->next->to;
740 0 : GDrawRequestExpose(cv->v,NULL,false);
741 : }
742 0 : }
743 :
744 : #ifdef DEBUG_FREEHAND
745 : static void RepeatFromFile(CharView *cv) {
746 : FILE *foo = fopen("mousemove","r");
747 : /*char buffer[100];*/
748 : GEvent e;
749 : int x,y,p,t;
750 :
751 : if ( foo==NULL )
752 : return;
753 :
754 : memset(&e,0,sizeof(e));
755 :
756 : e.w = cv->v;
757 : e.type = et_mousedown;
758 : fscanf(foo,"(%d,%d) (%*g,%*g) %d %d\n", &x, &y, &p, &t);
759 : e.u.mouse.x = x; e.u.mouse.y = y; e.u.mouse.pressure = p; e.u.mouse.time = t;
760 : CVMouseDownFreeHand(cv,&e);
761 :
762 : e.type = et_mousemove;
763 : while ( fscanf(foo,"(%d,%d) (%*g,%*g) %d %d\n", &x, &y, &p, &t)==4 ) {
764 : e.u.mouse.x = x; e.u.mouse.y = y; e.u.mouse.pressure = p; e.u.mouse.time = t;
765 : CVMouseMoveFreeHand(cv,&e);
766 : }
767 :
768 : e.type = et_mouseup;
769 : CVMouseUpFreeHand(cv,&e);
770 : fclose(foo);
771 : }
772 : #endif
773 :
774 0 : void CVMouseUpFreeHand(CharView *cv, GEvent *event) {
775 0 : TraceData *head = cv->freehand.head;
776 : TraceData *last;
777 : double dx, dy;
778 :
779 : #ifdef DEBUG_FREEHAND
780 : if ( event->u.mouse.clicks>1 ) {
781 : TraceDataFree(cv->freehand.head);
782 : cv->freehand.head = cv->freehand.last = NULL;
783 : RepeatFromFile(cv);
784 : return;
785 : }
786 : #endif
787 0 : if ( head==NULL )
788 0 : return;
789 :
790 0 : if ( TraceDataCleanup(cv)>=4 ) {
791 : /* If there are fewer than 4 points then assume they made a mistake */
792 : /* (or intended a double click) and just ignore the path */
793 0 : last = cv->freehand.last;
794 0 : if ( (dx=head->x-last->x)<0 ) dx = -dx;
795 0 : if ( (dy=head->y-last->y)<0 ) dy = -dy;
796 :
797 0 : if (( event->u.chr.state&ksm_meta ) || (dx+dy)*cv->scale > 4 )
798 0 : TraceDataClose(cv,event);
799 : else {
800 0 : SplinePointListsFree(cv->freehand.current_trace);
801 0 : cv->freehand.current_trace = TraceCurve(cv);
802 : }
803 0 : if ( cv->freehand.current_trace!=NULL ) {
804 0 : CVPreserveState((CharViewBase *) cv);
805 0 : if ( cv->b.layerheads[cv->b.drawmode]->order2 )
806 0 : cv->freehand.current_trace = SplineSetsTTFApprox(cv->freehand.current_trace);
807 0 : if ( CVFreeHandInfo()->stroke_type==si_centerline ) {
808 0 : cv->freehand.current_trace->next = cv->b.layerheads[cv->b.drawmode]->splines;
809 0 : cv->b.layerheads[cv->b.drawmode]->splines = cv->freehand.current_trace;
810 : } else {
811 0 : SplineSet *ss = cv->freehand.current_trace;
812 0 : while ( ss->next!=NULL )
813 0 : ss = ss->next;
814 0 : ss->next = cv->b.layerheads[cv->b.drawmode]->splines;
815 0 : cv->b.layerheads[cv->b.drawmode]->splines = cv->freehand.current_trace->next;
816 0 : cv->freehand.current_trace->next = NULL;
817 : /*SplinePointListsFree(cv->freehand.current_trace);*/
818 : }
819 0 : cv->freehand.current_trace = NULL;
820 : }
821 : } else {
822 0 : SplinePointListsFree(cv->freehand.current_trace);
823 0 : cv->freehand.current_trace = NULL;
824 : }
825 0 : TraceDataFree(cv->freehand.head);
826 0 : cv->freehand.head = cv->freehand.last = NULL;
827 0 : CVCharChangedUpdate(&cv->b);
828 : #ifdef DEBUG_FREEHAND
829 : fflush( stdout );
830 : #endif
831 : }
|