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