1 /* Copyright (C) 2002-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 "fvfonts.h"
33 #include "gkeysym.h"
34 #include "splinefit.h"
35 #include "splinefont.h"
36 #include "splineutil.h"
37 #include "splineutil2.h"
38 
39 #include <math.h>
40 
41 #ifdef FONTFORGE_CONFIG_TILEPATH
42 /* Given a path and a splineset */
43 /* Treat the splineset as a tile and lay it down on the path until we reach the*/
44 /*  end of the path */
45 /* More precisely, find the length of the path */
46 /* Find the height of the tile */
47 /* We'll need length/height tiles */
48 /* For each tile */
49 /*  For a point on the central (in x) axis of the tile */
50 /*   Use its y-position to figure out how far along the path we are ( y-pos/length ) */
51 /*   Then this point should be moved to exactly that point */
52 /*  For a point off the central axis */
53 /*   Perform the above calculation, and */
54 /*   Find the normal vector to the path */
55 /*   Our new location should be: */
56 /*	the location found above + our xoffset * (normal vector) */
57 /*  Do that for a lot of points on each spline of the tile and then */
58 /*   use approximate spline from points to find the new splines */
59 /* Complications: */
60 /*  There may not be an integral number of tiles, so we must be prepared to truncate some splines */
61 
62 typedef struct tiledata {
63     SplineSet *basetile;	/* Moved so that ymin==0, and x is adjusted */
64 				/*  about the x-axis as implied by tilepos */
65     SplineSet *firsttile;
66     SplineSet *lasttile;
67     SplineSet *isolatedtile;
68     SplineSet *tileset;		/* As many copies of the basetile as we are */
69 				/*  going to need. Each successive one bb.maxy */
70 			        /*  higher than the last */
71     SplineSet *result;		/* Final result after transformation */
72     DBounds bb, fbb, lbb, ibb;	/* Of the basetile, first & last tiles */
73     uint8 include_white, finclude_white, linclude_white, iinclude_white;
74     bigreal xscale[3];
75 
76     SplineSet *path;
77     bigreal plength;		/* Length of path */
78     int pcnt;			/* Number of splines in path */
79     int nsamples;
80     struct tdsample {
81 	real dx, dy;		/* offset from path->first->me */
82 	real c,s;		/* cos/sin of normal vector pointing right of path */
83     } *samples;			/* an array of [nsamples+1] actually */
84     int njoins;
85     struct jsample {
86 	real dx, dy;
87 	real c1,s1;
88 	real c2,s2;
89 	real sofar;
90     } *joins;			/* an array of [pcnt or pcnt-1], one of each join */
91 
92     enum tilepos { tp_left, tp_center, tp_right } tilepos;
93     enum tilescale { ts_tile, ts_tilescale, ts_scale } tilescale;
94     /* ts_scale means that we scale the one tile until it height is the same */
95     /*	 as plength */
96     /* ts_tile means that we lay down as many tiles as we need so that */
97     /*	 n*tile-height == plength. Note: n need not be an integer, so we */
98     /*   may be an incomplete tile => incomplete splines (a spline may even */
99     /*   get cut so that it becomes two splines) */
100     /* ts_tilescale means that we find n = floor(plength/tile-height) and */
101     /*	 scale = plength/(n*tile-height). We scale the tile by "scale", and */
102     /*   then lay down n of them */
103 
104     int doallpaths;
105 
106     BasePoint patternSize;		/* Last 3 fields used only for patterns */
107     IPoint repeatCnt;			/*  And not for tilepath */
108     Layer *pattern;
109 } TD;
110 enum whitespace_type { ws_include=0x1, ws_but_not_first=0x2 };
111 
TDMakeSamples(TD * td)112 static int TDMakeSamples(TD *td) {
113     Spline *spline, *first;
114     bigreal len, slen, sofar, t, toff, dt_per_sample;
115     int i,end, base, pcnt;
116     bigreal sx, sy, angle;
117 
118     first = NULL; len = 0; pcnt = 0;
119     for ( spline=td->path->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
120 	if ( first==NULL ) first = spline;
121 	len += SplineLength(spline);
122 	++pcnt;
123     }
124     if ( len==0 )
125 return( false );
126     td->plength = len;
127     td->pcnt = pcnt;
128 
129     td->nsamples = ceil(len)+10;
130     td->samples = malloc((td->nsamples+1)*sizeof(struct tdsample));
131     td->joins = malloc(td->pcnt*sizeof(struct jsample));
132 
133     i = 0; pcnt = 0;
134     first = NULL; sofar = 0;
135     for ( spline=td->path->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
136 	if ( first==NULL ) first = spline;
137 	slen = SplineLength(spline);
138 	/* I'm assuming that length is approximately linear in t */
139 	toff = (i - td->nsamples*sofar/len)/slen;
140 	base = i;
141 	end = floor(td->nsamples*(sofar+slen)/len);
142 	dt_per_sample = end==i?1:(1.0-toff)/(end-i);
143 	if ( spline->to->next==NULL || spline->to->next==first )
144 	    end = td->nsamples;
145 	while ( i<=end ) {
146 	    t = toff + (i-base)*dt_per_sample;
147 	    if ( i==td->nsamples || t>1 ) t = 1;
148 	    td->samples[i].dx = ((spline->splines[0].a*t+spline->splines[0].b)*t+spline->splines[0].c)*t + spline->splines[0].d /*-
149 		    td->path->first->me.x*/;
150 	    td->samples[i].dy = ((spline->splines[1].a*t+spline->splines[1].b)*t+spline->splines[1].c)*t + spline->splines[1].d /*-
151 		    td->path->first->me.y*/;
152 	    sx = (3*spline->splines[0].a*t+2*spline->splines[0].b)*t+spline->splines[0].c;
153 	    sy = (3*spline->splines[1].a*t+2*spline->splines[1].b)*t+spline->splines[1].c;
154 	    if ( sx==0 && sy==0 ) {
155 		sx = spline->to->me.x - spline->from->me.x;
156 		sy = spline->to->me.y - spline->from->me.y;
157 	    }
158 	    angle = atan2(sy,sx) - FF_PI/2;
159 	    td->samples[i].c = cos(angle);
160 	    td->samples[i].s = sin(angle);
161 	    if ( td->samples[i].s>-.00001 && td->samples[i].s<.00001 ) { td->samples[i].s=0; td->samples[i].c = ( td->samples[i].c>0 )? 1 : -1; }
162 	    if ( td->samples[i].c>-.00001 && td->samples[i].c<.00001 ) { td->samples[i].c=0; td->samples[i].s = ( td->samples[i].s>0 )? 1 : -1; }
163 	    ++i;
164 	}
165 	sofar += slen;
166 	if (( pcnt<td->pcnt-1 || td->path->first==td->path->last ) &&
167 		spline->to->next!=NULL &&
168 		!((spline->to->pointtype==pt_curve && !spline->to->nonextcp && !spline->to->noprevcp) ||
169 		  (spline->to->pointtype==pt_hvcurve && !spline->to->nonextcp && !spline->to->noprevcp) ||
170 		  (spline->to->pointtype==pt_tangent && spline->to->nonextcp+spline->to->noprevcp==1 )) ) {
171 	    /* We aren't interested in joins where the two splines are tangent */
172 	    Spline *next = spline->to->next;
173 	    td->joins[pcnt].sofar = sofar;
174 	    td->joins[pcnt].dx = spline->to->me.x;
175 	    td->joins[pcnt].dy = spline->to->me.y;
176 	    /* there are two normals at a join, one for each spline */
177 	    /*  it should bisect the normal vectors of the two splines */
178 	    sx = next->splines[0].c; sy = next->splines[1].c;
179 	    angle = atan2(sy,sx) - FF_PI/2;
180 	    td->joins[pcnt].c1 = cos(angle);
181 	    td->joins[pcnt].s1 = sin(angle);
182 	    if ( td->joins[pcnt].s1>-.00001 && td->joins[pcnt].s1<.00001 ) { td->joins[pcnt].s1=0; td->joins[pcnt].c1 = ( td->joins[pcnt].c1>0 )? 1 : -1; }
183 	    if ( td->joins[pcnt].c1>-.00001 && td->joins[pcnt].c1<.00001 ) { td->joins[pcnt].c1=0; td->joins[pcnt].s1 = ( td->joins[pcnt].s1>0 )? 1 : -1; }
184 
185 	    sx = (3*spline->splines[0].a+2*spline->splines[0].b)+spline->splines[0].c;
186 	    sy = (3*spline->splines[1].a+2*spline->splines[1].b)+spline->splines[1].c;
187 	    angle = atan2(sy,sx) - FF_PI/2;
188 	    td->joins[pcnt].c2 = cos(angle);
189 	    td->joins[pcnt].s2 = sin(angle);
190 	    if ( td->joins[pcnt].s2>-.00001 && td->joins[pcnt].s2<.00001 ) { td->joins[pcnt].s2=0; td->joins[pcnt].c2 = ( td->joins[pcnt].c2>0 )? 1 : -1; }
191 	    if ( td->joins[pcnt].c2>-.00001 && td->joins[pcnt].c2<.00001 ) { td->joins[pcnt].c2=0; td->joins[pcnt].s2 = ( td->joins[pcnt].s2>0 )? 1 : -1; }
192 	    ++pcnt;
193 	}
194     }
195     td->njoins = pcnt;
196     if ( i!=td->nsamples+1 )
197 	IError("Sample failure %d is not %d", i, td->samples+1 );
198 return( true );
199 }
200 
TDAddPoints(TD * td)201 static void TDAddPoints(TD *td) {
202     /* Insert additional points in the tileset roughly at the locations */
203     /*  corresponding to the ends of the splines in the path */
204     SplineSet *spl;
205     Spline *spline, *first, *tsp;
206     bigreal len;
207     bigreal ts[3];
208 
209     first = NULL; len = 0;
210     for ( spline=td->path->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
211 	if ( first==NULL ) first = spline;
212 	if ( spline->to->next==NULL || spline->to->next==first )
213     break;
214 	len += SplineLength(spline);
215 
216 	for ( spl=td->tileset; spl!=NULL; spl=spl->next ) {
217 	    for ( tsp=spl->first->next; tsp!=NULL ; tsp = tsp->to->next ) {
218 		if ( RealApprox(tsp->to->me.y,len) || RealApprox(tsp->from->me.y,len))
219 		    /* Do Nothing, already broken here */;
220 		else if ( (tsp->to->me.y>len || tsp->to->prevcp.y>len || tsp->from->me.y>len || tsp->from->nextcp.y>len) &&
221 			  (tsp->to->me.y<len || tsp->to->prevcp.y<len || tsp->from->me.y<len || tsp->from->nextcp.y<len) &&
222 			  CubicSolve(&tsp->splines[1],len,ts) ) {
223 		    SplinePoint *mid = SplineBisect(tsp,ts[0]);
224 		    tsp = mid->next;
225 		}
226 		if ( tsp->to == spl->first )
227 	    break;
228 	    }
229 	}
230     }
231 }
232 
SplineSplitAtY(Spline * spline,real y)233 static void SplineSplitAtY(Spline *spline,real y) {
234     bigreal ts[3];
235     SplinePoint *last;
236 
237     if ( spline->from->me.y<=y && spline->from->nextcp.y<=y &&
238 	    spline->to->me.y<=y && spline->to->prevcp.y<=y )
239 return;
240     if ( spline->from->me.y>=y && spline->from->nextcp.y>=y &&
241 	    spline->to->me.y>=y && spline->to->prevcp.y>=y )
242 return;
243 
244 
245     if ( !CubicSolve(&spline->splines[1],y,ts) )
246 return;
247 
248     last = spline->to;
249     spline = SplineSplit(spline,ts);
250     while ( spline->to!=last ) {
251 	if ( spline->to->me.y!=y ) {
252 	    real diff = y-spline->to->me.y;
253 	    spline->to->me.y = y;
254 	    spline->to->prevcp.y += diff;
255 	    spline->to->nextcp.y += diff;
256 	    SplineRefigure(spline); SplineRefigure(spline->to->next);
257 	}
258 	spline = spline->to->next;
259     }
260 }
261 
_SplinesRemoveBetween(Spline * spline,Spline * beyond,SplineSet * spl)262 static void _SplinesRemoveBetween( Spline *spline, Spline *beyond, SplineSet *spl ) {
263     Spline *next;
264 
265     while ( spline!=NULL && spline!=beyond ) {
266 	next = spline->to->next;
267 	if ( spline->from!=spl->last && spline->from!=spl->first )
268 	    SplinePointFree(spline->from);
269 	SplineFree(spline);
270 	spline = next;
271     }
272 }
273 
SplinePointListTruncateAtY(SplineSet * spl,real y)274 static SplineSet *SplinePointListTruncateAtY(SplineSet *spl,real y) {
275     SplineSet *prev=NULL, *ss=spl, *nprev, *snext, *ns;
276     Spline *spline, *next;
277 
278     for ( ; spl!=NULL; spl = spl->next ) {
279 	for ( spline=spl->first->next ; spline!=NULL; spline = next ) {
280 	    next = spline->to->next;
281 	    SplineSplitAtY(spline,y);
282 	    if ( next==NULL || next->from == spl->last )
283 	break;
284 	}
285     }
286 
287     prev = NULL;
288     y += 1/128.0;		/* Small fudge factor for rounding errors */
289     for ( spl=ss; spl!=NULL; spl = snext ) {
290 	snext = spl->next;
291 	nprev = spl;
292 	for ( spline=spl->first->next ; spline!=NULL ; spline = next ) {
293 	    next = spline->to->next;
294 	    if ( spline->from->me.y<=y && spline->from->nextcp.y<=y &&
295 		    spline->to->me.y<=y && spline->to->prevcp.y<=y ) {
296 		if ( spline->to==spl->first )
297 	break;
298 		else
299 	continue;
300 	    }
301 	    /* Remove this spline */
302 	    while ( next!=NULL && next->from!=spl->first &&
303 		    (next->from->me.y>y || next->from->nextcp.y>y ||
304 		     next->to->me.y>y || next->to->prevcp.y>y) )
305 		next = next->to->next;
306 	    if ( next==NULL || next->from==spl->first ) {
307 		/* The area to be removed continues to the end of splineset */
308 		if ( spline==spl->first->next ) {
309 		    /* Remove entire splineset */
310 		    if ( prev==NULL )
311 			ss = snext;
312 		    else
313 			prev->next = snext;
314 		    SplinePointListFree(spl);
315 		    nprev = prev;
316 	break;
317 		}
318 		spl->last = spline->from;
319 		spline->from->next = NULL;
320 		spl->first->prev = NULL;
321 		_SplinesRemoveBetween(spline,next,spl);
322 	break;
323 	    } else {
324 		if ( spline==spl->first->next ) {
325 		    /* Remove everything before next */
326 		    next->from->prev = NULL;
327 		    spl->first->next = NULL;
328 		    spl->first = next->from;
329 		} else if ( spl->first==spl->last ) {
330 		    /* rotate splineset so break is at start and end. */
331 		    spl->last = spline->from;
332 		    spl->first = next->from;
333 		    next->from->prev = NULL;
334 		    spline->from->next = NULL;
335 		} else {
336 		    /* Split into two splinesets and remove all between */
337 		    ns = chunkalloc(sizeof(SplineSet));
338 		    ns->first = next->from;
339 		    ns->last = spl->last;
340 		    spl->last = spline->from;
341 		    spline->from->next = NULL;
342 		    spl->first->prev = NULL;
343 		    next->from->prev = NULL;
344 		    ns->last->next = NULL;
345 		    ns->next = spl->next;
346 		    spl->next = ns->next;
347 		    nprev = ns;
348 		}
349 		_SplinesRemoveBetween(spline,next,spl);
350 		spl = nprev;
351 	    }
352 	}
353 	prev = nprev;
354     }
355 return( ss );
356 }
357 
SplinePointListMerge(SplineSet * old,SplineSet * new)358 static SplineSet *SplinePointListMerge(SplineSet *old,SplineSet *new) {
359     /* Merge the new splineset into the old looking for any endpoints */
360     /*  common to both, and if any are found, merging them */
361     SplineSet *test1, *next;
362     SplineSet *oldold = old;
363 
364     while ( new!=NULL ) {
365 	next = new->next;
366 	if ( new->first!=new->last ) {
367 	    for ( test1=oldold; test1!=NULL; test1=test1->next ) {
368 		if ( test1->first!=test1->last &&
369 			((test1->first->me.x==new->first->me.x && test1->first->me.y==new->first->me.y) ||
370 			 (test1->last->me.x==new->first->me.x && test1->last->me.y==new->first->me.y) ||
371 			 (test1->first->me.x==new->last->me.x && test1->first->me.y==new->last->me.y) ||
372 			 (test1->last->me.x==new->last->me.x && test1->last->me.y==new->last->me.y)) )
373 		    break;
374 	    }
375 	    if ( test1!=NULL ) {
376 		if ((test1->first->me.x==new->first->me.x && test1->first->me.y==new->first->me.y) ||
377 			(test1->last->me.x==new->last->me.x && test1->last->me.y==new->last->me.y))
378 		    SplineSetReverse(new);
379 		if ( test1->last->me.x==new->first->me.x && test1->last->me.y==new->first->me.y ) {
380 		    test1->last->nextcp = new->first->nextcp;
381 		    test1->last->nonextcp = new->first->nonextcp;
382 		    test1->last->nextcpdef = new->first->nextcpdef;
383 		    test1->last->next = new->first->next;
384 		    new->first->next->from = test1->last;
385 		    test1->last = new->last;
386 		    SplinePointFree(new->first);
387 		    new->first = new->last = NULL;
388 		    SplinePointListFree(new);
389 		    if ( test1->last->me.x == test1->first->me.x &&
390 			    test1->last->me.y == test1->first->me.y ) {
391 			test1->first->prevcp = test1->last->prevcp;
392 			test1->first->noprevcp = test1->last->noprevcp;
393 			test1->first->prevcpdef = test1->last->prevcpdef;
394 			test1->last->prev->to = test1->first;
395 			SplinePointFree(test1->last);
396 			test1->last = test1->first;
397 		    }
398 		} else {
399 		    test1->first->prevcp = new->last->prevcp;
400 		    test1->first->noprevcp = new->last->noprevcp;
401 		    test1->first->prevcpdef = new->last->prevcpdef;
402 		    test1->first->prev = new->last->prev;
403 		    new->last->prev->to = test1->first;
404 		    test1->first = new->first;
405 		    SplinePointFree(new->last);
406 		    new->first = new->last = NULL;
407 		    SplinePointListFree(new);
408 		}
409 		new = next;
410     continue;
411 	    }
412 	}
413 	new->next = old;
414 	old = new;
415 	new = next;
416     }
417 return( old );
418 }
419 
420 #define Round_Up_At	.5
TileLine(TD * td)421 static void TileLine(TD *td) {
422     int tilecnt=1, i;
423     bigreal scale=1, y;
424     real trans[6];
425     SplineSet *new;
426     int use_first=false, use_last=false, use_isolated=false;
427 
428     switch ( td->tilescale ) {
429       case ts_tile:
430 	if ( td->path->first->prev!=NULL )	/* Closed contours have no ends => all tiles intermediate */
431 	    tilecnt = ceil( td->plength/td->bb.maxy );
432 	else if ( td->plength<=td->ibb.maxy ) {
433 	    tilecnt = 1;
434 	    use_isolated = true;
435 	} else if ( td->plength<=td->fbb.maxy ) {
436 	    tilecnt = 1;
437 	    use_first = true;
438 	} else if ( td->plength<=td->fbb.maxy+td->lbb.maxy && td->firsttile!=NULL && td->lasttile!=NULL ) {
439 	    tilecnt = 2;
440 	    use_first = use_last = true;
441 	} else {
442 	    use_first = (td->firsttile!=NULL);
443 	    use_last = (td->lasttile!=NULL);
444 	    tilecnt = use_first+use_last+ceil( (td->plength-td->fbb.maxy-td->lbb.maxy)/td->bb.maxy );
445 	}
446       break;
447       case ts_scale:
448 	if ( td->isolatedtile!=NULL ) {
449 	    use_isolated = true;
450 	    scale = td->plength/td->ibb.maxy;
451 	} else
452 	    scale = td->plength/td->bb.maxy;
453       break;
454       case ts_tilescale:
455 	tilecnt = -1;
456 	if ( td->path->first->prev!=NULL )	/* Closed contours have no ends => all tiles intermediate */
457 	    scale = td->plength/td->bb.maxy;
458 	else if ( td->isolatedtile!=NULL &&
459 		(( td->firsttile!=NULL && td->lasttile!=NULL && td->plength<td->fbb.maxy+Round_Up_At*td->lbb.maxy) ||
460 		 ( td->firsttile!=NULL && td->lasttile==NULL && td->plength<td->fbb.maxy+Round_Up_At*td->bb.maxy) ||
461 		 ( td->firsttile==NULL && td->lasttile==NULL && td->plength<(1+Round_Up_At)*td->bb.maxy)) ) {
462 	    use_isolated = true;
463 	    scale = td->plength/td->ibb.maxy;
464 	    tilecnt = 1;
465 	} else if ( td->firsttile!=NULL && td->lasttile!=NULL ) {
466 	    if ( td->plength<td->fbb.maxy+Round_Up_At*td->lbb.maxy ) {
467 		use_first = true;
468 		tilecnt = 1;
469 		scale = td->plength/td->fbb.maxy;
470 	    } else if ( td->plength<td->fbb.maxy+td->lbb.maxy+Round_Up_At*td->bb.maxy ) {
471 		use_first = use_last = true;
472 		tilecnt = 2;
473 		scale = 2*td->plength/(td->fbb.maxy+td->lbb.maxy);
474 	    } else {
475 		use_first = use_last = true;
476 		scale = 2 + (td->plength-td->fbb.maxy-td->lbb.maxy)/td->bb.maxy;
477 	    }
478 	} else if ( td->firsttile!=NULL ) {
479 	    if ( td->plength<td->fbb.maxy+Round_Up_At*td->bb.maxy ) {
480 		use_first = true;
481 		tilecnt = 1;
482 		scale = td->plength/td->fbb.maxy;
483 	    } else {
484 		use_first = true;
485 		scale = 1 + (td->plength-td->fbb.maxy)/td->bb.maxy;
486 	    }
487 	} else if ( td->lasttile!=NULL ) {
488 	    if ( td->plength<td->lbb.maxy+Round_Up_At*td->bb.maxy ) {
489 		use_last = true;
490 		tilecnt = 1;
491 		scale = td->plength/td->lbb.maxy;
492 	    } else {
493 		use_last = true;
494 		scale = 1 + (td->plength-td->lbb.maxy)/td->bb.maxy;
495 	    }
496 	} else
497 	    scale = td->plength/td->bb.maxy;
498 	if ( tilecnt == -1 ) {
499 	    tilecnt = floor( scale );
500 	    if ( tilecnt==0 )
501 		tilecnt = 1;
502 	    else if ( scale-tilecnt>Round_Up_At )
503 		++tilecnt;
504 	    scale = td->plength/(use_first*td->fbb.maxy + use_last*td->lbb.maxy +
505 			(tilecnt-use_first-use_last)*td->bb.maxy);
506 	}
507       break;
508     }
509 
510     trans[0] = 1; trans[3] = scale;		/* Only scale y */
511     trans[1] = trans[2] = trans[4] = trans[5] = 0;
512     y = 0;
513     for ( i=0; i<tilecnt; ++i ) {
514 	int which = (i==0 && use_first) ? 1 :
515 		    (i==0 && use_isolated ) ? 3 :
516 		    (i==tilecnt-1 && use_last ) ? 2 :
517 			    0;
518 	new = SplinePointListCopy((&td->basetile)[which]);
519 	trans[5] = y;
520 	new = SplinePointListTransform(new,trans,tpt_AllPoints);
521 	if ( i==tilecnt-1 && td->tilescale==ts_tile )
522 	    new = SplinePointListTruncateAtY(new,td->plength);
523 	td->tileset = SplinePointListMerge(td->tileset,new);
524 	y += (&td->bb)[which].maxy*scale;
525     }
526     if ( td->pcnt>1 ) {
527 	/* If there are fewer tiles than there are spline elements, then we */
528 	/*  may not be able to do a good job approximating (suppose the path */
529 	/*  draws a circle, but there is just one tile, a straight line. */
530 	/*  without some extra points in the middle of that line there is no */
531 	/*  way to make a circle). So here we add some extra breaks */
532 	/* Actually, it's worse than that. If the transition isn't smooth */
533 	/*  then we'll always want those extra points... */
534 	TDAddPoints(td);
535     }
536 }
537 
AdjustPoint(TD * td,Spline * spline,bigreal t,FitPoint * to)538 static void AdjustPoint(TD *td,Spline *spline,bigreal t, FitPoint *to) {
539     bigreal x, y;
540     bigreal pos;
541     int low;
542     bigreal dx, dy, c, s;
543     int i;
544 
545     to->t = t;
546 
547     x = ((spline->splines[0].a*t+spline->splines[0].b)*t+spline->splines[0].c)*t + spline->splines[0].d;
548     y = ((spline->splines[1].a*t+spline->splines[1].b)*t+spline->splines[1].c)*t + spline->splines[1].d;
549 
550     for ( i=td->pcnt-2; i>=0; --i )
551 	if ( RealNearish(y,td->joins[i].sofar) )
552     break;
553     if ( i>=0 ) {
554 	bigreal x1,y1, x2, y2, dx1, dx2, dy1, dy2;
555 	x1 = td->joins[i].dx + td->joins[i].c1*x;
556 	y1 = td->joins[i].dy + td->joins[i].s1*x;
557 	dx1 = -td->joins[i].s1;
558 	dy1 = td->joins[i].c1;
559 
560 	x2 = td->joins[i].dx + td->joins[i].c2*x;
561 	y2 = td->joins[i].dy + td->joins[i].s2*x;
562 	dx2 = -td->joins[i].s2;
563 	dy2 = td->joins[i].c2;
564 	/* there are two lines at a join and I need to find the intersection */
565 	if ( dy2>-.00001 && dy2<.00001 ) {
566 	    to->p.y = y2;
567 	    if ( dy1>-.00001 && dy1<.00001 )	/* essentially parallel */
568 		to->p.x = x2;
569 	    else
570 		to->p.x = x1 + dx1*(y2-y1)/dy1;
571 	} else {
572 	    bigreal s=(dy1*dx2/dy2-dx1);
573 	    if ( s>-.00001 && s<.00001 ) {	/* essentially parallel */
574 		to->p.x = x1; to->p.y = y1;
575 	    } else {
576 		bigreal t1 = (x1-x2- dx2/dy2*(y1-y2))/s;
577 		to->p.x = x1 + dx1*t1;
578 		to->p.y = y1 + dy1*t1;
579 	    }
580 	}
581     } else {
582 	pos = y/td->plength;
583 	if ( pos<0 ) pos=0;		/* should not happen */
584 	if ( pos>1 ) pos = 1;
585 
586 	pos *= td->nsamples;
587 	low = floor(pos);
588 	pos -= low;
589 
590 	if ( pos==0 || low==td->nsamples ) {
591 	    dx = td->samples[low].dx;
592 	    dy = td->samples[low].dy;
593 	    c = td->samples[low].c;
594 	    s = td->samples[low].s;
595 	} else {
596 	    dx = (td->samples[low].dx*(1-pos) + td->samples[low+1].dx*pos);
597 	    dy = (td->samples[low].dy*(1-pos) + td->samples[low+1].dy*pos);
598 	    c = (td->samples[low].c*(1-pos) + td->samples[low+1].c*pos);
599 	    s = (td->samples[low].s*(1-pos) + td->samples[low+1].s*pos);
600 	}
601 
602 	to->p.x = dx + c*x;
603 	to->p.y = dy + s*x;
604     }
605 }
606 
TDMakePoint(TD * td,Spline * old,real t)607 static SplinePoint *TDMakePoint(TD *td,Spline *old,real t) {
608     FitPoint fp;
609     SplinePoint *new;
610 
611     AdjustPoint(td,old,t,&fp);
612     new = chunkalloc(sizeof(SplinePoint));
613     new->me.x = fp.p.x; new->me.y = fp.p.y;
614     new->nextcp = new->me;
615     new->prevcp = new->me;
616     new->nonextcp = new->noprevcp = true;
617     new->nextcpdef = new->prevcpdef = false;
618 return( new );
619 }
620 
AdjustSpline(TD * td,Spline * old,SplinePoint * newfrom,SplinePoint * newto,int order2)621 static Spline *AdjustSpline(TD *td,Spline *old,SplinePoint *newfrom,SplinePoint *newto,
622 	int order2) {
623     FitPoint fps[15];
624     int i;
625     bigreal t;
626 
627     if ( newfrom==NULL )
628 	newfrom = TDMakePoint(td,old,0);
629     if ( newto==NULL )
630 	newto = TDMakePoint(td,old,1);
631     for ( i=1, t=1/16.0; i<16; ++i, t+= 1/16.0 )
632 	AdjustPoint(td,old,t,&fps[i-1]);
633 return( ApproximateSplineFromPoints(newfrom,newto,fps,15, order2) );
634 }
635 
AdjustSplineSet(TD * td,int order2)636 static void AdjustSplineSet(TD *td,int order2) {
637     SplineSet *spl, *last=NULL, *new;
638     Spline *spline, *s;
639     SplinePoint *lastsp, *nextsp, *sp;
640 
641     if ( td->result!=NULL )
642 	for ( last=td->result ; last->next!=NULL; last = last->next );
643 
644     for ( spl=td->tileset; spl!=NULL; spl=spl->next ) {
645 	new = chunkalloc(sizeof(SplineSet));
646 	if ( last==NULL )
647 	    td->result = new;
648 	else
649 	    last->next = new;
650 	last = new;
651 	new->first = lastsp = TDMakePoint(td,spl->first->next,0);
652 	nextsp = NULL;
653 	for ( spline=spl->first->next; spline!=NULL; spline=spline->to->next ) {
654 	    if ( spline->to==spl->first )
655 		nextsp = new->first;
656 	    s = AdjustSpline(td,spline,lastsp,nextsp,order2);
657 	    lastsp = s->to;
658 	    if ( nextsp!=NULL )
659 	break;
660 	}
661 	if ( lastsp!=new->first &&
662 		RealNearish(lastsp->me.x,new->first->me.x) &&
663 		RealNearish(lastsp->me.y,new->first->me.y) ) {
664 	    new->first->prev = lastsp->prev;
665 	    new->first->prevcp = lastsp->prevcp;
666 	    new->first->noprevcp = lastsp->noprevcp;
667 	    new->first->prevcpdef = lastsp->prevcpdef;
668 	    lastsp->prev->to = new->first;
669 	    new->last = new->first;
670 	    SplinePointFree(lastsp);
671 	} else
672 	    new->last = lastsp;
673 
674 	for ( sp = new->first; sp!=NULL; ) {
675 	    SplinePointCategorize(sp);
676 	    if ( sp->next==NULL )
677 	break;
678 	    sp = sp->next->to;
679 	    if ( sp==new->first )
680 	break;
681 	}
682     }
683 }
684 
TileSplineSets(TD * td,SplineSet ** head,int order2)685 static void TileSplineSets(TD *td,SplineSet **head,int order2) {
686     SplineSet *prev=NULL, *spl, *next;
687 
688     for ( spl = *head; spl!=NULL; spl = next ) {
689 	next = spl->next;
690 	if ( td->doallpaths || PointListIsSelected(spl)) {
691 	    if ( prev==NULL )
692 		*head = next;
693 	    else
694 		prev->next = next;
695 	    td->path = spl;
696 	    if ( TDMakeSamples(td)) {
697 		TileLine(td);
698 		AdjustSplineSet(td,order2);
699 		free( td->samples );
700 		free( td->joins );
701 		SplinePointListsFree(td->tileset);
702 	    }
703 	    SplinePointListFree(td->path);
704 	    td->path = td->tileset = NULL;
705 	} else
706 	    prev = spl;
707     }
708     SPLCategorizePoints(td->result);
709     if ( *head==NULL )
710 	*head = td->result;
711     else {
712 	for ( spl= *head; spl->next!=NULL; spl = spl->next );
713 	spl->next = td->result;
714     }
715 }
716 
TileIt(SplineSet ** head,struct tiledata * td,int doall,int order2)717 static void TileIt(SplineSet **head,struct tiledata *td,
718 	int doall,int order2) {
719     real trans[6];
720     int i;
721     SplineSet *thistile;
722 
723     td->doallpaths = doall;
724 
725     trans[0] = trans[3] = 1;
726     trans[1] = trans[2] = 0;
727     for ( i=0; i<4; ++i ) if ( (thistile = (&td->basetile)[i])!=NULL ) {
728 	DBounds *bb = &(&td->bb)[i];
729 	SplineSetFindBounds(thistile,bb);
730 	trans[5] = (&td->include_white)[i]&ws_include ? 0 : -bb->miny;
731 	trans[4] = -bb->minx;
732 	if ( td->tilepos==tp_center )
733 	    trans[4] -= (bb->maxx-bb->minx)/2;
734 	else if ( td->tilepos==tp_left )
735 	    trans[4] = -bb->maxx;
736 	if ( trans[4]!=0 || trans[5]!=0 )
737 	    SplinePointListTransform(thistile,trans,tpt_AllPoints);
738 	SplineSetFindBounds(thistile,bb);
739     }
740     td->tileset = td->result = NULL;
741 
742     TileSplineSets(td,head,order2);
743 }
744 
745 static enum tilepos tilepos=tp_center;
746 static enum tilescale tilescale=ts_tilescale;
747 static int include_whitespace[4] = {0,0,0,0};
748 static SplineSet *last_tiles[4];
749 
750 #define CID_Center	1001
751 #define CID_Left	1002
752 #define CID_Right	1003
753 #define	CID_Tile	1011
754 #define CID_TileScale	1012
755 #define CID_Scale	1013
756 #define CID_IncludeWhiteSpaceBelowTile	1021	/* +[0...3] */
757 #define CID_FirstTile	1025			/* +[0...3] for the other tiles */
758 
TPDSubResize(TilePathDlg * tpd,GEvent * event)759 static void TPDSubResize(TilePathDlg *tpd, GEvent *event) {
760     int width, height;
761     int i;
762 
763     if ( !event->u.resize.sized )
764 return;
765 
766     width = event->u.resize.size.width;
767     height = event->u.resize.size.height;
768     if ( width!=tpd->cv_width || height!=tpd->cv_height ) {
769 	tpd->cv_width = width; tpd->cv_height = height;
770 	for ( i=0; i<4; ++i ) {
771 	    CharView *cv = (&tpd->cv_first)+i;
772 	    GDrawResize(cv->gw,width,height);
773 	}
774     }
775 
776     GDrawSync(NULL);
777     GDrawProcessPendingEvents(NULL);
778 }
779 
780 static char *tilenames[] = { N_("First"), N_("Medial"), N_("Final"), N_("Isolated") };
TPDDraw(TilePathDlg * tpd,GWindow pixmap,GEvent * event)781 static void TPDDraw(TilePathDlg *tpd, GWindow pixmap, GEvent *event) {
782     GRect r,pos;
783     int i;
784 
785     GDrawSetLineWidth(pixmap,0);
786     for ( i=0; i<4; ++i ) {
787 	CharView *cv = (&tpd->cv_first)+i;
788 
789 	GGadgetGetSize(GWidgetGetControl(tpd->gw,CID_FirstTile+i),&pos);
790 	r.x = pos.x; r.y = pos.y-1;
791 	r.width = pos.width+1; r.height = pos.height+1;
792 	GDrawDrawRect(pixmap,&r,0);
793 
794 	GDrawSetFont(pixmap,cv->inactive ? tpd->plain : tpd->bold);
795 	GDrawDrawText8(pixmap,r.x,pos.y-2-tpd->fh+tpd->as,_(tilenames[i]),-1,0);
796     }
797 }
798 
TPDMakeActive(TilePathDlg * tpd,CharView * cv)799 static void TPDMakeActive(TilePathDlg *tpd,CharView *cv) {
800     int i;
801 
802     if ( tpd==NULL )
803 return;
804     for ( i=0; i<4; ++i )
805 	(&tpd->cv_first)[i].inactive = true;
806     cv->inactive = false;
807     GDrawSetUserData(tpd->gw,cv);
808     for ( i=0; i<4; ++i )
809 	GDrawRequestExpose((&tpd->cv_first)[i].v,NULL,false);
810     GDrawRequestExpose(tpd->gw,NULL,false);
811 }
812 
TPDChar(TilePathDlg * tpd,GEvent * event)813 static void TPDChar(TilePathDlg *tpd, GEvent *event) {
814     int i;
815     for ( i=0; i<4; ++i )
816 	if ( !(&tpd->cv_first)[i].inactive )
817     break;
818 
819     if ( event->u.chr.keysym==GK_Tab || event->u.chr.keysym==GK_BackTab ) {
820 	if ( event->u.chr.keysym==GK_Tab ) ++i; else --i;
821 	if ( i<0 ) i=3; else if ( i>3 ) i = 0;
822 	TPDMakeActive(tpd,(&tpd->cv_first)+i);
823     } else
824 	CVChar((&tpd->cv_first)+i,event);
825 }
826 
TPD_DoClose(struct cvcontainer * cvc)827 static void TPD_DoClose(struct cvcontainer *cvc) {
828     TilePathDlg *tpd = (TilePathDlg *) cvc;
829     int i;
830 
831     for ( i=0; i<4; ++i ) {
832 	SplineChar *msc = &(&tpd->sc_first)[i];
833 	SplinePointListsFree(msc->layers[0].splines);
834 	SplinePointListsFree(msc->layers[1].splines);
835 	free( msc->layers );
836     }
837 
838     tpd->done = true;
839 }
840 
tpd_sub_e_h(GWindow gw,GEvent * event)841 static int tpd_sub_e_h(GWindow gw, GEvent *event) {
842     TilePathDlg *tpd;
843 
844     if ( event->type==et_destroy )
845 return( true );
846 
847     tpd = (TilePathDlg *) ((CharViewBase *) GDrawGetUserData(gw))->container;
848 
849     switch ( event->type ) {
850       case et_resize:
851 	if ( event->u.resize.sized )
852 	    TPDSubResize(tpd,event);
853       break;
854       case et_char:
855 	TPDChar(tpd,event);
856       break;
857     }
858 return( true );
859 }
860 
tpd_e_h(GWindow gw,GEvent * event)861 static int tpd_e_h(GWindow gw, GEvent *event) {
862     TilePathDlg *tpd;
863     int i;
864 
865     if ( event->type == et_destroy )
866 return( true );
867 
868     tpd = (TilePathDlg *) ((CharViewBase *) GDrawGetUserData(gw))->container;
869     switch ( event->type ) {
870       case et_expose:
871 	TPDDraw(tpd, gw, event);
872       break;
873       case et_char:
874 	TPDChar(tpd,event);
875       break;
876       case et_close:
877 	TPD_DoClose((struct cvcontainer *) tpd);
878       break;
879       case et_create:
880       break;
881       case et_map:
882 	for ( i=0; i<4; ++i ) {
883 	    CharView *cv = (&tpd->cv_first)+i;
884 	    if ( !cv->inactive ) {
885 		if ( event->u.map.is_visible )
886 		    CVPaletteActivate(cv);
887 		else
888 		    CVPalettesHideIfMine(cv);
889 	break;
890 	    }
891 	}
892 	/* tpd->isvisible = event->u.map.is_visible; */
893       break;
894     }
895 return( true );
896 }
897 
TilePathD_Cancel(GGadget * g,GEvent * e)898 static int TilePathD_Cancel(GGadget *g, GEvent *e) {
899     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
900 	TilePathDlg *tpd = (TilePathDlg *) (((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container);
901 	TPD_DoClose(&tpd->base);
902     }
903 return( true );
904 }
905 
TPD_Useless(SplineSet * ss)906 static int TPD_Useless(SplineSet *ss) {
907     DBounds bb;
908 
909     if ( ss==NULL )
910 return( true );
911     SplineSetFindBounds(ss,&bb);
912 return( bb.maxy==bb.miny );
913 }
914 
TilePathD_OK(GGadget * g,GEvent * e)915 static int TilePathD_OK(GGadget *g, GEvent *e) {
916     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
917 	TilePathDlg *tpd = (TilePathDlg *) (((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container);
918 	struct tiledata *td = tpd->td;
919 
920 	if ( GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_Center)) )
921 	    td->tilepos = tp_center;
922 	else if ( GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_Left)) )
923 	    td->tilepos = tp_left;
924 	else
925 	    td->tilepos = tp_right;
926 	if ( GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_Tile)) )
927 	    td->tilescale = ts_tile;
928 	else if ( GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_TileScale)) )
929 	    td->tilescale = ts_tilescale;
930 	else
931 	    td->tilescale = ts_scale;
932 	if ( TPD_Useless(tpd->sc_medial.layers[ly_fore].splines) &&
933 		(td->tilescale!=ts_scale ||
934 		 TPD_Useless(tpd->sc_isolated.layers[ly_fore].splines)) ) {
935 	    if ( td->tilescale == ts_scale )
936 		ff_post_error(_("Bad Tile"),_("You must specify an isolated (or medial) tile"));
937 	    else
938 		ff_post_error(_("Bad Tile"),_("You must specify a medial tile"));
939 return( true );
940 	}
941 
942 	tilepos = td->tilepos;
943 	tilescale = td->tilescale;
944 
945 	td->firsttile = tpd->sc_first.layers[ly_fore].splines;
946 	    tpd->sc_first.layers[ly_fore].splines = NULL;
947 	include_whitespace[0] = td->finclude_white = GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_IncludeWhiteSpaceBelowTile+0))?ws_include:0;
948 	td->basetile = tpd->sc_medial.layers[ly_fore].splines;
949 	    tpd->sc_medial.layers[ly_fore].splines = NULL;
950 	include_whitespace[1] = td->include_white = GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_IncludeWhiteSpaceBelowTile+1))?ws_include:0;
951 	td->lasttile = tpd->sc_final.layers[ly_fore].splines;
952 	    tpd->sc_final.layers[ly_fore].splines = NULL;
953 	include_whitespace[2] = td->linclude_white = GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_IncludeWhiteSpaceBelowTile+2))?ws_include:0;
954 	td->isolatedtile = tpd->sc_isolated.layers[ly_fore].splines;
955 	    tpd->sc_isolated.layers[ly_fore].splines = NULL;
956 	include_whitespace[3] = td->iinclude_white = GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_IncludeWhiteSpaceBelowTile+3))?ws_include:0;
957 
958 	TPD_DoClose(&tpd->base);
959 	tpd->oked = true;
960     }
961 return( true );
962 }
963 
TPD_Can_Navigate(struct cvcontainer * cvc,enum nav_type type)964 static int TPD_Can_Navigate(struct cvcontainer *cvc, enum nav_type type) {
965 return( false );
966 }
967 
TPD_Can_Open(struct cvcontainer * cvc)968 static int TPD_Can_Open(struct cvcontainer *cvc) {
969 return( false );
970 }
971 
SF_Of_TPD(struct cvcontainer * foo)972 static SplineFont *SF_Of_TPD(struct cvcontainer *foo) {
973 return( NULL );
974 }
975 
976 struct cvcontainer_funcs tilepath_funcs = {
977     cvc_tilepath,
978     (void (*) (struct cvcontainer *cvc,CharViewBase *cv)) TPDMakeActive,
979     (void (*) (struct cvcontainer *cvc,void *)) TPDChar,
980     TPD_Can_Navigate,
981     NULL,
982     TPD_Can_Open,
983     TPD_DoClose,
984     SF_Of_TPD
985 };
986 
987 
TPDInit(TilePathDlg * tpd,SplineFont * sf)988 static void TPDInit(TilePathDlg *tpd,SplineFont *sf) {
989     int i;
990 
991     memset(tpd,0,sizeof(*tpd));
992     tpd->base.funcs = &tilepath_funcs;
993     tpd->base_sf = sf;
994 
995     for ( i=0; i<4; ++i ) {
996 	SplineChar *msc = &(&tpd->sc_first)[i];
997 	CharView *mcv = &(&tpd->cv_first)[i];
998 	msc->orig_pos = i;
999 	msc->unicodeenc = -1;
1000 	msc->name = i==0 ? "First" :
1001 		    i==1 ? "Medial"  :
1002 		    i==2 ? "Last":
1003 			    "Isolated";
1004 	msc->parent = &tpd->dummy_sf;
1005 	msc->layer_cnt = 2;
1006 	msc->layers = calloc(2,sizeof(Layer));
1007 	LayerDefault(&msc->layers[0]);
1008 	LayerDefault(&msc->layers[1]);
1009 	tpd->chars[i] = msc;
1010 
1011 	mcv->b.sc = msc;
1012 	mcv->b.layerheads[dm_fore] = &msc->layers[ly_fore];
1013 	mcv->b.layerheads[dm_back] = &msc->layers[ly_back];
1014 	mcv->b.layerheads[dm_grid] = &tpd->dummy_sf.grid;
1015 	msc->layers[ly_fore].splines = SplinePointListCopy(last_tiles[i]);
1016 	mcv->b.drawmode = dm_fore;
1017 	mcv->b.container = (struct cvcontainer *) tpd;
1018 	mcv->inactive = i!=0;
1019     }
1020     tpd->dummy_sf.glyphs = tpd->chars;
1021     tpd->dummy_sf.glyphcnt = tpd->dummy_sf.glyphmax = 4;
1022     tpd->dummy_sf.pfminfo.fstype = -1;
1023     tpd->dummy_sf.pfminfo.stylemap = -1;
1024     tpd->dummy_sf.fontname = tpd->dummy_sf.fullname = tpd->dummy_sf.familyname = "dummy";
1025     tpd->dummy_sf.weight = "Medium";
1026     tpd->dummy_sf.origname = "dummy";
1027     tpd->dummy_sf.ascent = sf->ascent;
1028     tpd->dummy_sf.descent = sf->descent;
1029     tpd->dummy_sf.layers = tpd->layerinfo;
1030     tpd->dummy_sf.layer_cnt = 2;
1031     tpd->layerinfo[ly_back].order2 = sf->layers[ly_back].order2;
1032     tpd->layerinfo[ly_back].name = _("Back");
1033     tpd->layerinfo[ly_fore].order2 = sf->layers[ly_fore].order2;
1034     tpd->layerinfo[ly_fore].name = _("Fore");
1035     tpd->dummy_sf.grid.order2 = sf->grid.order2;
1036     tpd->dummy_sf.anchor = NULL;
1037 
1038     tpd->dummy_sf.fv = (FontViewBase *) &tpd->dummy_fv;
1039     tpd->dummy_fv.b.active_layer = ly_fore;
1040     tpd->dummy_fv.b.sf = &tpd->dummy_sf;
1041     tpd->dummy_fv.b.selected = tpd->sel;
1042     tpd->dummy_fv.cbw = tpd->dummy_fv.cbh = default_fv_font_size+1;
1043     tpd->dummy_fv.magnify = 1;
1044 
1045     tpd->dummy_fv.b.map = &tpd->dummy_map;
1046     tpd->dummy_map.map = tpd->map;
1047     tpd->dummy_map.backmap = tpd->backmap;
1048     tpd->dummy_map.enccount = tpd->dummy_map.encmax = tpd->dummy_map.backmax = 4;
1049     tpd->dummy_map.enc = &custom;
1050 }
1051 
TileAsk(struct tiledata * td,SplineFont * sf)1052 static int TileAsk(struct tiledata *td,SplineFont *sf) {
1053     TilePathDlg tpd;
1054     GRect pos;
1055     GWindow gw;
1056     GWindowAttrs wattrs;
1057     GGadgetCreateData gcd[24], boxes[5], *harray[8], *varray[5],
1058 	*rhvarray[4][5], *chvarray[4][5];
1059     GTextInfo label[24];
1060     FontRequest rq;
1061     int as, ds, ld;
1062     int i,k;
1063     static GFont *font = NULL, *bold=NULL;
1064 
1065     TPDInit( &tpd,sf );
1066     tpd.td = td;
1067     memset(td,0,sizeof(*td));
1068 
1069     memset(&wattrs,0,sizeof(wattrs));
1070     wattrs.mask = wam_events|wam_cursor|wam_isdlg|wam_restrict|wam_undercursor|wam_utf8_wtitle;
1071     wattrs.is_dlg = true;
1072     wattrs.restrict_input_to_me = 1;
1073     wattrs.undercursor = 1;
1074     wattrs.event_masks = -1;
1075     wattrs.cursor = ct_pointer;
1076     wattrs.utf8_window_title = _("Tile Path");
1077     pos.width = 600;
1078     pos.height = 300;
1079     tpd.gw = gw = GDrawCreateTopWindow(NULL,&pos,tpd_e_h,&tpd.cv_first,&wattrs);
1080 
1081     if ( font==NULL ) {
1082 	memset(&rq,0,sizeof(rq));
1083 	rq.utf8_family_name = SANS_UI_FAMILIES;
1084 	rq.point_size = 12;
1085 	rq.weight = 400;
1086 	font = GDrawInstanciateFont(NULL,&rq);
1087 	font = GResourceFindFont("TilePath.Font",font);
1088 
1089 	GDrawDecomposeFont(font, &rq);
1090 	rq.weight = 700;
1091 	bold = GDrawInstanciateFont(NULL,&rq);
1092 	bold = GResourceFindFont("TilePath.BoldFont",bold);
1093     }
1094     tpd.plain = font;
1095     tpd.bold = bold;
1096     GDrawWindowFontMetrics(tpd.gw,tpd.plain,&as,&ds,&ld);
1097     tpd.fh = as+ds; tpd.as = as;
1098 
1099     memset(&label,0,sizeof(label));
1100     memset(&gcd,0,sizeof(gcd));
1101     memset(&boxes,0,sizeof(boxes));
1102 
1103     k = 0;
1104     gcd[k].gd.flags = gg_visible|gg_enabled ;		/* This space is for the menubar */
1105     gcd[k].gd.pos.height = 18; gcd[k].gd.pos.width = 20;
1106     gcd[k++].creator = GSpacerCreate;
1107 
1108     for ( i=0; i<4; ++i ) {
1109 	gcd[k].gd.pos.height = tpd.fh;
1110 	gcd[k].gd.flags = gg_visible | gg_enabled;
1111 	gcd[k++].creator = GSpacerCreate;
1112 	chvarray[0][i] = &gcd[k-1];
1113 
1114 	gcd[k].gd.pos.width = gcd[k].gd.pos.height = 200;
1115 	gcd[k].gd.flags = gg_visible | gg_enabled;
1116 	gcd[k].gd.cid = CID_FirstTile+i;
1117 	gcd[k].gd.u.drawable_e_h = tpd_sub_e_h;
1118 	gcd[k++].creator = GDrawableCreate;
1119 	chvarray[1][i] = &gcd[k-1];
1120 
1121 	gcd[k].gd.pos.x = gcd[0].gd.pos.x; gcd[k].gd.pos.y = gcd[6].gd.pos.y+24;
1122 	gcd[k].gd.flags = gg_visible | gg_enabled;
1123 	if ( include_whitespace[i] ) gcd[k].gd.flags |= gg_cb_on;
1124 	label[k].text = (unichar_t *) _("Include Whitespace below Tile");
1125 	label[k].text_is_1byte = true;
1126 	label[k].text_in_resource = true;
1127 	gcd[k].gd.label = &label[k];
1128 	gcd[k].gd.popup_msg = _("Normally the Tile will consist of everything\nwithin the minimum bounding box of the tile --\nso adjacent tiles will abut directly on one\nanother. If you wish whitespace between tiles\nset this flag");
1129 	gcd[k].gd.cid = CID_IncludeWhiteSpaceBelowTile+i;
1130 	gcd[k++].creator = GCheckBoxCreate;
1131 	chvarray[2][i] = &gcd[k-1];
1132     }
1133     chvarray[0][4] = chvarray[1][4] = chvarray[2][4] = chvarray[3][0] = NULL;
1134 
1135     gcd[k].gd.pos.x = 6; gcd[k].gd.pos.y = 6;
1136     gcd[k].gd.flags = gg_visible | gg_enabled;
1137     gcd[k].gd.mnemonic = 'L';
1138     label[k].text = (unichar_t *) _("_Left");
1139     label[k].text_is_1byte = true;
1140     label[k].text_in_resource = true;
1141     gcd[k].gd.label = &label[k];
1142     gcd[k].gd.popup_msg = _("The tiles should be placed to the left of the path\nas the path is traced from its start point to its end");
1143     gcd[k].gd.cid = CID_Left;
1144     gcd[k++].creator = GRadioCreate;
1145     rhvarray[0][0] = &gcd[k-1];
1146 
1147     gcd[k].gd.pos.x = 60; gcd[k].gd.pos.y = gcd[0].gd.pos.y;
1148     gcd[k].gd.flags = gg_visible | gg_enabled;
1149     gcd[k].gd.mnemonic = 'C';
1150     label[k].text = (unichar_t *) _("C_enter");
1151     label[k].text_is_1byte = true;
1152     label[k].text_in_resource = true;
1153     gcd[k].gd.label = &label[k];
1154     gcd[k].gd.popup_msg = _("The tiles should be centered on the path");
1155     gcd[k].gd.cid = CID_Center;
1156     gcd[k++].creator = GRadioCreate;
1157     rhvarray[0][1] = &gcd[k-1];
1158 
1159     gcd[k].gd.pos.x = 140; gcd[k].gd.pos.y = gcd[1].gd.pos.y;
1160     gcd[k].gd.flags = gg_visible | gg_enabled;
1161     gcd[k].gd.mnemonic = 'R';
1162     label[k].text = (unichar_t *) _("_Right");
1163     label[k].text_is_1byte = true;
1164     label[k].text_in_resource = true;
1165     gcd[k].gd.label = &label[k];
1166     gcd[k].gd.popup_msg = _("The tiles should be placed to the right of the path\nas the path is traced from its start point to its end");
1167     gcd[k].gd.cid = CID_Right;
1168     gcd[k++].creator = GRadioCreate;
1169     rhvarray[0][2] = &gcd[k-1];
1170     rhvarray[0][3] = GCD_Glue; rhvarray[0][4] = NULL;
1171 
1172     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = GDrawPointsToPixels(NULL,gcd[2].gd.pos.y+20);
1173     gcd[k].gd.pos.width = pos.width-10;
1174     gcd[k].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels ;
1175     gcd[k++].creator = GLineCreate;
1176     rhvarray[1][0] = &gcd[k-1];
1177     rhvarray[1][1] = rhvarray[1][2] = GCD_ColSpan;
1178     rhvarray[1][3] = GCD_Glue; rhvarray[1][4] = NULL;
1179 
1180     gcd[k].gd.pos.x = gcd[0].gd.pos.x; gcd[k].gd.pos.y = gcd[2].gd.pos.y+24;
1181     gcd[k].gd.flags = gg_visible | gg_enabled;
1182     gcd[k].gd.mnemonic = 'T';
1183     label[k].text = (unichar_t *) _("_Tile");
1184     label[k].text_is_1byte = true;
1185     label[k].text_in_resource = true;
1186     gcd[k].gd.label = &label[k];
1187     gcd[k].gd.popup_msg = _("Multiple copies of the selection should be tiled onto the path");
1188     gcd[k].gd.cid = CID_Tile;
1189     gcd[k++].creator = GRadioCreate;
1190     rhvarray[2][0] = &gcd[k-1];
1191 
1192     gcd[k].gd.pos.x = gcd[1].gd.pos.x; gcd[k].gd.pos.y = gcd[4].gd.pos.y;
1193     gcd[k].gd.flags = gg_visible | gg_enabled;
1194     gcd[k].gd.mnemonic = 'a';
1195     label[k].text = (unichar_t *) _("Sc_ale & Tile");
1196     label[k].text_is_1byte = true;
1197     label[k].text_in_resource = true;
1198     gcd[k].gd.label = &label[k];
1199     gcd[k].gd.popup_msg = _("An integral number of the selection will be used to cover the path.\nIf the path length is not evenly divisible by the selection's\nheight, then the selection should be scaled slightly.");
1200     gcd[k].gd.cid = CID_TileScale;
1201     gcd[k++].creator = GRadioCreate;
1202     rhvarray[2][1] = &gcd[k-1];
1203 
1204     gcd[k].gd.pos.x = gcd[2].gd.pos.x; gcd[k].gd.pos.y = gcd[5].gd.pos.y;
1205     gcd[k].gd.flags = gg_visible | gg_enabled;
1206     gcd[k].gd.mnemonic = 'S';
1207     label[k].text = (unichar_t *) _("_Scale");
1208     label[k].text_is_1byte = true;
1209     label[k].text_in_resource = true;
1210     gcd[k].gd.label = &label[k];
1211     gcd[k].gd.popup_msg = _("The selection should be scaled so that it will cover the path's length");
1212     gcd[k].gd.cid = CID_Scale;
1213     gcd[k++].creator = GRadioCreate;
1214     rhvarray[2][2] = &gcd[k-1];
1215     rhvarray[2][3] = GCD_Glue; rhvarray[2][4] = NULL;
1216     rhvarray[3][0] = NULL;
1217 
1218     gcd[k-7+tilepos].gd.flags |= gg_cb_on;
1219     gcd[k-3+tilescale].gd.flags |= gg_cb_on;
1220 
1221     label[k].text = (unichar_t *) _("_OK");
1222     label[k].text_is_1byte = true;
1223     label[k].text_in_resource = true;
1224     gcd[k].gd.label = &label[k];
1225     gcd[k].gd.flags = gg_enabled|gg_visible|gg_but_default;
1226     gcd[k].gd.handle_controlevent = TilePathD_OK;
1227     gcd[k++].creator = GButtonCreate;
1228 
1229     label[k].text = (unichar_t *) _("_Cancel");
1230     label[k].text_is_1byte = true;
1231     label[k].text_in_resource = true;
1232     gcd[k].gd.label = &label[k];
1233     gcd[k].gd.flags = gg_enabled|gg_visible|gg_but_cancel;
1234     gcd[k].gd.handle_controlevent = TilePathD_Cancel;
1235     gcd[k++].creator = GButtonCreate;
1236 
1237     harray[0] = GCD_Glue; harray[1] = &gcd[k-2]; harray[2] = GCD_Glue;
1238     harray[3] = GCD_Glue; harray[4] = &gcd[k-1]; harray[5] = GCD_Glue;
1239     harray[6] = NULL;
1240 
1241     boxes[2].gd.flags = gg_enabled|gg_visible;
1242     boxes[2].gd.u.boxelements = harray;
1243     boxes[2].creator = GHBoxCreate;
1244 
1245     boxes[3].gd.flags = gg_enabled|gg_visible;
1246     boxes[3].gd.u.boxelements = chvarray[0];
1247     boxes[3].creator = GHVBoxCreate;
1248 
1249     boxes[4].gd.flags = gg_enabled|gg_visible;
1250     boxes[4].gd.u.boxelements = rhvarray[0];
1251     boxes[4].creator = GHVBoxCreate;
1252 
1253     varray[0] = &gcd[0];
1254     varray[1] = &boxes[3];
1255     varray[2] = &boxes[4];
1256     varray[3] = &boxes[2];
1257     varray[4] = NULL;
1258 
1259     boxes[0].gd.flags = gg_enabled|gg_visible;
1260     boxes[0].gd.u.boxelements = varray;
1261     boxes[0].creator = GVBoxCreate;
1262 
1263     GGadgetsCreate(gw,boxes);
1264 
1265     TPDCharViewInits(&tpd,CID_FirstTile);
1266 
1267     GHVBoxSetExpandableRow(boxes[0].ret,1);
1268     GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
1269     GHVBoxSetExpandableRow(boxes[3].ret,1);
1270     GHVBoxSetPadding(boxes[3].ret, 2, 2);
1271     GHVBoxSetExpandableCol(boxes[4].ret,gb_expandglue);
1272     GGadgetResize(boxes[0].ret,pos.width,pos.height);
1273 
1274     TPDMakeActive(&tpd,&tpd.cv_medial);
1275 
1276     GDrawResize(gw,1000,400);		/* Force a resize event */
1277 
1278     GDrawSetVisible(tpd.gw,true);
1279 
1280     while ( !tpd.done )
1281 	GDrawProcessOneEvent(NULL);
1282 
1283     for ( i=0; i<4; ++i ) {
1284 	CharView *cv = &tpd.cv_first + i;
1285 	if ( cv->backimgs!=NULL ) {
1286 	    GDrawDestroyWindow(cv->backimgs);
1287 	    cv->backimgs = NULL;
1288 	}
1289 	CVPalettesHideIfMine(cv);
1290     }
1291     GDrawDestroyWindow(tpd.gw);
1292 return( tpd.oked );
1293 }
1294 
TDFree(struct tiledata * td)1295 static void TDFree(struct tiledata *td) {
1296     int i;
1297 
1298     for ( i=0 ; i<4; ++i )
1299 	SplinePointListFree( last_tiles[i]);
1300 
1301     last_tiles[0] = td->firsttile;
1302     last_tiles[1] = td->basetile;
1303     last_tiles[2] = td->lasttile;
1304     last_tiles[3] = td->isolatedtile;
1305 }
1306 
CVTile(CharView * cv)1307 void CVTile(CharView *cv) {
1308     struct tiledata td;
1309     int anypoints, anyrefs, anyimages, anyattach;
1310 
1311     CVAnySel(cv,&anypoints,&anyrefs,&anyimages,&anyattach);
1312     if ( anyrefs || anyimages || anyattach )
1313 return;
1314 
1315     if ( !TileAsk(&td,cv->b.sc->parent))
1316 return;
1317 
1318     CVPreserveState((CharViewBase *) cv);
1319     TileIt(&cv->b.layerheads[cv->b.drawmode]->splines,&td, !anypoints,cv->b.layerheads[cv->b.drawmode]->order2);
1320     CVCharChangedUpdate(&cv->b);
1321     TDFree(&td);
1322     cv->lastselpt = NULL;
1323 }
1324 
SCTile(SplineChar * sc,int layer)1325 void SCTile(SplineChar *sc,int layer) {
1326     struct tiledata td;
1327 
1328     if ( sc->layers[layer].splines==NULL )
1329 return;
1330 
1331     if ( !TileAsk(&td,sc->parent))
1332 return;
1333 
1334     SCPreserveLayer(sc,layer,false);
1335     TileIt(&sc->layers[layer].splines,&td, true, sc->layers[layer].order2);
1336     SCCharChangedUpdate(sc,layer);
1337     TDFree(&td);
1338 }
1339 
FVTile(FontView * fv)1340 void FVTile(FontView *fv) {
1341     struct tiledata td;
1342     SplineChar *sc;
1343     int i, gid;
1344     int layer = fv->b.active_layer;
1345 
1346     for ( i=0; i<fv->b.map->enccount; ++i )
1347 	if ( fv->b.selected[i] && (gid=fv->b.map->map[i])!=-1 &&
1348 		(sc=fv->b.sf->glyphs[gid])!=NULL && sc->layers[ly_fore].splines!=NULL )
1349     break;
1350     if ( i==fv->b.map->enccount )
1351 return;
1352 
1353     if ( !TileAsk(&td,fv->b.sf))
1354 return;
1355 
1356     SFUntickAll(fv->b.sf);
1357     for ( i=0; i<fv->b.map->enccount; ++i )
1358 	if ( fv->b.selected[i] && (gid=fv->b.map->map[i])!=-1 &&
1359 		(sc=fv->b.sf->glyphs[gid])!=NULL && !sc->ticked &&
1360 		sc->layers[layer].splines!=NULL ) {
1361 	    sc->ticked = true;
1362 	    SCPreserveLayer(sc,layer,false);
1363 	    TileIt(&sc->layers[layer].splines,&td, true, fv->b.sf->layers[layer].order2);
1364 	    SCCharChangedUpdate(sc,layer);
1365 	}
1366     TDFree(&td);
1367 }
1368 
1369 /* ************************************************************************** */
1370 /* ****************************** Tile Pattern ****************************** */
1371 /* ************************************************************************** */
1372 
SCTilePattern(SplineChar * sc,int layer,Layer * pattern,BasePoint * size,IPoint * cnt)1373 static void SCTilePattern(SplineChar *sc, int layer, Layer *pattern,
1374 	BasePoint *size, IPoint *cnt) {
1375     Layer *ly;
1376     SplineSet *ss, *lastss;
1377     RefChar *ref, *lastref, *r;
1378     real transform[6];
1379     int i,j;
1380 
1381     if ( pattern==NULL || cnt->x<=0 || cnt->y<=0 || size->x<=0 || size->y<=0 ||
1382 	    (pattern->splines==NULL && (pattern->refs==NULL || layer==ly_grid )))
1383 return;
1384 
1385     SCPreserveLayer(sc,layer,false);
1386 
1387     if ( layer==ly_grid )
1388 	ly = &sc->parent->grid;
1389     else if ( layer<0 )
1390 	ly = &sc->layers[ly_fore];
1391     else
1392 	ly = &sc->layers[layer];
1393 
1394     memset(transform,0,sizeof(transform));
1395     transform[0] = transform[3] = 1.0;
1396 
1397     if ( ly->splines==NULL )
1398 	lastss = NULL;
1399     else
1400 	for ( lastss=ly->splines; lastss->next!=NULL; lastss=lastss->next );
1401     if ( ly->refs==NULL )
1402 	lastref = NULL;
1403     else
1404 	for ( lastref=ly->refs; lastref->next!=NULL; lastref=lastref->next );
1405 
1406     for ( i=0; i<cnt->x; ++i ) for ( j=0; j<cnt->y; ++j ) {
1407 	transform[4] = i*size->x; transform[5] = j*size->y;
1408 	if ( pattern->splines!=NULL ) {
1409 	    ss = SplinePointListTransform(SplinePointListCopy(pattern->splines),transform,tpt_AllPoints);
1410 	    if ( lastss!=NULL )
1411 		lastss->next = ss;
1412 	    else
1413 		ly->splines = ss;
1414 	    for ( ; ss->next!=NULL; ss=ss->next );
1415 	    lastss = ss;
1416 	}
1417 	if ( pattern->refs!=NULL ) {
1418 	    ref = RefCharsCopy(pattern->refs);
1419 	    ref->transform[4] += transform[4];
1420 	    ref->transform[5] += transform[5];
1421 	    if ( lastref!=NULL )
1422 		lastref->next = ref;
1423 	    else
1424 		ly->refs = ref;
1425 	    for ( r=ref; r!=NULL; r=r->next ) {
1426 		SCMakeDependent(sc,r->sc);
1427 		SCReinstanciateRefChar(sc,r,layer);
1428 		lastref = r;
1429 	    }
1430 	}
1431     }
1432     SCCharChangedUpdate(sc,layer);
1433 }
1434 
1435 static SplineSet *last_pattern=NULL;
1436 static BasePoint patternSize;
1437 static IPoint patternRepeat = { 10, 10 };
1438 
1439 #define	CID_Tile	1011		/* Same as above */
1440 #define CID_PatternWidth	1012
1441 #define CID_PatternHeight	1013
1442 #define CID_XRepeat	1014
1443 #define CID_YRepeat	1015
1444 
PTDSubResize(TilePathDlg * tpd,GEvent * event)1445 static void PTDSubResize(TilePathDlg *tpd, GEvent *event) {
1446     int width, height;
1447 
1448     if ( !event->u.resize.sized )
1449 return;
1450 
1451     width = event->u.resize.size.width;
1452     height = event->u.resize.size.height;
1453     if ( width!=tpd->cv_width || height!=tpd->cv_height ) {
1454 	tpd->cv_width = width; tpd->cv_height = height;
1455 	 {
1456 	    CharView *cv = &tpd->cv_first;
1457 	    GDrawResize(cv->gw,width,height);
1458 	}
1459     }
1460 
1461     GDrawSync(NULL);
1462     GDrawProcessPendingEvents(NULL);
1463 }
1464 
PTDDraw(TilePathDlg * tpd,GWindow pixmap,GEvent * event)1465 static void PTDDraw(TilePathDlg *tpd, GWindow pixmap, GEvent *event) {
1466     GRect r,pos;
1467 
1468     GDrawSetLineWidth(pixmap,0);
1469 
1470     GGadgetGetSize(GWidgetGetControl(tpd->gw,CID_Tile),&pos);
1471     r.x = pos.x; r.y = pos.y-1;
1472     r.width = pos.width+1; r.height = pos.height+1;
1473     GDrawDrawRect(pixmap,&r,0);
1474 }
1475 
PTDMakeActive(TilePathDlg * ptd,CharView * cv)1476 static void PTDMakeActive(TilePathDlg *ptd,CharView *cv) {
1477 
1478     if ( ptd==NULL )
1479 return;
1480     cv->inactive = false;
1481     GDrawSetUserData(ptd->gw,cv);
1482 }
1483 
PTDChar(TilePathDlg * ptd,GEvent * event)1484 static void PTDChar(TilePathDlg *ptd, GEvent *event) {
1485 
1486     CVChar(&ptd->cv_first,event);
1487 }
1488 
PTD_DoClose(struct cvcontainer * cvc)1489 static void PTD_DoClose(struct cvcontainer *cvc) {
1490     TilePathDlg *ptd = (TilePathDlg *) cvc;
1491     SplineChar *msc = &ptd->sc_first;
1492 
1493     SplinePointListsFree(msc->layers[0].splines);
1494     RefCharsFree(msc->layers[0].refs);
1495     SplinePointListsFree(msc->layers[1].splines);
1496     RefCharsFree(msc->layers[1].refs);
1497     free( msc->layers );
1498 
1499     ptd->done = true;
1500 }
1501 
ptd_sub_e_h(GWindow gw,GEvent * event)1502 static int ptd_sub_e_h(GWindow gw, GEvent *event) {
1503     TilePathDlg *ptd;
1504 
1505     if ( event->type==et_destroy )
1506 return( true );
1507 
1508     ptd = (TilePathDlg *) ((CharViewBase *) GDrawGetUserData(gw))->container;
1509 
1510     switch ( event->type ) {
1511       case et_resize:
1512 	if ( event->u.resize.sized )
1513 	    PTDSubResize(ptd,event);
1514       break;
1515       case et_char:
1516 	PTDChar(ptd,event);
1517       break;
1518     }
1519 return( true );
1520 }
1521 
ptd_e_h(GWindow gw,GEvent * event)1522 static int ptd_e_h(GWindow gw, GEvent *event) {
1523     TilePathDlg *ptd;
1524 
1525     if ( event->type == et_destroy )
1526 return( true );
1527 
1528     ptd = (TilePathDlg *) ((CharViewBase *) GDrawGetUserData(gw))->container;
1529     switch ( event->type ) {
1530       case et_expose:
1531 	PTDDraw(ptd, gw, event);
1532       break;
1533       case et_char:
1534 	PTDChar(ptd,event);
1535       break;
1536       case et_close:
1537 	PTD_DoClose((struct cvcontainer *) ptd);
1538       break;
1539       case et_timer: {
1540 	SplineSet *ss;
1541 	char buffer[32];
1542 	int err;
1543 
1544 	for ( ss = ptd->sc_first.layers[ly_back].splines; ss!=NULL; ss=ss->next ) {
1545 	    if ( ss->first->prev!=NULL || ss->first->next==NULL || ss->first->next->to!=ss->last )
1546 	continue;
1547 	    err = 0;
1548 	    if ( ss->first->me.x==ss->last->me.x ) {
1549 		/* Don't make the change if they are the same (or there's an */
1550 		/*  error) as the user might be busy editing and that would */
1551 		/*  lose his/her work */
1552 		if ( !RealNear(GetCalmReal8(ptd->gw,CID_PatternWidth,_("Width"),&err),
1553 			ss->first->me.x) && !err ) {
1554 		    sprintf( buffer,"%g",ss->first->me.x);
1555 		    GGadgetSetTitle8(GWidgetGetControl(ptd->gw,CID_PatternWidth),buffer);
1556 		}
1557 	    } else if ( ss->first->me.y==ss->last->me.y ) {
1558 		if ( !RealNear(GetCalmReal8(ptd->gw,CID_PatternHeight,_("Height"),&err),
1559 			ss->first->me.y) && !err ) {
1560 		    sprintf( buffer,"%g",ss->first->me.y);
1561 		    GGadgetSetTitle8(GWidgetGetControl(ptd->gw,CID_PatternHeight),buffer);
1562 		}
1563 	    }
1564 	}
1565       } break;
1566       case et_map:
1567 	{
1568 	    CharView *cv = &ptd->cv_first;
1569 	    if ( !cv->inactive ) {
1570 		if ( event->u.map.is_visible )
1571 		    CVPaletteActivate(cv);
1572 		else
1573 		    CVPalettesHideIfMine(cv);
1574 	break;
1575 	    }
1576 	}
1577 	/* ptd->isvisible = event->u.map.is_visible; */
1578       break;
1579     }
1580 return( true );
1581 }
1582 
PTD_RefigureBackground(GGadget * g,GEvent * e)1583 static int PTD_RefigureBackground(GGadget *g, GEvent *e) {
1584     if ( e==NULL || (e->type==et_controlevent && e->u.control.subtype == et_textchanged )) {
1585 	TilePathDlg *ptd = (TilePathDlg *) (((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container);
1586 	int hsize, vsize;
1587 	int err = 0;
1588 	SplinePoint *sp1, *sp2;
1589 	SplineSet *ss;
1590 
1591 	hsize = GetCalmReal8(ptd->gw,CID_PatternWidth,_("Width"),&err);
1592 	vsize = GetCalmReal8(ptd->gw,CID_PatternHeight,_("Height"),&err);
1593 	if ( err )
1594 return( true );
1595 	ptd->sc_first.width = hsize;
1596 	SplinePointListFree(ptd->sc_first.layers[ly_back].splines);
1597 	sp1 = SplinePointCreate(-1000,vsize);
1598 	sp2 = SplinePointCreate(2000,vsize);
1599 	SplineMake(sp1,sp2,ptd->sc_first.layers[ly_back].order2);
1600 	ss = chunkalloc(sizeof(SplineSet));
1601 	ss->first = sp1; ss->last = sp2;
1602 	ptd->sc_first.layers[ly_back].splines = ss;
1603 	sp1 = SplinePointCreate(hsize,-1000);
1604 	sp2 = SplinePointCreate(hsize,2000);
1605 	SplineMake(sp1,sp2,ptd->sc_first.layers[ly_back].order2);
1606 	ss = chunkalloc(sizeof(SplineSet));
1607 	ss->first = sp1; ss->last = sp2;
1608 	ptd->sc_first.layers[ly_back].splines->next = ss;
1609 	GDrawRequestExpose(ptd->cv_first.v,NULL,false);
1610     }
1611 return( true );
1612 }
1613 
PTD_Cancel(GGadget * g,GEvent * e)1614 static int PTD_Cancel(GGadget *g, GEvent *e) {
1615     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1616 	TilePathDlg *ptd = (TilePathDlg *) (((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container);
1617 	PTD_DoClose(&ptd->base);
1618     }
1619 return( true );
1620 }
1621 
PTD_OK(GGadget * g,GEvent * e)1622 static int PTD_OK(GGadget *g, GEvent *e) {
1623     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1624 	TilePathDlg *ptd = (TilePathDlg *) (((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container);
1625 	struct tiledata *td = ptd->td;
1626 	int err = 0;
1627 
1628 	td->patternSize.x = GetReal8(ptd->gw,CID_PatternWidth,_("Width"),&err);
1629 	td->patternSize.y = GetReal8(ptd->gw,CID_PatternHeight,_("Height"),&err);
1630 
1631 	td->repeatCnt.x = GetInt8(ptd->gw,CID_XRepeat,_("X Repeat Count"),&err);
1632 	td->repeatCnt.y = GetInt8(ptd->gw,CID_YRepeat,_("Y Repeat Count"),&err);
1633 	if ( err )
1634 return( true );
1635 	if ( td->patternSize.x<=0 || td->patternSize.y<=0 ) {
1636 	    ff_post_error(_("Bad Pattern Size"),_("The pattern size (width & height) must be a positive number"));
1637 return( true );
1638 	}
1639 	if ( td->repeatCnt.x<=0 || td->repeatCnt.y<=0 || td->repeatCnt.x>2000 || td->repeatCnt.y>2000 ) {
1640 	    ff_post_error(_("Bad Pattern Size"),_("The repeat counts must be positive numbers"));
1641 return( true );
1642 	}
1643 
1644 	td->pattern = calloc(1,sizeof(Layer));
1645 	td->pattern->splines = SplinePointListCopy(ptd->sc_first.layers[ly_fore].splines);
1646 	td->pattern->refs = RefCharsCopy(ptd->sc_first.layers[ly_fore].refs);
1647 
1648 	if ( td->pattern->splines==NULL && td->pattern->refs==NULL ) {
1649 	    ff_post_error(_("Bad Pattern"),_("You must specify a pattern"));
1650 return( true );
1651 	}
1652 
1653 	PTD_DoClose(&ptd->base);
1654 	ptd->oked = true;
1655     }
1656 return( true );
1657 }
1658 
PTD_Can_Navigate(struct cvcontainer * cvc,enum nav_type type)1659 static int PTD_Can_Navigate(struct cvcontainer *cvc, enum nav_type type) {
1660 return( false );
1661 }
1662 
PTD_Can_Open(struct cvcontainer * cvc)1663 static int PTD_Can_Open(struct cvcontainer *cvc) {
1664 return( false );
1665 }
1666 
SF_Of_PTD(struct cvcontainer * cvc)1667 static SplineFont *SF_Of_PTD(struct cvcontainer *cvc) {
1668     TilePathDlg *ptd = (TilePathDlg *) cvc;
1669 return( ptd->base_sf );
1670 }
1671 
1672 struct cvcontainer_funcs tilepattern_funcs = {
1673     cvc_multiplepattern,
1674     (void (*) (struct cvcontainer *cvc,CharViewBase *cv)) PTDMakeActive,
1675     (void (*) (struct cvcontainer *cvc,void *)) PTDChar,
1676     PTD_Can_Navigate,
1677     NULL,
1678     PTD_Can_Open,
1679     PTD_DoClose,
1680     SF_Of_PTD
1681 };
1682 
1683 
PTDInit(TilePathDlg * ptd,SplineFont * sf)1684 static void PTDInit(TilePathDlg *ptd,SplineFont *sf) {
1685 
1686     memset(ptd,0,sizeof(*ptd));
1687     ptd->base.funcs = &tilepattern_funcs;
1688     ptd->base_sf = sf;
1689 
1690     {
1691 	SplineChar *msc = &ptd->sc_first;
1692 	CharView *mcv = &ptd->cv_first;
1693 	msc->orig_pos = 0;
1694 	msc->unicodeenc = -1;
1695 	msc->name = "Pattern";
1696 	msc->parent = &ptd->dummy_sf;
1697 	msc->layer_cnt = 2;
1698 	msc->layers = calloc(2,sizeof(Layer));
1699 	LayerDefault(&msc->layers[0]);
1700 	LayerDefault(&msc->layers[1]);
1701 	ptd->chars[0] = msc;
1702 
1703 	mcv->b.sc = msc;
1704 	mcv->b.layerheads[dm_fore] = &msc->layers[ly_fore];
1705 	mcv->b.layerheads[dm_back] = &msc->layers[ly_back];
1706 	mcv->b.layerheads[dm_grid] = &ptd->dummy_sf.grid;
1707 	msc->layers[ly_fore].splines = SplinePointListCopy(last_pattern);
1708 	mcv->b.drawmode = dm_fore;
1709 	mcv->b.container = (struct cvcontainer *) ptd;
1710 	mcv->inactive = false;
1711     }
1712     ptd->dummy_sf.glyphs = ptd->chars;
1713     ptd->dummy_sf.glyphcnt = ptd->dummy_sf.glyphmax = 1;
1714     ptd->dummy_sf.pfminfo.fstype = -1;
1715     ptd->dummy_sf.pfminfo.stylemap = -1;
1716     ptd->dummy_sf.fontname = ptd->dummy_sf.fullname = ptd->dummy_sf.familyname = "dummy";
1717     ptd->dummy_sf.weight = "Medium";
1718     ptd->dummy_sf.origname = "dummy";
1719     ptd->dummy_sf.ascent = sf->ascent;
1720     ptd->dummy_sf.descent = sf->descent;
1721     ptd->dummy_sf.layers = ptd->layerinfo;
1722     ptd->dummy_sf.layer_cnt = 2;
1723     ptd->layerinfo[ly_back].order2 = sf->layers[ly_back].order2;
1724     ptd->layerinfo[ly_back].name = _("Size");
1725     ptd->layerinfo[ly_fore].order2 = sf->layers[ly_fore].order2;
1726     ptd->layerinfo[ly_fore].name = _("Pattern");
1727     ptd->dummy_sf.grid.order2 = sf->grid.order2;
1728     ptd->dummy_sf.anchor = NULL;
1729 
1730     ptd->dummy_sf.fv = (FontViewBase *) &ptd->dummy_fv;
1731     ptd->dummy_fv.b.active_layer = ly_fore;
1732     ptd->dummy_fv.b.sf = &ptd->dummy_sf;
1733     ptd->dummy_fv.b.selected = ptd->sel;
1734     ptd->dummy_fv.cbw = ptd->dummy_fv.cbh = default_fv_font_size+1;
1735     ptd->dummy_fv.magnify = 1;
1736 
1737     ptd->dummy_fv.b.map = &ptd->dummy_map;
1738     ptd->dummy_map.map = ptd->map;
1739     ptd->dummy_map.backmap = ptd->backmap;
1740     ptd->dummy_map.enccount = ptd->dummy_map.encmax = ptd->dummy_map.backmax = 1;
1741     ptd->dummy_map.enc = &custom;
1742 }
1743 
TilePatternAsk(struct tiledata * td,SplineFont * sf)1744 static int TilePatternAsk(struct tiledata *td,SplineFont *sf) {
1745     TilePathDlg ptd;
1746     GRect pos;
1747     GWindow gw;
1748     GWindowAttrs wattrs;
1749     GGadgetCreateData gcd[24], boxes[5], *harray1[8], *harray2[8], *varray[10],
1750 	*harray[8];
1751     GTextInfo label[24];
1752     int k;
1753     char width[30], height[30], xr[30], yr[30];
1754 
1755     PTDInit( &ptd,sf );
1756     ptd.td = td;
1757     memset(td,0,sizeof(*td));
1758 
1759     memset(&wattrs,0,sizeof(wattrs));
1760     wattrs.mask = wam_events|wam_cursor|wam_isdlg|wam_restrict|wam_undercursor|wam_utf8_wtitle;
1761     wattrs.is_dlg = true;
1762     wattrs.restrict_input_to_me = 1;
1763     wattrs.undercursor = 1;
1764     wattrs.event_masks = -1;
1765     wattrs.cursor = ct_pointer;
1766     wattrs.utf8_window_title = _("Tile Pattern");
1767     pos.width = 600;
1768     pos.height = 300;
1769     ptd.gw = gw = GDrawCreateTopWindow(NULL,&pos,ptd_e_h,&ptd.cv_first,&wattrs);
1770 
1771     memset(&label,0,sizeof(label));
1772     memset(&gcd,0,sizeof(gcd));
1773     memset(&boxes,0,sizeof(boxes));
1774 
1775     k = 0;
1776     gcd[k].gd.flags = gg_visible|gg_enabled ;		/* This space is for the menubar */
1777     gcd[k].gd.pos.height = 18; gcd[k].gd.pos.width = 20;
1778     gcd[k++].creator = GSpacerCreate;
1779     varray[0] = &gcd[k-1];
1780 
1781     gcd[k].gd.pos.width = gcd[k].gd.pos.height = 200;
1782     gcd[k].gd.flags = gg_visible | gg_enabled;
1783     gcd[k].gd.cid = CID_Tile;
1784     gcd[k].gd.u.drawable_e_h = ptd_sub_e_h;
1785     gcd[k++].creator = GDrawableCreate;
1786     varray[1] = &gcd[k-1];
1787 
1788     gcd[k].gd.flags = gg_visible | gg_enabled;
1789     label[k].text = (unichar_t *) _("Pattern Size:");
1790     label[k].text_is_1byte = true;
1791     gcd[k].gd.label = &label[k];
1792     gcd[k++].creator = GLabelCreate;
1793     varray[2] = &gcd[k-1];
1794 
1795     gcd[k].gd.flags = gg_visible | gg_enabled;
1796     label[k].text = (unichar_t *) _("Width:");
1797     label[k].text_is_1byte = true;
1798     gcd[k].gd.label = &label[k];
1799     gcd[k++].creator = GLabelCreate;
1800     harray1[0] = &gcd[k-1];
1801 
1802     if ( last_pattern==NULL )
1803 	sprintf( width, "%d", sf->ascent+sf->descent );
1804     else
1805 	sprintf( width, "%g", patternSize.x );
1806     gcd[k].gd.flags = gg_visible | gg_enabled;
1807     label[k].text = (unichar_t *) width;
1808     label[k].text_is_1byte = true;
1809     gcd[k].gd.label = &label[k];
1810     gcd[k].gd.cid = CID_PatternWidth;
1811     gcd[k].gd.handle_controlevent = PTD_RefigureBackground;
1812     gcd[k++].creator = GTextFieldCreate;
1813     harray1[1] = &gcd[k-1];
1814 
1815     gcd[k].gd.flags = gg_visible | gg_enabled;
1816     label[k].text = (unichar_t *) _("Height:");
1817     label[k].text_is_1byte = true;
1818     gcd[k].gd.label = &label[k];
1819     gcd[k++].creator = GLabelCreate;
1820     harray1[2] = &gcd[k-1];
1821 
1822     if ( last_pattern==NULL )
1823 	sprintf( height, "%d", sf->ascent+sf->descent );
1824     else
1825 	sprintf( height, "%g", patternSize.y );
1826     gcd[k].gd.flags = gg_visible | gg_enabled;
1827     label[k].text = (unichar_t *) height;
1828     label[k].text_is_1byte = true;
1829     gcd[k].gd.label = &label[k];
1830     gcd[k].gd.cid = CID_PatternHeight;
1831     gcd[k].gd.handle_controlevent = PTD_RefigureBackground;
1832     gcd[k++].creator = GTextFieldCreate;
1833     harray1[3] = &gcd[k-1]; harray1[4] = NULL;
1834 
1835     boxes[2].gd.flags = gg_enabled|gg_visible;
1836     boxes[2].gd.u.boxelements = harray1;
1837     boxes[2].creator = GHBoxCreate;
1838     varray[3] = &boxes[2];
1839 
1840     gcd[k].gd.flags = gg_visible | gg_enabled;
1841     label[k].text = (unichar_t *) _("Repeat Counts:");
1842     label[k].text_is_1byte = true;
1843     gcd[k].gd.label = &label[k];
1844     gcd[k++].creator = GLabelCreate;
1845     varray[4] = &gcd[k-1];
1846 
1847     gcd[k].gd.flags = gg_visible | gg_enabled;
1848     label[k].text = (unichar_t *) _("X:");
1849     label[k].text_is_1byte = true;
1850     gcd[k].gd.label = &label[k];
1851     gcd[k++].creator = GLabelCreate;
1852     harray2[0] = &gcd[k-1];
1853 
1854     sprintf( xr, "%d", patternRepeat.x );
1855     gcd[k].gd.flags = gg_visible | gg_enabled;
1856     label[k].text = (unichar_t *) xr;
1857     label[k].text_is_1byte = true;
1858     gcd[k].gd.label = &label[k];
1859     gcd[k].gd.cid = CID_XRepeat;
1860     gcd[k++].creator = GTextFieldCreate;
1861     harray2[1] = &gcd[k-1];
1862 
1863     gcd[k].gd.flags = gg_visible | gg_enabled;
1864     label[k].text = (unichar_t *) _("Y:");
1865     label[k].text_is_1byte = true;
1866     gcd[k].gd.label = &label[k];
1867     gcd[k++].creator = GLabelCreate;
1868     harray2[2] = &gcd[k-1];
1869 
1870     sprintf( yr, "%d", patternRepeat.y );
1871     gcd[k].gd.flags = gg_visible | gg_enabled;
1872     label[k].text = (unichar_t *) yr;
1873     label[k].text_is_1byte = true;
1874     gcd[k].gd.label = &label[k];
1875     gcd[k].gd.cid = CID_YRepeat;
1876     gcd[k++].creator = GTextFieldCreate;
1877     harray2[3] = &gcd[k-1]; harray2[4] = NULL;
1878 
1879     boxes[3].gd.flags = gg_enabled|gg_visible;
1880     boxes[3].gd.u.boxelements = harray2;
1881     boxes[3].creator = GHBoxCreate;
1882     varray[5] = &boxes[3];
1883 
1884 
1885     label[k].text = (unichar_t *) _("_OK");
1886     label[k].text_is_1byte = true;
1887     label[k].text_in_resource = true;
1888     gcd[k].gd.label = &label[k];
1889     gcd[k].gd.flags = gg_enabled|gg_visible|gg_but_default;
1890     gcd[k].gd.handle_controlevent = PTD_OK;
1891     gcd[k++].creator = GButtonCreate;
1892 
1893     label[k].text = (unichar_t *) _("_Cancel");
1894     label[k].text_is_1byte = true;
1895     label[k].text_in_resource = true;
1896     gcd[k].gd.label = &label[k];
1897     gcd[k].gd.flags = gg_enabled|gg_visible|gg_but_cancel;
1898     gcd[k].gd.handle_controlevent = PTD_Cancel;
1899     gcd[k++].creator = GButtonCreate;
1900 
1901     harray[0] = GCD_Glue; harray[1] = &gcd[k-2]; harray[2] = GCD_Glue;
1902     harray[3] = GCD_Glue; harray[4] = &gcd[k-1]; harray[5] = GCD_Glue;
1903     harray[6] = NULL;
1904 
1905     boxes[4].gd.flags = gg_enabled|gg_visible;
1906     boxes[4].gd.u.boxelements = harray;
1907     boxes[4].creator = GHBoxCreate;
1908     varray[6] = &boxes[4]; varray[7] = NULL;
1909 
1910     boxes[0].gd.flags = gg_enabled|gg_visible;
1911     boxes[0].gd.u.boxelements = varray;
1912     boxes[0].creator = GVBoxCreate;
1913 
1914     GGadgetsCreate(gw,boxes);
1915 
1916     PTDCharViewInits(&ptd,CID_Tile);
1917 
1918     GHVBoxSetExpandableRow(boxes[0].ret,1);
1919     GHVBoxSetExpandableCol(boxes[4].ret,gb_expandgluesame);
1920     GGadgetResize(boxes[0].ret,pos.width,pos.height);
1921 
1922     PTD_RefigureBackground(GWidgetGetControl(ptd.gw,CID_PatternWidth),NULL);
1923 
1924     PTDMakeActive(&ptd,&ptd.cv_first);
1925 
1926     GDrawResize(gw,400,400);		/* Force a resize event */
1927 
1928     GDrawSetVisible(ptd.gw,true);
1929     GDrawRequestTimer(ptd.gw,1000,1000,NULL);
1930 
1931     while ( !ptd.done )
1932 	GDrawProcessOneEvent(NULL);
1933 
1934     {
1935 	CharView *cv = &ptd.cv_first;
1936 	if ( cv->backimgs!=NULL ) {
1937 	    GDrawDestroyWindow(cv->backimgs);
1938 	    cv->backimgs = NULL;
1939 	}
1940 	CVPalettesHideIfMine(cv);
1941     }
1942     GDrawDestroyWindow(ptd.gw);
1943 return( ptd.oked );
1944 }
1945 
TPFree(struct tiledata * td)1946 static void TPFree(struct tiledata *td) {
1947 
1948     SplinePointListFree(last_pattern);
1949     last_pattern = td->pattern->splines;
1950     RefCharsFree(td->pattern->refs);
1951     free(td->pattern);
1952     patternSize = td->patternSize;
1953     patternRepeat = td->repeatCnt;
1954 }
1955 
CVPatternTile(CharView * cv)1956 void CVPatternTile(CharView *cv) {
1957     struct tiledata td;
1958 
1959     if ( !TilePatternAsk(&td,cv->b.sc->parent))
1960 return;
1961 
1962     SCTilePattern(cv->b.sc,CVLayer((CharViewBase *) cv),td.pattern, &td.patternSize, &td.repeatCnt);
1963     TPFree(&td);
1964     cv->lastselpt = NULL;
1965 }
1966 
FVPatternTile(FontView * fv)1967 void FVPatternTile(FontView *fv) {
1968     struct tiledata td;
1969     SplineChar *sc;
1970     int i, gid;
1971     int layer = fv->b.active_layer;
1972 
1973     for ( i=0; i<fv->b.map->enccount; ++i )
1974 	if ( fv->b.selected[i] && (gid=fv->b.map->map[i])!=-1 &&
1975 		(sc=fv->b.sf->glyphs[gid])!=NULL )
1976     break;
1977     if ( i==fv->b.map->enccount )
1978 return;
1979 
1980     if ( !TilePatternAsk(&td,fv->b.sf))
1981 return;
1982 
1983     SFUntickAll(fv->b.sf);
1984     for ( i=0; i<fv->b.map->enccount; ++i )
1985 	if ( fv->b.selected[i] && (gid=fv->b.map->map[i])!=-1 &&
1986 		(sc=fv->b.sf->glyphs[gid])!=NULL && !sc->ticked  ) {
1987 	    sc->ticked = true;
1988 	    SCTilePattern(sc,layer,td.pattern, &td.patternSize, &td.repeatCnt);
1989 	}
1990     TPFree(&td);
1991 }
1992 #endif 		/* FONTFORGE_CONFIG_TILEPATH */
1993