1 /* $XConsortium: paths.c,v 1.4 91/10/10 11:18:40 rws Exp $ */
2 /* Copyright International Business Machines, Corp. 1991
3  * All Rights Reserved
4  * Copyright Lexmark International, Inc. 1991
5  * All Rights Reserved
6  *
7  * License to use, copy, modify, and distribute this software and its
8  * documentation for any purpose and without fee is hereby granted,
9  * provided that the above copyright notice appear in all copies and that
10  * both that copyright notice and this permission notice appear in
11  * supporting documentation, and that the name of IBM or Lexmark not be
12  * used in advertising or publicity pertaining to distribution of the
13  * software without specific, written prior permission.
14  *
15  * IBM AND LEXMARK PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY WARRANTIES OF
16  * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO ANY
17  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
18  * AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  THE ENTIRE RISK AS TO THE
19  * QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING ANY DUTY TO SUPPORT
20  * OR MAINTAIN, BELONGS TO THE LICENSEE.  SHOULD ANY PORTION OF THE
21  * SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM OR LEXMARK) ASSUMES THE
22  * ENTIRE COST OF ALL SERVICING, REPAIR AND CORRECTION.  IN NO EVENT SHALL
23  * IBM OR LEXMARK BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
24  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
25  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
26  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
27  * THIS SOFTWARE.
28  */
29  /* PATHS    CWEB         V0021 ********                             */
30 /*
31 :h1 id=paths.PATHS Module - Path Operator Handler
32 
33 This is the module that is responsible for building and transforming
34 path lists.
35 
36 &author. Jeffrey B. Lotspiech (lotspiech@almaden.ibm.com)
37 
38 
39 :h3.Include Files
40 
41 The included files are:
42 */
43 
44 #include  <stdio.h>
45 #include <stdlib.h>
46                              /*   after the system includes  (dsr)           */
47 #include  "types.h"
48 #include  "objects.h"
49 #include  "spaces.h"
50 #include  "paths.h"
51 #include  "regions.h"      /* understands about Union                      */
52 #include  "fonts.h"        /* understands about TEXTTYPEs                  */
53 #include  "pictures.h"     /* understands about handles                    */
54 #include  "strokes.h"      /* understands how to coerce stroke paths       */
55 #include  "trig.h"
56 static int UnClose();
57 
58 /*
59 :h3.Routines Available to the TYPE1IMAGER User
60 
61 The PATHS routines that are made available to the outside user are:
62 */
63 
64 /*SHARED LINE(S) ORIGINATED HERE*/
65 /*
66 :h3.Functions Provided to Other Modules
67 
68 The path routines that are made available to other TYPE1IMAGER modules
69 are defined here:
70 */
71 
72 /*SHARED LINE(S) ORIGINATED HERE*/
73 /*
74 NOTE:  because of the casts put in the macros for Loc, ArcCA, Conic,
75 RoundConic, PathSegment, and JoinSegment, we cannot use the macro names
76 when the functions are actually defined.  We have to use the unique
77 names with their unique first two characters.  Thus, if anyone in the
78 future ever decided to change the first two characters, it would not be
79 enough just to change the macro (as it would for most other functions).
80 He would have to also change the function definition.
81 */
82 /*
83 :h3.Macros Provided to Other Modules
84 
85 The CONCAT macro is defined here and used in the STROKES module.  See
86 :hdref refid=pathmac..
87 */
88 
89 /*SHARED LINE(S) ORIGINATED HERE*/
90 
91 /*
92 :h2.Path Segment Structures
93 
94 A path is represented as a linked list of the following structure:
95 */
96 
97 /*SHARED LINE(S) ORIGINATED HERE*/
98 /*
99 When 'link' is NULL, we are at the last segment in the path (surprise!).
100 
101 'last' is only non-NULL on the first segment of a path,
102 for all the other segments 'last' == NULL.  We test for a non-NULL
103 'last' (ISPATHANCHOR predicate) when we are given an alleged path
104 to make sure the user is not trying to pull a fast one on us.
105 
106 A path may be a collection of disjoint paths.  Every break in the
107 disjoint path is represented by a MOVETYPE segment.
108 
109 Closed paths are discussed in :hdref refid=close..
110 
111 :h3.CopyPath() - Physically Duplicating a Path
112 
113 This simple function illustrates moving through the path linked list.
114 Duplicating a segment just involves making a copy of it, except for
115 text, which has some auxilliary things involved.  We don't feel
116 competent to duplicate text in this module, so we call someone who
117 knows how (in the FONTS module).
118 */
CopyPath(p0)119 struct segment *CopyPath(p0)
120        register struct segment *p0;  /* path to duplicate                    */
121 {
122        register struct segment *p,*n=NULL,*last=NULL,*anchor;
123 
124        for (p = p0, anchor = NULL; p != NULL; p = p->link) {
125 
126                ARGCHECK((!ISPATHTYPE(p->type) || (p != p0 && p->last != NULL)),
127                        "CopyPath: invalid segment", p, NULL, (0), struct segment *);
128 
129                if (p->type == TEXTTYPE)
130                        n = (struct segment *) CopyText(p);
131                else
132                        n = (struct segment *)Allocate(p->size, p, 0);
133                n->last = NULL;
134                if (anchor == NULL)
135                        anchor = n;
136                else
137                        last->link = n;
138                last = n;
139        }
140 /*
141 At this point we have a chain of newly allocated segments hanging off
142 'anchor'.  We need to make sure the first segment points to the last:
143 */
144        if (anchor != NULL) {
145                n->link = NULL;
146                anchor->last = n;
147        }
148 
149        return(anchor);
150 }
151 /*
152 :h3.KillPath() - Destroying a Path
153 
154 Destroying a path is simply a matter of freeing each segment in the
155 linked list.  Again, we let the experts handle text.
156 */
KillPath(p)157 void KillPath(p)
158        register struct segment *p;  /* path to destroy                       */
159 {
160        register struct segment *linkp;  /* temp register holding next segment*/
161 
162        /* return conditional based on reference count 3-26-91 PNM */
163        if ( (--(p->references) > 1) ||
164           ( (p->references == 1) && !ISPERMANENT(p->flag) ) )
165            return;
166 
167        while (p != NULL) {
168                if (!ISPATHTYPE(p->type)) {
169                        ArgErr("KillPath: bad segment", p, NULL);
170                        return;
171                }
172                linkp = p->link;
173                if (p->type == TEXTTYPE)
174                        KillText(p);
175                else
176                        Free(p);
177                p = linkp;
178        }
179 }
180 
181 /*
182 :h2 id=location."location" Objects
183 
184 The TYPE1IMAGER user creates and destroys objects of type "location".  These
185 objects locate points for the primitive path operators.  We play a trick
186 here and store these objects in the same "segment" structure used for
187 paths, with a type field == MOVETYPE.
188 
189 This allows the Line() operator, for example, to be very trivial:
190 It merely stamps its input structure as a LINETYPE and returns it to the
191 caller--assuming, of course, the input structure was not permanent (as
192 it usually isn't).
193 
194 :h3.The "movesegment" Template Structure
195 
196 This template is used as a generic segment structure for Allocate:
197 */
198 
199 /* added reference field 1 to temporary template below 3-26-91 PNM */
200 static struct segment movetemplate = { MOVETYPE, 0, 1, sizeof(struct segment), 0,
201                 NULL, NULL, {0, 0} };
202 /*
203 :h3.Loc() - Create an "Invisible Line" Between (0,0) and a Point
204 
205 */
206 
t1_Loc(S,x,y)207 struct segment *t1_Loc(S, x, y)
208        register struct XYspace *S;  /* coordinate space to interpret X,Y     */
209        DOUBLE x,y;           /* destination point                            */
210 {
211        register struct segment *r;
212 
213 
214        IfTrace3((MustTraceCalls),"..Loc(S=%p, x=%f, y=%f)\n", S, x, y);
215 
216        r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
217        TYPECHECK("Loc", S, SPACETYPE, r, (0), struct segment *);
218 
219        r->last = r;
220        r->context = S->context;
221        (*S->convert)(&r->dest, S, x, y);
222        ConsumeSpace(S);
223        return(r);
224 }
225 /*
226 :h3.ILoc() - Loc() With Integer Arguments
227 
228 */
ILoc(S,x,y)229 struct segment *ILoc(S, x, y)
230        register struct XYspace *S;  /* coordinate space to interpret X,Y     */
231        register int x,y;        /* destination point                         */
232 {
233        register struct segment *r;
234 
235        IfTrace3((MustTraceCalls),"..ILoc(S=%p, x=%d, y=%d)\n",
236                                     S, (LONG) x, (LONG) y);
237        r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
238        TYPECHECK("Loc", S, SPACETYPE, r, (0), struct segment *);
239 
240        r->last = r;
241        r->context = S->context;
242        (*S->iconvert)(&r->dest, S, (LONG) x, (LONG) y);
243        ConsumeSpace(S);
244        return(r);
245 }
246 
247 /*
248 :h3.SubLoc() - Vector Subtraction of Two Locition Objects
249 
250 This user operator subtracts two location objects, yielding a new
251 location object that is the result.
252 
253 The symmetrical function AddLoc() is totally redundent with Join(),
254 so it is not provided.
255 */
256 
SubLoc(p1,p2)257 struct segment *SubLoc(p1, p2)
258        register struct segment *p1;
259        register struct segment *p2;
260 {
261        IfTrace2((MustTraceCalls),"SubLoc(%p, %p)\n", p1, p2);
262 
263        ARGCHECK(!ISLOCATION(p1), "SubLoc: bad first arg", p1, NULL, (0), struct segment *);
264        ARGCHECK(!ISLOCATION(p2), "SubLoc: bad second arg", p2, NULL, (0), struct segment *);
265        p1 = UniquePath(p1);
266        p1->dest.x -= p2->dest.x;
267        p1->dest.y -= p2->dest.y;
268        ConsumePath(p2);
269        return(p1);
270 }
271 
272 /*
273 :h2.Straight Line Segments
274 
275 :h3.PathSegment() - Create a Generic Path Segment
276 
277 Many routines need a LINETYPE or MOVETYPE path segment, but do not
278 want to go through the external user's interface, because, for example,
279 they already know the "fractpel" destination of the segment and the
280 conversion is unnecessary.  PathSegment() is an internal routine
281 provided to the rest of TYPE1IMAGER for handling these cases.
282 */
283 
t1_PathSegment(type,x,y)284 struct segment *t1_PathSegment(type, x, y)
285        int type;             /* LINETYPE or MOVETYPE                         */
286        fractpel x,y;         /* where to go to, if known                     */
287 {
288        register struct segment *r;  /* newly created segment                 */
289 
290        r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
291        r->type = type;
292        r->last = r;          /* last points to itself for singleton          */
293        r->dest.x = x;
294        r->dest.y = y;
295        return(r);
296 }
297 /*
298 :h3.Line() - Create a Line Segment Between (0,0) and a Point P
299 
300 This involves just creating and filling out a segment structure:
301 */
Line(P)302 struct segment *Line(P)
303        register struct segment *P;  /* relevant coordinate space             */
304 {
305 
306        IfTrace1((MustTraceCalls),"..Line(%p)\n", P);
307        ARGCHECK(!ISLOCATION(P), "Line: arg not a location", P, NULL, (0), struct segment *);
308 
309        P = UniquePath(P);
310        P->type = LINETYPE;
311        return(P);
312 }
313 /*
314 :h2.Curved Path Segments
315 
316 We need more points to describe curves.  So, the structures for curved
317 path segments are slightly different.  The first part is identical;
318 the curved structures are larger with the extra points on the end.
319 
320 :h3.Bezier Segment Structure
321 
322 We support third order Bezier curves.  They are specified with four
323 control points A, B, C, and D.  The curve starts at A with slope AB
324 and ends at D with slope CD.  The curvature at the point A is inversely
325 related to the length |AB|, and the curvature at the point D is
326 inversely related to the length |CD|.  Point A is always point (0,0).
327 
328 */
329 
330 /*SHARED LINE(S) ORIGINATED HERE*/
331 /*
332 :h3.Bezier() - Generate a Bezier Segment
333 
334 This is just a simple matter of filling out a 'beziersegment' structure:
335 */
336 
Bezier(B,C,D)337 struct beziersegment *Bezier(B, C, D)
338        register struct segment *B;  /* second control point                  */
339        register struct segment *C;  /* third control point                   */
340        register struct segment *D;  /* fourth control point (ending point)   */
341 {
342 /* added reference field of 1 to temporary template below 3-26-91  PNM */
343        static struct beziersegment template =
344                     { BEZIERTYPE, 0, 1, sizeof(struct beziersegment), 0,
345                       NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 } };
346 
347        register struct beziersegment *r;  /* output segment                  */
348 
349        IfTrace3((MustTraceCalls),"..Bezier(%p, %p, %p)\n", B, C, D);
350        ARGCHECK(!ISLOCATION(B), "Bezier: bad B", B, NULL, (2,C,D), struct beziersegment *);
351        ARGCHECK(!ISLOCATION(C), "Bezier: bad C", C, NULL, (2,B,D), struct beziersegment *);
352        ARGCHECK(!ISLOCATION(D), "Bezier: bad D", D, NULL, (2,B,C), struct beziersegment *);
353 
354        r = (struct beziersegment *)Allocate(sizeof(struct beziersegment), &template, 0);
355        r->last = (struct segment *) r;
356        r->dest.x = D->dest.x;
357        r->dest.y = D->dest.y;
358        r->B.x = B->dest.x;
359        r->B.y = B->dest.y;
360        r->C.x = C->dest.x;
361        r->C.y = C->dest.y;
362 
363        ConsumePath(B);
364        ConsumePath(C);
365        ConsumePath(D);
366        return(r);
367 }
368 
369 /*
370 :h2.Font "Hint" Segments
371 
372 :h3.Hint() - A Font 'Hint' Segment
373 
374 This is temporary code while we experiment with hints.
375 */
376 
377 /*SHARED LINE(S) ORIGINATED HERE*/
Hint(S,ref,width,orientation,hinttype,adjusttype,direction,label)378 struct hintsegment *Hint(S, ref, width, orientation, hinttype, adjusttype, direction, label)
379        struct XYspace *S;
380        float ref;
381        float width;
382        char orientation;
383        char hinttype;
384        char adjusttype;
385        char direction;
386        int label;
387 {
388 /* added reference field of 1 to hintsegment template below 3-26-91 PNM */
389        static struct hintsegment template = { HINTTYPE, 0, 1, sizeof(struct hintsegment), 0,
390                                           NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 },
391                                           ' ', ' ', ' ', ' ', 0};
392 
393        register struct hintsegment *r;
394 
395        r = (struct hintsegment *)Allocate(sizeof(struct hintsegment), &template, 0);
396 
397        r->orientation = orientation;
398        if (width == 0.0)  width = 1.0;
399 
400        if (orientation == 'h') {
401                (*S->convert)(&r->ref, S, 0.0, ref);
402                (*S->convert)(&r->width, S, 0.0, width);
403        }
404        else if (orientation == 'v') {
405                (*S->convert)(&r->ref, S, ref, 0.0);
406                (*S->convert)(&r->width, S, width, 0.0);
407        }
408        else
409                return((struct hintsegment *)ArgErr("Hint: orient not 'h' or 'v'", NULL, NULL));
410        if (r->width.x < 0)  r->width.x = - r->width.x;
411        if (r->width.y < 0)  r->width.y = - r->width.y;
412        r->hinttype = hinttype;
413        r->adjusttype = adjusttype;
414        r->direction = direction;
415        r->label = label;
416        r->last = (struct segment *) r;
417        ConsumeSpace(S);
418        return(r);
419 }
420 
421 /*
422 */
423 
424 /*SHARED LINE(S) ORIGINATED HERE*/
425 
426 /*
427 POP removes the first segment in a path 'p' and Frees it.  'p' is left
428 pointing to the end of the path:
429 */
430 #define POP(p) \
431      { register struct segment *linkp; \
432        linkp = p->link; \
433        if (linkp != NULL) \
434                linkp->last = p->last; \
435        Free(p); \
436        p = linkp; }
437 /*
438 INSERT inserts a single segment in the middle of a chain.  'b' is
439 the segment before, 'p' the segment to be inserted, and 'a' the
440 segment after.
441 */
442 #define INSERT(b,p,a)  b->link=p; p->link=a; p->last=NULL
443 
444 /*
445 :h3.Join() - Join Two Objects Together
446 
447 If these are paths, this operator simply invokes the CONCAT macro.
448 Why so much code then, you ask?  Well we have to check for object
449 types other than paths, and also check for certain path consistency
450 rules.
451 */
452 
Join(p1,p2)453 struct segment *Join(p1, p2)
454        register struct segment *p1,*p2;
455 {
456        IfTrace2((MustTraceCalls && PathDebug > 1),"..Join(%p, %p)\n", p1, p2);
457        IfTrace2((MustTraceCalls && PathDebug <=1),"..Join(%p, %p)\n", p1, p2);
458 /*
459 We start with a whole bunch of very straightforward argument tests:
460 */
461        if (p2 != NULL) {
462                if (!ISPATHTYPE(p2->type)) {
463 
464                        if (p1 == NULL)
465                                return((struct segment *)Unique(p2));
466 
467                        switch (p1->type) {
468 
469                            case REGIONTYPE:
470 
471                            case STROKEPATHTYPE:
472                                p1 = CoercePath(p1);
473                                break;
474 
475                            default:
476                                return((struct segment *)BegHandle(p1, p2));
477                        }
478                }
479 
480                ARGCHECK((p2->last == NULL), "Join: right arg not anchor", p2, NULL, (1,p1), struct segment *);
481                p2 = UniquePath(p2);
482 
483 /*
484 In certain circumstances, we don't have to duplicate a permanent
485 location.  (We would just end up destroying it anyway).  These cases
486 are when 'p2' begins with a move-type segment:
487 */
488                if (p2->type == TEXTTYPE || p2->type == MOVETYPE) {
489                        if (p1 == NULL)
490                                return(p2);
491                        if (ISLOCATION(p1)) {
492                                p2->dest.x += p1->dest.x;
493                                p2->dest.y += p1->dest.y;
494                                ConsumePath(p1);
495                                return(p2);
496                        }
497                }
498        }
499        else
500                return((struct segment *)Unique(p1));
501 
502        if (p1 != NULL) {
503                if (!ISPATHTYPE(p1->type))
504 
505                        switch (p2->type) {
506 
507                            case REGIONTYPE:
508 
509                            case STROKEPATHTYPE:
510                                p2 = CoercePath(p2);
511                                break;
512 
513                            default:
514                                return((struct segment *)EndHandle(p1, p2));
515                        }
516 
517                ARGCHECK((p1->last == NULL), "Join: left arg not anchor", p1, NULL, (1,p2), struct segment *);
518                p1 = UniquePath(p1);
519        }
520        else
521                return(p2);
522 
523 /*
524 At this point all the checking is done.  We have two temporary non-null
525 path types in 'p1' and 'p2'.  If p1 ends with a MOVE, and p2 begins with
526 a MOVE, we collapse the two MOVEs into one.  We enforce the rule that
527 there may not be two MOVEs in a row:
528 */
529 
530        if (p1->last->type == MOVETYPE && p2->type == MOVETYPE) {
531                p1->last->flag |= p2->flag;
532                p1->last->dest.x += p2->dest.x;
533                p1->last->dest.y += p2->dest.y;
534                POP(p2);
535                if (p2 == NULL)
536                        return(p1);
537        }
538 /*
539 Now we check for another silly rule.  If a path has any TEXTTYPEs,
540 then it must have only TEXTTYPEs and MOVETYPEs, and furthermore,
541 it must begin with a TEXTTYPE.  This rule makes it easy to check
542 for the special case of text.  If necessary, we will coerce
543 TEXTTYPEs into paths so we don't mix TEXTTYPEs with normal paths.
544 */
545        if (p1->type == TEXTTYPE) {
546                if (p2->type != TEXTTYPE && !ISLOCATION(p2))
547                        p1 = CoerceText(p1);
548        }
549        else {
550                if (p2->type == TEXTTYPE) {
551                        if (ISLOCATION(p1)) {
552                                p2->dest.x += p1->dest.x;
553                                p2->dest.y += p1->dest.y;
554                                Free(p1);
555                                return(p2);
556                        }
557                        else
558                                p2 = CoerceText(p2);
559                }
560        }
561 /*
562 Thank God!  Finally!  It's hard to believe, but we are now able to
563 actually do the join.  This is just invoking the CONCAT macro:
564 */
565        CONCAT(p1, p2);
566 
567        return(p1);
568 }
569 
570 /*
571 :h3.JoinSegment() - Create a Path Segment and Join It to a Known Path
572 
573 This internal function is quicker than a full-fledged join because
574 it can do much less checking.
575 */
576 
t1_JoinSegment(before,type,x,y,after)577 struct segment *t1_JoinSegment(before, type, x, y, after)
578        register struct segment *before;  /* path to join before new segment  */
579        int type;             /* type of new segment (MOVETYPE or LINETYPE)   */
580        fractpel x,y;         /* x,y of new segment                           */
581        register struct segment *after;  /* path to join after new segment    */
582 {
583        register struct segment *r;  /* returned path built here              */
584 
585        r = PathSegment(type, x, y);
586        if (before != NULL) {
587                CONCAT(before, r);
588                r = before;
589        }
590        else
591                r->context = after->context;
592        if (after != NULL)
593                CONCAT(r, after);
594        return(r);
595 }
596 
597 /*
598 :h2.Other Path Functions
599 
600 */
601 
602 
t1_ClosePath(p0,lastonly)603 struct segment *t1_ClosePath(p0,lastonly)
604        register struct segment *p0;  /* path to close                        */
605        register int lastonly;  /*  flag deciding to close all subpaths or... */
606 {
607        register struct segment *p,*last=NULL,*start;  /* used in looping through path */
608        register fractpel x,y;  /* current position in path                   */
609        register fractpel firstx=0,firsty=0;  /* start position of sub path       */
610        register struct segment *lastnonhint=NULL;  /* last non-hint segment in path */
611 
612        IfTrace1((MustTraceCalls),"ClosePath(%p)\n", p0);
613        if (p0 != NULL && p0->type == TEXTTYPE)
614                return(UniquePath(p0));
615        if (p0->type == STROKEPATHTYPE)
616                return((struct segment *)Unique(p0));
617        /*
618        * NOTE: a null closed path is different from a null open path
619        * and is denoted by a closed (0,0) move segment.  We make
620        * sure this path begins and ends with a MOVETYPE:
621        */
622        if (p0 == NULL || p0->type != MOVETYPE)
623                p0 = JoinSegment(NULL, MOVETYPE, 0, 0, p0);
624        TYPECHECK("ClosePath", p0, MOVETYPE, NULL, (0), struct segment *);
625        if (p0->last->type != MOVETYPE)
626                p0 = JoinSegment(p0, MOVETYPE, 0, 0, NULL);
627 
628        p0 = UniquePath(p0);
629 
630 /*
631 We now begin a loop through the path,
632 incrementing current 'x' and 'y'.  We are searching
633 for MOVETYPE segments (breaks in the path) that are not already closed.
634 At each break, we insert a close segment.
635 */
636        for (p = p0, x = y = 0, start = NULL;
637             p != NULL;
638             x += p->dest.x, y += p->dest.y, last = p, p = p->link)
639        {
640 
641                if (p->type == MOVETYPE) {
642                        if (start != NULL && (lastonly?p->link==NULL:TRUE) &&
643                              !(ISCLOSED(start->flag) && LASTCLOSED(last->flag))) {
644                                register struct segment *r;  /* newly created */
645 
646                                start->flag |= ISCLOSED(ON);
647                                r = PathSegment(LINETYPE, firstx - x,
648                                                          firsty - y);
649                                INSERT(last, r, p);
650                                r->flag |= LASTCLOSED(ON);
651                                /*< adjust 'last' if possible for a 0,0 close >*/
652 {
653 
654 #define   CLOSEFUDGE    3    /* if we are this close, let's change last segment */
655 
656        if (r->dest.x != 0 || r->dest.y != 0) {
657                if (r->dest.x <= CLOSEFUDGE && r->dest.x >= -CLOSEFUDGE
658                     && r->dest.y <= CLOSEFUDGE && r->dest.y >= -CLOSEFUDGE) {
659                        IfTrace2((PathDebug),
660                                "ClosePath forced closed by (%d,%d)\n",
661                                       r->dest.x, r->dest.y);
662                        lastnonhint->dest.x += r->dest.x;
663                        lastnonhint->dest.y += r->dest.y;
664                        r->dest.x = r->dest.y = 0;
665                }
666        }
667 }
668                                if (p->link != NULL) {
669                                        p->dest.x += x - firstx;
670                                        p->dest.y += y - firsty;
671                                        x = firstx;
672                                        y = firsty;
673                                }
674                        }
675                        start = p;
676                        firstx = x + p->dest.x;
677                        firsty = y + p->dest.y;
678                }
679                else if (p->type != HINTTYPE)
680                        lastnonhint = p;
681        }
682        return(p0);
683 }
684 /*
685 */
686 /*
687 :h2.Reversing the Direction of a Path
688 
689 This turned out to be more difficult than I thought at first.  The
690 trickiness was due to the fact that closed paths must remain closed,
691 etc.
692 
693 We need three subroutines:
694 */
695 
696 static struct segment *SplitPath(); /* break a path at any point             */
697 static struct segment *DropSubPath();  /* breaks a path after first sub-path */
698 static struct segment *ReverseSubPath();  /* reverses a single sub-path      */
699 
700 /*
701 :h3.Reverse() - User Operator to Reverse a Path
702 
703 This operator reverses the entire path.
704 */
705 
Reverse(p)706 struct segment *Reverse(p)
707        register struct segment *p;    /* full path to reverse                */
708 {
709        register struct segment *r;    /* output path built here              */
710        register struct segment *nextp;  /* contains next sub-path            */
711 
712        IfTrace1((MustTraceCalls),"Reverse(%p)\n", p);
713 
714        if (p == NULL)
715                return(NULL);
716 
717        ARGCHECK(!ISPATHANCHOR(p), "Reverse: invalid path", p, NULL, (0), struct segment *);
718 
719        if (p->type == TEXTTYPE)
720                p = CoerceText(p);
721        p = UniquePath(p);
722 
723        r = NULL;
724 
725        do {
726                nextp = DropSubPath(p);
727                p = ReverseSubPath(p);
728                r = Join(p, r);
729                p = nextp;
730 
731        } while (p != NULL);
732 
733        return(r);
734 }
735 
736 /*
737 :h4.ReverseSubPath() - Subroutine to Reverse a Single Sub-Path
738 */
739 
ReverseSubPath(p)740 static struct segment *ReverseSubPath(p)
741        register struct segment *p;  /* input path                            */
742 {
743        register struct segment *r;  /* reversed path will be created here    */
744        register struct segment *nextp;  /* temporary variable used in loop   */
745        register int wasclosed;  /* flag, path was closed                     */
746 
747        if (p == NULL)
748                return(NULL);
749 
750        wasclosed = ISCLOSED(p->flag);
751        r = NULL;
752 
753        do {
754 /*
755 First we reverse the direction of this segment and clean up its flags:
756 */
757                p->dest.x = - p->dest.x;  p->dest.y = - p->dest.y;
758                p->flag &= ~(ISCLOSED(ON) | LASTCLOSED(ON));
759 
760                switch (p->type) {
761 
762                    case LINETYPE:
763                    case MOVETYPE:
764                        break;
765 
766                    case CONICTYPE:
767                    {
768 /*
769 The logic of this is that the new M point (stored relative to the new
770 beginning) is (M - C).  However, C ("dest") has already been reversed
771 So, we add "dest" instead of subtracting it:
772 */
773                        register struct conicsegment *cp = (struct conicsegment *) p;
774 
775                        cp->M.x += p->dest.x;  cp->M.y += p->dest.y;
776                    }
777                        break;
778 
779                    case BEZIERTYPE:
780                    {
781                        register struct beziersegment *bp = (struct beziersegment *) p;
782 
783                        bp->B.x += p->dest.x;  bp->B.y += p->dest.y;
784                        bp->C.x += p->dest.x;  bp->C.y += p->dest.y;
785                    }
786                        break;
787 
788                    case HINTTYPE:
789                    {
790                        register struct hintsegment *hp = (struct hintsegment *) p;
791 
792                        hp->ref.x = -hp->ref.x;  hp->ref.y = -hp->ref.y;
793                    }
794                        break;
795 
796                    default:
797                        abort("Reverse: bad path segment", 23);
798                }
799 /*
800 We need to reverse the order of segments too, so we break this segment
801 off of the input path, and tack it on the front of the growing path
802 in 'r':
803 */
804                nextp = p->link;
805                p->link = NULL;
806                p->last = p;
807                if (r != NULL)
808                        CONCAT(p,r);  /* leaves result in 'p'... not what we want */
809                r = p;
810                p = nextp;    /* advance to next segment in input path        */
811 
812        } while (p != NULL);
813 
814        if (wasclosed)
815                r = ClosePath(r);
816 
817        return(r);
818 }
819 
820 /*
821 :h4.DropSubPath() - Drops the First Sub-Path Off a Path
822 
823 This subroutine returns the remaining sub-path(s).  While doing so, it
824 breaks the input path after the first sub-path so that a pointer to
825 the original path now contains the first sub-path only.
826 */
827 
DropSubPath(p0)828 static struct segment *DropSubPath(p0)
829        register struct segment *p0;  /* original path                        */
830 {
831        register struct segment *p;  /* returned remainder here               */
832 
833        for (p = p0; p->link != NULL; p = p->link) {
834                if (p->link->type == MOVETYPE)
835                        break;
836        }
837 
838        return(SplitPath(p0, p));
839 }
840 
SplitPath(anchor,before)841 static struct segment *SplitPath(anchor, before)
842        register struct segment *anchor;
843        register struct segment *before;
844 {
845        register struct segment *r;
846 
847        if (before == anchor->last)
848                return(NULL);
849 
850        r = before->link;
851        r->last = anchor->last;
852        anchor->last = before;
853        before->link = NULL;
854 
855        return(r);
856 }
857 
858 
859 /*
860 :h3.ReverseSubPaths() - Reverse the Direction of Sub-paths Within a Path
861 
862 This user operator reverses the sub-paths in a path, but leaves the
863 'move' segments unchanged.  It builds on top of the subroutines
864 already established.
865 */
866 
ReverseSubPaths(p)867 struct segment *ReverseSubPaths(p)
868        register struct segment *p;  /* input path                            */
869 {
870        register struct segment *r;  /* reversed path will be created here    */
871        register struct segment *nextp;  /* temporary variable used in loop   */
872        int wasclosed;        /* flag; subpath was closed                     */
873        register struct segment *nomove;  /* the part of sub-path without move segment */
874        struct fractpoint delta;
875 
876        IfTrace1((MustTraceCalls),"ReverseSubPaths(%p)\n", p);
877 
878        if (p == NULL)
879                return(NULL);
880 
881        ARGCHECK(!ISPATHANCHOR(p), "ReverseSubPaths: invalid path", p, NULL, (0), struct segment *);
882 
883        if (p->type == TEXTTYPE)
884                p = CoerceText(p);
885        if (p->type != MOVETYPE)
886                p = JoinSegment(NULL, MOVETYPE, 0, 0, p);
887 
888        p = UniquePath(p);
889 
890        r = NULL;
891 
892        for (; p != NULL;) {
893                nextp = DropSubPath(p);
894                wasclosed = ISCLOSED(p->flag);
895                if (wasclosed)
896                        UnClose(p);
897 
898                nomove = SplitPath(p, p);
899                r = Join(r, p);
900 
901                PathDelta(nomove, &delta);
902 
903                nomove = ReverseSubPath(nomove);
904                p->dest.x += delta.x;
905                p->dest.y += delta.y;
906                if (nextp != NULL) {
907                        nextp->dest.x += delta.x;
908                        nextp->dest.y += delta.y;
909                }
910                if (wasclosed) {
911                        nomove = ClosePath(nomove);
912                        nextp->dest.x -= delta.x;
913                        nextp->dest.y -= delta.y;
914                }
915                r = Join(r, nomove);
916                p = nextp;
917 
918        }
919 
920        return(r);
921 }
922 
UnClose(p0)923 static int UnClose(p0)
924        register struct segment *p0;
925 {
926        register struct segment *p;
927 
928        for (p=p0; p->link->link != NULL; p=p->link) { ; }
929 
930        if (!LASTCLOSED(p->link->flag))
931                abort("UnClose:  no LASTCLOSED", 24);
932 
933        Free(SplitPath(p0, p));
934        p0->flag &= ~ISCLOSED(ON);
935        return(0);
936 
937 }
938 
939 /*
940 :h2.Transforming and Putting Handles on Paths
941 
942 :h3.PathTransform() - Transform a Path
943 
944 Transforming a path involves transforming all the points.  In order
945 that closed paths do not become "unclosed" when their relative
946 positions are slightly changed due to loss of arithmetic precision,
947 all point transformations are in absolute coordinates.
948 
949 (It might be better to reset the "absolute" coordinates every time a
950 move segment is encountered.  This would mean that we could accumulate
951 error from subpath to subpath, but we would be less likely to make
952 the "big error" where our fixed point arithmetic "wraps".  However, I
953 think I'll keep it this way until something happens to convince me
954 otherwise.)
955 
956 The transform is described as a "space", that way we can use our
957 old friend the "iconvert" function, which should be very efficient.
958 */
959 
PathTransform(p0,S)960 struct segment *PathTransform(p0, S)
961        register struct segment *p0;    /* path to transform                  */
962        register struct XYspace *S;     /* pseudo space to transform in       */
963 {
964        register struct segment *p;   /* to loop through path with            */
965        register fractpel newx,newy;  /* current transformed position in path */
966        register fractpel oldx,oldy;  /* current untransformed position in path */
967        register fractpel savex,savey;  /* save path delta x,y                */
968 
969        p0 = UniquePath(p0);
970 
971        newx = newy = oldx = oldy = 0;
972 
973        for (p=p0; p != NULL; p=p->link) {
974 
975                savex = p->dest.x;   savey = p->dest.y;
976 
977                (*S->iconvert)(&p->dest, S, p->dest.x + oldx, p->dest.y + oldy);
978                p->dest.x -= newx;
979                p->dest.y -= newy;
980 
981                switch (p->type) {
982 
983                    case LINETYPE:
984                    case MOVETYPE:
985                        break;
986 
987                    case CONICTYPE:
988                    {
989                        register struct conicsegment *cp = (struct conicsegment *) p;
990 
991                        (*S->iconvert)(&cp->M, S, cp->M.x + oldx, cp->M.y + oldy);
992                        cp->M.x -= newx;
993                        cp->M.y -= newy;
994                        /*
995                        * Note roundness doesn't change... linear transform
996                        */
997                        break;
998                    }
999 
1000 
1001                    case BEZIERTYPE:
1002                    {
1003                        register struct beziersegment *bp = (struct beziersegment *) p;
1004 
1005                        (*S->iconvert)(&bp->B, S, bp->B.x + oldx, bp->B.y + oldy);
1006                        bp->B.x -= newx;
1007                        bp->B.y -= newy;
1008                        (*S->iconvert)(&bp->C, S, bp->C.x + oldx, bp->C.y + oldy);
1009                        bp->C.x -= newx;
1010                        bp->C.y -= newy;
1011                        break;
1012                    }
1013 
1014                    case HINTTYPE:
1015                    {
1016                        register struct hintsegment *hp = (struct hintsegment *) p;
1017 
1018                        (*S->iconvert)(&hp->ref, S, hp->ref.x + oldx, hp->ref.y + oldy);
1019                        hp->ref.x -= newx;
1020                        hp->ref.y -= newy;
1021                        (*S->iconvert)(&hp->width, S, hp->width.x, hp->width.y);
1022                        /* Note: width is not relative to origin */
1023                        break;
1024                    }
1025 
1026                    case TEXTTYPE:
1027                    {
1028                         XformText(p,S);
1029                         break;
1030                    }
1031 
1032                    default:
1033                        IfTrace1(TRUE,"path = %p\n", p);
1034                        abort("PathTransform:  invalid segment", 25);
1035                }
1036                oldx += savex;
1037                oldy += savey;
1038                newx += p->dest.x;
1039                newy += p->dest.y;
1040        }
1041        return(p0);
1042 }
1043 
1044 /*
1045 :h3.PathDelta() - Return a Path's Ending Point
1046 */
1047 
PathDelta(p,pt)1048 void PathDelta(p, pt)
1049        register struct segment *p; /* input path                             */
1050        register struct fractpoint *pt; /* pointer to x,y to set              */
1051 {
1052        struct fractpoint mypoint;  /* I pass this to TextDelta               */
1053        register fractpel x,y;  /* working variables for path current point   */
1054 
1055        for (x=y=0; p != NULL; p=p->link) {
1056                x += p->dest.x;
1057                y += p->dest.y;
1058                if (p->type == TEXTTYPE) {
1059                        TextDelta(p, &mypoint);
1060                        x += mypoint.x;
1061                        y += mypoint.y;
1062                }
1063        }
1064 
1065        pt->x = x;
1066        pt->y = y;
1067 }
1068 
1069 /*
1070 :h3.BoundingBox() - Produce a Bounding Box Path
1071 
1072 This function is called by image code, when we know the size of the
1073 image in pels, and need to get a bounding box path that surrounds it.
1074 The starting/ending handle is in the lower right hand corner.
1075 */
BoundingBox(h,w)1076 struct segment *BoundingBox(h, w)
1077        register pel h,w;     /* size of box                                  */
1078 {
1079        register struct segment *path;
1080 
1081        path = PathSegment(LINETYPE, -TOFRACTPEL(w), 0);
1082        path = JoinSegment(NULL, LINETYPE, 0, -TOFRACTPEL(h), path);
1083        path = JoinSegment(NULL, LINETYPE, TOFRACTPEL(w), 0, path);
1084        path = ClosePath(path);
1085 
1086        return(path);
1087 }
1088 
1089 /*
1090 :h2.Querying Locations and Paths
1091 
1092 :h3.QueryLoc() - Return the X,Y of a Locition
1093 */
1094 
QueryLoc(P,S,xP,yP)1095 void QueryLoc(P, S, xP, yP)
1096        register struct segment *P;  /* location to query, not consumed       */
1097        register struct XYspace *S;  /* XY space to return coordinates in     */
1098        register DOUBLE *xP,*yP;  /* coordinates returned here                */
1099 {
1100        IfTrace4((MustTraceCalls),"QueryLoc(P=%p, S=%p, (%f, %f))\n",
1101                                             P, S, *xP, *yP);
1102        if (!ISLOCATION(P)) {
1103                ArgErr("QueryLoc: first arg not a location", P, NULL);
1104                return;
1105        }
1106        if (S->type != SPACETYPE) {
1107                ArgErr("QueryLoc: second arg not a space", S, NULL);
1108                return;
1109        }
1110        UnConvert(S, &P->dest, xP, yP);
1111 }
1112 /*
1113 :h3.QueryPath() - Find Out the Type of Segment at the Head of a Path
1114 
1115 This is a very simple routine that looks at the first segment of a
1116 path and tells the caller what it is, as well as returning the control
1117 point(s) of the path segment.  Different path segments have different
1118 number of control points.  If the caller knows that the segment is
1119 a move segment, for example, he only needs to pass pointers to return
1120 one control point.
1121 */
1122 
QueryPath(path,typeP,Bp,Cp,Dp,fP)1123 void QueryPath(path, typeP, Bp, Cp, Dp, fP)
1124        register struct segment *path;  /* path to check                      */
1125        register int *typeP;  /* return the type of path here                 */
1126        register struct segment **Bp;  /* return location of first point      */
1127        register struct segment **Cp;  /* return location of second point     */
1128        register struct segment **Dp;  /* return location of third point      */
1129        register DOUBLE *fP;  /* return Conic sharpness                       */
1130 {
1131        register int coerced = FALSE;  /* did I coerce a text path?           */
1132 
1133        IfTrace3((MustTraceCalls), "QueryPath(%p, %p, %p, ...)\n",
1134                                              path, typeP, Bp);
1135        if (path == NULL) {
1136                *typeP = -1;
1137                return;
1138        }
1139        if (!ISPATHANCHOR(path)) {
1140                ArgErr("QueryPath: arg not a valid path", path, NULL);
1141        }
1142        if (path->type == TEXTTYPE) {
1143                path = CoerceText(path);
1144                coerced = TRUE;
1145        }
1146 
1147        switch (path->type) {
1148 
1149            case MOVETYPE:
1150                *typeP = 0;
1151                *Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
1152                break;
1153 
1154            case LINETYPE:
1155                *typeP = (LASTCLOSED(path->flag)) ? 4 : 1;
1156                *Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
1157                break;
1158 
1159            case CONICTYPE:
1160            {
1161                register struct conicsegment *cp = (struct conicsegment *) path;
1162 
1163                *typeP = 2;
1164                *Bp = PathSegment(MOVETYPE, cp->M.x, cp->M.y);
1165                *Cp = PathSegment(MOVETYPE, cp->dest.x, cp->dest.y);
1166                *fP = cp->roundness;
1167            }
1168                break;
1169 
1170            case BEZIERTYPE:
1171            {
1172                register struct beziersegment *bp = (struct beziersegment *) path;
1173 
1174                *typeP = 3;
1175                *Bp = PathSegment(MOVETYPE, bp->B.x, bp->B.y);
1176                *Cp = PathSegment(MOVETYPE, bp->C.x, bp->C.y);
1177                *Dp = PathSegment(MOVETYPE, bp->dest.x, bp->dest.y);
1178            }
1179                break;
1180 
1181            case HINTTYPE:
1182                *typeP = 5;
1183                break;
1184 
1185            default:
1186                abort("QueryPath: unknown segment", 26);
1187        }
1188        if (coerced)
1189                KillPath(path);
1190 }
1191 /*
1192 :h3.QueryBounds() - Return the Bounding Box of a Path
1193 
1194 Returns the bounding box by setting the user's variables.
1195 */
1196 
QueryBounds(p0,S,xminP,yminP,xmaxP,ymaxP)1197 void QueryBounds(p0, S, xminP, yminP, xmaxP, ymaxP)
1198        register struct segment *p0;  /* object to check for bound            */
1199        struct XYspace *S;    /* coordinate space of returned values          */
1200        DOUBLE *xminP,*yminP; /* lower left hand corner (set by routine)      */
1201        DOUBLE *xmaxP,*ymaxP; /* upper right hand corner (set by routine)     */
1202 {
1203        register struct segment *path;  /* loop variable for path segments    */
1204        register fractpel lastx,lasty;  /* loop variables:  previous endingpoint */
1205        register fractpel x,y;  /* loop variables:  current ending point      */
1206        struct fractpoint min;  /* registers to keep lower left hand corner   */
1207        struct fractpoint max;  /* registers to keep upper right hand corner  */
1208        int coerced = FALSE;  /* we have coerced the path from another object */
1209        DOUBLE x1,y1,x2,y2,x3,y3,x4,y4;  /* corners of rectangle in space X   */
1210 
1211        IfTrace2((MustTraceCalls), "QueryBounds(%p, %p,", p0, S);
1212        IfTrace4((MustTraceCalls), " %p, %p, %p, %p)\n",
1213                                   xminP, yminP, xmaxP, ymaxP);
1214        if (S->type != SPACETYPE) {
1215                ArgErr("QueryBounds:  bad XYspace", S, NULL);
1216                return;
1217        }
1218 
1219        min.x = min.y = max.x = max.y = 0;
1220        if (p0 != NULL) {
1221                if (!ISPATHANCHOR(p0)) {
1222                        switch(p0->type) {
1223                            case STROKEPATHTYPE:
1224       /* replaced DupStrokePath() with Dup() 3-26-91 PNM */
1225                                p0 = (struct segment *) DoStroke(Dup(p0));
1226                                /* no break here, we have a region in p0 */
1227                            case REGIONTYPE:
1228                                p0 = RegionBounds(p0);
1229                                break;
1230 
1231                            case PICTURETYPE:
1232                                p0 = PictureBounds(p0);
1233                                break;
1234 
1235                            default:
1236                                ArgErr("QueryBounds:  bad object", p0, NULL);
1237                                return;
1238                        }
1239                        coerced = TRUE;
1240                }
1241                if (p0->type == TEXTTYPE) {
1242     /* replaced CopyPath() with Dup() 3-26-91 PNM */
1243                        p0 = (struct segment *)CoerceText(Dup(p0));  /* there are faster ways */
1244                        coerced = TRUE;
1245                }
1246                if (p0->type == MOVETYPE) {
1247                        min.x = max.x = p0->dest.x;
1248                        min.y = max.y = p0->dest.y;
1249                }
1250        }
1251        lastx = lasty = 0;
1252 
1253        for (path = p0; path != NULL; path = path->link) {
1254 
1255                x = lastx + path->dest.x;
1256                y = lasty + path->dest.y;
1257 
1258                switch (path->type) {
1259 
1260                    case LINETYPE:
1261                        break;
1262 
1263                    case CONICTYPE:
1264                    {
1265                        register struct conicsegment *cp = (struct conicsegment *) path;
1266                        register fractpel Mx = lastx + cp->M.x;
1267                        register fractpel My = lasty + cp->M.y;
1268                        register fractpel deltax = 0.5 * cp->roundness * cp->dest.x;
1269                        register fractpel deltay = 0.5 * cp->roundness * cp->dest.y;
1270                        register fractpel Px = Mx - deltax;
1271                        register fractpel Py = My - deltay;
1272                        register fractpel Qx = Mx + deltax;
1273                        register fractpel Qy = My + deltay;
1274 
1275 
1276                        if (Mx < min.x) min.x = Mx;
1277                        else if (Mx > max.x) max.x = Mx;
1278                        if (My < min.y) min.y = My;
1279                        else if (My > max.y) max.y = My;
1280 
1281                        if (Px < min.x) min.x = Px;
1282                        else if (Px > max.x) max.x = Px;
1283                        if (Py < min.y) min.y = Py;
1284                        else if (Py > max.y) max.y = Py;
1285 
1286                        if (Qx < min.x) min.x = Qx;
1287                        else if (Qx > max.x) max.x = Qx;
1288                        if (Qy < min.y) min.y = Qy;
1289                        else if (Qy > max.y) max.y = Qy;
1290                    }
1291                        break;
1292 
1293 
1294                    case MOVETYPE:
1295                        /*
1296                        * We can't risk adding trailing Moves to the
1297                        * bounding box:
1298                        */
1299                        if (path->link == NULL)
1300                                goto done;  /* God forgive me                 */
1301                        break;
1302 
1303                    case BEZIERTYPE:
1304                    {
1305                        register struct beziersegment *bp = (struct beziersegment *) path;
1306                        register fractpel Bx = lastx + bp->B.x;
1307                        register fractpel By = lasty + bp->B.y;
1308                        register fractpel Cx = lastx + bp->C.x;
1309                        register fractpel Cy = lasty + bp->C.y;
1310 
1311                        if (Bx < min.x) min.x = Bx;
1312                        else if (Bx > max.x) max.x = Bx;
1313                        if (By < min.y) min.y = By;
1314                        else if (By > max.y) max.y = By;
1315 
1316                        if (Cx < min.x) min.x = Cx;
1317                        else if (Cx > max.x) max.x = Cx;
1318                        if (Cy < min.y) min.y = Cy;
1319                        else if (Cy > max.y) max.y = Cy;
1320                    }
1321                        break;
1322 
1323                    case HINTTYPE:
1324                        break;
1325                    default:
1326                        abort("QueryBounds: unknown type", 27);
1327                }
1328 
1329                if (x < min.x) min.x = x;
1330                else if (x > max.x) max.x = x;
1331                if (y < min.y) min.y = y;
1332                else if (y > max.y) max.y = y;
1333 
1334                lastx = x;   lasty = y;
1335        }
1336 done:
1337        UnConvert(S, &min, &x1, &y1);
1338        UnConvert(S, &max, &x4, &y4);
1339        x = min.x;  min.x = max.x; max.x = x;
1340        UnConvert(S, &min, &x2, &y2);
1341        UnConvert(S, &max, &x3, &y3);
1342 
1343        *xminP = *xmaxP = x1;
1344        if (x2 < *xminP)  *xminP = x2;
1345        else if (x2 > *xmaxP)  *xmaxP = x2;
1346        if (x3 < *xminP)  *xminP = x3;
1347        else if (x3 > *xmaxP)  *xmaxP = x3;
1348        if (x4 < *xminP)  *xminP = x4;
1349        else if (x4 > *xmaxP)  *xmaxP = x4;
1350 
1351        *yminP = *ymaxP = y1;
1352        if (y2 < *yminP)  *yminP = y2;
1353        else if (y2 > *ymaxP)  *ymaxP = y2;
1354        if (y3 < *yminP)  *yminP = y3;
1355        else if (y3 > *ymaxP)  *ymaxP = y3;
1356        if (y4 < *yminP)  *yminP = y4;
1357        else if (y4 > *ymaxP)  *ymaxP = y4;
1358 
1359        if (coerced)
1360                Destroy(p0);
1361 }
1362 /*
1363 :h3.BoxPath()
1364 */
BoxPath(S,h,w)1365 struct segment *BoxPath(S, h, w)
1366        struct XYspace *S;
1367        int h,w;
1368 {
1369        struct segment *path;
1370 
1371        path = Join( Line(ILoc(S, w, 0)), Line(ILoc(S, 0, h)) );
1372        path = JoinSegment(path, LINETYPE, -path->dest.x, -path->dest.y, NULL);
1373        return(ClosePath(path));
1374 }
1375 
1376 /*
1377 :h3.DropSegment() - Drop the First Segment in a Path
1378 
1379 This routine takes the path and returns a new path that is one segment
1380 shorter.  It can be used in conjunction with QueryPath(), for example,
1381 to ask about an entire path.
1382 */
1383 
DropSegment(path)1384 struct segment *DropSegment(path)
1385        register struct segment *path;
1386 {
1387        IfTrace1((MustTraceCalls),"DropSegment(%p)\n", path);
1388        if (path != NULL && path->type == STROKEPATHTYPE)
1389                path = CoercePath(path);
1390        ARGCHECK((path == NULL || !ISPATHANCHOR(path)),
1391                  "DropSegment: arg not a non-null path", path, path, (0), struct segment *);
1392        if (path->type == TEXTTYPE)
1393                path = CoerceText(path);
1394        path = UniquePath(path);
1395 
1396        POP(path);
1397        return(path);
1398 }
1399 /*
1400 :h3.HeadSegment() - Return the First Segment in a Path
1401 
1402 This routine takes the path and returns a new path consists of the
1403 first segment only.
1404 */
1405 
HeadSegment(path)1406 struct segment *HeadSegment(path)
1407        register struct segment *path;  /* input path                         */
1408 {
1409        IfTrace1((MustTraceCalls),"HeadSegment(%p)\n", path);
1410        if (path == NULL)
1411                return(NULL);
1412        if (path->type == STROKEPATHTYPE)
1413                path = CoercePath(path);
1414        ARGCHECK(!ISPATHANCHOR(path), "HeadSegment: arg not a path", path, path, (0), struct segment *);
1415        if (path->type == TEXTTYPE)
1416                path = CoerceText(path);
1417        path = UniquePath(path);
1418 
1419        if (path->link != NULL)
1420                KillPath(path->link);
1421        path->link = NULL;
1422        path->last = path;
1423        return(path);
1424 }
1425 
1426 /*
1427 :h2.Path Debug Routines
1428 
1429 :h3.DumpPath() - Display a Path on the Trace File
1430   removed by RMz, 1999-06-07
1431 */
1432 
1433 /*
1434 void DumpPath(p)
1435        register struct segment *p;
1436 {
1437        register fractpel x,y;
1438        register fractpel lastx,lasty;
1439        DOUBLE roundness;
1440 
1441        IfTrace1(TRUE,"Dumping path, anchor=%p:\n", p);
1442        lastx = lasty = 0;
1443 
1444        for (;p != NULL; p=p->link) {
1445 
1446                IfTrace0(TRUE,". ");
1447                x = p->dest.x;
1448                y = p->dest.y;
1449                switch (p->type) {
1450 
1451                    case LINETYPE:
1452                        IfTrace1(TRUE,". line<%x> to", (LONG) p->flag);
1453                        IfTrace4(TRUE," (%d,%d), delta=(%d,%d)",
1454                                  x + lastx, y + lasty, x, y);
1455                        break;
1456 
1457                    case MOVETYPE:
1458                        IfTrace1(TRUE,"MOVE<%x> to", (LONG) p->flag);
1459                        IfTrace4(TRUE,"(%d,%d), delta=(%d,%d)",
1460                                  x + lastx, y + lasty, x, y);
1461                        break;
1462 
1463                    case CONICTYPE:
1464                    {
1465                        register struct conicsegment *cp = (struct conicsegment *) p;
1466 
1467                        roundness = cp->roundness;
1468                        IfTrace2(TRUE, ". conic to (%d,%d),",
1469                                                   x + lastx, y + lasty);
1470                        IfTrace3(TRUE," M=(%d,%d), r=%p", cp->M.x + lastx,
1471                                                    cp->M.y + lasty, &roundness);
1472                    }
1473                        break;
1474 
1475                    case BEZIERTYPE:
1476                    {
1477                        register struct beziersegment *bp = (struct beziersegment *) p;
1478 
1479                        IfTrace4(TRUE,". bezier to (%d,%d), B=(%d,%d)",
1480                                        x + lastx, y + lasty,
1481                                        bp->B.x + lastx, bp->B.y + lasty);
1482                        IfTrace2(TRUE, ", C=(%d,%d)",
1483                                        bp->C.x + lastx, bp->C.y + lasty);
1484                    }
1485                        break;
1486 
1487                    case HINTTYPE:
1488                    {
1489                        register struct hintsegment *hp = (struct hintsegment *) p;
1490 
1491                        IfTrace4(TRUE,". hint ref=(%d,%d), width=(%d,%d)",
1492                                        hp->ref.x + lastx, hp->ref.y + lasty,
1493                                        hp->width.x, hp->width.y);
1494                        IfTrace4(TRUE, ", %c %c %c %c",
1495                                        hp->orientation, hp->hinttype,
1496                                        hp->adjusttype, hp->direction);
1497                        IfTrace1(TRUE, ", %d", (LONG) hp->label);
1498                    }
1499                        break;
1500 
1501                    case TEXTTYPE:
1502                        DumpText(p);
1503                        break;
1504 
1505                    default:
1506                        IfTrace0(TRUE, "bad path segment?");
1507                }
1508                IfTrace1(TRUE," at %p\n", p);
1509                lastx += x;
1510                lasty += y;
1511        }
1512 }
1513 
1514 */
1515