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