Back to index

tetex-bin  3.0
paths.c
Go to the documentation of this file.
00001 /* $XConsortium: paths.c,v 1.4 91/10/10 11:18:40 rws Exp $ */
00002 /* Copyright International Business Machines, Corp. 1991
00003  * All Rights Reserved
00004  * Copyright Lexmark International, Inc. 1991
00005  * All Rights Reserved
00006  *
00007  * License to use, copy, modify, and distribute this software and its
00008  * documentation for any purpose and without fee is hereby granted,
00009  * provided that the above copyright notice appear in all copies and that
00010  * both that copyright notice and this permission notice appear in
00011  * supporting documentation, and that the name of IBM or Lexmark not be
00012  * used in advertising or publicity pertaining to distribution of the
00013  * software without specific, written prior permission.
00014  *
00015  * IBM AND LEXMARK PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY WARRANTIES OF
00016  * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO ANY
00017  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
00018  * AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  THE ENTIRE RISK AS TO THE
00019  * QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING ANY DUTY TO SUPPORT
00020  * OR MAINTAIN, BELONGS TO THE LICENSEE.  SHOULD ANY PORTION OF THE
00021  * SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM OR LEXMARK) ASSUMES THE
00022  * ENTIRE COST OF ALL SERVICING, REPAIR AND CORRECTION.  IN NO EVENT SHALL
00023  * IBM OR LEXMARK BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
00024  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
00025  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
00026  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
00027  * THIS SOFTWARE.
00028  */
00029  /* PATHS    CWEB         V0021 ********                             */
00030 /*
00031 :h1 id=paths.PATHS Module - Path Operator Handler
00032  
00033 This is the module that is responsible for building and transforming
00034 path lists.
00035  
00036 &author. Jeffrey B. Lotspiech (lotspiech@almaden.ibm.com)
00037  
00038  
00039 :h3.Include Files
00040  
00041 The included files are:
00042 */
00043  
00044                              /*   after the system includes  (dsr)           */
00045 #include  "types.h"
00046 #include  "objects.h"
00047 #include  "spaces.h"
00048 #include  "paths.h"
00049 #include  "regions.h"      /* understands about Union                      */
00050 #include  "fonts.h"        /* understands about TEXTTYPEs                  */
00051 #include  "pictures.h"     /* understands about handles                    */
00052 #include  "strokes.h"      /* understands how to coerce stroke paths       */
00053 #include  "trig.h"
00054 
00055 #ifdef KPATHSEA
00056 static int UnClose P1H(struct segment *);
00057 extern void DumpText P1H(struct segment *);
00058 #else
00059 static Unclose();
00060 #endif
00061 
00062 /*
00063 :h3.Routines Available to the TYPE1IMAGER User
00064  
00065 The PATHS routines that are made available to the outside user are:
00066 */
00067  
00068 /*SHARED LINE(S) ORIGINATED HERE*/
00069 /*
00070 :h3.Functions Provided to Other Modules
00071  
00072 The path routines that are made available to other TYPE1IMAGER modules
00073 are defined here:
00074 */
00075  
00076 /*SHARED LINE(S) ORIGINATED HERE*/
00077 /*
00078 NOTE:  because of the casts put in the macros for Loc, ArcCA, Conic,
00079 RoundConic, PathSegment, and JoinSegment, we cannot use the macro names
00080 when the functions are actually defined.  We have to use the unique
00081 names with their unique first two characters.  Thus, if anyone in the
00082 future ever decided to change the first two characters, it would not be
00083 enough just to change the macro (as it would for most other functions).
00084 He would have to also change the function definition.
00085 */
00086 /*
00087 :h3.Macros Provided to Other Modules
00088  
00089 The CONCAT macro is defined here and used in the STROKES module.  See
00090 :hdref refid=pathmac..
00091 */
00092  
00093 /*SHARED LINE(S) ORIGINATED HERE*/
00094  
00095 /*
00096 :h2.Path Segment Structures
00097  
00098 A path is represented as a linked list of the following structure:
00099 */
00100  
00101 /*SHARED LINE(S) ORIGINATED HERE*/
00102 /*
00103 When 'link' is NULL, we are at the last segment in the path (surprise!).
00104  
00105 'last' is only non-NULL on the first segment of a path,
00106 for all the other segments 'last' == NULL.  We test for a non-NULL
00107 'last' (ISPATHANCHOR predicate) when we are given an alleged path
00108 to make sure the user is not trying to pull a fast one on us.
00109  
00110 A path may be a collection of disjoint paths.  Every break in the
00111 disjoint path is represented by a MOVETYPE segment.
00112  
00113 Closed paths are discussed in :hdref refid=close..
00114  
00115 :h3.CopyPath() - Physically Duplicating a Path
00116  
00117 This simple function illustrates moving through the path linked list.
00118 Duplicating a segment just involves making a copy of it, except for
00119 text, which has some auxilliary things involved.  We don't feel
00120 competent to duplicate text in this module, so we call someone who
00121 knows how (in the FONTS module).
00122 */
00123 struct segment *CopyPath(p0)
00124        register struct segment *p0;  /* path to duplicate                    */
00125 {
00126        register struct segment *p,*n,*last,*anchor;
00127  
00128        for (p = p0, anchor = NULL; p != NULL; p = p->link) {
00129  
00130                ARGCHECK((!ISPATHTYPE(p->type) || (p != p0 && p->last != NULL)),
00131                        "CopyPath: invalid segment", p, NULL, (0), struct segment *);
00132  
00133                if (p->type == TEXTTYPE)
00134                        n = (struct segment *) CopyText(p);
00135                else
00136                        n = (struct segment *)Allocate(p->size, p, 0);
00137                n->last = NULL;
00138                if (anchor == NULL)
00139                        anchor = n;
00140                else
00141                        last->link = n;
00142                last = n;
00143        }
00144 /*
00145 At this point we have a chain of newly allocated segments hanging off
00146 'anchor'.  We need to make sure the first segment points to the last:
00147 */
00148        if (anchor != NULL) {
00149                n->link = NULL;
00150                anchor->last = n;
00151        }
00152  
00153        return(anchor);
00154 }
00155 /*
00156 :h3.KillPath() - Destroying a Path
00157  
00158 Destroying a path is simply a matter of freeing each segment in the
00159 linked list.  Again, we let the experts handle text.
00160 */
00161 void KillPath(p)
00162        register struct segment *p;  /* path to destroy                       */
00163 {
00164        register struct segment *linkp;  /* temp register holding next segment*/
00165  
00166        /* return conditional based on reference count 3-26-91 PNM */
00167        if ( (--(p->references) > 1) ||
00168           ( (p->references == 1) && !ISPERMANENT(p->flag) ) )
00169            return;
00170  
00171        while (p != NULL) {
00172                if (!ISPATHTYPE(p->type)) {
00173                        ArgErr("KillPath: bad segment", p, NULL);
00174                        return;
00175                }
00176                linkp = p->link;
00177                if (p->type == TEXTTYPE)
00178                        KillText(p);
00179                else
00180                        Free(p);
00181                p = linkp;
00182        }
00183 }
00184  
00185 /*
00186 :h2 id=location."location" Objects
00187  
00188 The TYPE1IMAGER user creates and destroys objects of type "location".  These
00189 objects locate points for the primitive path operators.  We play a trick
00190 here and store these objects in the same "segment" structure used for
00191 paths, with a type field == MOVETYPE.
00192  
00193 This allows the Line() operator, for example, to be very trivial:
00194 It merely stamps its input structure as a LINETYPE and returns it to the
00195 caller--assuming, of course, the input structure was not permanent (as
00196 it usually isn't).
00197  
00198 :h3.The "movesegment" Template Structure
00199  
00200 This template is used as a generic segment structure for Allocate:
00201 */
00202  
00203 /* added reference field 1 to temporary template below 3-26-91 PNM */
00204 static struct segment movetemplate = { MOVETYPE, 0, 1, sizeof(struct segment), 0,
00205                 NULL, NULL, 0, 0 };
00206 /*
00207 :h3.Loc() - Create an "Invisible Line" Between (0,0) and a Point
00208  
00209 */
00210  
00211 struct segment *t1_Loc(S, x, y)
00212        register struct XYspace *S;  /* coordinate space to interpret X,Y     */
00213        DOUBLE x,y;           /* destination point                            */
00214 {
00215        register struct segment *r;
00216  
00217  
00218        IfTrace3((MustTraceCalls),"..Loc(S=%z, x=%f, y=%f)\n", S, &x, &y);
00219  
00220        r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
00221        TYPECHECK("Loc", S, SPACETYPE, r, (0), struct segment *);
00222  
00223        r->last = r;
00224        r->context = S->context;
00225        (*S->convert)(&r->dest, S, x, y);
00226        ConsumeSpace(S);
00227        return(r);
00228 }
00229 /*
00230 :h3.ILoc() - Loc() With Integer Arguments
00231  
00232 */
00233 struct segment *ILoc(S, x, y)
00234        register struct XYspace *S;  /* coordinate space to interpret X,Y     */
00235        register int x,y;        /* destination point                         */
00236 {
00237        register struct segment *r;
00238  
00239        IfTrace3((MustTraceCalls),"..ILoc(S=%z, x=%d, y=%d)\n",
00240                                     S, (LONG) x, (LONG) y);
00241        r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
00242        TYPECHECK("Loc", S, SPACETYPE, r, (0), struct segment *);
00243  
00244        r->last = r;
00245        r->context = S->context;
00246        (*S->iconvert)(&r->dest, S, (LONG) x, (LONG) y);
00247        ConsumeSpace(S);
00248        return(r);
00249 }
00250  
00251 /*
00252 :h3.SubLoc() - Vector Subtraction of Two Locition Objects
00253  
00254 This user operator subtracts two location objects, yielding a new
00255 location object that is the result.
00256  
00257 The symmetrical function AddLoc() is totally redundent with Join(),
00258 so it is not provided.
00259 */
00260  
00261 struct segment *SubLoc(p1, p2)
00262        register struct segment *p1;
00263        register struct segment *p2;
00264 {
00265        IfTrace2((MustTraceCalls),"SubLoc(%z, %z)\n", p1, p2);
00266  
00267        ARGCHECK(!ISLOCATION(p1), "SubLoc: bad first arg", p1, NULL, (0), struct segment *);
00268        ARGCHECK(!ISLOCATION(p2), "SubLoc: bad second arg", p2, NULL, (0), struct segment *);
00269        p1 = UniquePath(p1);
00270        p1->dest.x -= p2->dest.x;
00271        p1->dest.y -= p2->dest.y;
00272        ConsumePath(p2);
00273        return(p1);
00274 }
00275  
00276 /*
00277 :h2.Straight Line Segments
00278  
00279 :h3.PathSegment() - Create a Generic Path Segment
00280  
00281 Many routines need a LINETYPE or MOVETYPE path segment, but do not
00282 want to go through the external user's interface, because, for example,
00283 they already know the "fractpel" destination of the segment and the
00284 conversion is unnecessary.  PathSegment() is an internal routine
00285 provided to the rest of TYPE1IMAGER for handling these cases.
00286 */
00287  
00288 struct segment *t1_PathSegment(type, x, y)
00289        int type;             /* LINETYPE or MOVETYPE                         */
00290        fractpel x,y;         /* where to go to, if known                     */
00291 {
00292        register struct segment *r;  /* newly created segment                 */
00293  
00294        r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
00295        r->type = type;
00296        r->last = r;          /* last points to itself for singleton          */
00297        r->dest.x = x;
00298        r->dest.y = y;
00299        return(r);
00300 }
00301 /*
00302 :h3.Line() - Create a Line Segment Between (0,0) and a Point P
00303  
00304 This involves just creating and filling out a segment structure:
00305 */
00306 struct segment *Line(P)
00307        register struct segment *P;  /* relevant coordinate space             */
00308 {
00309  
00310        IfTrace1((MustTraceCalls),"..Line(%z)\n", P);
00311        ARGCHECK(!ISLOCATION(P), "Line: arg not a location", P, NULL, (0), struct segment *);
00312  
00313        P = UniquePath(P);
00314        P->type = LINETYPE;
00315        return(P);
00316 }
00317 /*
00318 :h2.Curved Path Segments
00319  
00320 We need more points to describe curves.  So, the structures for curved
00321 path segments are slightly different.  The first part is identical;
00322 the curved structures are larger with the extra points on the end.
00323  
00324 :h3.Bezier Segment Structure
00325  
00326 We support third order Bezier curves.  They are specified with four
00327 control points A, B, C, and D.  The curve starts at A with slope AB
00328 and ends at D with slope CD.  The curvature at the point A is inversely
00329 related to the length |AB|, and the curvature at the point D is
00330 inversely related to the length |CD|.  Point A is always point (0,0).
00331  
00332 */
00333  
00334 /*SHARED LINE(S) ORIGINATED HERE*/
00335 /*
00336 :h3.Bezier() - Generate a Bezier Segment
00337  
00338 This is just a simple matter of filling out a 'beziersegment' structure:
00339 */
00340  
00341 struct beziersegment *Bezier(B, C, D)
00342        register struct segment *B;  /* second control point                  */
00343        register struct segment *C;  /* third control point                   */
00344        register struct segment *D;  /* fourth control point (ending point)   */
00345 {
00346 /* added reference field of 1 to temporary template below 3-26-91  PNM */
00347        static struct beziersegment template =
00348                     { BEZIERTYPE, 0, 1, sizeof(struct beziersegment), 0,
00349                       NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 } };
00350  
00351        register struct beziersegment *r;  /* output segment                  */
00352  
00353        IfTrace3((MustTraceCalls),"..Bezier(%z, %z, %z)\n", B, C, D);
00354        ARGCHECK(!ISLOCATION(B), "Bezier: bad B", B, NULL, (2,C,D), struct beziersegment *);
00355        ARGCHECK(!ISLOCATION(C), "Bezier: bad C", C, NULL, (2,B,D), struct beziersegment *);
00356        ARGCHECK(!ISLOCATION(D), "Bezier: bad D", D, NULL, (2,B,C), struct beziersegment *);
00357  
00358        r = (struct beziersegment *)Allocate(sizeof(struct beziersegment), &template, 0);
00359        r->last = (struct segment *) r;
00360        r->dest.x = D->dest.x;
00361        r->dest.y = D->dest.y;
00362        r->B.x = B->dest.x;
00363        r->B.y = B->dest.y;
00364        r->C.x = C->dest.x;
00365        r->C.y = C->dest.y;
00366  
00367        ConsumePath(B);
00368        ConsumePath(C);
00369        ConsumePath(D);
00370        return(r);
00371 }
00372  
00373 /*
00374 :h2.Font "Hint" Segments
00375  
00376 :h3.Hint() - A Font 'Hint' Segment
00377  
00378 This is temporary code while we experiment with hints.
00379 */
00380  
00381 /*SHARED LINE(S) ORIGINATED HERE*/
00382 struct hintsegment *Hint(S, ref, width, orientation, hinttype, adjusttype, direction, label)
00383        struct XYspace *S;
00384        float ref;
00385        float width;
00386        char orientation;
00387        char hinttype;
00388        char adjusttype;
00389        char direction;
00390        int label;
00391 {
00392 /* added reference field of 1 to hintsegment template below 3-26-91 PNM */
00393        static struct hintsegment template = { HINTTYPE, 0, 1, sizeof(struct hintsegment), 0,
00394                                           NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 },
00395                                           ' ', ' ', ' ', ' ', 0};
00396  
00397        register struct hintsegment *r;
00398  
00399        r = (struct hintsegment *)Allocate(sizeof(struct hintsegment), &template, 0);
00400  
00401        r->orientation = orientation;
00402        if (width == 0.0)  width = 1.0;
00403  
00404        if (orientation == 'h') {
00405                (*S->convert)(&r->ref, S, 0.0, ref);
00406                (*S->convert)(&r->width, S, 0.0, width);
00407        }
00408        else if (orientation == 'v') {
00409                (*S->convert)(&r->ref, S, ref, 0.0);
00410                (*S->convert)(&r->width, S, width, 0.0);
00411        }
00412        else
00413                return((struct hintsegment *)ArgErr("Hint: orient not 'h' or 'v'", NULL, NULL));
00414        if (r->width.x < 0)  r->width.x = - r->width.x;
00415        if (r->width.y < 0)  r->width.y = - r->width.y;
00416        r->hinttype = hinttype;
00417        r->adjusttype = adjusttype;
00418        r->direction = direction;
00419        r->label = label;
00420        r->last = (struct segment *) r;
00421        ConsumeSpace(S);
00422        return(r);
00423 }
00424  
00425 /*
00426 */
00427  
00428 /*SHARED LINE(S) ORIGINATED HERE*/
00429  
00430 /*
00431 POP removes the first segment in a path 'p' and Frees it.  'p' is left
00432 pointing to the end of the path:
00433 */
00434 #define POP(p) \
00435      { register struct segment *linkp; \
00436        linkp = p->link; \
00437        if (linkp != NULL) \
00438                linkp->last = p->last; \
00439        Free(p); \
00440        p = linkp; }
00441 /*
00442 INSERT inserts a single segment in the middle of a chain.  'b' is
00443 the segment before, 'p' the segment to be inserted, and 'a' the
00444 segment after.
00445 */
00446 #define INSERT(b,p,a)  b->link=p; p->link=a; p->last=NULL
00447  
00448 /*
00449 :h3.Join() - Join Two Objects Together
00450  
00451 If these are paths, this operator simply invokes the CONCAT macro.
00452 Why so much code then, you ask?  Well we have to check for object
00453 types other than paths, and also check for certain path consistency
00454 rules.
00455 */
00456  
00457 struct segment *Join(p1, p2)
00458        register struct segment *p1,*p2;
00459 {
00460        IfTrace2((MustTraceCalls && PathDebug > 1),"..Join(%z, %z)\n", p1, p2);
00461        IfTrace2((MustTraceCalls && PathDebug <=1),"..Join(%x, %x)\n", p1, p2);
00462 /*
00463 We start with a whole bunch of very straightforward argument tests:
00464 */
00465        if (p2 != NULL) {
00466                if (!ISPATHTYPE(p2->type)) {
00467  
00468                        if (p1 == NULL)
00469                                return((struct segment *)Unique(p2));
00470  
00471                        switch (p1->type) {
00472  
00473                            case REGIONTYPE:
00474  
00475                            case STROKEPATHTYPE:
00476                                p1 = CoercePath(p1);
00477                                break;
00478  
00479                            default:
00480                                return((struct segment *)BegHandle(p1, p2));
00481                        }
00482                }
00483  
00484                ARGCHECK((p2->last == NULL), "Join: right arg not anchor", p2, NULL, (1,p1), struct segment *);
00485                p2 = UniquePath(p2);
00486  
00487 /*
00488 In certain circumstances, we don't have to duplicate a permanent
00489 location.  (We would just end up destroying it anyway).  These cases
00490 are when 'p2' begins with a move-type segment:
00491 */
00492                if (p2->type == TEXTTYPE || p2->type == MOVETYPE) {
00493                        if (p1 == NULL)
00494                                return(p2);
00495                        if (ISLOCATION(p1)) {
00496                                p2->dest.x += p1->dest.x;
00497                                p2->dest.y += p1->dest.y;
00498                                ConsumePath(p1);
00499                                return(p2);
00500                        }
00501                }
00502        }
00503        else
00504                return((struct segment *)Unique(p1));
00505  
00506        if (p1 != NULL) {
00507                if (!ISPATHTYPE(p1->type))
00508  
00509                        switch (p2->type) {
00510  
00511                            case REGIONTYPE:
00512  
00513                            case STROKEPATHTYPE:
00514                                p2 = CoercePath(p2);
00515                                break;
00516  
00517                            default:
00518                                return((struct segment *)EndHandle(p1, p2));
00519                        }
00520  
00521                ARGCHECK((p1->last == NULL), "Join: left arg not anchor", p1, NULL, (1,p2), struct segment *);
00522                p1 = UniquePath(p1);
00523        }
00524        else
00525                return(p2);
00526  
00527 /*
00528 At this point all the checking is done.  We have two temporary non-null
00529 path types in 'p1' and 'p2'.  If p1 ends with a MOVE, and p2 begins with
00530 a MOVE, we collapse the two MOVEs into one.  We enforce the rule that
00531 there may not be two MOVEs in a row:
00532 */
00533  
00534        if (p1->last->type == MOVETYPE && p2->type == MOVETYPE) {
00535                p1->last->flag |= p2->flag;
00536                p1->last->dest.x += p2->dest.x;
00537                p1->last->dest.y += p2->dest.y;
00538                POP(p2);
00539                if (p2 == NULL)
00540                        return(p1);
00541        }
00542 /*
00543 Now we check for another silly rule.  If a path has any TEXTTYPEs,
00544 then it must have only TEXTTYPEs and MOVETYPEs, and furthermore,
00545 it must begin with a TEXTTYPE.  This rule makes it easy to check
00546 for the special case of text.  If necessary, we will coerce
00547 TEXTTYPEs into paths so we don't mix TEXTTYPEs with normal paths.
00548 */
00549        if (p1->type == TEXTTYPE) {
00550                if (p2->type != TEXTTYPE && !ISLOCATION(p2))
00551                        p1 = CoerceText(p1);
00552        }
00553        else {
00554                if (p2->type == TEXTTYPE) {
00555                        if (ISLOCATION(p1)) {
00556                                p2->dest.x += p1->dest.x;
00557                                p2->dest.y += p1->dest.y;
00558                                Free(p1);
00559                                return(p2);
00560                        }
00561                        else
00562                                p2 = CoerceText(p2);
00563                }
00564        }
00565 /*
00566 Thank God!  Finally!  It's hard to believe, but we are now able to
00567 actually do the join.  This is just invoking the CONCAT macro:
00568 */
00569        CONCAT(p1, p2);
00570  
00571        return(p1);
00572 }
00573  
00574 /*
00575 :h3.JoinSegment() - Create a Path Segment and Join It to a Known Path
00576  
00577 This internal function is quicker than a full-fledged join because
00578 it can do much less checking.
00579 */
00580  
00581 struct segment *t1_JoinSegment(before, type, x, y, after)
00582        register struct segment *before;  /* path to join before new segment  */
00583        int type;             /* type of new segment (MOVETYPE or LINETYPE)   */
00584        fractpel x,y;         /* x,y of new segment                           */
00585        register struct segment *after;  /* path to join after new segment    */
00586 {
00587        register struct segment *r;  /* returned path built here              */
00588  
00589        r = PathSegment(type, x, y);
00590        if (before != NULL) {
00591                CONCAT(before, r);
00592                r = before;
00593        }
00594        else
00595                r->context = after->context;
00596        if (after != NULL)
00597                CONCAT(r, after);
00598        return(r);
00599 }
00600  
00601 /*
00602 :h2.Other Path Functions
00603  
00604 */
00605  
00606  
00607 struct segment *t1_ClosePath(p0,lastonly)
00608        register struct segment *p0;  /* path to close                        */
00609        register int lastonly;  /*  flag deciding to close all subpaths or... */
00610 {
00611        register struct segment *p,*last,*start;  /* used in looping through path */
00612        register fractpel x,y;  /* current position in path                   */
00613        register fractpel firstx,firsty;  /* start position of sub path       */
00614        register struct segment *lastnonhint;  /* last non-hint segment in path */
00615  
00616        IfTrace1((MustTraceCalls),"ClosePath(%z)\n", p0);
00617        if (p0 != NULL && p0->type == TEXTTYPE)
00618                return(UniquePath(p0));
00619        if (p0->type == STROKEPATHTYPE)
00620                return((struct segment *)Unique(p0));
00621        /*
00622        * NOTE: a null closed path is different from a null open path
00623        * and is denoted by a closed (0,0) move segment.  We make
00624        * sure this path begins and ends with a MOVETYPE:
00625        */
00626        if (p0 == NULL || p0->type != MOVETYPE)
00627                p0 = JoinSegment(NULL, MOVETYPE, 0, 0, p0);
00628        TYPECHECK("ClosePath", p0, MOVETYPE, NULL, (0), struct segment *);
00629        if (p0->last->type != MOVETYPE)
00630                p0 = JoinSegment(p0, MOVETYPE, 0, 0, NULL);
00631  
00632        p0 = UniquePath(p0);
00633  
00634 /*
00635 We now begin a loop through the path,
00636 incrementing current 'x' and 'y'.  We are searching
00637 for MOVETYPE segments (breaks in the path) that are not already closed.
00638 At each break, we insert a close segment.
00639 */
00640        for (p = p0, x = y = 0, start = NULL;
00641             p != NULL;
00642             x += p->dest.x, y += p->dest.y, last = p, p = p->link)
00643        {
00644  
00645                if (p->type == MOVETYPE) {
00646                        if (start != NULL && (lastonly?p->link==NULL:TRUE) &&
00647                              !(ISCLOSED(start->flag) && LASTCLOSED(last->flag))) {
00648                                register struct segment *r;  /* newly created */
00649  
00650                                start->flag |= ISCLOSED(ON);
00651                                r = PathSegment(LINETYPE, firstx - x,
00652                                                          firsty - y);
00653                                INSERT(last, r, p);
00654                                r->flag |= LASTCLOSED(ON);
00655                                /*< adjust 'last' if possible for a 0,0 close >*/
00656 {
00657  
00658 #define   CLOSEFUDGE    3    /* if we are this close, let's change last segment */
00659  
00660        if (r->dest.x != 0 || r->dest.y != 0) {
00661                if (r->dest.x <= CLOSEFUDGE && r->dest.x >= -CLOSEFUDGE
00662                     && r->dest.y <= CLOSEFUDGE && r->dest.y >= -CLOSEFUDGE) {
00663                        IfTrace2((PathDebug),
00664                                "ClosePath forced closed by (%p,%p)\n",
00665                                       r->dest.x, r->dest.y);
00666                        lastnonhint->dest.x += r->dest.x;
00667                        lastnonhint->dest.y += r->dest.y;
00668                        r->dest.x = r->dest.y = 0;
00669                }
00670        }
00671 }
00672                                if (p->link != NULL) {
00673                                        p->dest.x += x - firstx;
00674                                        p->dest.y += y - firsty;
00675                                        x = firstx;
00676                                        y = firsty;
00677                                }
00678                        }
00679                        start = p;
00680                        firstx = x + p->dest.x;
00681                        firsty = y + p->dest.y;
00682                }
00683                else if (p->type != HINTTYPE)
00684                        lastnonhint = p;
00685        }
00686        return(p0);
00687 }
00688 /*
00689 */
00690 /*
00691 :h2.Reversing the Direction of a Path
00692  
00693 This turned out to be more difficult than I thought at first.  The
00694 trickiness was due to the fact that closed paths must remain closed,
00695 etc.
00696  
00697 We need three subroutines:
00698 */
00699  
00700 static struct segment *SplitPath(); /* break a path at any point             */
00701 static struct segment *DropSubPath();  /* breaks a path after first sub-path */
00702 static struct segment *ReverseSubPath();  /* reverses a single sub-path      */
00703  
00704 /*
00705 :h3.Reverse() - User Operator to Reverse a Path
00706  
00707 This operator reverses the entire path.
00708 */
00709  
00710 struct segment *Reverse(p)
00711        register struct segment *p;    /* full path to reverse                */
00712 {
00713        register struct segment *r;    /* output path built here              */
00714        register struct segment *nextp;  /* contains next sub-path            */
00715  
00716        IfTrace1((MustTraceCalls),"Reverse(%z)\n", p);
00717  
00718        if (p == NULL)
00719                return(NULL);
00720  
00721        ARGCHECK(!ISPATHANCHOR(p), "Reverse: invalid path", p, NULL, (0), struct segment *);
00722  
00723        if (p->type == TEXTTYPE)
00724                p = CoerceText(p);
00725        p = UniquePath(p);
00726  
00727        r = NULL;
00728  
00729        do {
00730                nextp = DropSubPath(p);
00731                p = ReverseSubPath(p);
00732                r = Join(p, r);
00733                p = nextp;
00734  
00735        } while (p != NULL);
00736  
00737        return(r);
00738 }
00739  
00740 /*
00741 :h4.ReverseSubPath() - Subroutine to Reverse a Single Sub-Path
00742 */
00743  
00744 static struct segment *ReverseSubPath(p)
00745        register struct segment *p;  /* input path                            */
00746 {
00747        register struct segment *r;  /* reversed path will be created here    */
00748        register struct segment *nextp;  /* temporary variable used in loop   */
00749        register int wasclosed;  /* flag, path was closed                     */
00750  
00751        if (p == NULL)
00752                return(NULL);
00753  
00754        wasclosed = ISCLOSED(p->flag);
00755        r = NULL;
00756  
00757        do {
00758 /*
00759 First we reverse the direction of this segment and clean up its flags:
00760 */
00761                p->dest.x = - p->dest.x;  p->dest.y = - p->dest.y;
00762                p->flag &= ~(ISCLOSED(ON) | LASTCLOSED(ON));
00763  
00764                switch (p->type) {
00765  
00766                    case LINETYPE:
00767                    case MOVETYPE:
00768                        break;
00769  
00770                    case CONICTYPE:
00771                    {
00772 /*
00773 The logic of this is that the new M point (stored relative to the new
00774 beginning) is (M - C).  However, C ("dest") has already been reversed
00775 So, we add "dest" instead of subtracting it:
00776 */
00777                        register struct conicsegment *cp = (struct conicsegment *) p;
00778  
00779                        cp->M.x += cp->dest.x;  cp->M.y += cp->dest.y;
00780                    }
00781                        break;
00782  
00783                    case BEZIERTYPE:
00784                    {
00785                        register struct beziersegment *bp = (struct beziersegment *) p;
00786  
00787                        bp->B.x += bp->dest.x;  bp->B.y += bp->dest.y;
00788                        bp->C.x += bp->dest.x;  bp->C.y += bp->dest.y;
00789                    }
00790                        break;
00791  
00792                    case HINTTYPE:
00793                    {
00794                        register struct hintsegment *hp = (struct hintsegment *) p;
00795  
00796                        hp->ref.x = -hp->ref.x;  hp->ref.y = -hp->ref.y;
00797                    }
00798                        break;
00799  
00800                    default:
00801                        t1_abort("Reverse: bad path segment");
00802                }
00803 /*
00804 We need to reverse the order of segments too, so we break this segment
00805 off of the input path, and tack it on the front of the growing path
00806 in 'r':
00807 */
00808                nextp = p->link;
00809                p->link = NULL;
00810                p->last = p;
00811                if (r != NULL)
00812                        CONCAT(p,r);  /* leaves result in 'p'... not what we want */
00813                r = p;
00814                p = nextp;    /* advance to next segment in input path        */
00815  
00816        } while (p != NULL);
00817  
00818        if (wasclosed)
00819                r = ClosePath(r);
00820  
00821        return(r);
00822 }
00823  
00824 /*
00825 :h4.DropSubPath() - Drops the First Sub-Path Off a Path
00826  
00827 This subroutine returns the remaining sub-path(s).  While doing so, it
00828 breaks the input path after the first sub-path so that a pointer to
00829 the original path now contains the first sub-path only.
00830 */
00831  
00832 static struct segment *DropSubPath(p0)
00833        register struct segment *p0;  /* original path                        */
00834 {
00835        register struct segment *p;  /* returned remainder here               */
00836  
00837        for (p = p0; p->link != NULL; p = p->link) {
00838                if (p->link->type == MOVETYPE)
00839                        break;
00840        }
00841  
00842        return(SplitPath(p0, p));
00843 }
00844  
00845 static struct segment *SplitPath(anchor, before)
00846        register struct segment *anchor;
00847        register struct segment *before;
00848 {
00849        register struct segment *r;
00850  
00851        if (before == anchor->last)
00852                return(NULL);
00853  
00854        r = before->link;
00855        r->last = anchor->last;
00856        anchor->last = before;
00857        before->link = NULL;
00858  
00859        return(r);
00860 }
00861  
00862  
00863 /*
00864 :h3.ReverseSubPaths() - Reverse the Direction of Sub-paths Within a Path
00865  
00866 This user operator reverses the sub-paths in a path, but leaves the
00867 'move' segments unchanged.  It builds on top of the subroutines
00868 already established.
00869 */
00870  
00871 struct segment *ReverseSubPaths(p)
00872        register struct segment *p;  /* input path                            */
00873 {
00874        register struct segment *r;  /* reversed path will be created here    */
00875        register struct segment *nextp;  /* temporary variable used in loop   */
00876        int wasclosed;        /* flag; subpath was closed                     */
00877        register struct segment *nomove;  /* the part of sub-path without move segment */
00878        struct fractpoint delta;
00879  
00880        IfTrace1((MustTraceCalls),"ReverseSubPaths(%z)\n", p);
00881  
00882        if (p == NULL)
00883                return(NULL);
00884  
00885        ARGCHECK(!ISPATHANCHOR(p), "ReverseSubPaths: invalid path", p, NULL, (0), struct segment *);
00886  
00887        if (p->type == TEXTTYPE)
00888                p = CoerceText(p);
00889        if (p->type != MOVETYPE)
00890                p = JoinSegment(NULL, MOVETYPE, 0, 0, p);
00891  
00892        p = UniquePath(p);
00893  
00894        r = NULL;
00895  
00896        for (; p != NULL;) {
00897                nextp = DropSubPath(p);
00898                wasclosed = ISCLOSED(p->flag);
00899                if (wasclosed)
00900                        UnClose(p);
00901  
00902                nomove = SplitPath(p, p);
00903                r = Join(r, p);
00904  
00905                PathDelta(nomove, &delta);
00906  
00907                nomove = ReverseSubPath(nomove);
00908                p->dest.x += delta.x;
00909                p->dest.y += delta.y;
00910                if (nextp != NULL) {
00911                        nextp->dest.x += delta.x;
00912                        nextp->dest.y += delta.y;
00913                }
00914                if (wasclosed) {
00915                        nomove = ClosePath(nomove);
00916                        nextp->dest.x -= delta.x;
00917                        nextp->dest.y -= delta.y;
00918                }
00919                r = Join(r, nomove);
00920                p = nextp;
00921  
00922        }
00923  
00924        return(r);
00925 }
00926  
00927 static UnClose(p0)
00928        register struct segment *p0;
00929 {
00930        register struct segment *p;
00931  
00932        for (p=p0; p->link->link != NULL; p=p->link) { ; }
00933  
00934        if (!LASTCLOSED(p->link->flag))
00935                t1_abort("UnClose:  no LASTCLOSED");
00936  
00937        Free(SplitPath(p0, p));
00938        p0->flag &= ~ISCLOSED(ON);
00939 }
00940  
00941 /*
00942 :h2.Transforming and Putting Handles on Paths
00943  
00944 :h3.PathTransform() - Transform a Path
00945  
00946 Transforming a path involves transforming all the points.  In order
00947 that closed paths do not become "unclosed" when their relative
00948 positions are slightly changed due to loss of arithmetic precision,
00949 all point transformations are in absolute coordinates.
00950  
00951 (It might be better to reset the "absolute" coordinates every time a
00952 move segment is encountered.  This would mean that we could accumulate
00953 error from subpath to subpath, but we would be less likely to make
00954 the "big error" where our fixed point arithmetic "wraps".  However, I
00955 think I'll keep it this way until something happens to convince me
00956 otherwise.)
00957  
00958 The transform is described as a "space", that way we can use our
00959 old friend the "iconvert" function, which should be very efficient.
00960 */
00961  
00962 struct segment *PathTransform(p0, S)
00963        register struct segment *p0;    /* path to transform                  */
00964        register struct XYspace *S;     /* pseudo space to transform in       */
00965 {
00966        register struct segment *p;   /* to loop through path with            */
00967        register fractpel newx,newy;  /* current transformed position in path */
00968        register fractpel oldx,oldy;  /* current untransformed position in path */
00969        register fractpel savex,savey;  /* save path delta x,y                */
00970  
00971        p0 = UniquePath(p0);
00972  
00973        newx = newy = oldx = oldy = 0;
00974  
00975        for (p=p0; p != NULL; p=p->link) {
00976  
00977                savex = p->dest.x;   savey = p->dest.y;
00978  
00979                (*S->iconvert)(&p->dest, S, p->dest.x + oldx, p->dest.y + oldy);
00980                p->dest.x -= newx;
00981                p->dest.y -= newy;
00982  
00983                switch (p->type) {
00984  
00985                    case LINETYPE:
00986                    case MOVETYPE:
00987                        break;
00988  
00989                    case CONICTYPE:
00990                    {
00991                        register struct conicsegment *cp = (struct conicsegment *) p;
00992  
00993                        (*S->iconvert)(&cp->M, S, cp->M.x + oldx, cp->M.y + oldy);
00994                        cp->M.x -= newx;
00995                        cp->M.y -= newy;
00996                        /*
00997                        * Note roundness doesn't change... linear transform
00998                        */
00999                        break;
01000                    }
01001  
01002  
01003                    case BEZIERTYPE:
01004                    {
01005                        register struct beziersegment *bp = (struct beziersegment *) p;
01006  
01007                        (*S->iconvert)(&bp->B, S, bp->B.x + oldx, bp->B.y + oldy);
01008                        bp->B.x -= newx;
01009                        bp->B.y -= newy;
01010                        (*S->iconvert)(&bp->C, S, bp->C.x + oldx, bp->C.y + oldy);
01011                        bp->C.x -= newx;
01012                        bp->C.y -= newy;
01013                        break;
01014                    }
01015  
01016                    case HINTTYPE:
01017                    {
01018                        register struct hintsegment *hp = (struct hintsegment *) p;
01019  
01020                        (*S->iconvert)(&hp->ref, S, hp->ref.x + oldx, hp->ref.y + oldy);
01021                        hp->ref.x -= newx;
01022                        hp->ref.y -= newy;
01023                        (*S->iconvert)(&hp->width, S, hp->width.x, hp->width.y);
01024                        /* Note: width is not relative to origin */
01025                        break;
01026                    }
01027  
01028                    case TEXTTYPE:
01029                    {
01030                         XformText(p,S);
01031                         break;
01032                    }
01033  
01034                    default:
01035                        IfTrace1(TRUE,"path = %z\n", p);
01036                        t1_abort("PathTransform:  invalid segment");
01037                }
01038                oldx += savex;
01039                oldy += savey;
01040                newx += p->dest.x;
01041                newy += p->dest.y;
01042        }
01043        return(p0);
01044 }
01045  
01046 /*
01047 :h3.PathDelta() - Return a Path's Ending Point
01048 */
01049  
01050 void PathDelta(p, pt)
01051        register struct segment *p; /* input path                             */
01052        register struct fractpoint *pt; /* pointer to x,y to set              */
01053 {
01054        struct fractpoint mypoint;  /* I pass this to TextDelta               */
01055        register fractpel x,y;  /* working variables for path current point   */
01056  
01057        for (x=y=0; p != NULL; p=p->link) {
01058                x += p->dest.x;
01059                y += p->dest.y;
01060                if (p->type == TEXTTYPE) {
01061                        TextDelta(p, &mypoint);
01062                        x += mypoint.x;
01063                        y += mypoint.y;
01064                }
01065        }
01066  
01067        pt->x = x;
01068        pt->y = y;
01069 }
01070  
01071 /*
01072 :h3.BoundingBox() - Produce a Bounding Box Path
01073  
01074 This function is called by image code, when we know the size of the
01075 image in pels, and need to get a bounding box path that surrounds it.
01076 The starting/ending handle is in the lower right hand corner.
01077 */
01078 struct segment *BoundingBox(h, w)
01079        register pel h,w;     /* size of box                                  */
01080 {
01081        register struct segment *path;
01082  
01083        path = PathSegment(LINETYPE, -TOFRACTPEL(w), 0);
01084        path = JoinSegment(NULL, LINETYPE, 0, -TOFRACTPEL(h), path);
01085        path = JoinSegment(NULL, LINETYPE, TOFRACTPEL(w), 0, path);
01086        path = ClosePath(path);
01087  
01088        return(path);
01089 }
01090  
01091 /*
01092 :h2.Querying Locations and Paths
01093  
01094 :h3.QueryLoc() - Return the X,Y of a Locition
01095 */
01096  
01097 void QueryLoc(P, S, xP, yP)
01098        register struct segment *P;  /* location to query, not consumed       */
01099        register struct XYspace *S;  /* XY space to return coordinates in     */
01100        register DOUBLE *xP,*yP;  /* coordinates returned here                */
01101 {
01102        IfTrace4((MustTraceCalls),"QueryLoc(P=%z, S=%z, (%x, %x))\n",
01103                                             P, S, xP, yP);
01104        if (!ISLOCATION(P)) {
01105                ArgErr("QueryLoc: first arg not a location", P, NULL);
01106                return;
01107        }
01108        if (S->type != SPACETYPE) {
01109                ArgErr("QueryLoc: second arg not a space", S, NULL);
01110                return;
01111        }
01112        UnConvert(S, &P->dest, xP, yP);
01113 }
01114 /*
01115 :h3.QueryPath() - Find Out the Type of Segment at the Head of a Path
01116  
01117 This is a very simple routine that looks at the first segment of a
01118 path and tells the caller what it is, as well as returning the control
01119 point(s) of the path segment.  Different path segments have different
01120 number of control points.  If the caller knows that the segment is
01121 a move segment, for example, he only needs to pass pointers to return
01122 one control point.
01123 */
01124  
01125 void QueryPath(path, typeP, Bp, Cp, Dp, fP)
01126        register struct segment *path;  /* path to check                      */
01127        register int *typeP;  /* return the type of path here                 */
01128        register struct segment **Bp;  /* return location of first point      */
01129        register struct segment **Cp;  /* return location of second point     */
01130        register struct segment **Dp;  /* return location of third point      */
01131        register DOUBLE *fP;  /* return Conic sharpness                       */
01132 {
01133        register int coerced = FALSE;  /* did I coerce a text path?           */
01134  
01135        IfTrace3((MustTraceCalls), "QueryPath(%z, %x, %x, ...)\n",
01136                                              path, typeP, Bp);
01137        if (path == NULL) {
01138                *typeP = -1;
01139                return;
01140        }
01141        if (!ISPATHANCHOR(path)) {
01142                ArgErr("QueryPath: arg not a valid path", path, NULL);
01143        }
01144        if (path->type == TEXTTYPE) {
01145                path = CoerceText(path);
01146                coerced = TRUE;
01147        }
01148  
01149        switch (path->type) {
01150  
01151            case MOVETYPE:
01152                *typeP = 0;
01153                *Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
01154                break;
01155  
01156            case LINETYPE:
01157                *typeP = (LASTCLOSED(path->flag)) ? 4 : 1;
01158                *Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
01159                break;
01160  
01161            case CONICTYPE:
01162            {
01163                register struct conicsegment *cp = (struct conicsegment *) path;
01164  
01165                *typeP = 2;
01166                *Bp = PathSegment(MOVETYPE, cp->M.x, cp->M.y);
01167                *Cp = PathSegment(MOVETYPE, cp->dest.x, cp->dest.y);
01168                *fP = cp->roundness;
01169            }
01170                break;
01171  
01172            case BEZIERTYPE:
01173            {
01174                register struct beziersegment *bp = (struct beziersegment *) path;
01175  
01176                *typeP = 3;
01177                *Bp = PathSegment(MOVETYPE, bp->B.x, bp->B.y);
01178                *Cp = PathSegment(MOVETYPE, bp->C.x, bp->C.y);
01179                *Dp = PathSegment(MOVETYPE, bp->dest.x, bp->dest.y);
01180            }
01181                break;
01182  
01183            case HINTTYPE:
01184                *typeP = 5;
01185                break;
01186  
01187            default:
01188                t1_abort("QueryPath: unknown segment");
01189        }
01190        if (coerced)
01191                KillPath(path);
01192 }
01193 /*
01194 :h3.QueryBounds() - Return the Bounding Box of a Path
01195  
01196 Returns the bounding box by setting the user's variables.
01197 */
01198  
01199 void QueryBounds(p0, S, xminP, yminP, xmaxP, ymaxP)
01200        register struct segment *p0;  /* object to check for bound            */
01201        struct XYspace *S;    /* coordinate space of returned values          */
01202        DOUBLE *xminP,*yminP; /* lower left hand corner (set by routine)      */
01203        DOUBLE *xmaxP,*ymaxP; /* upper right hand corner (set by routine)     */
01204 {
01205        register struct segment *path;  /* loop variable for path segments    */
01206        register fractpel lastx,lasty;  /* loop variables:  previous endingpoint */
01207        register fractpel x,y;  /* loop variables:  current ending point      */
01208        struct fractpoint min;  /* registers to keep lower left hand corner   */
01209        struct fractpoint max;  /* registers to keep upper right hand corner  */
01210        int coerced = FALSE;  /* we have coerced the path from another object */
01211        DOUBLE x1,y1,x2,y2,x3,y3,x4,y4;  /* corners of rectangle in space X   */
01212  
01213        IfTrace2((MustTraceCalls), "QueryBounds(%z, %z,", p0, S);
01214        IfTrace4((MustTraceCalls), " %x, %x, %x, %x)\n",
01215                                   xminP, yminP, xmaxP, ymaxP);
01216        if (S->type != SPACETYPE) {
01217                ArgErr("QueryBounds:  bad XYspace", S, NULL);
01218                return;
01219        }
01220  
01221        min.x = min.y = max.x = max.y = 0;
01222        if (p0 != NULL) {
01223                if (!ISPATHANCHOR(p0)) {
01224                        switch(p0->type) {
01225                            case STROKEPATHTYPE:
01226       /* replaced DupStrokePath() with Dup() 3-26-91 PNM */
01227                                p0 = (struct segment *) DoStroke(Dup(p0));
01228                                /* no break here, we have a region in p0 */
01229                            case REGIONTYPE:
01230                                p0 = RegionBounds(p0);
01231                                break;
01232  
01233                            case PICTURETYPE:
01234                                p0 = PictureBounds(p0);
01235                                break;
01236  
01237                            default:
01238                                ArgErr("QueryBounds:  bad object", p0, NULL);
01239                                return;
01240                        }
01241                        coerced = TRUE;
01242                }
01243                if (p0->type == TEXTTYPE) {
01244     /* replaced CopyPath() with Dup() 3-26-91 PNM */
01245                        p0 = (struct segment *)CoerceText(Dup(p0));  /* there are faster ways */
01246                        coerced = TRUE;
01247                }
01248                if (p0->type == MOVETYPE) {
01249                        min.x = max.x = p0->dest.x;
01250                        min.y = max.y = p0->dest.y;
01251                }
01252        }
01253        lastx = lasty = 0;
01254  
01255        for (path = p0; path != NULL; path = path->link) {
01256  
01257                x = lastx + path->dest.x;
01258                y = lasty + path->dest.y;
01259  
01260                switch (path->type) {
01261  
01262                    case LINETYPE:
01263                        break;
01264  
01265                    case CONICTYPE:
01266                    {
01267                        register struct conicsegment *cp = (struct conicsegment *) path;
01268                        register fractpel Mx = lastx + cp->M.x;
01269                        register fractpel My = lasty + cp->M.y;
01270                        register fractpel deltax = 0.5 * cp->roundness * cp->dest.x;
01271                        register fractpel deltay = 0.5 * cp->roundness * cp->dest.y;
01272                        register fractpel Px = Mx - deltax;
01273                        register fractpel Py = My - deltay;
01274                        register fractpel Qx = Mx + deltax;
01275                        register fractpel Qy = My + deltay;
01276  
01277  
01278                        if (Mx < min.x) min.x = Mx;
01279                        else if (Mx > max.x) max.x = Mx;
01280                        if (My < min.y) min.y = My;
01281                        else if (My > max.y) max.y = My;
01282  
01283                        if (Px < min.x) min.x = Px;
01284                        else if (Px > max.x) max.x = Px;
01285                        if (Py < min.y) min.y = Py;
01286                        else if (Py > max.y) max.y = Py;
01287  
01288                        if (Qx < min.x) min.x = Qx;
01289                        else if (Qx > max.x) max.x = Qx;
01290                        if (Qy < min.y) min.y = Qy;
01291                        else if (Qy > max.y) max.y = Qy;
01292                    }
01293                        break;
01294  
01295  
01296                    case MOVETYPE:
01297                        /*
01298                        * We can't risk adding trailing Moves to the
01299                        * bounding box:
01300                        */
01301                        if (path->link == NULL)
01302                                goto done;  /* God forgive me                 */
01303                        break;
01304  
01305                    case BEZIERTYPE:
01306                    {
01307                        register struct beziersegment *bp = (struct beziersegment *) path;
01308                        register fractpel Bx = lastx + bp->B.x;
01309                        register fractpel By = lasty + bp->B.y;
01310                        register fractpel Cx = lastx + bp->C.x;
01311                        register fractpel Cy = lasty + bp->C.y;
01312  
01313                        if (Bx < min.x) min.x = Bx;
01314                        else if (Bx > max.x) max.x = Bx;
01315                        if (By < min.y) min.y = By;
01316                        else if (By > max.y) max.y = By;
01317  
01318                        if (Cx < min.x) min.x = Cx;
01319                        else if (Cx > max.x) max.x = Cx;
01320                        if (Cy < min.y) min.y = Cy;
01321                        else if (Cy > max.y) max.y = Cy;
01322                    }
01323                        break;
01324  
01325                    case HINTTYPE:
01326                        break;
01327                    default:
01328                        t1_abort("QueryBounds: unknown type");
01329                }
01330  
01331                if (x < min.x) min.x = x;
01332                else if (x > max.x) max.x = x;
01333                if (y < min.y) min.y = y;
01334                else if (y > max.y) max.y = y;
01335  
01336                lastx = x;   lasty = y;
01337        }
01338 done:
01339        UnConvert(S, &min, &x1, &y1);
01340        UnConvert(S, &max, &x4, &y4);
01341        x = min.x;  min.x = max.x; max.x = x;
01342        UnConvert(S, &min, &x2, &y2);
01343        UnConvert(S, &max, &x3, &y3);
01344  
01345        *xminP = *xmaxP = x1;
01346        if (x2 < *xminP)  *xminP = x2;
01347        else if (x2 > *xmaxP)  *xmaxP = x2;
01348        if (x3 < *xminP)  *xminP = x3;
01349        else if (x3 > *xmaxP)  *xmaxP = x3;
01350        if (x4 < *xminP)  *xminP = x4;
01351        else if (x4 > *xmaxP)  *xmaxP = x4;
01352  
01353        *yminP = *ymaxP = y1;
01354        if (y2 < *yminP)  *yminP = y2;
01355        else if (y2 > *ymaxP)  *ymaxP = y2;
01356        if (y3 < *yminP)  *yminP = y3;
01357        else if (y3 > *ymaxP)  *ymaxP = y3;
01358        if (y4 < *yminP)  *yminP = y4;
01359        else if (y4 > *ymaxP)  *ymaxP = y4;
01360  
01361        if (coerced)
01362                Destroy(p0);
01363 }
01364 /*
01365 :h3.BoxPath()
01366 */
01367 struct segment *BoxPath(S, h, w)
01368        struct XYspace *S;
01369        int h,w;
01370 {
01371        struct segment *path;
01372  
01373        path = Join( Line(ILoc(S, w, 0)), Line(ILoc(S, 0, h)) );
01374        path = JoinSegment(path, LINETYPE, -path->dest.x, -path->dest.y, NULL);
01375        return(ClosePath(path));
01376 }
01377  
01378 /*
01379 :h3.DropSegment() - Drop the First Segment in a Path
01380  
01381 This routine takes the path and returns a new path that is one segment
01382 shorter.  It can be used in conjunction with QueryPath(), for example,
01383 to ask about an entire path.
01384 */
01385  
01386 struct segment *DropSegment(path)
01387        register struct segment *path;
01388 {
01389        IfTrace1((MustTraceCalls),"DropSegment(%z)\n", path);
01390        if (path != NULL && path->type == STROKEPATHTYPE)
01391                path = CoercePath(path);
01392        ARGCHECK((path == NULL || !ISPATHANCHOR(path)),
01393                  "DropSegment: arg not a non-null path", path, path, (0), struct segment *);
01394        if (path->type == TEXTTYPE)
01395                path = CoerceText(path);
01396        path = UniquePath(path);
01397  
01398        POP(path);
01399        return(path);
01400 }
01401 /*
01402 :h3.HeadSegment() - Return the First Segment in a Path
01403  
01404 This routine takes the path and returns a new path consists of the
01405 first segment only.
01406 */
01407  
01408 struct segment *HeadSegment(path)
01409        register struct segment *path;  /* input path                         */
01410 {
01411        IfTrace1((MustTraceCalls),"HeadSegment(%z)\n", path);
01412        if (path == NULL)
01413                return(NULL);
01414        if (path->type == STROKEPATHTYPE)
01415                path = CoercePath(path);
01416        ARGCHECK(!ISPATHANCHOR(path), "HeadSegment: arg not a path", path, path, (0), struct segment *);
01417        if (path->type == TEXTTYPE)
01418                path = CoerceText(path);
01419        path = UniquePath(path);
01420  
01421        if (path->link != NULL)
01422                KillPath(path->link);
01423        path->link = NULL;
01424        path->last = path;
01425        return(path);
01426 }
01427  
01428 /*
01429 :h2.Path Debug Routines
01430  
01431 :h3.DumpPath() - Display a Path on the Trace File
01432 */
01433  
01434 void DumpPath(p)
01435        register struct segment *p;
01436 {
01437        register fractpel x,y;
01438        register fractpel lastx,lasty;
01439        DOUBLE roundness;
01440  
01441        IfTrace1(TRUE,"Dumping path, anchor=%x:\n", p);
01442        lastx = lasty = 0;
01443  
01444        for (;p != NULL; p=p->link) {
01445  
01446                IfTrace0(TRUE,". ");
01447                x = p->dest.x;
01448                y = p->dest.y;
01449                switch (p->type) {
01450  
01451                    case LINETYPE:
01452                        IfTrace1(TRUE,". line<%x> to", (LONG) p->flag);
01453                        IfTrace4(TRUE," (%p,%p), delta=(%p,%p)",
01454                                  x + lastx, y + lasty, x, y);
01455                        break;
01456  
01457                    case MOVETYPE:
01458                        IfTrace1(TRUE,"MOVE<%x> to", (LONG) p->flag);
01459                        IfTrace4(TRUE,"(%p,%p), delta=(%p,%p)",
01460                                  x + lastx, y + lasty, x, y);
01461                        break;
01462  
01463                    case CONICTYPE:
01464                    {
01465                        register struct conicsegment *cp = (struct conicsegment *) p;
01466  
01467                        roundness = cp->roundness;
01468                        IfTrace2(TRUE, ". conic to (%p,%p),",
01469                                                   x + lastx, y + lasty);
01470                        IfTrace3(TRUE," M=(%p,%p), r=%f", cp->M.x + lastx,
01471                                                    cp->M.y + lasty, &roundness);
01472                    }
01473                        break;
01474  
01475                    case BEZIERTYPE:
01476                    {
01477                        register struct beziersegment *bp = (struct beziersegment *) p;
01478  
01479                        IfTrace4(TRUE,". bezier to (%p,%p), B=(%p,%p)",
01480                                        x + lastx, y + lasty,
01481                                        bp->B.x + lastx, bp->B.y + lasty);
01482                        IfTrace2(TRUE, ", C=(%p,%p)",
01483                                        bp->C.x + lastx, bp->C.y + lasty);
01484                    }
01485                        break;
01486  
01487                    case HINTTYPE:
01488                    {
01489                        register struct hintsegment *hp = (struct hintsegment *) p;
01490  
01491                        IfTrace4(TRUE,". hint ref=(%p,%p), width=(%p,%p)",
01492                                        hp->ref.x + lastx, hp->ref.y + lasty,
01493                                        hp->width.x, hp->width.y);
01494                        IfTrace4(TRUE, ", %c %c %c %c",
01495                                        hp->orientation, hp->hinttype,
01496                                        hp->adjusttype, hp->direction);
01497                        IfTrace1(TRUE, ", %ld", (LONG) hp->label);
01498                    }
01499                        break;
01500  
01501                    case TEXTTYPE:
01502                        DumpText(p);
01503                        break;
01504  
01505                    default:
01506                        IfTrace0(TRUE, "bad path segment?");
01507                }
01508                IfTrace1(TRUE," at %x\n", p);
01509                lastx += x;
01510                lasty += y;
01511        }
01512 }
01513  
01514