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