1 /* $XConsortium: paths.c,v 1.4 91/10/10 11:18:40 rws Exp $ */
2 /* Copyright International Business Machines, Corp. 1991
4 * Copyright Lexmark International, Inc. 1991
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.
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
29 /* PATHS CWEB V0021 ******** */
31 :h1 id=paths.PATHS Module - Path Operator Handler
33 This is the module that is responsible for building and transforming
36 &author. Jeffrey B. Lotspiech (lotspiech@almaden.ibm.com)
41 The included files are:
44 /* after the system includes (dsr) */
48 #include "regions.h" /* understands about Union */
49 #include "fonts.h" /* understands about TEXTTYPEs */
50 #include "pictures.h" /* understands about handles */
51 #include "strokes.h" /* understands how to coerce stroke paths */
56 :h3.Routines Available to the TYPE1IMAGER User
58 The PATHS routines that are made available to the outside user are:
61 /*SHARED LINE(S) ORIGINATED HERE*/
63 :h3.Functions Provided to Other Modules
65 The path routines that are made available to other TYPE1IMAGER modules
69 /*SHARED LINE(S) ORIGINATED HERE*/
71 NOTE: because of the casts put in the macros for Loc, ArcCA, Conic,
72 RoundConic, PathSegment, and JoinSegment, we cannot use the macro names
73 when the functions are actually defined. We have to use the unique
74 names with their unique first two characters. Thus, if anyone in the
75 future ever decided to change the first two characters, it would not be
76 enough just to change the macro (as it would for most other functions).
77 He would have to also change the function definition.
80 :h3.Macros Provided to Other Modules
82 The CONCAT macro is defined here and used in the STROKES module. See
83 :hdref refid=pathmac..
86 /*SHARED LINE(S) ORIGINATED HERE*/
89 :h2.Path Segment Structures
91 A path is represented as a linked list of the following structure:
94 /*SHARED LINE(S) ORIGINATED HERE*/
96 When 'link' is NULL, we are at the last segment in the path (surprise!).
98 'last' is only non-NULL on the first segment of a path,
99 for all the other segments 'last' == NULL. We test for a non-NULL
100 'last' (ISPATHANCHOR predicate) when we are given an alleged path
101 to make sure the user is not trying to pull a fast one on us.
103 A path may be a collection of disjoint paths. Every break in the
104 disjoint path is represented by a MOVETYPE segment.
106 Closed paths are discussed in :hdref refid=close..
108 :h3.CopyPath() - Physically Duplicating a Path
110 This simple function illustrates moving through the path linked list.
111 Duplicating a segment just involves making a copy of it, except for
112 text, which has some auxilliary things involved. We don't feel
113 competent to duplicate text in this module, so we call someone who
114 knows how (in the FONTS module).
116 struct segment *CopyPath(p0)
117 register struct segment *p0; /* path to duplicate */
119 register struct segment *p,*n,*last,*anchor;
121 for (p = p0, anchor = NULL; p != NULL; p = p->link) {
123 ARGCHECK((!ISPATHTYPE(p->type) || (p != p0 && p->last != NULL)),
124 "CopyPath: invalid segment", p, NULL, (0), struct segment *);
126 if (p->type == TEXTTYPE)
127 n = (struct segment *) CopyText(p);
129 n = (struct segment *)Allocate(p->size, p, 0);
138 At this point we have a chain of newly allocated segments hanging off
139 'anchor'. We need to make sure the first segment points to the last:
141 if (anchor != NULL) {
149 :h3.KillPath() - Destroying a Path
151 Destroying a path is simply a matter of freeing each segment in the
152 linked list. Again, we let the experts handle text.
155 register struct segment *p; /* path to destroy */
157 register struct segment *linkp; /* temp register holding next segment*/
159 /* return conditional based on reference count 3-26-91 PNM */
160 if ( (--(p->references) > 1) ||
161 ( (p->references == 1) && !ISPERMANENT(p->flag) ) )
165 if (!ISPATHTYPE(p->type)) {
166 ArgErr("KillPath: bad segment", p, NULL);
170 if (p->type == TEXTTYPE)
179 :h2 id=location."location" Objects
181 The TYPE1IMAGER user creates and destroys objects of type "location". These
182 objects locate points for the primitive path operators. We play a trick
183 here and store these objects in the same "segment" structure used for
184 paths, with a type field == MOVETYPE.
186 This allows the Line() operator, for example, to be very trivial:
187 It merely stamps its input structure as a LINETYPE and returns it to the
188 caller--assuming, of course, the input structure was not permanent (as
191 :h3.The "movesegment" Template Structure
193 This template is used as a generic segment structure for Allocate:
196 /* added reference field 1 to temporary template below 3-26-91 PNM */
197 static struct segment movetemplate = { MOVETYPE, 0, 1, sizeof(struct segment), 0,
200 :h3.Loc() - Create an "Invisible Line" Between (0,0) and a Point
204 struct segment *t1_Loc(S, x, y)
205 register struct XYspace *S; /* coordinate space to interpret X,Y */
206 double x,y; /* destination point */
208 register struct segment *r;
211 IfTrace3((MustTraceCalls),"..Loc(S=%z, x=%f, y=%f)\n", S, &x, &y);
213 r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
214 TYPECHECK("Loc", S, SPACETYPE, r, (0), struct segment *);
217 r->context = S->context;
218 (*S->convert)(&r->dest, S, x, y);
223 :h3.ILoc() - Loc() With Integer Arguments
226 struct segment *ILoc(S, x, y)
227 register struct XYspace *S; /* coordinate space to interpret X,Y */
228 register int x,y; /* destination point */
230 register struct segment *r;
232 IfTrace3((MustTraceCalls),"..ILoc(S=%z, x=%d, y=%d)\n",
233 S, (long) x, (long) y);
234 r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
235 TYPECHECK("Loc", S, SPACETYPE, r, (0), struct segment *);
238 r->context = S->context;
239 (*S->iconvert)(&r->dest, S, (long) x, (long) y);
245 :h3.SubLoc() - Vector Subtraction of Two Locition Objects
247 This user operator subtracts two location objects, yielding a new
248 location object that is the result.
250 The symmetrical function AddLoc() is totally redundent with Join(),
251 so it is not provided.
254 struct segment *SubLoc(p1, p2)
255 register struct segment *p1;
256 register struct segment *p2;
258 IfTrace2((MustTraceCalls),"SubLoc(%z, %z)\n", p1, p2);
260 ARGCHECK(!ISLOCATION(p1), "SubLoc: bad first arg", p1, NULL, (0), struct segment *);
261 ARGCHECK(!ISLOCATION(p2), "SubLoc: bad second arg", p2, NULL, (0), struct segment *);
263 p1->dest.x -= p2->dest.x;
264 p1->dest.y -= p2->dest.y;
270 :h2.Straight Line Segments
272 :h3.PathSegment() - Create a Generic Path Segment
274 Many routines need a LINETYPE or MOVETYPE path segment, but do not
275 want to go through the external user's interface, because, for example,
276 they already know the "fractpel" destination of the segment and the
277 conversion is unnecessary. PathSegment() is an internal routine
278 provided to the rest of TYPE1IMAGER for handling these cases.
281 struct segment *t1_PathSegment(type, x, y)
282 int type; /* LINETYPE or MOVETYPE */
283 fractpel x,y; /* where to go to, if known */
285 register struct segment *r; /* newly created segment */
287 r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
289 r->last = r; /* last points to itself for singleton */
295 :h3.Line() - Create a Line Segment Between (0,0) and a Point P
297 This involves just creating and filling out a segment structure:
299 struct segment *Line(P)
300 register struct segment *P; /* relevant coordinate space */
303 IfTrace1((MustTraceCalls),"..Line(%z)\n", P);
304 ARGCHECK(!ISLOCATION(P), "Line: arg not a location", P, NULL, (0), struct segment *);
311 :h2.Curved Path Segments
313 We need more points to describe curves. So, the structures for curved
314 path segments are slightly different. The first part is identical;
315 the curved structures are larger with the extra points on the end.
317 :h3.Bezier Segment Structure
319 We support third order Bezier curves. They are specified with four
320 control points A, B, C, and D. The curve starts at A with slope AB
321 and ends at D with slope CD. The curvature at the point A is inversely
322 related to the length |AB|, and the curvature at the point D is
323 inversely related to the length |CD|. Point A is always point (0,0).
327 /*SHARED LINE(S) ORIGINATED HERE*/
329 :h3.Bezier() - Generate a Bezier Segment
331 This is just a simple matter of filling out a 'beziersegment' structure:
334 struct beziersegment *Bezier(B, C, D)
335 register struct segment *B; /* second control point */
336 register struct segment *C; /* third control point */
337 register struct segment *D; /* fourth control point (ending point) */
339 /* added reference field of 1 to temporary template below 3-26-91 PNM */
340 static struct beziersegment template =
341 { BEZIERTYPE, 0, 1, sizeof(struct beziersegment), 0,
342 NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 } };
344 register struct beziersegment *r; /* output segment */
346 IfTrace3((MustTraceCalls),"..Bezier(%z, %z, %z)\n", B, C, D);
347 ARGCHECK(!ISLOCATION(B), "Bezier: bad B", B, NULL, (2,C,D), struct beziersegment *);
348 ARGCHECK(!ISLOCATION(C), "Bezier: bad C", C, NULL, (2,B,D), struct beziersegment *);
349 ARGCHECK(!ISLOCATION(D), "Bezier: bad D", D, NULL, (2,B,C), struct beziersegment *);
351 r = (struct beziersegment *)Allocate(sizeof(struct beziersegment), &template, 0);
352 r->last = (struct segment *) r;
353 r->dest.x = D->dest.x;
354 r->dest.y = D->dest.y;
367 :h2.Font "Hint" Segments
369 :h3.Hint() - A Font 'Hint' Segment
371 This is temporary code while we experiment with hints.
374 /*SHARED LINE(S) ORIGINATED HERE*/
375 struct hintsegment *Hint(S, ref, width, orientation, hinttype, adjusttype, direction, label)
385 /* added reference field of 1 to hintsegment template below 3-26-91 PNM */
386 static struct hintsegment template = { HINTTYPE, 0, 1, sizeof(struct hintsegment), 0,
387 NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 },
388 ' ', ' ', ' ', ' ', 0};
390 register struct hintsegment *r;
392 r = (struct hintsegment *)Allocate(sizeof(struct hintsegment), &template, 0);
394 r->orientation = orientation;
395 if (width == 0.0) width = 1.0;
397 if (orientation == 'h') {
398 (*S->convert)(&r->ref, S, 0.0, ref);
399 (*S->convert)(&r->width, S, 0.0, width);
401 else if (orientation == 'v') {
402 (*S->convert)(&r->ref, S, ref, 0.0);
403 (*S->convert)(&r->width, S, width, 0.0);
406 return((struct hintsegment *)ArgErr("Hint: orient not 'h' or 'v'", NULL, NULL));
407 if (r->width.x < 0) r->width.x = - r->width.x;
408 if (r->width.y < 0) r->width.y = - r->width.y;
409 r->hinttype = hinttype;
410 r->adjusttype = adjusttype;
411 r->direction = direction;
413 r->last = (struct segment *) r;
421 /*SHARED LINE(S) ORIGINATED HERE*/
424 POP removes the first segment in a path 'p' and Frees it. 'p' is left
425 pointing to the end of the path:
428 { register struct segment *linkp; \
431 linkp->last = p->last; \
435 INSERT inserts a single segment in the middle of a chain. 'b' is
436 the segment before, 'p' the segment to be inserted, and 'a' the
439 #define INSERT(b,p,a) b->link=p; p->link=a; p->last=NULL
442 :h3.Join() - Join Two Objects Together
444 If these are paths, this operator simply invokes the CONCAT macro.
445 Why so much code then, you ask? Well we have to check for object
446 types other than paths, and also check for certain path consistency
450 struct segment *Join(p1, p2)
451 register struct segment *p1,*p2;
453 IfTrace2((MustTraceCalls && PathDebug > 1),"..Join(%z, %z)\n", p1, p2);
454 IfTrace2((MustTraceCalls && PathDebug <=1),"..Join(%x, %x)\n", p1, p2);
456 We start with a whole bunch of very straightforward argument tests:
459 if (!ISPATHTYPE(p2->type)) {
462 return((struct segment *)Unique(p2));
473 return((struct segment *)BegHandle(p1, p2));
477 ARGCHECK((p2->last == NULL), "Join: right arg not anchor", p2, NULL, (1,p1), struct segment *);
481 In certain circumstances, we don't have to duplicate a permanent
482 location. (We would just end up destroying it anyway). These cases
483 are when 'p2' begins with a move-type segment:
485 if (p2->type == TEXTTYPE || p2->type == MOVETYPE) {
488 if (ISLOCATION(p1)) {
489 p2->dest.x += p1->dest.x;
490 p2->dest.y += p1->dest.y;
497 return((struct segment *)Unique(p1));
500 if (!ISPATHTYPE(p1->type))
511 return((struct segment *)EndHandle(p1, p2));
514 ARGCHECK((p1->last == NULL), "Join: left arg not anchor", p1, NULL, (1,p2), struct segment *);
521 At this point all the checking is done. We have two temporary non-null
522 path types in 'p1' and 'p2'. If p1 ends with a MOVE, and p2 begins with
523 a MOVE, we collapse the two MOVEs into one. We enforce the rule that
524 there may not be two MOVEs in a row:
527 if (p1->last->type == MOVETYPE && p2->type == MOVETYPE) {
528 p1->last->flag |= p2->flag;
529 p1->last->dest.x += p2->dest.x;
530 p1->last->dest.y += p2->dest.y;
536 Now we check for another silly rule. If a path has any TEXTTYPEs,
537 then it must have only TEXTTYPEs and MOVETYPEs, and furthermore,
538 it must begin with a TEXTTYPE. This rule makes it easy to check
539 for the special case of text. If necessary, we will coerce
540 TEXTTYPEs into paths so we don't mix TEXTTYPEs with normal paths.
542 if (p1->type == TEXTTYPE) {
543 if (p2->type != TEXTTYPE && !ISLOCATION(p2))
547 if (p2->type == TEXTTYPE) {
548 if (ISLOCATION(p1)) {
549 p2->dest.x += p1->dest.x;
550 p2->dest.y += p1->dest.y;
559 Thank God! Finally! It's hard to believe, but we are now able to
560 actually do the join. This is just invoking the CONCAT macro:
568 :h3.JoinSegment() - Create a Path Segment and Join It to a Known Path
570 This internal function is quicker than a full-fledged join because
571 it can do much less checking.
574 struct segment *t1_JoinSegment(before, type, x, y, after)
575 register struct segment *before; /* path to join before new segment */
576 int type; /* type of new segment (MOVETYPE or LINETYPE) */
577 fractpel x,y; /* x,y of new segment */
578 register struct segment *after; /* path to join after new segment */
580 register struct segment *r; /* returned path built here */
582 r = PathSegment(type, x, y);
583 if (before != NULL) {
588 r->context = after->context;
595 :h2.Other Path Functions
600 struct segment *t1_ClosePath(p0,lastonly)
601 register struct segment *p0; /* path to close */
602 register int lastonly; /* flag deciding to close all subpaths or... */
604 register struct segment *p,*last,*start; /* used in looping through path */
605 register fractpel x,y; /* current position in path */
606 register fractpel firstx,firsty; /* start position of sub path */
607 register struct segment *lastnonhint; /* last non-hint segment in path */
609 IfTrace1((MustTraceCalls),"ClosePath(%z)\n", p0);
610 if (p0 != NULL && p0->type == TEXTTYPE)
611 return(UniquePath(p0));
612 if (p0->type == STROKEPATHTYPE)
613 return((struct segment *)Unique(p0));
615 * NOTE: a null closed path is different from a null open path
616 * and is denoted by a closed (0,0) move segment. We make
617 * sure this path begins and ends with a MOVETYPE:
619 if (p0 == NULL || p0->type != MOVETYPE)
620 p0 = JoinSegment(NULL, MOVETYPE, 0, 0, p0);
621 TYPECHECK("ClosePath", p0, MOVETYPE, NULL, (0), struct segment *);
622 if (p0->last->type != MOVETYPE)
623 p0 = JoinSegment(p0, MOVETYPE, 0, 0, NULL);
628 We now begin a loop through the path,
629 incrementing current 'x' and 'y'. We are searching
630 for MOVETYPE segments (breaks in the path) that are not already closed.
631 At each break, we insert a close segment.
633 for (p = p0, x = y = 0, start = NULL;
635 x += p->dest.x, y += p->dest.y, last = p, p = p->link)
638 if (p->type == MOVETYPE) {
639 if (start != NULL && (lastonly?p->link==NULL:TRUE) &&
640 !(ISCLOSED(start->flag) && LASTCLOSED(last->flag))) {
641 register struct segment *r; /* newly created */
643 start->flag |= ISCLOSED(ON);
644 r = PathSegment(LINETYPE, firstx - x,
647 r->flag |= LASTCLOSED(ON);
648 /*< adjust 'last' if possible for a 0,0 close >*/
651 #define CLOSEFUDGE 3 /* if we are this close, let's change last segment */
653 if (r->dest.x != 0 || r->dest.y != 0) {
654 if (r->dest.x <= CLOSEFUDGE && r->dest.x >= -CLOSEFUDGE
655 && r->dest.y <= CLOSEFUDGE && r->dest.y >= -CLOSEFUDGE) {
656 IfTrace2((PathDebug),
657 "ClosePath forced closed by (%p,%p)\n",
658 r->dest.x, r->dest.y);
659 lastnonhint->dest.x += r->dest.x;
660 lastnonhint->dest.y += r->dest.y;
661 r->dest.x = r->dest.y = 0;
665 if (p->link != NULL) {
666 p->dest.x += x - firstx;
667 p->dest.y += y - firsty;
673 firstx = x + p->dest.x;
674 firsty = y + p->dest.y;
676 else if (p->type != HINTTYPE)
684 :h2.Reversing the Direction of a Path
686 This turned out to be more difficult than I thought at first. The
687 trickiness was due to the fact that closed paths must remain closed,
690 We need three subroutines:
693 static struct segment *SplitPath(); /* break a path at any point */
694 static struct segment *DropSubPath(); /* breaks a path after first sub-path */
695 static struct segment *ReverseSubPath(); /* reverses a single sub-path */
698 :h3.Reverse() - User Operator to Reverse a Path
700 This operator reverses the entire path.
703 struct segment *Reverse(p)
704 register struct segment *p; /* full path to reverse */
706 register struct segment *r; /* output path built here */
707 register struct segment *nextp; /* contains next sub-path */
709 IfTrace1((MustTraceCalls),"Reverse(%z)\n", p);
714 ARGCHECK(!ISPATHANCHOR(p), "Reverse: invalid path", p, NULL, (0), struct segment *);
716 if (p->type == TEXTTYPE)
723 nextp = DropSubPath(p);
724 p = ReverseSubPath(p);
734 :h4.ReverseSubPath() - Subroutine to Reverse a Single Sub-Path
737 static struct segment *ReverseSubPath(p)
738 register struct segment *p; /* input path */
740 register struct segment *r; /* reversed path will be created here */
741 register struct segment *nextp; /* temporary variable used in loop */
742 register int wasclosed; /* flag, path was closed */
747 wasclosed = ISCLOSED(p->flag);
752 First we reverse the direction of this segment and clean up its flags:
754 p->dest.x = - p->dest.x; p->dest.y = - p->dest.y;
755 p->flag &= ~(ISCLOSED(ON) | LASTCLOSED(ON));
766 The logic of this is that the new M point (stored relative to the new
767 beginning) is (M - C). However, C ("dest") has already been reversed
768 So, we add "dest" instead of subtracting it:
770 register struct conicsegment *cp = (struct conicsegment *) p;
772 cp->M.x += cp->dest.x; cp->M.y += cp->dest.y;
778 register struct beziersegment *bp = (struct beziersegment *) p;
780 bp->B.x += bp->dest.x; bp->B.y += bp->dest.y;
781 bp->C.x += bp->dest.x; bp->C.y += bp->dest.y;
787 register struct hintsegment *hp = (struct hintsegment *) p;
789 hp->ref.x = -hp->ref.x; hp->ref.y = -hp->ref.y;
794 abort("Reverse: bad path segment");
797 We need to reverse the order of segments too, so we break this segment
798 off of the input path, and tack it on the front of the growing path
805 CONCAT(p,r); /* leaves result in 'p'... not what we want */
807 p = nextp; /* advance to next segment in input path */
818 :h4.DropSubPath() - Drops the First Sub-Path Off a Path
820 This subroutine returns the remaining sub-path(s). While doing so, it
821 breaks the input path after the first sub-path so that a pointer to
822 the original path now contains the first sub-path only.
825 static struct segment *DropSubPath(p0)
826 register struct segment *p0; /* original path */
828 register struct segment *p; /* returned remainder here */
830 for (p = p0; p->link != NULL; p = p->link) {
831 if (p->link->type == MOVETYPE)
835 return(SplitPath(p0, p));
838 static struct segment *SplitPath(anchor, before)
839 register struct segment *anchor;
840 register struct segment *before;
842 register struct segment *r;
844 if (before == anchor->last)
848 r->last = anchor->last;
849 anchor->last = before;
857 :h3.ReverseSubPaths() - Reverse the Direction of Sub-paths Within a Path
859 This user operator reverses the sub-paths in a path, but leaves the
860 'move' segments unchanged. It builds on top of the subroutines
864 struct segment *ReverseSubPaths(p)
865 register struct segment *p; /* input path */
867 register struct segment *r; /* reversed path will be created here */
868 register struct segment *nextp; /* temporary variable used in loop */
869 int wasclosed; /* flag; subpath was closed */
870 register struct segment *nomove; /* the part of sub-path without move segment */
871 struct fractpoint delta;
873 IfTrace1((MustTraceCalls),"ReverseSubPaths(%z)\n", p);
878 ARGCHECK(!ISPATHANCHOR(p), "ReverseSubPaths: invalid path", p, NULL, (0), struct segment *);
880 if (p->type == TEXTTYPE)
882 if (p->type != MOVETYPE)
883 p = JoinSegment(NULL, MOVETYPE, 0, 0, p);
890 nextp = DropSubPath(p);
891 wasclosed = ISCLOSED(p->flag);
895 nomove = SplitPath(p, p);
898 PathDelta(nomove, &delta);
900 nomove = ReverseSubPath(nomove);
901 p->dest.x += delta.x;
902 p->dest.y += delta.y;
904 nextp->dest.x += delta.x;
905 nextp->dest.y += delta.y;
908 nomove = ClosePath(nomove);
909 nextp->dest.x -= delta.x;
910 nextp->dest.y -= delta.y;
921 register struct segment *p0;
923 register struct segment *p;
925 for (p=p0; p->link->link != NULL; p=p->link) { ; }
927 if (!LASTCLOSED(p->link->flag))
928 abort("UnClose: no LASTCLOSED");
930 Free(SplitPath(p0, p));
931 p0->flag &= ~ISCLOSED(ON);
935 :h2.Transforming and Putting Handles on Paths
937 :h3.PathTransform() - Transform a Path
939 Transforming a path involves transforming all the points. In order
940 that closed paths do not become "unclosed" when their relative
941 positions are slightly changed due to loss of arithmetic precision,
942 all point transformations are in absolute coordinates.
944 (It might be better to reset the "absolute" coordinates every time a
945 move segment is encountered. This would mean that we could accumulate
946 error from subpath to subpath, but we would be less likely to make
947 the "big error" where our fixed point arithmetic "wraps". However, I
948 think I'll keep it this way until something happens to convince me
951 The transform is described as a "space", that way we can use our
952 old friend the "iconvert" function, which should be very efficient.
955 struct segment *PathTransform(p0, S)
956 register struct segment *p0; /* path to transform */
957 register struct XYspace *S; /* pseudo space to transform in */
959 register struct segment *p; /* to loop through path with */
960 register fractpel newx,newy; /* current transformed position in path */
961 register fractpel oldx,oldy; /* current untransformed position in path */
962 register fractpel savex,savey; /* save path delta x,y */
966 newx = newy = oldx = oldy = 0;
968 for (p=p0; p != NULL; p=p->link) {
970 savex = p->dest.x; savey = p->dest.y;
972 (*S->iconvert)(&p->dest, S, p->dest.x + oldx, p->dest.y + oldy);
984 register struct conicsegment *cp = (struct conicsegment *) p;
986 (*S->iconvert)(&cp->M, S, cp->M.x + oldx, cp->M.y + oldy);
990 * Note roundness doesn't change... linear transform
998 register struct beziersegment *bp = (struct beziersegment *) p;
1000 (*S->iconvert)(&bp->B, S, bp->B.x + oldx, bp->B.y + oldy);
1003 (*S->iconvert)(&bp->C, S, bp->C.x + oldx, bp->C.y + oldy);
1011 register struct hintsegment *hp = (struct hintsegment *) p;
1013 (*S->iconvert)(&hp->ref, S, hp->ref.x + oldx, hp->ref.y + oldy);
1016 (*S->iconvert)(&hp->width, S, hp->width.x, hp->width.y);
1017 /* Note: width is not relative to origin */
1028 IfTrace1(TRUE,"path = %z\n", p);
1029 abort("PathTransform: invalid segment");
1040 :h3.PathDelta() - Return a Path's Ending Point
1043 void PathDelta(p, pt)
1044 register struct segment *p; /* input path */
1045 register struct fractpoint *pt; /* pointer to x,y to set */
1047 struct fractpoint mypoint; /* I pass this to TextDelta */
1048 register fractpel x,y; /* working variables for path current point */
1050 for (x=y=0; p != NULL; p=p->link) {
1053 if (p->type == TEXTTYPE) {
1054 TextDelta(p, &mypoint);
1065 :h3.BoundingBox() - Produce a Bounding Box Path
1067 This function is called by image code, when we know the size of the
1068 image in pels, and need to get a bounding box path that surrounds it.
1069 The starting/ending handle is in the lower right hand corner.
1071 struct segment *BoundingBox(h, w)
1072 register pel h,w; /* size of box */
1074 register struct segment *path;
1076 path = PathSegment(LINETYPE, -TOFRACTPEL(w), 0);
1077 path = JoinSegment(NULL, LINETYPE, 0, -TOFRACTPEL(h), path);
1078 path = JoinSegment(NULL, LINETYPE, TOFRACTPEL(w), 0, path);
1079 path = ClosePath(path);
1085 :h2.Querying Locations and Paths
1087 :h3.QueryLoc() - Return the X,Y of a Locition
1090 void QueryLoc(P, S, xP, yP)
1091 register struct segment *P; /* location to query, not consumed */
1092 register struct XYspace *S; /* XY space to return coordinates in */
1093 register double *xP,*yP; /* coordinates returned here */
1095 IfTrace4((MustTraceCalls),"QueryLoc(P=%z, S=%z, (%x, %x))\n",
1097 if (!ISLOCATION(P)) {
1098 ArgErr("QueryLoc: first arg not a location", P, NULL);
1101 if (S->type != SPACETYPE) {
1102 ArgErr("QueryLoc: second arg not a space", S, NULL);
1105 UnConvert(S, &P->dest, xP, yP);
1108 :h3.QueryPath() - Find Out the Type of Segment at the Head of a Path
1110 This is a very simple routine that looks at the first segment of a
1111 path and tells the caller what it is, as well as returning the control
1112 point(s) of the path segment. Different path segments have different
1113 number of control points. If the caller knows that the segment is
1114 a move segment, for example, he only needs to pass pointers to return
1118 void QueryPath(path, typeP, Bp, Cp, Dp, fP)
1119 register struct segment *path; /* path to check */
1120 register int *typeP; /* return the type of path here */
1121 register struct segment **Bp; /* return location of first point */
1122 register struct segment **Cp; /* return location of second point */
1123 register struct segment **Dp; /* return location of third point */
1124 register double *fP; /* return Conic sharpness */
1126 register int coerced = FALSE; /* did I coerce a text path? */
1128 IfTrace3((MustTraceCalls), "QueryPath(%z, %x, %x, ...)\n",
1134 if (!ISPATHANCHOR(path)) {
1135 ArgErr("QueryPath: arg not a valid path", path, NULL);
1137 if (path->type == TEXTTYPE) {
1138 path = CoerceText(path);
1142 switch (path->type) {
1146 *Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
1150 *typeP = (LASTCLOSED(path->flag)) ? 4 : 1;
1151 *Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
1156 register struct conicsegment *cp = (struct conicsegment *) path;
1159 *Bp = PathSegment(MOVETYPE, cp->M.x, cp->M.y);
1160 *Cp = PathSegment(MOVETYPE, cp->dest.x, cp->dest.y);
1161 *fP = cp->roundness;
1167 register struct beziersegment *bp = (struct beziersegment *) path;
1170 *Bp = PathSegment(MOVETYPE, bp->B.x, bp->B.y);
1171 *Cp = PathSegment(MOVETYPE, bp->C.x, bp->C.y);
1172 *Dp = PathSegment(MOVETYPE, bp->dest.x, bp->dest.y);
1181 abort("QueryPath: unknown segment");
1187 :h3.QueryBounds() - Return the Bounding Box of a Path
1189 Returns the bounding box by setting the user's variables.
1192 void QueryBounds(p0, S, xminP, yminP, xmaxP, ymaxP)
1193 register struct segment *p0; /* object to check for bound */
1194 struct XYspace *S; /* coordinate space of returned values */
1195 double *xminP,*yminP; /* lower left hand corner (set by routine) */
1196 double *xmaxP,*ymaxP; /* upper right hand corner (set by routine) */
1198 register struct segment *path; /* loop variable for path segments */
1199 register fractpel lastx,lasty; /* loop variables: previous endingpoint */
1200 register fractpel x,y; /* loop variables: current ending point */
1201 struct fractpoint min; /* registers to keep lower left hand corner */
1202 struct fractpoint max; /* registers to keep upper right hand corner */
1203 int coerced = FALSE; /* we have coerced the path from another object */
1204 double x1,y1,x2,y2,x3,y3,x4,y4; /* corners of rectangle in space X */
1206 IfTrace2((MustTraceCalls), "QueryBounds(%z, %z,", p0, S);
1207 IfTrace4((MustTraceCalls), " %x, %x, %x, %x)\n",
1208 xminP, yminP, xmaxP, ymaxP);
1209 if (S->type != SPACETYPE) {
1210 ArgErr("QueryBounds: bad XYspace", S, NULL);
1214 min.x = min.y = max.x = max.y = 0;
1216 if (!ISPATHANCHOR(p0)) {
1218 case STROKEPATHTYPE:
1219 /* replaced DupStrokePath() with Dup() 3-26-91 PNM */
1220 p0 = (struct segment *) DoStroke(Dup(p0));
1221 /* no break here, we have a region in p0 */
1223 p0 = RegionBounds(p0);
1227 p0 = PictureBounds(p0);
1231 ArgErr("QueryBounds: bad object", p0, NULL);
1236 if (p0->type == TEXTTYPE) {
1237 /* replaced CopyPath() with Dup() 3-26-91 PNM */
1238 p0 = (struct segment *)CoerceText(Dup(p0)); /* there are faster ways */
1241 if (p0->type == MOVETYPE) {
1242 min.x = max.x = p0->dest.x;
1243 min.y = max.y = p0->dest.y;
1248 for (path = p0; path != NULL; path = path->link) {
1250 x = lastx + path->dest.x;
1251 y = lasty + path->dest.y;
1253 switch (path->type) {
1260 register struct conicsegment *cp = (struct conicsegment *) path;
1261 register fractpel Mx = lastx + cp->M.x;
1262 register fractpel My = lasty + cp->M.y;
1263 register fractpel deltax = 0.5 * cp->roundness * cp->dest.x;
1264 register fractpel deltay = 0.5 * cp->roundness * cp->dest.y;
1265 register fractpel Px = Mx - deltax;
1266 register fractpel Py = My - deltay;
1267 register fractpel Qx = Mx + deltax;
1268 register fractpel Qy = My + deltay;
1271 if (Mx < min.x) min.x = Mx;
1272 else if (Mx > max.x) max.x = Mx;
1273 if (My < min.y) min.y = My;
1274 else if (My > max.y) max.y = My;
1276 if (Px < min.x) min.x = Px;
1277 else if (Px > max.x) max.x = Px;
1278 if (Py < min.y) min.y = Py;
1279 else if (Py > max.y) max.y = Py;
1281 if (Qx < min.x) min.x = Qx;
1282 else if (Qx > max.x) max.x = Qx;
1283 if (Qy < min.y) min.y = Qy;
1284 else if (Qy > max.y) max.y = Qy;
1291 * We can't risk adding trailing Moves to the
1294 if (path->link == NULL)
1295 goto done; /* God forgive me */
1300 register struct beziersegment *bp = (struct beziersegment *) path;
1301 register fractpel Bx = lastx + bp->B.x;
1302 register fractpel By = lasty + bp->B.y;
1303 register fractpel Cx = lastx + bp->C.x;
1304 register fractpel Cy = lasty + bp->C.y;
1306 if (Bx < min.x) min.x = Bx;
1307 else if (Bx > max.x) max.x = Bx;
1308 if (By < min.y) min.y = By;
1309 else if (By > max.y) max.y = By;
1311 if (Cx < min.x) min.x = Cx;
1312 else if (Cx > max.x) max.x = Cx;
1313 if (Cy < min.y) min.y = Cy;
1314 else if (Cy > max.y) max.y = Cy;
1321 abort("QueryBounds: unknown type");
1324 if (x < min.x) min.x = x;
1325 else if (x > max.x) max.x = x;
1326 if (y < min.y) min.y = y;
1327 else if (y > max.y) max.y = y;
1329 lastx = x; lasty = y;
1332 UnConvert(S, &min, &x1, &y1);
1333 UnConvert(S, &max, &x4, &y4);
1334 x = min.x; min.x = max.x; max.x = x;
1335 UnConvert(S, &min, &x2, &y2);
1336 UnConvert(S, &max, &x3, &y3);
1338 *xminP = *xmaxP = x1;
1339 if (x2 < *xminP) *xminP = x2;
1340 else if (x2 > *xmaxP) *xmaxP = x2;
1341 if (x3 < *xminP) *xminP = x3;
1342 else if (x3 > *xmaxP) *xmaxP = x3;
1343 if (x4 < *xminP) *xminP = x4;
1344 else if (x4 > *xmaxP) *xmaxP = x4;
1346 *yminP = *ymaxP = y1;
1347 if (y2 < *yminP) *yminP = y2;
1348 else if (y2 > *ymaxP) *ymaxP = y2;
1349 if (y3 < *yminP) *yminP = y3;
1350 else if (y3 > *ymaxP) *ymaxP = y3;
1351 if (y4 < *yminP) *yminP = y4;
1352 else if (y4 > *ymaxP) *ymaxP = y4;
1360 struct segment *BoxPath(S, h, w)
1364 struct segment *path;
1366 path = Join( Line(ILoc(S, w, 0)), Line(ILoc(S, 0, h)) );
1367 path = JoinSegment(path, LINETYPE, -path->dest.x, -path->dest.y, NULL);
1368 return(ClosePath(path));
1372 :h3.DropSegment() - Drop the First Segment in a Path
1374 This routine takes the path and returns a new path that is one segment
1375 shorter. It can be used in conjunction with QueryPath(), for example,
1376 to ask about an entire path.
1379 struct segment *DropSegment(path)
1380 register struct segment *path;
1382 IfTrace1((MustTraceCalls),"DropSegment(%z)\n", path);
1383 if (path != NULL && path->type == STROKEPATHTYPE)
1384 path = CoercePath(path);
1385 ARGCHECK((path == NULL || !ISPATHANCHOR(path)),
1386 "DropSegment: arg not a non-null path", path, path, (0), struct segment *);
1387 if (path->type == TEXTTYPE)
1388 path = CoerceText(path);
1389 path = UniquePath(path);
1395 :h3.HeadSegment() - Return the First Segment in a Path
1397 This routine takes the path and returns a new path consists of the
1401 struct segment *HeadSegment(path)
1402 register struct segment *path; /* input path */
1404 IfTrace1((MustTraceCalls),"HeadSegment(%z)\n", path);
1407 if (path->type == STROKEPATHTYPE)
1408 path = CoercePath(path);
1409 ARGCHECK(!ISPATHANCHOR(path), "HeadSegment: arg not a path", path, path, (0), struct segment *);
1410 if (path->type == TEXTTYPE)
1411 path = CoerceText(path);
1412 path = UniquePath(path);
1414 if (path->link != NULL)
1415 KillPath(path->link);
1422 :h2.Path Debug Routines
1424 :h3.DumpPath() - Display a Path on the Trace File
1428 register struct segment *p;
1430 register fractpel x,y;
1431 register fractpel lastx,lasty;
1434 IfTrace1(TRUE,"Dumping path, anchor=%x:\n", p);
1437 for (;p != NULL; p=p->link) {
1439 IfTrace0(TRUE,". ");
1445 IfTrace1(TRUE,". line<%x> to", (long) p->flag);
1446 IfTrace4(TRUE," (%p,%p), delta=(%p,%p)",
1447 x + lastx, y + lasty, x, y);
1451 IfTrace1(TRUE,"MOVE<%x> to", (long) p->flag);
1452 IfTrace4(TRUE,"(%p,%p), delta=(%p,%p)",
1453 x + lastx, y + lasty, x, y);
1458 register struct conicsegment *cp = (struct conicsegment *) p;
1460 roundness = cp->roundness;
1461 IfTrace2(TRUE, ". conic to (%p,%p),",
1462 x + lastx, y + lasty);
1463 IfTrace3(TRUE," M=(%p,%p), r=%f", cp->M.x + lastx,
1464 cp->M.y + lasty, &roundness);
1470 register struct beziersegment *bp = (struct beziersegment *) p;
1472 IfTrace4(TRUE,". bezier to (%p,%p), B=(%p,%p)",
1473 x + lastx, y + lasty,
1474 bp->B.x + lastx, bp->B.y + lasty);
1475 IfTrace2(TRUE, ", C=(%p,%p)",
1476 bp->C.x + lastx, bp->C.y + lasty);
1482 register struct hintsegment *hp = (struct hintsegment *) p;
1484 IfTrace4(TRUE,". hint ref=(%p,%p), width=(%p,%p)",
1485 hp->ref.x + lastx, hp->ref.y + lasty,
1486 hp->width.x, hp->width.y);
1487 IfTrace4(TRUE, ", %c %c %c %c",
1488 hp->orientation, hp->hinttype,
1489 hp->adjusttype, hp->direction);
1490 IfTrace1(TRUE, ", %ld", (long) hp->label);
1499 IfTrace0(TRUE, "bad path segment?");
1501 IfTrace1(TRUE," at %x\n", p);