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