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