/* Copyright (C) 2002-2011 by George Williams */ /* * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "fontforgeui.h" #include #include #ifdef FONTFORGE_CONFIG_TILEPATH /* Given a path and a splineset */ /* Treat the splineset as a tile and lay it down on the path until we reach the*/ /* end of the path */ /* More precisely, find the length of the path */ /* Find the height of the tile */ /* We'll need length/height tiles */ /* For each tile */ /* For a point on the central (in x) axis of the tile */ /* Use its y-position to figure out how far along the path we are ( y-pos/length ) */ /* Then this point should be moved to exactly that point */ /* For a point off the central axis */ /* Perform the above calculation, and */ /* Find the normal vector to the path */ /* Our new location should be: */ /* the location found above + our xoffset * (normal vector) */ /* Do that for a lot of points on each spline of the tile and then */ /* use approximate spline from points to find the new splines */ /* Complications: */ /* There may not be an integral number of tiles, so we must be prepared to truncate some splines */ typedef struct tiledata { SplineSet *basetile; /* Moved so that ymin==0, and x is adjusted */ /* about the x-axis as implied by tilepos */ SplineSet *firsttile; SplineSet *lasttile; SplineSet *isolatedtile; SplineSet *tileset; /* As many copies of the basetile as we are */ /* going to need. Each successive one bb.maxy */ /* higher than the last */ SplineSet *result; /* Final result after transformation */ DBounds bb, fbb, lbb, ibb; /* Of the basetile, first & last tiles */ uint8 include_white, finclude_white, linclude_white, iinclude_white; bigreal xscale[3]; SplineSet *path; bigreal plength; /* Length of path */ int pcnt; /* Number of splines in path */ int nsamples; struct tdsample { real dx, dy; /* offset from path->first->me */ real c,s; /* cos/sin of normal vector pointing right of path */ } *samples; /* an array of [nsamples+1] actually */ int njoins; struct jsample { real dx, dy; real c1,s1; real c2,s2; real sofar; } *joins; /* an array of [pcnt or pcnt-1], one of each join */ enum tilepos { tp_left, tp_center, tp_right } tilepos; enum tilescale { ts_tile, ts_tilescale, ts_scale } tilescale; /* ts_scale means that we scale the one tile until it height is the same */ /* as plength */ /* ts_tile means that we lay down as many tiles as we need so that */ /* n*tile-height == plength. Note: n need not be an integer, so we */ /* may be an incomplete tile => incomplete splines (a spline may even */ /* get cut so that it becomes two splines) */ /* ts_tilescale means that we find n = floor(plength/tile-height) and */ /* scale = plength/(n*tile-height). We scale the tile by "scale", and */ /* then lay down n of them */ int doallpaths; BasePoint patternSize; /* Last 3 fields used only for patterns */ IPoint repeatCnt; /* And not for tilepath */ Layer *pattern; } TD; enum whitespace_type { ws_include=0x1, ws_but_not_first=0x2 }; static int TDMakeSamples(TD *td) { Spline *spline, *first; bigreal len, slen, sofar, t, toff, dt_per_sample; int i,end, base, pcnt; bigreal sx, sy, angle; first = NULL; len = 0; pcnt = 0; for ( spline=td->path->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) { if ( first==NULL ) first = spline; len += SplineLength(spline); ++pcnt; } if ( len==0 ) return( false ); td->plength = len; td->pcnt = pcnt; td->nsamples = ceil(len)+10; td->samples = galloc((td->nsamples+1)*sizeof(struct tdsample)); td->joins = galloc(td->pcnt*sizeof(struct jsample)); i = 0; pcnt = 0; first = NULL; sofar = 0; for ( spline=td->path->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) { if ( first==NULL ) first = spline; slen = SplineLength(spline); /* I'm assuming that length is approximately linear in t */ toff = (i - td->nsamples*sofar/len)/slen; base = i; end = floor(td->nsamples*(sofar+slen)/len); dt_per_sample = end==i?1:(1.0-toff)/(end-i); if ( spline->to->next==NULL || spline->to->next==first ) end = td->nsamples; while ( i<=end ) { t = toff + (i-base)*dt_per_sample; if ( i==td->nsamples || t>1 ) t = 1; td->samples[i].dx = ((spline->splines[0].a*t+spline->splines[0].b)*t+spline->splines[0].c)*t + spline->splines[0].d /*- td->path->first->me.x*/; td->samples[i].dy = ((spline->splines[1].a*t+spline->splines[1].b)*t+spline->splines[1].c)*t + spline->splines[1].d /*- td->path->first->me.y*/; sx = (3*spline->splines[0].a*t+2*spline->splines[0].b)*t+spline->splines[0].c; sy = (3*spline->splines[1].a*t+2*spline->splines[1].b)*t+spline->splines[1].c; if ( sx==0 && sy==0 ) { sx = spline->to->me.x - spline->from->me.x; sy = spline->to->me.y - spline->from->me.y; } angle = atan2(sy,sx) - 3.1415926535897932/2; td->samples[i].c = cos(angle); td->samples[i].s = sin(angle); 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; } 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; } ++i; } sofar += slen; if (( pcntpcnt-1 || td->path->first==td->path->last ) && spline->to->next!=NULL && !((spline->to->pointtype==pt_curve && !spline->to->nonextcp && !spline->to->noprevcp) || (spline->to->pointtype==pt_hvcurve && !spline->to->nonextcp && !spline->to->noprevcp) || (spline->to->pointtype==pt_tangent && spline->to->nonextcp+spline->to->noprevcp==1 )) ) { /* We aren't interested in joins where the two splines are tangent */ Spline *next = spline->to->next; td->joins[pcnt].sofar = sofar; td->joins[pcnt].dx = spline->to->me.x; td->joins[pcnt].dy = spline->to->me.y; /* there are two normals at a join, one for each spline */ /* it should bisect the normal vectors of the two splines */ sx = next->splines[0].c; sy = next->splines[1].c; angle = atan2(sy,sx) - 3.1415926535897932/2; td->joins[pcnt].c1 = cos(angle); td->joins[pcnt].s1 = sin(angle); 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; } 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; } sx = (3*spline->splines[0].a+2*spline->splines[0].b)+spline->splines[0].c; sy = (3*spline->splines[1].a+2*spline->splines[1].b)+spline->splines[1].c; angle = atan2(sy,sx) - 3.1415926535897932/2; td->joins[pcnt].c2 = cos(angle); td->joins[pcnt].s2 = sin(angle); 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; } 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; } ++pcnt; } } td->njoins = pcnt; if ( i!=td->nsamples+1 ) IError("Sample failure %d is not %d", i, td->samples+1 ); return( true ); } static void TDAddPoints(TD *td) { /* Insert additional points in the tileset roughly at the locations */ /* corresponding to the ends of the splines in the path */ SplineSet *spl; Spline *spline, *first, *tsp; bigreal len; bigreal ts[3]; first = NULL; len = 0; for ( spline=td->path->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) { if ( first==NULL ) first = spline; if ( spline->to->next==NULL || spline->to->next==first ) break; len += SplineLength(spline); for ( spl=td->tileset; spl!=NULL; spl=spl->next ) { for ( tsp=spl->first->next; tsp!=NULL ; tsp = tsp->to->next ) { if ( RealApprox(tsp->to->me.y,len) || RealApprox(tsp->from->me.y,len)) /* Do Nothing, already broken here */; else if ( (tsp->to->me.y>len || tsp->to->prevcp.y>len || tsp->from->me.y>len || tsp->from->nextcp.y>len) && (tsp->to->me.yto->prevcp.yfrom->me.yfrom->nextcp.ysplines[1],len,ts) ) { SplinePoint *mid = SplineBisect(tsp,ts[0]); tsp = mid->next; } if ( tsp->to == spl->first ) break; } } } } static void SplineSplitAtY(Spline *spline,real y) { bigreal ts[3]; SplinePoint *last; if ( spline->from->me.y<=y && spline->from->nextcp.y<=y && spline->to->me.y<=y && spline->to->prevcp.y<=y ) return; if ( spline->from->me.y>=y && spline->from->nextcp.y>=y && spline->to->me.y>=y && spline->to->prevcp.y>=y ) return; if ( !CubicSolve(&spline->splines[1],y,ts) ) return; last = spline->to; spline = SplineSplit(spline,ts); while ( spline->to!=last ) { if ( spline->to->me.y!=y ) { real diff = y-spline->to->me.y; spline->to->me.y = y; spline->to->prevcp.y += diff; spline->to->nextcp.y += diff; SplineRefigure(spline); SplineRefigure(spline->to->next); } spline = spline->to->next; } } static void _SplinesRemoveBetween( Spline *spline, Spline *beyond, SplineSet *spl ) { Spline *next; while ( spline!=NULL && spline!=beyond ) { next = spline->to->next; if ( spline->from!=spl->last && spline->from!=spl->first ) SplinePointFree(spline->from); SplineFree(spline); spline = next; } } static SplineSet *SplinePointListTruncateAtY(SplineSet *spl,real y) { SplineSet *prev=NULL, *ss=spl, *nprev, *snext, *ns; Spline *spline, *next; for ( ; spl!=NULL; spl = spl->next ) { for ( spline=spl->first->next ; spline!=NULL; spline = next ) { next = spline->to->next; SplineSplitAtY(spline,y); if ( next==NULL || next->from == spl->last ) break; } } prev = NULL; y += 1/128.0; /* Small fudge factor for rounding errors */ for ( spl=ss; spl!=NULL; spl = snext ) { snext = spl->next; nprev = spl; for ( spline=spl->first->next ; spline!=NULL ; spline = next ) { next = spline->to->next; if ( spline->from->me.y<=y && spline->from->nextcp.y<=y && spline->to->me.y<=y && spline->to->prevcp.y<=y ) { if ( spline->to==spl->first ) break; else continue; } /* Remove this spline */ while ( next!=NULL && next->from!=spl->first && (next->from->me.y>y || next->from->nextcp.y>y || next->to->me.y>y || next->to->prevcp.y>y) ) next = next->to->next; if ( next==NULL || next->from==spl->first ) { /* The area to be removed continues to the end of splineset */ if ( spline==spl->first->next ) { /* Remove entire splineset */ if ( prev==NULL ) ss = snext; else prev->next = snext; SplinePointListFree(spl); nprev = prev; break; } spl->last = spline->from; spline->from->next = NULL; spl->first->prev = NULL; _SplinesRemoveBetween(spline,next,spl); break; } else { if ( spline==spl->first->next ) { /* Remove everything before next */ next->from->prev = NULL; spl->first->next = NULL; spl->first = next->from; } else if ( spl->first==spl->last ) { /* rotate splineset so break is at start and end. */ spl->last = spline->from; spl->first = next->from; next->from->prev = NULL; spline->from->next = NULL; } else { /* Split into two splinesets and remove all between */ ns = chunkalloc(sizeof(SplineSet)); ns->first = next->from; ns->last = spl->last; spl->last = spline->from; spline->from->next = NULL; spl->first->prev = NULL; next->from->prev = NULL; ns->last->next = NULL; ns->next = spl->next; spl->next = ns->next; nprev = ns; } _SplinesRemoveBetween(spline,next,spl); spl = nprev; } } prev = nprev; } return( ss ); } static SplineSet *SplinePointListMerge(SplineSet *old,SplineSet *new) { /* Merge the new splineset into the old looking for any endpoints */ /* common to both, and if any are found, merging them */ SplineSet *test1, *next; SplineSet *oldold = old; while ( new!=NULL ) { next = new->next; if ( new->first!=new->last ) { for ( test1=oldold; test1!=NULL; test1=test1->next ) { if ( test1->first!=test1->last && ((test1->first->me.x==new->first->me.x && test1->first->me.y==new->first->me.y) || (test1->last->me.x==new->first->me.x && test1->last->me.y==new->first->me.y) || (test1->first->me.x==new->last->me.x && test1->first->me.y==new->last->me.y) || (test1->last->me.x==new->last->me.x && test1->last->me.y==new->last->me.y)) ) break; } if ( test1!=NULL ) { if ((test1->first->me.x==new->first->me.x && test1->first->me.y==new->first->me.y) || (test1->last->me.x==new->last->me.x && test1->last->me.y==new->last->me.y)) SplineSetReverse(new); if ( test1->last->me.x==new->first->me.x && test1->last->me.y==new->first->me.y ) { test1->last->nextcp = new->first->nextcp; test1->last->nonextcp = new->first->nonextcp; test1->last->nextcpdef = new->first->nextcpdef; test1->last->next = new->first->next; new->first->next->from = test1->last; test1->last = new->last; SplinePointFree(new->first); new->first = new->last = NULL; SplinePointListFree(new); if ( test1->last->me.x == test1->first->me.x && test1->last->me.y == test1->first->me.y ) { test1->first->prevcp = test1->last->prevcp; test1->first->noprevcp = test1->last->noprevcp; test1->first->prevcpdef = test1->last->prevcpdef; test1->last->prev->to = test1->first; SplinePointFree(test1->last); test1->last = test1->first; } } else { test1->first->prevcp = new->last->prevcp; test1->first->noprevcp = new->last->noprevcp; test1->first->prevcpdef = new->last->prevcpdef; test1->first->prev = new->last->prev; new->last->prev->to = test1->first; test1->first = new->first; SplinePointFree(new->last); new->first = new->last = NULL; SplinePointListFree(new); } new = next; continue; } } new->next = old; old = new; new = next; } return( old ); } #define Round_Up_At .5 static void TileLine(TD *td) { int tilecnt=1, i; bigreal scale=1, y; real trans[6]; SplineSet *new; int use_first=false, use_last=false, use_isolated=false; switch ( td->tilescale ) { case ts_tile: if ( td->path->first->prev!=NULL ) /* Closed contours have no ends => all tiles intermediate */ tilecnt = ceil( td->plength/td->bb.maxy ); else if ( td->plength<=td->ibb.maxy ) { tilecnt = 1; use_isolated = true; } else if ( td->plength<=td->fbb.maxy ) { tilecnt = 1; use_first = true; } else if ( td->plength<=td->fbb.maxy+td->lbb.maxy && td->firsttile!=NULL && td->lasttile!=NULL ) { tilecnt = 2; use_first = use_last = true; } else { use_first = (td->firsttile!=NULL); use_last = (td->lasttile!=NULL); tilecnt = use_first+use_last+ceil( (td->plength-td->fbb.maxy-td->lbb.maxy)/td->bb.maxy ); } break; case ts_scale: if ( td->isolatedtile!=NULL ) { use_isolated = true; scale = td->plength/td->ibb.maxy; } else scale = td->plength/td->bb.maxy; break; case ts_tilescale: tilecnt = -1; if ( td->path->first->prev!=NULL ) /* Closed contours have no ends => all tiles intermediate */ scale = td->plength/td->bb.maxy; else if ( td->isolatedtile!=NULL && (( td->firsttile!=NULL && td->lasttile!=NULL && td->plengthfbb.maxy+Round_Up_At*td->lbb.maxy) || ( td->firsttile!=NULL && td->lasttile==NULL && td->plengthfbb.maxy+Round_Up_At*td->bb.maxy) || ( td->firsttile==NULL && td->lasttile==NULL && td->plength<(1+Round_Up_At)*td->bb.maxy)) ) { use_isolated = true; scale = td->plength/td->ibb.maxy; tilecnt = 1; } else if ( td->firsttile!=NULL && td->lasttile!=NULL ) { if ( td->plengthfbb.maxy+Round_Up_At*td->lbb.maxy ) { use_first = true; tilecnt = 1; scale = td->plength/td->fbb.maxy; } else if ( td->plengthfbb.maxy+td->lbb.maxy+Round_Up_At*td->bb.maxy ) { use_first = use_last = true; tilecnt = 2; scale = 2*td->plength/(td->fbb.maxy+td->lbb.maxy); } else { use_first = use_last = true; scale = 2 + (td->plength-td->fbb.maxy-td->lbb.maxy)/td->bb.maxy; } } else if ( td->firsttile!=NULL ) { if ( td->plengthfbb.maxy+Round_Up_At*td->bb.maxy ) { use_first = true; tilecnt = 1; scale = td->plength/td->fbb.maxy; } else { use_first = true; scale = 1 + (td->plength-td->fbb.maxy)/td->bb.maxy; } } else if ( td->lasttile!=NULL ) { if ( td->plengthlbb.maxy+Round_Up_At*td->bb.maxy ) { use_last = true; tilecnt = 1; scale = td->plength/td->lbb.maxy; } else { use_last = true; scale = 1 + (td->plength-td->lbb.maxy)/td->bb.maxy; } } else scale = td->plength/td->bb.maxy; if ( tilecnt == -1 ) { tilecnt = floor( scale ); if ( tilecnt==0 ) tilecnt = 1; else if ( scale-tilecnt>Round_Up_At ) ++tilecnt; scale = td->plength/(use_first*td->fbb.maxy + use_last*td->lbb.maxy + (tilecnt-use_first-use_last)*td->bb.maxy); } break; } trans[0] = 1; trans[3] = scale; /* Only scale y */ trans[1] = trans[2] = trans[4] = trans[5] = 0; y = 0; for ( i=0; ibasetile)[which]); trans[5] = y; new = SplinePointListTransform(new,trans,tpt_AllPoints); if ( i==tilecnt-1 && td->tilescale==ts_tile ) new = SplinePointListTruncateAtY(new,td->plength); td->tileset = SplinePointListMerge(td->tileset,new); y += (&td->bb)[which].maxy*scale; } if ( td->pcnt>1 ) { /* If there are fewer tiles than there are spline elements, then we */ /* may not be able to do a good job approximating (suppose the path */ /* draws a circle, but there is just one tile, a straight line. */ /* without some extra points in the middle of that line there is no */ /* way to make a circle). So here we add some extra breaks */ /* Actually, it's worse than that. If the transition isn't smooth */ /* then we'll always want those extra points... */ TDAddPoints(td); } } static void AdjustPoint(TD *td,Spline *spline,bigreal t,TPoint *to) { bigreal x, y; bigreal pos; int low; bigreal dx, dy, c, s; int i; to->t = t; x = ((spline->splines[0].a*t+spline->splines[0].b)*t+spline->splines[0].c)*t + spline->splines[0].d; y = ((spline->splines[1].a*t+spline->splines[1].b)*t+spline->splines[1].c)*t + spline->splines[1].d; for ( i=td->pcnt-2; i>=0; --i ) if ( RealNearish(y,td->joins[i].sofar) ) break; if ( i>=0 ) { bigreal x1,y1, x2, y2, dx1, dx2, dy1, dy2; x1 = td->joins[i].dx + td->joins[i].c1*x; y1 = td->joins[i].dy + td->joins[i].s1*x; dx1 = -td->joins[i].s1; dy1 = td->joins[i].c1; x2 = td->joins[i].dx + td->joins[i].c2*x; y2 = td->joins[i].dy + td->joins[i].s2*x; dx2 = -td->joins[i].s2; dy2 = td->joins[i].c2; /* there are two lines at a join and I need to find the intersection */ if ( dy2>-.00001 && dy2<.00001 ) { to->y = y2; if ( dy1>-.00001 && dy1<.00001 ) /* essentially parallel */ to->x = x2; else to->x = x1 + dx1*(y2-y1)/dy1; } else { bigreal s=(dy1*dx2/dy2-dx1); if ( s>-.00001 && s<.00001 ) { /* essentially parallel */ to->x = x1; to->y = y1; } else { bigreal t1 = (x1-x2- dx2/dy2*(y1-y2))/s; to->x = x1 + dx1*t1; to->y = y1 + dy1*t1; } } } else { pos = y/td->plength; if ( pos<0 ) pos=0; /* should not happen */ if ( pos>1 ) pos = 1; pos *= td->nsamples; low = floor(pos); pos -= low; if ( pos==0 || low==td->nsamples ) { dx = td->samples[low].dx; dy = td->samples[low].dy; c = td->samples[low].c; s = td->samples[low].s; } else { dx = (td->samples[low].dx*(1-pos) + td->samples[low+1].dx*pos); dy = (td->samples[low].dy*(1-pos) + td->samples[low+1].dy*pos); c = (td->samples[low].c*(1-pos) + td->samples[low+1].c*pos); s = (td->samples[low].s*(1-pos) + td->samples[low+1].s*pos); } to->x = dx + c*x; to->y = dy + s*x; } } static SplinePoint *TDMakePoint(TD *td,Spline *old,real t) { TPoint tp; SplinePoint *new; AdjustPoint(td,old,t,&tp); new = chunkalloc(sizeof(SplinePoint)); new->me.x = tp.x; new->me.y = tp.y; new->nextcp = new->me; new->prevcp = new->me; new->nonextcp = new->noprevcp = true; new->nextcpdef = new->prevcpdef = false; return( new ); } static Spline *AdjustSpline(TD *td,Spline *old,SplinePoint *newfrom,SplinePoint *newto, int order2) { TPoint tps[15]; int i; bigreal t; if ( newfrom==NULL ) newfrom = TDMakePoint(td,old,0); if ( newto==NULL ) newto = TDMakePoint(td,old,1); for ( i=1, t=1/16.0; i<16; ++i, t+= 1/16.0 ) AdjustPoint(td,old,t,&tps[i-1]); return( ApproximateSplineFromPoints(newfrom,newto,tps,15, order2) ); } static void AdjustSplineSet(TD *td,int order2) { SplineSet *spl, *last=NULL, *new; Spline *spline, *s; SplinePoint *lastsp, *nextsp, *sp; if ( td->result!=NULL ) for ( last=td->result ; last->next!=NULL; last = last->next ); for ( spl=td->tileset; spl!=NULL; spl=spl->next ) { new = chunkalloc(sizeof(SplineSet)); if ( last==NULL ) td->result = new; else last->next = new; last = new; new->first = lastsp = TDMakePoint(td,spl->first->next,0); nextsp = NULL; for ( spline=spl->first->next; spline!=NULL; spline=spline->to->next ) { if ( spline->to==spl->first ) nextsp = new->first; s = AdjustSpline(td,spline,lastsp,nextsp,order2); lastsp = s->to; if ( nextsp!=NULL ) break; } if ( lastsp!=new->first && RealNearish(lastsp->me.x,new->first->me.x) && RealNearish(lastsp->me.y,new->first->me.y) ) { new->first->prev = lastsp->prev; new->first->prevcp = lastsp->prevcp; new->first->noprevcp = lastsp->noprevcp; new->first->prevcpdef = lastsp->prevcpdef; lastsp->prev->to = new->first; new->last = new->first; SplinePointFree(lastsp); } else new->last = lastsp; for ( sp = new->first; sp!=NULL; ) { SplinePointCatagorize(sp); if ( sp->next==NULL ) break; sp = sp->next->to; if ( sp==new->first ) break; } } } static void TileSplineSets(TD *td,SplineSet **head,int order2) { SplineSet *prev=NULL, *spl, *next; for ( spl = *head; spl!=NULL; spl = next ) { next = spl->next; if ( td->doallpaths || PointListIsSelected(spl)) { if ( prev==NULL ) *head = next; else prev->next = next; td->path = spl; if ( TDMakeSamples(td)) { TileLine(td); AdjustSplineSet(td,order2); free( td->samples ); free( td->joins ); SplinePointListsFree(td->tileset); } SplinePointListFree(td->path); td->path = td->tileset = NULL; } else prev = spl; } SPLCatagorizePoints(td->result); if ( *head==NULL ) *head = td->result; else { for ( spl= *head; spl->next!=NULL; spl = spl->next ); spl->next = td->result; } } static void TileIt(SplineSet **head,struct tiledata *td, int doall,int order2) { real trans[6]; int i; SplineSet *thistile; td->doallpaths = doall; trans[0] = trans[3] = 1; trans[1] = trans[2] = 0; for ( i=0; i<4; ++i ) if ( (thistile = (&td->basetile)[i])!=NULL ) { DBounds *bb = &(&td->bb)[i]; SplineSetFindBounds(thistile,bb); trans[5] = (&td->include_white)[i]&ws_include ? 0 : -bb->miny; trans[4] = -bb->minx; if ( td->tilepos==tp_center ) trans[4] -= (bb->maxx-bb->minx)/2; else if ( td->tilepos==tp_left ) trans[4] = -bb->maxx; if ( trans[4]!=0 || trans[5]!=0 ) SplinePointListTransform(thistile,trans,tpt_AllPoints); SplineSetFindBounds(thistile,bb); } td->tileset = td->result = NULL; TileSplineSets(td,head,order2); } static enum tilepos tilepos=tp_center; static enum tilescale tilescale=ts_tilescale; static int include_whitespace[4] = {0,0,0,0}; static SplineSet *last_tiles[4]; #define CID_Center 1001 #define CID_Left 1002 #define CID_Right 1003 #define CID_Tile 1011 #define CID_TileScale 1012 #define CID_Scale 1013 #define CID_IncludeWhiteSpaceBelowTile 1021 /* +[0...3] */ #define CID_FirstTile 1025 /* +[0...3] for the other tiles */ static void TPDSubResize(TilePathDlg *tpd, GEvent *event) { int width, height; int i; if ( !event->u.resize.sized ) return; width = event->u.resize.size.width; height = event->u.resize.size.height; if ( width!=tpd->cv_width || height!=tpd->cv_height ) { tpd->cv_width = width; tpd->cv_height = height; for ( i=0; i<4; ++i ) { CharView *cv = (&tpd->cv_first)+i; GDrawResize(cv->gw,width,height); } } GDrawSync(NULL); GDrawProcessPendingEvents(NULL); } static char *tilenames[] = { N_("First"), N_("Medial"), N_("Final"), N_("Isolated") }; static void TPDDraw(TilePathDlg *tpd, GWindow pixmap, GEvent *event) { GRect r,pos; int i; GDrawSetLineWidth(pixmap,0); for ( i=0; i<4; ++i ) { CharView *cv = (&tpd->cv_first)+i; GGadgetGetSize(GWidgetGetControl(tpd->gw,CID_FirstTile+i),&pos); r.x = pos.x; r.y = pos.y-1; r.width = pos.width+1; r.height = pos.height+1; GDrawDrawRect(pixmap,&r,0); GDrawSetFont(pixmap,cv->inactive ? tpd->plain : tpd->bold); GDrawDrawBiText8(pixmap,r.x,pos.y-2-tpd->fh+tpd->as,_(tilenames[i]),-1,NULL,0); } } static void TPDMakeActive(TilePathDlg *tpd,CharView *cv) { int i; if ( tpd==NULL ) return; for ( i=0; i<4; ++i ) (&tpd->cv_first)[i].inactive = true; cv->inactive = false; GDrawSetUserData(tpd->gw,cv); for ( i=0; i<4; ++i ) GDrawRequestExpose((&tpd->cv_first)[i].v,NULL,false); GDrawRequestExpose(tpd->gw,NULL,false); } static void TPDChar(TilePathDlg *tpd, GEvent *event) { int i; for ( i=0; i<4; ++i ) if ( !(&tpd->cv_first)[i].inactive ) break; if ( event->u.chr.keysym==GK_Tab || event->u.chr.keysym==GK_BackTab ) { if ( event->u.chr.keysym==GK_Tab ) ++i; else --i; if ( i<0 ) i=3; else if ( i>3 ) i = 0; TPDMakeActive(tpd,(&tpd->cv_first)+i); } else CVChar((&tpd->cv_first)+i,event); } static void TPD_DoClose(struct cvcontainer *cvc) { TilePathDlg *tpd = (TilePathDlg *) cvc; int i; for ( i=0; i<4; ++i ) { SplineChar *msc = &(&tpd->sc_first)[i]; SplinePointListsFree(msc->layers[0].splines); SplinePointListsFree(msc->layers[1].splines); free( msc->layers ); } tpd->done = true; } static int tpd_sub_e_h(GWindow gw, GEvent *event) { TilePathDlg *tpd; if ( event->type==et_destroy ) return( true ); tpd = (TilePathDlg *) ((CharViewBase *) GDrawGetUserData(gw))->container; switch ( event->type ) { case et_resize: if ( event->u.resize.sized ) TPDSubResize(tpd,event); break; case et_char: TPDChar(tpd,event); break; } return( true ); } static int tpd_e_h(GWindow gw, GEvent *event) { TilePathDlg *tpd; int i; if ( event->type == et_destroy ) return( true ); tpd = (TilePathDlg *) ((CharViewBase *) GDrawGetUserData(gw))->container; switch ( event->type ) { case et_expose: TPDDraw(tpd, gw, event); break; case et_char: TPDChar(tpd,event); break; case et_close: TPD_DoClose((struct cvcontainer *) tpd); break; case et_create: break; case et_map: for ( i=0; i<4; ++i ) { CharView *cv = (&tpd->cv_first)+i; if ( !cv->inactive ) { if ( event->u.map.is_visible ) CVPaletteActivate(cv); else CVPalettesHideIfMine(cv); break; } } /* tpd->isvisible = event->u.map.is_visible; */ break; } return( true ); } static int TilePathD_Cancel(GGadget *g, GEvent *e) { if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) { TilePathDlg *tpd = (TilePathDlg *) (((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container); TPD_DoClose(&tpd->base); } return( true ); } static int TPD_Useless(SplineSet *ss) { DBounds bb; if ( ss==NULL ) return( true ); SplineSetFindBounds(ss,&bb); return( bb.maxy==bb.miny ); } static int TilePathD_OK(GGadget *g, GEvent *e) { if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) { TilePathDlg *tpd = (TilePathDlg *) (((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container); struct tiledata *td = tpd->td; if ( GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_Center)) ) td->tilepos = tp_center; else if ( GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_Left)) ) td->tilepos = tp_left; else td->tilepos = tp_right; if ( GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_Tile)) ) td->tilescale = ts_tile; else if ( GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_TileScale)) ) td->tilescale = ts_tilescale; else td->tilescale = ts_scale; if ( TPD_Useless(tpd->sc_medial.layers[ly_fore].splines) && (td->tilescale!=ts_scale || TPD_Useless(tpd->sc_isolated.layers[ly_fore].splines)) ) { if ( td->tilescale == ts_scale ) ff_post_error(_("Bad Tile"),_("You must specify an isolated (or medial) tile")); else ff_post_error(_("Bad Tile"),_("You must specify a medial tile")); return( true ); } tilepos = td->tilepos; tilescale = td->tilescale; td->firsttile = tpd->sc_first.layers[ly_fore].splines; tpd->sc_first.layers[ly_fore].splines = NULL; include_whitespace[0] = td->finclude_white = GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_IncludeWhiteSpaceBelowTile+0))?ws_include:0; td->basetile = tpd->sc_medial.layers[ly_fore].splines; tpd->sc_medial.layers[ly_fore].splines = NULL; include_whitespace[1] = td->include_white = GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_IncludeWhiteSpaceBelowTile+1))?ws_include:0; td->lasttile = tpd->sc_final.layers[ly_fore].splines; tpd->sc_final.layers[ly_fore].splines = NULL; include_whitespace[2] = td->linclude_white = GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_IncludeWhiteSpaceBelowTile+2))?ws_include:0; td->isolatedtile = tpd->sc_isolated.layers[ly_fore].splines; tpd->sc_isolated.layers[ly_fore].splines = NULL; include_whitespace[3] = td->iinclude_white = GGadgetIsChecked(GWidgetGetControl(tpd->gw,CID_IncludeWhiteSpaceBelowTile+3))?ws_include:0; TPD_DoClose(&tpd->base); tpd->oked = true; } return( true ); } static int TPD_Can_Navigate(struct cvcontainer *cvc, enum nav_type type) { return( false ); } static int TPD_Can_Open(struct cvcontainer *cvc) { return( false ); } static SplineFont *SF_Of_TPD(struct cvcontainer *foo) { return( NULL ); } struct cvcontainer_funcs tilepath_funcs = { cvc_tilepath, (void (*) (struct cvcontainer *cvc,CharViewBase *cv)) TPDMakeActive, (void (*) (struct cvcontainer *cvc,void *)) TPDChar, TPD_Can_Navigate, NULL, TPD_Can_Open, TPD_DoClose, SF_Of_TPD }; static void TPDInit(TilePathDlg *tpd,SplineFont *sf) { int i; memset(tpd,0,sizeof(*tpd)); tpd->base.funcs = &tilepath_funcs; tpd->base_sf = sf; for ( i=0; i<4; ++i ) { SplineChar *msc = &(&tpd->sc_first)[i]; CharView *mcv = &(&tpd->cv_first)[i]; msc->orig_pos = i; msc->unicodeenc = -1; msc->name = i==0 ? "First" : i==1 ? "Medial" : i==2 ? "Last": "Isolated"; msc->parent = &tpd->dummy_sf; msc->layer_cnt = 2; msc->layers = gcalloc(2,sizeof(Layer)); LayerDefault(&msc->layers[0]); LayerDefault(&msc->layers[1]); tpd->chars[i] = msc; mcv->b.sc = msc; mcv->b.layerheads[dm_fore] = &msc->layers[ly_fore]; mcv->b.layerheads[dm_back] = &msc->layers[ly_back]; mcv->b.layerheads[dm_grid] = &tpd->dummy_sf.grid; msc->layers[ly_fore].splines = SplinePointListCopy(last_tiles[i]); mcv->b.drawmode = dm_fore; mcv->b.container = (struct cvcontainer *) tpd; mcv->inactive = i!=0; } tpd->dummy_sf.glyphs = tpd->chars; tpd->dummy_sf.glyphcnt = tpd->dummy_sf.glyphmax = 4; tpd->dummy_sf.pfminfo.fstype = -1; tpd->dummy_sf.fontname = tpd->dummy_sf.fullname = tpd->dummy_sf.familyname = "dummy"; tpd->dummy_sf.weight = "Medium"; tpd->dummy_sf.origname = "dummy"; tpd->dummy_sf.ascent = sf->ascent; tpd->dummy_sf.descent = sf->descent; tpd->dummy_sf.layers = tpd->layerinfo; tpd->dummy_sf.layer_cnt = 2; tpd->layerinfo[ly_back].order2 = sf->layers[ly_back].order2; tpd->layerinfo[ly_back].name = _("Back"); tpd->layerinfo[ly_fore].order2 = sf->layers[ly_fore].order2; tpd->layerinfo[ly_fore].name = _("Fore"); tpd->dummy_sf.grid.order2 = sf->grid.order2; tpd->dummy_sf.anchor = NULL; tpd->dummy_sf.fv = (FontViewBase *) &tpd->dummy_fv; tpd->dummy_fv.b.active_layer = ly_fore; tpd->dummy_fv.b.sf = &tpd->dummy_sf; tpd->dummy_fv.b.selected = tpd->sel; tpd->dummy_fv.cbw = tpd->dummy_fv.cbh = default_fv_font_size+1; tpd->dummy_fv.magnify = 1; tpd->dummy_fv.b.map = &tpd->dummy_map; tpd->dummy_map.map = tpd->map; tpd->dummy_map.backmap = tpd->backmap; tpd->dummy_map.enccount = tpd->dummy_map.encmax = tpd->dummy_map.backmax = 4; tpd->dummy_map.enc = &custom; } static int TileAsk(struct tiledata *td,SplineFont *sf) { TilePathDlg tpd; GRect pos; GWindow gw; GWindowAttrs wattrs; GGadgetCreateData gcd[24], boxes[5], *harray[8], *varray[5], *rhvarray[4][5], *chvarray[4][5]; GTextInfo label[24]; FontRequest rq; int as, ds, ld; int i,k; static GFont *font = NULL, *bold=NULL; TPDInit( &tpd,sf ); tpd.td = td; memset(td,0,sizeof(*td)); memset(&wattrs,0,sizeof(wattrs)); wattrs.mask = wam_events|wam_cursor|wam_isdlg|wam_restrict|wam_undercursor|wam_utf8_wtitle; wattrs.is_dlg = true; wattrs.restrict_input_to_me = 1; wattrs.undercursor = 1; wattrs.event_masks = -1; wattrs.cursor = ct_pointer; wattrs.utf8_window_title = _("Tile Path"); pos.width = 600; pos.height = 300; tpd.gw = gw = GDrawCreateTopWindow(NULL,&pos,tpd_e_h,&tpd.cv_first,&wattrs); if ( font==NULL ) { memset(&rq,0,sizeof(rq)); rq.utf8_family_name = SANS_UI_FAMILIES; rq.point_size = 12; rq.weight = 400; font = GDrawInstanciateFont(NULL,&rq); font = GResourceFindFont("TilePath.Font",font); GDrawDecomposeFont(font, &rq); rq.weight = 700; bold = GDrawInstanciateFont(NULL,&rq); bold = GResourceFindFont("TilePath.BoldFont",bold); } tpd.plain = font; tpd.bold = bold; GDrawFontMetrics(tpd.plain,&as,&ds,&ld); tpd.fh = as+ds; tpd.as = as; memset(&label,0,sizeof(label)); memset(&gcd,0,sizeof(gcd)); memset(&boxes,0,sizeof(boxes)); k = 0; gcd[k].gd.flags = gg_visible|gg_enabled ; /* This space is for the menubar */ gcd[k].gd.pos.height = 18; gcd[k].gd.pos.width = 20; gcd[k++].creator = GSpacerCreate; for ( i=0; i<4; ++i ) { gcd[k].gd.pos.height = tpd.fh; gcd[k].gd.flags = gg_visible | gg_enabled; gcd[k++].creator = GSpacerCreate; chvarray[0][i] = &gcd[k-1]; gcd[k].gd.pos.width = gcd[k].gd.pos.height = 200; gcd[k].gd.flags = gg_visible | gg_enabled; gcd[k].gd.cid = CID_FirstTile+i; gcd[k].gd.u.drawable_e_h = tpd_sub_e_h; gcd[k++].creator = GDrawableCreate; chvarray[1][i] = &gcd[k-1]; gcd[k].gd.pos.x = gcd[0].gd.pos.x; gcd[k].gd.pos.y = gcd[6].gd.pos.y+24; gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup; if ( include_whitespace[i] ) gcd[k].gd.flags |= gg_cb_on; label[k].text = (unichar_t *) _("Include Whitespace below Tile"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.popup_msg = (unichar_t *) _("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"); gcd[k].gd.cid = CID_IncludeWhiteSpaceBelowTile+i; gcd[k++].creator = GCheckBoxCreate; chvarray[2][i] = &gcd[k-1]; } chvarray[0][4] = chvarray[1][4] = chvarray[2][4] = chvarray[3][0] = NULL; gcd[k].gd.pos.x = 6; gcd[k].gd.pos.y = 6; gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup; gcd[k].gd.mnemonic = 'L'; label[k].text = (unichar_t *) _("_Left"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.popup_msg = (unichar_t *) _("The tiles should be placed to the left of the path\nas the path is traced from its start point to its end"); gcd[k].gd.cid = CID_Left; gcd[k++].creator = GRadioCreate; rhvarray[0][0] = &gcd[k-1]; gcd[k].gd.pos.x = 60; gcd[k].gd.pos.y = gcd[0].gd.pos.y; gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup; gcd[k].gd.mnemonic = 'C'; label[k].text = (unichar_t *) _("C_enter"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.popup_msg = (unichar_t *) _("The tiles should be centered on the path"); gcd[k].gd.cid = CID_Center; gcd[k++].creator = GRadioCreate; rhvarray[0][1] = &gcd[k-1]; gcd[k].gd.pos.x = 140; gcd[k].gd.pos.y = gcd[1].gd.pos.y; gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup; gcd[k].gd.mnemonic = 'R'; label[k].text = (unichar_t *) _("_Right"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.popup_msg = (unichar_t *) _("The tiles should be placed to the right of the path\nas the path is traced from its start point to its end"); gcd[k].gd.cid = CID_Right; gcd[k++].creator = GRadioCreate; rhvarray[0][2] = &gcd[k-1]; rhvarray[0][3] = GCD_Glue; rhvarray[0][4] = NULL; gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = GDrawPointsToPixels(NULL,gcd[2].gd.pos.y+20); gcd[k].gd.pos.width = pos.width-10; gcd[k].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels ; gcd[k++].creator = GLineCreate; rhvarray[1][0] = &gcd[k-1]; rhvarray[1][1] = rhvarray[1][2] = GCD_ColSpan; rhvarray[1][3] = GCD_Glue; rhvarray[1][4] = NULL; gcd[k].gd.pos.x = gcd[0].gd.pos.x; gcd[k].gd.pos.y = gcd[2].gd.pos.y+24; gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup; gcd[k].gd.mnemonic = 'T'; label[k].text = (unichar_t *) _("_Tile"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.popup_msg = (unichar_t *) _("Multiple copies of the selection should be tiled onto the path"); gcd[k].gd.cid = CID_Tile; gcd[k++].creator = GRadioCreate; rhvarray[2][0] = &gcd[k-1]; gcd[k].gd.pos.x = gcd[1].gd.pos.x; gcd[k].gd.pos.y = gcd[4].gd.pos.y; gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup; gcd[k].gd.mnemonic = 'a'; label[k].text = (unichar_t *) _("Sc_ale & Tile"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.popup_msg = (unichar_t *) _("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."); gcd[k].gd.cid = CID_TileScale; gcd[k++].creator = GRadioCreate; rhvarray[2][1] = &gcd[k-1]; gcd[k].gd.pos.x = gcd[2].gd.pos.x; gcd[k].gd.pos.y = gcd[5].gd.pos.y; gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup; gcd[k].gd.mnemonic = 'S'; label[k].text = (unichar_t *) _("_Scale"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.popup_msg = (unichar_t *) _("The selection should be scaled so that it will cover the path's length"); gcd[k].gd.cid = CID_Scale; gcd[k++].creator = GRadioCreate; rhvarray[2][2] = &gcd[k-1]; rhvarray[2][3] = GCD_Glue; rhvarray[2][4] = NULL; rhvarray[3][0] = NULL; gcd[k-7+tilepos].gd.flags |= gg_cb_on; gcd[k-3+tilescale].gd.flags |= gg_cb_on; label[k].text = (unichar_t *) _("_OK"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.flags = gg_enabled|gg_visible|gg_but_default; gcd[k].gd.handle_controlevent = TilePathD_OK; gcd[k++].creator = GButtonCreate; label[k].text = (unichar_t *) _("_Cancel"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.flags = gg_enabled|gg_visible|gg_but_cancel; gcd[k].gd.handle_controlevent = TilePathD_Cancel; gcd[k++].creator = GButtonCreate; harray[0] = GCD_Glue; harray[1] = &gcd[k-2]; harray[2] = GCD_Glue; harray[3] = GCD_Glue; harray[4] = &gcd[k-1]; harray[5] = GCD_Glue; harray[6] = NULL; boxes[2].gd.flags = gg_enabled|gg_visible; boxes[2].gd.u.boxelements = harray; boxes[2].creator = GHBoxCreate; boxes[3].gd.flags = gg_enabled|gg_visible; boxes[3].gd.u.boxelements = chvarray[0]; boxes[3].creator = GHVBoxCreate; boxes[4].gd.flags = gg_enabled|gg_visible; boxes[4].gd.u.boxelements = rhvarray[0]; boxes[4].creator = GHVBoxCreate; varray[0] = &gcd[0]; varray[1] = &boxes[3]; varray[2] = &boxes[4]; varray[3] = &boxes[2]; varray[4] = NULL; boxes[0].gd.flags = gg_enabled|gg_visible; boxes[0].gd.u.boxelements = varray; boxes[0].creator = GVBoxCreate; GGadgetsCreate(gw,boxes); TPDCharViewInits(&tpd,CID_FirstTile); GHVBoxSetExpandableRow(boxes[0].ret,1); GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame); GHVBoxSetExpandableRow(boxes[3].ret,1); GHVBoxSetPadding(boxes[3].ret, 2, 2); GHVBoxSetExpandableCol(boxes[4].ret,gb_expandglue); GGadgetResize(boxes[0].ret,pos.width,pos.height); TPDMakeActive(&tpd,&tpd.cv_medial); GDrawResize(gw,1000,400); /* Force a resize event */ GDrawSetVisible(tpd.gw,true); while ( !tpd.done ) GDrawProcessOneEvent(NULL); for ( i=0; i<4; ++i ) { CharView *cv = &tpd.cv_first + i; if ( cv->backimgs!=NULL ) { GDrawDestroyWindow(cv->backimgs); cv->backimgs = NULL; } CVPalettesHideIfMine(cv); } GDrawDestroyWindow(tpd.gw); return( tpd.oked ); } static void TDFree(struct tiledata *td) { int i; for ( i=0 ; i<4; ++i ) SplinePointListFree( last_tiles[i]); last_tiles[0] = td->firsttile; last_tiles[1] = td->basetile; last_tiles[2] = td->lasttile; last_tiles[3] = td->isolatedtile; } void CVTile(CharView *cv) { struct tiledata td; int anypoints, anyrefs, anyimages, anyattach; CVAnySel(cv,&anypoints,&anyrefs,&anyimages,&anyattach); if ( anyrefs || anyimages || anyattach ) return; if ( !TileAsk(&td,cv->b.sc->parent)) return; CVPreserveState((CharViewBase *) cv); TileIt(&cv->b.layerheads[cv->b.drawmode]->splines,&td, !anypoints,cv->b.layerheads[cv->b.drawmode]->order2); CVCharChangedUpdate(&cv->b); TDFree(&td); cv->lastselpt = NULL; } void SCTile(SplineChar *sc,int layer) { struct tiledata td; if ( sc->layers[layer].splines==NULL ) return; if ( !TileAsk(&td,sc->parent)) return; SCPreserveLayer(sc,layer,false); TileIt(&sc->layers[layer].splines,&td, true, sc->layers[layer].order2); SCCharChangedUpdate(sc,layer); TDFree(&td); } void FVTile(FontView *fv) { struct tiledata td; SplineChar *sc; int i, gid; int layer = fv->b.active_layer; for ( i=0; ib.map->enccount; ++i ) if ( fv->b.selected[i] && (gid=fv->b.map->map[i])!=-1 && (sc=fv->b.sf->glyphs[gid])!=NULL && sc->layers[ly_fore].splines!=NULL ) break; if ( i==fv->b.map->enccount ) return; if ( !TileAsk(&td,fv->b.sf)) return; SFUntickAll(fv->b.sf); for ( i=0; ib.map->enccount; ++i ) if ( fv->b.selected[i] && (gid=fv->b.map->map[i])!=-1 && (sc=fv->b.sf->glyphs[gid])!=NULL && !sc->ticked && sc->layers[layer].splines!=NULL ) { sc->ticked = true; SCPreserveLayer(sc,layer,false); TileIt(&sc->layers[layer].splines,&td, true, fv->b.sf->layers[layer].order2); SCCharChangedUpdate(sc,layer); } TDFree(&td); } /* ************************************************************************** */ /* ****************************** Tile Pattern ****************************** */ /* ************************************************************************** */ static void SCTilePattern(SplineChar *sc, int layer, Layer *pattern, BasePoint *size, IPoint *cnt) { Layer *ly; SplineSet *ss, *lastss; RefChar *ref, *lastref, *r; real transform[6]; int i,j; if ( pattern==NULL || cnt->x<=0 || cnt->y<=0 || size->x<=0 || size->y<=0 || (pattern->splines==NULL && (pattern->refs==NULL || layer==ly_grid ))) return; SCPreserveLayer(sc,layer,false); if ( layer==ly_grid ) ly = &sc->parent->grid; else if ( layer<0 ) ly = &sc->layers[ly_fore]; else ly = &sc->layers[layer]; memset(transform,0,sizeof(transform)); transform[0] = transform[3] = 1.0; if ( ly->splines==NULL ) lastss = NULL; else for ( lastss=ly->splines; lastss->next!=NULL; lastss=lastss->next ); if ( ly->refs==NULL ) lastref = NULL; else for ( lastref=ly->refs; lastref->next!=NULL; lastref=lastref->next ); for ( i=0; ix; ++i ) for ( j=0; jy; ++j ) { transform[4] = i*size->x; transform[5] = j*size->y; if ( pattern->splines!=NULL ) { ss = SplinePointListTransform(SplinePointListCopy(pattern->splines),transform,tpt_AllPoints); if ( lastss!=NULL ) lastss->next = ss; else ly->splines = ss; for ( ; ss->next!=NULL; ss=ss->next ); lastss = ss; } if ( pattern->refs!=NULL ) { ref = RefCharsCopy(pattern->refs); ref->transform[4] += transform[4]; ref->transform[5] += transform[5]; if ( lastref!=NULL ) lastref->next = ref; else ly->refs = ref; for ( r=ref; r!=NULL; r=r->next ) { SCMakeDependent(sc,r->sc); SCReinstanciateRefChar(sc,r,layer); lastref = r; } } } SCCharChangedUpdate(sc,layer); } static SplineSet *last_pattern=NULL; static BasePoint patternSize; static IPoint patternRepeat = { 10, 10 }; #define CID_Tile 1011 /* Same as above */ #define CID_PatternWidth 1012 #define CID_PatternHeight 1013 #define CID_XRepeat 1014 #define CID_YRepeat 1015 static void PTDSubResize(TilePathDlg *tpd, GEvent *event) { int width, height; if ( !event->u.resize.sized ) return; width = event->u.resize.size.width; height = event->u.resize.size.height; if ( width!=tpd->cv_width || height!=tpd->cv_height ) { tpd->cv_width = width; tpd->cv_height = height; { CharView *cv = &tpd->cv_first; GDrawResize(cv->gw,width,height); } } GDrawSync(NULL); GDrawProcessPendingEvents(NULL); } static void PTDDraw(TilePathDlg *tpd, GWindow pixmap, GEvent *event) { GRect r,pos; GDrawSetLineWidth(pixmap,0); GGadgetGetSize(GWidgetGetControl(tpd->gw,CID_Tile),&pos); r.x = pos.x; r.y = pos.y-1; r.width = pos.width+1; r.height = pos.height+1; GDrawDrawRect(pixmap,&r,0); } static void PTDMakeActive(TilePathDlg *ptd,CharView *cv) { if ( ptd==NULL ) return; cv->inactive = false; GDrawSetUserData(ptd->gw,cv); } static void PTDChar(TilePathDlg *ptd, GEvent *event) { CVChar(&ptd->cv_first,event); } static void PTD_DoClose(struct cvcontainer *cvc) { TilePathDlg *ptd = (TilePathDlg *) cvc; SplineChar *msc = &ptd->sc_first; SplinePointListsFree(msc->layers[0].splines); RefCharsFree(msc->layers[0].refs); SplinePointListsFree(msc->layers[1].splines); RefCharsFree(msc->layers[1].refs); free( msc->layers ); ptd->done = true; } static int ptd_sub_e_h(GWindow gw, GEvent *event) { TilePathDlg *ptd; if ( event->type==et_destroy ) return( true ); ptd = (TilePathDlg *) ((CharViewBase *) GDrawGetUserData(gw))->container; switch ( event->type ) { case et_resize: if ( event->u.resize.sized ) PTDSubResize(ptd,event); break; case et_char: PTDChar(ptd,event); break; } return( true ); } static int ptd_e_h(GWindow gw, GEvent *event) { TilePathDlg *ptd; if ( event->type == et_destroy ) return( true ); ptd = (TilePathDlg *) ((CharViewBase *) GDrawGetUserData(gw))->container; switch ( event->type ) { case et_expose: PTDDraw(ptd, gw, event); break; case et_char: PTDChar(ptd,event); break; case et_close: PTD_DoClose((struct cvcontainer *) ptd); break; case et_timer: { SplineSet *ss; char buffer[32]; int err; for ( ss = ptd->sc_first.layers[ly_back].splines; ss!=NULL; ss=ss->next ) { if ( ss->first->prev!=NULL || ss->first->next==NULL || ss->first->next->to!=ss->last ) continue; err = 0; if ( ss->first->me.x==ss->last->me.x ) { /* Don't make the change if they are the same (or there's an */ /* error) as the user might be busy editing and that would */ /* lose his/her work */ if ( !RealNear(GetCalmReal8(ptd->gw,CID_PatternWidth,_("Width"),&err), ss->first->me.x) && !err ) { sprintf( buffer,"%g",ss->first->me.x); GGadgetSetTitle8(GWidgetGetControl(ptd->gw,CID_PatternWidth),buffer); } } else if ( ss->first->me.y==ss->last->me.y ) { if ( !RealNear(GetCalmReal8(ptd->gw,CID_PatternHeight,_("Height"),&err), ss->first->me.y) && !err ) { sprintf( buffer,"%g",ss->first->me.y); GGadgetSetTitle8(GWidgetGetControl(ptd->gw,CID_PatternHeight),buffer); } } } #if 0 if ( GetCalmInt8(ptd->gw,CID_PatternWidth,_("Width"),&err)!=ptd->sc_first.width && !err ) { sprintf( buffer,"%d",ptd->sc_first.width); GGadgetSetTitle8(GWidgetGetControl(ptd->gw,CID_PatternWidth),buffer); } #endif } break; case et_map: { CharView *cv = &ptd->cv_first; if ( !cv->inactive ) { if ( event->u.map.is_visible ) CVPaletteActivate(cv); else CVPalettesHideIfMine(cv); break; } } /* ptd->isvisible = event->u.map.is_visible; */ break; } return( true ); } static int PTD_RefigureBackground(GGadget *g, GEvent *e) { if ( e==NULL || (e->type==et_controlevent && e->u.control.subtype == et_textchanged )) { TilePathDlg *ptd = (TilePathDlg *) (((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container); int hsize, vsize; int err = 0; SplinePoint *sp1, *sp2; SplineSet *ss; hsize = GetCalmReal8(ptd->gw,CID_PatternWidth,_("Width"),&err); vsize = GetCalmReal8(ptd->gw,CID_PatternHeight,_("Height"),&err); if ( err ) return( true ); ptd->sc_first.width = hsize; SplinePointListFree(ptd->sc_first.layers[ly_back].splines); sp1 = SplinePointCreate(-1000,vsize); sp2 = SplinePointCreate(2000,vsize); SplineMake(sp1,sp2,ptd->sc_first.layers[ly_back].order2); ss = chunkalloc(sizeof(SplineSet)); ss->first = sp1; ss->last = sp2; ptd->sc_first.layers[ly_back].splines = ss; sp1 = SplinePointCreate(hsize,-1000); sp2 = SplinePointCreate(hsize,2000); SplineMake(sp1,sp2,ptd->sc_first.layers[ly_back].order2); ss = chunkalloc(sizeof(SplineSet)); ss->first = sp1; ss->last = sp2; ptd->sc_first.layers[ly_back].splines->next = ss; GDrawRequestExpose(ptd->cv_first.v,NULL,false); } return( true ); } static int PTD_Cancel(GGadget *g, GEvent *e) { if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) { TilePathDlg *ptd = (TilePathDlg *) (((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container); PTD_DoClose(&ptd->base); } return( true ); } static int PTD_OK(GGadget *g, GEvent *e) { if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) { TilePathDlg *ptd = (TilePathDlg *) (((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container); struct tiledata *td = ptd->td; int err = 0; td->patternSize.x = GetReal8(ptd->gw,CID_PatternWidth,_("Width"),&err); td->patternSize.y = GetReal8(ptd->gw,CID_PatternHeight,_("Height"),&err); td->repeatCnt.x = GetInt8(ptd->gw,CID_XRepeat,_("X Repeat Count"),&err); td->repeatCnt.y = GetInt8(ptd->gw,CID_YRepeat,_("Y Repeat Count"),&err); if ( err ) return( true ); if ( td->patternSize.x<=0 || td->patternSize.y<=0 ) { ff_post_error(_("Bad Pattern Size"),_("The pattern size (width & height) must be a positive number")); return( true ); } if ( td->repeatCnt.x<=0 || td->repeatCnt.y<=0 || td->repeatCnt.x>2000 || td->repeatCnt.y>2000 ) { ff_post_error(_("Bad Pattern Size"),_("The repeat counts must be positive numbers")); return( true ); } td->pattern = gcalloc(1,sizeof(Layer)); td->pattern->splines = SplinePointListCopy(ptd->sc_first.layers[ly_fore].splines); td->pattern->refs = RefCharsCopy(ptd->sc_first.layers[ly_fore].refs); if ( td->pattern->splines==NULL && td->pattern->refs==NULL ) { ff_post_error(_("Bad Pattern"),_("You must specify a pattern")); return( true ); } PTD_DoClose(&ptd->base); ptd->oked = true; } return( true ); } static int PTD_Can_Navigate(struct cvcontainer *cvc, enum nav_type type) { return( false ); } static int PTD_Can_Open(struct cvcontainer *cvc) { return( false ); } static SplineFont *SF_Of_PTD(struct cvcontainer *cvc) { TilePathDlg *ptd = (TilePathDlg *) cvc; return( ptd->base_sf ); } struct cvcontainer_funcs tilepattern_funcs = { cvc_multiplepattern, (void (*) (struct cvcontainer *cvc,CharViewBase *cv)) PTDMakeActive, (void (*) (struct cvcontainer *cvc,void *)) PTDChar, PTD_Can_Navigate, NULL, PTD_Can_Open, PTD_DoClose, SF_Of_PTD }; static void PTDInit(TilePathDlg *ptd,SplineFont *sf) { memset(ptd,0,sizeof(*ptd)); ptd->base.funcs = &tilepattern_funcs; ptd->base_sf = sf; { SplineChar *msc = &ptd->sc_first; CharView *mcv = &ptd->cv_first; msc->orig_pos = 0; msc->unicodeenc = -1; msc->name = "Pattern"; msc->parent = &ptd->dummy_sf; msc->layer_cnt = 2; msc->layers = gcalloc(2,sizeof(Layer)); LayerDefault(&msc->layers[0]); LayerDefault(&msc->layers[1]); ptd->chars[0] = msc; mcv->b.sc = msc; mcv->b.layerheads[dm_fore] = &msc->layers[ly_fore]; mcv->b.layerheads[dm_back] = &msc->layers[ly_back]; mcv->b.layerheads[dm_grid] = &ptd->dummy_sf.grid; msc->layers[ly_fore].splines = SplinePointListCopy(last_pattern); mcv->b.drawmode = dm_fore; mcv->b.container = (struct cvcontainer *) ptd; mcv->inactive = false; } ptd->dummy_sf.glyphs = ptd->chars; ptd->dummy_sf.glyphcnt = ptd->dummy_sf.glyphmax = 1; ptd->dummy_sf.pfminfo.fstype = -1; ptd->dummy_sf.fontname = ptd->dummy_sf.fullname = ptd->dummy_sf.familyname = "dummy"; ptd->dummy_sf.weight = "Medium"; ptd->dummy_sf.origname = "dummy"; ptd->dummy_sf.ascent = sf->ascent; ptd->dummy_sf.descent = sf->descent; ptd->dummy_sf.layers = ptd->layerinfo; ptd->dummy_sf.layer_cnt = 2; ptd->layerinfo[ly_back].order2 = sf->layers[ly_back].order2; ptd->layerinfo[ly_back].name = _("Size"); ptd->layerinfo[ly_fore].order2 = sf->layers[ly_fore].order2; ptd->layerinfo[ly_fore].name = _("Pattern"); ptd->dummy_sf.grid.order2 = sf->grid.order2; ptd->dummy_sf.anchor = NULL; ptd->dummy_sf.fv = (FontViewBase *) &ptd->dummy_fv; ptd->dummy_fv.b.active_layer = ly_fore; ptd->dummy_fv.b.sf = &ptd->dummy_sf; ptd->dummy_fv.b.selected = ptd->sel; ptd->dummy_fv.cbw = ptd->dummy_fv.cbh = default_fv_font_size+1; ptd->dummy_fv.magnify = 1; ptd->dummy_fv.b.map = &ptd->dummy_map; ptd->dummy_map.map = ptd->map; ptd->dummy_map.backmap = ptd->backmap; ptd->dummy_map.enccount = ptd->dummy_map.encmax = ptd->dummy_map.backmax = 1; ptd->dummy_map.enc = &custom; } static int TilePatternAsk(struct tiledata *td,SplineFont *sf) { TilePathDlg ptd; GRect pos; GWindow gw; GWindowAttrs wattrs; GGadgetCreateData gcd[24], boxes[5], *harray1[8], *harray2[8], *varray[10], *harray[8]; GTextInfo label[24]; int k; char width[30], height[30], xr[30], yr[30]; PTDInit( &ptd,sf ); ptd.td = td; memset(td,0,sizeof(*td)); memset(&wattrs,0,sizeof(wattrs)); wattrs.mask = wam_events|wam_cursor|wam_isdlg|wam_restrict|wam_undercursor|wam_utf8_wtitle; wattrs.is_dlg = true; wattrs.restrict_input_to_me = 1; wattrs.undercursor = 1; wattrs.event_masks = -1; wattrs.cursor = ct_pointer; wattrs.utf8_window_title = _("Tile Pattern"); pos.width = 600; pos.height = 300; ptd.gw = gw = GDrawCreateTopWindow(NULL,&pos,ptd_e_h,&ptd.cv_first,&wattrs); memset(&label,0,sizeof(label)); memset(&gcd,0,sizeof(gcd)); memset(&boxes,0,sizeof(boxes)); k = 0; gcd[k].gd.flags = gg_visible|gg_enabled ; /* This space is for the menubar */ gcd[k].gd.pos.height = 18; gcd[k].gd.pos.width = 20; gcd[k++].creator = GSpacerCreate; varray[0] = &gcd[k-1]; gcd[k].gd.pos.width = gcd[k].gd.pos.height = 200; gcd[k].gd.flags = gg_visible | gg_enabled; gcd[k].gd.cid = CID_Tile; gcd[k].gd.u.drawable_e_h = ptd_sub_e_h; gcd[k++].creator = GDrawableCreate; varray[1] = &gcd[k-1]; gcd[k].gd.flags = gg_visible | gg_enabled; label[k].text = (unichar_t *) _("Pattern Size:"); label[k].text_is_1byte = true; gcd[k].gd.label = &label[k]; gcd[k++].creator = GLabelCreate; varray[2] = &gcd[k-1]; gcd[k].gd.flags = gg_visible | gg_enabled; label[k].text = (unichar_t *) _("Width:"); label[k].text_is_1byte = true; gcd[k].gd.label = &label[k]; gcd[k++].creator = GLabelCreate; harray1[0] = &gcd[k-1]; if ( last_pattern==NULL ) sprintf( width, "%d", sf->ascent+sf->descent ); else sprintf( width, "%g", patternSize.x ); gcd[k].gd.flags = gg_visible | gg_enabled; label[k].text = (unichar_t *) width; label[k].text_is_1byte = true; gcd[k].gd.label = &label[k]; gcd[k].gd.cid = CID_PatternWidth; gcd[k].gd.handle_controlevent = PTD_RefigureBackground; gcd[k++].creator = GTextFieldCreate; harray1[1] = &gcd[k-1]; gcd[k].gd.flags = gg_visible | gg_enabled; label[k].text = (unichar_t *) _("Height:"); label[k].text_is_1byte = true; gcd[k].gd.label = &label[k]; gcd[k++].creator = GLabelCreate; harray1[2] = &gcd[k-1]; if ( last_pattern==NULL ) sprintf( height, "%d", sf->ascent+sf->descent ); else sprintf( height, "%g", patternSize.y ); gcd[k].gd.flags = gg_visible | gg_enabled; label[k].text = (unichar_t *) height; label[k].text_is_1byte = true; gcd[k].gd.label = &label[k]; gcd[k].gd.cid = CID_PatternHeight; gcd[k].gd.handle_controlevent = PTD_RefigureBackground; gcd[k++].creator = GTextFieldCreate; harray1[3] = &gcd[k-1]; harray1[4] = NULL; boxes[2].gd.flags = gg_enabled|gg_visible; boxes[2].gd.u.boxelements = harray1; boxes[2].creator = GHBoxCreate; varray[3] = &boxes[2]; gcd[k].gd.flags = gg_visible | gg_enabled; label[k].text = (unichar_t *) _("Repeat Counts:"); label[k].text_is_1byte = true; gcd[k].gd.label = &label[k]; gcd[k++].creator = GLabelCreate; varray[4] = &gcd[k-1]; gcd[k].gd.flags = gg_visible | gg_enabled; label[k].text = (unichar_t *) _("X:"); label[k].text_is_1byte = true; gcd[k].gd.label = &label[k]; gcd[k++].creator = GLabelCreate; harray2[0] = &gcd[k-1]; sprintf( xr, "%d", patternRepeat.x ); gcd[k].gd.flags = gg_visible | gg_enabled; label[k].text = (unichar_t *) xr; label[k].text_is_1byte = true; gcd[k].gd.label = &label[k]; gcd[k].gd.cid = CID_XRepeat; gcd[k++].creator = GTextFieldCreate; harray2[1] = &gcd[k-1]; gcd[k].gd.flags = gg_visible | gg_enabled; label[k].text = (unichar_t *) _("Y:"); label[k].text_is_1byte = true; gcd[k].gd.label = &label[k]; gcd[k++].creator = GLabelCreate; harray2[2] = &gcd[k-1]; sprintf( yr, "%d", patternRepeat.y ); gcd[k].gd.flags = gg_visible | gg_enabled; label[k].text = (unichar_t *) yr; label[k].text_is_1byte = true; gcd[k].gd.label = &label[k]; gcd[k].gd.cid = CID_YRepeat; gcd[k++].creator = GTextFieldCreate; harray2[3] = &gcd[k-1]; harray2[4] = NULL; boxes[3].gd.flags = gg_enabled|gg_visible; boxes[3].gd.u.boxelements = harray2; boxes[3].creator = GHBoxCreate; varray[5] = &boxes[3]; label[k].text = (unichar_t *) _("_OK"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.flags = gg_enabled|gg_visible|gg_but_default; gcd[k].gd.handle_controlevent = PTD_OK; gcd[k++].creator = GButtonCreate; label[k].text = (unichar_t *) _("_Cancel"); label[k].text_is_1byte = true; label[k].text_in_resource = true; gcd[k].gd.label = &label[k]; gcd[k].gd.flags = gg_enabled|gg_visible|gg_but_cancel; gcd[k].gd.handle_controlevent = PTD_Cancel; gcd[k++].creator = GButtonCreate; harray[0] = GCD_Glue; harray[1] = &gcd[k-2]; harray[2] = GCD_Glue; harray[3] = GCD_Glue; harray[4] = &gcd[k-1]; harray[5] = GCD_Glue; harray[6] = NULL; boxes[4].gd.flags = gg_enabled|gg_visible; boxes[4].gd.u.boxelements = harray; boxes[4].creator = GHBoxCreate; varray[6] = &boxes[4]; varray[7] = NULL; boxes[0].gd.flags = gg_enabled|gg_visible; boxes[0].gd.u.boxelements = varray; boxes[0].creator = GVBoxCreate; GGadgetsCreate(gw,boxes); PTDCharViewInits(&ptd,CID_Tile); GHVBoxSetExpandableRow(boxes[0].ret,1); GHVBoxSetExpandableCol(boxes[4].ret,gb_expandgluesame); GGadgetResize(boxes[0].ret,pos.width,pos.height); PTD_RefigureBackground(GWidgetGetControl(ptd.gw,CID_PatternWidth),NULL); PTDMakeActive(&ptd,&ptd.cv_first); GDrawResize(gw,400,400); /* Force a resize event */ GDrawSetVisible(ptd.gw,true); GDrawRequestTimer(ptd.gw,1000,1000,NULL); while ( !ptd.done ) GDrawProcessOneEvent(NULL); { CharView *cv = &ptd.cv_first; if ( cv->backimgs!=NULL ) { GDrawDestroyWindow(cv->backimgs); cv->backimgs = NULL; } CVPalettesHideIfMine(cv); } GDrawDestroyWindow(ptd.gw); return( ptd.oked ); } static void TPFree(struct tiledata *td) { SplinePointListFree(last_pattern); last_pattern = td->pattern->splines; RefCharsFree(td->pattern->refs); free(td->pattern); patternSize = td->patternSize; patternRepeat = td->repeatCnt; } void CVPatternTile(CharView *cv) { struct tiledata td; if ( !TilePatternAsk(&td,cv->b.sc->parent)) return; SCTilePattern(cv->b.sc,CVLayer((CharViewBase *) cv),td.pattern, &td.patternSize, &td.repeatCnt); TPFree(&td); cv->lastselpt = NULL; } #if 0 void SCPatternTile(SplineChar *sc,int layer) { struct tiledata td; if ( !TilePatternAsk(&td,sc->parent)) return; SCTilePattern(sc,layer,td.pattern, &td.patternSize, &td.repeatCnt); TPFree(&td); } #endif void FVPatternTile(FontView *fv) { struct tiledata td; SplineChar *sc; int i, gid; int layer = fv->b.active_layer; for ( i=0; ib.map->enccount; ++i ) if ( fv->b.selected[i] && (gid=fv->b.map->map[i])!=-1 && (sc=fv->b.sf->glyphs[gid])!=NULL ) break; if ( i==fv->b.map->enccount ) return; if ( !TilePatternAsk(&td,fv->b.sf)) return; SFUntickAll(fv->b.sf); for ( i=0; ib.map->enccount; ++i ) if ( fv->b.selected[i] && (gid=fv->b.map->map[i])!=-1 && (sc=fv->b.sf->glyphs[gid])!=NULL && !sc->ticked ) { sc->ticked = true; SCTilePattern(sc,layer,td.pattern, &td.patternSize, &td.repeatCnt); } TPFree(&td); } #endif /* FONTFORGE_CONFIG_TILEPATH */