📄 paths.c
字号:
float ref; float width; char orientation; char hinttype; char adjusttype; char direction; int label;{/* added reference field of 1 to hintsegment template below 3-26-91 PNM */ static struct hintsegment template = { HINTTYPE, 0, 1, sizeof(struct hintsegment), 0, NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 }, ' ', ' ', ' ', ' ', 0}; register struct hintsegment *r; r = (struct hintsegment *)Allocate(sizeof(struct hintsegment), &template, 0); r->orientation = orientation; if (width == 0.0) width = 1.0; if (orientation == 'h') { (*S->convert)(&r->ref, S, 0.0, ref); (*S->convert)(&r->width, S, 0.0, width); } else if (orientation == 'v') { (*S->convert)(&r->ref, S, ref, 0.0); (*S->convert)(&r->width, S, width, 0.0); } else return((struct hintsegment *)ArgErr("Hint: orient not 'h' or 'v'", NULL, NULL)); if (r->width.x < 0) r->width.x = - r->width.x; if (r->width.y < 0) r->width.y = - r->width.y; r->hinttype = hinttype; r->adjusttype = adjusttype; r->direction = direction; r->label = label; r->last = (struct segment *) r; ConsumeSpace(S); return(r);} /**/ /*SHARED LINE(S) ORIGINATED HERE*/ /*POP removes the first segment in a path 'p' and Frees it. 'p' is leftpointing to the end of the path:*/#define POP(p) \ { register struct segment *linkp; \ linkp = p->link; \ if (linkp != NULL) \ linkp->last = p->last; \ Free(p); \ p = linkp; }/*INSERT inserts a single segment in the middle of a chain. 'b' isthe segment before, 'p' the segment to be inserted, and 'a' thesegment after.*/#define INSERT(b,p,a) b->link=p; p->link=a; p->last=NULL /*:h3.Join() - Join Two Objects Together If these are paths, this operator simply invokes the CONCAT macro.Why so much code then, you ask? Well we have to check for objecttypes other than paths, and also check for certain path consistencyrules.*/ struct segment *Join(p1, p2) register struct segment *p1,*p2;{ IfTrace2((MustTraceCalls && PathDebug > 1),"..Join(%p, %p)\n", p1, p2); IfTrace2((MustTraceCalls && PathDebug <=1),"..Join(%p, %p)\n", p1, p2);/*We start with a whole bunch of very straightforward argument tests:*/ if (p2 != NULL) { if (!ISPATHTYPE(p2->type)) { if (p1 == NULL) return((struct segment *)Unique(p2)); switch (p1->type) { case REGIONTYPE: case STROKEPATHTYPE: p1 = CoercePath(p1); break; default: return((struct segment *)BegHandle(p1, p2)); } } ARGCHECK((p2->last == NULL), "Join: right arg not anchor", p2, NULL, (1,p1), struct segment *); p2 = UniquePath(p2); /*In certain circumstances, we don't have to duplicate a permanentlocation. (We would just end up destroying it anyway). These casesare when 'p2' begins with a move-type segment:*/ if (p2->type == TEXTTYPE || p2->type == MOVETYPE) { if (p1 == NULL) return(p2); if (ISLOCATION(p1)) { p2->dest.x += p1->dest.x; p2->dest.y += p1->dest.y; ConsumePath(p1); return(p2); } } } else return((struct segment *)Unique(p1)); if (p1 != NULL) { if (!ISPATHTYPE(p1->type)) switch (p2->type) { case REGIONTYPE: case STROKEPATHTYPE: p2 = CoercePath(p2); break; default: return((struct segment *)EndHandle(p1, p2)); } ARGCHECK((p1->last == NULL), "Join: left arg not anchor", p1, NULL, (1,p2), struct segment *); p1 = UniquePath(p1); } else return(p2); /*At this point all the checking is done. We have two temporary non-nullpath types in 'p1' and 'p2'. If p1 ends with a MOVE, and p2 begins witha MOVE, we collapse the two MOVEs into one. We enforce the rule thatthere may not be two MOVEs in a row:*/ if (p1->last->type == MOVETYPE && p2->type == MOVETYPE) { p1->last->flag |= p2->flag; p1->last->dest.x += p2->dest.x; p1->last->dest.y += p2->dest.y; POP(p2); if (p2 == NULL) return(p1); }/*Now we check for another silly rule. If a path has any TEXTTYPEs,then it must have only TEXTTYPEs and MOVETYPEs, and furthermore,it must begin with a TEXTTYPE. This rule makes it easy to checkfor the special case of text. If necessary, we will coerceTEXTTYPEs into paths so we don't mix TEXTTYPEs with normal paths.*/ if (p1->type == TEXTTYPE) { if (p2->type != TEXTTYPE && !ISLOCATION(p2)) p1 = CoerceText(p1); } else { if (p2->type == TEXTTYPE) { if (ISLOCATION(p1)) { p2->dest.x += p1->dest.x; p2->dest.y += p1->dest.y; Free(p1); return(p2); } else p2 = CoerceText(p2); } }/*Thank God! Finally! It's hard to believe, but we are now able toactually do the join. This is just invoking the CONCAT macro:*/ CONCAT(p1, p2); return(p1);} /*:h3.JoinSegment() - Create a Path Segment and Join It to a Known Path This internal function is quicker than a full-fledged join becauseit can do much less checking.*/ struct segment *t1_JoinSegment(before, type, x, y, after) register struct segment *before; /* path to join before new segment */ int type; /* type of new segment (MOVETYPE or LINETYPE) */ fractpel x,y; /* x,y of new segment */ register struct segment *after; /* path to join after new segment */{ register struct segment *r; /* returned path built here */ r = PathSegment(type, x, y); if (before != NULL) { CONCAT(before, r); r = before; } else r->context = after->context; if (after != NULL) CONCAT(r, after); return(r);} /*:h2.Other Path Functions */ struct segment *t1_ClosePath(p0,lastonly) register struct segment *p0; /* path to close */ register int lastonly; /* flag deciding to close all subpaths or... */{ register struct segment *p,*last=NULL,*start; /* used in looping through path */ register fractpel x,y; /* current position in path */ register fractpel firstx=0,firsty=0; /* start position of sub path */ register struct segment *lastnonhint=NULL; /* last non-hint segment in path */ IfTrace1((MustTraceCalls),"ClosePath(%p)\n", p0); if (p0 != NULL && p0->type == TEXTTYPE) return(UniquePath(p0)); if (p0->type == STROKEPATHTYPE) return((struct segment *)Unique(p0)); /* * NOTE: a null closed path is different from a null open path * and is denoted by a closed (0,0) move segment. We make * sure this path begins and ends with a MOVETYPE: */ if (p0 == NULL || p0->type != MOVETYPE) p0 = JoinSegment(NULL, MOVETYPE, 0, 0, p0); TYPECHECK("ClosePath", p0, MOVETYPE, NULL, (0), struct segment *); if (p0->last->type != MOVETYPE) p0 = JoinSegment(p0, MOVETYPE, 0, 0, NULL); p0 = UniquePath(p0); /*We now begin a loop through the path,incrementing current 'x' and 'y'. We are searchingfor MOVETYPE segments (breaks in the path) that are not already closed.At each break, we insert a close segment.*/ for (p = p0, x = y = 0, start = NULL; p != NULL; x += p->dest.x, y += p->dest.y, last = p, p = p->link) { if (p->type == MOVETYPE) { if (start != NULL && (lastonly?p->link==NULL:TRUE) && !(ISCLOSED(start->flag) && LASTCLOSED(last->flag))) { register struct segment *r; /* newly created */ start->flag |= ISCLOSED(ON); r = PathSegment(LINETYPE, firstx - x, firsty - y); INSERT(last, r, p); r->flag |= LASTCLOSED(ON); /*< adjust 'last' if possible for a 0,0 close >*/{ #define CLOSEFUDGE 3 /* if we are this close, let's change last segment */ if (r->dest.x != 0 || r->dest.y != 0) { if (r->dest.x <= CLOSEFUDGE && r->dest.x >= -CLOSEFUDGE && r->dest.y <= CLOSEFUDGE && r->dest.y >= -CLOSEFUDGE) { IfTrace2((PathDebug), "ClosePath forced closed by (%d,%d)\n", r->dest.x, r->dest.y); lastnonhint->dest.x += r->dest.x; lastnonhint->dest.y += r->dest.y; r->dest.x = r->dest.y = 0; } }} if (p->link != NULL) { p->dest.x += x - firstx; p->dest.y += y - firsty; x = firstx; y = firsty; } } start = p; firstx = x + p->dest.x; firsty = y + p->dest.y; } else if (p->type != HINTTYPE) lastnonhint = p; } return(p0);}/**//*:h2.Reversing the Direction of a Path This turned out to be more difficult than I thought at first. Thetrickiness was due to the fact that closed paths must remain closed,etc. We need three subroutines:*/ static struct segment *SplitPath(); /* break a path at any point */static struct segment *DropSubPath(); /* breaks a path after first sub-path */static struct segment *ReverseSubPath(); /* reverses a single sub-path */ /*:h3.Reverse() - User Operator to Reverse a Path This operator reverses the entire path.*/ struct segment *Reverse(p) register struct segment *p; /* full path to reverse */{ register struct segment *r; /* output path built here */ register struct segment *nextp; /* contains next sub-path */ IfTrace1((MustTraceCalls),"Reverse(%p)\n", p); if (p == NULL) return(NULL); ARGCHECK(!ISPATHANCHOR(p), "Reverse: invalid path", p, NULL, (0), struct segment *); if (p->type == TEXTTYPE) p = CoerceText(p); p = UniquePath(p); r = NULL; do { nextp = DropSubPath(p); p = ReverseSubPath(p); r = Join(p, r); p = nextp; } while (p != NULL); return(r);} /*:h4.ReverseSubPath() - Subroutine to Reverse a Single Sub-Path*/ static struct segment *ReverseSubPath(p) register struct segment *p; /* input path */{ register struct segment *r; /* reversed path will be created here */ register struct segment *nextp; /* temporary variable used in loop */ register int wasclosed; /* flag, path was closed */ if (p == NULL) return(NULL); wasclosed = ISCLOSED(p->flag); r = NULL; do {/*First we reverse the direction of this segment and clean up its flags:*/ p->dest.x = - p->dest.x; p->dest.y = - p->dest.y; p->flag &= ~(ISCLOSED(ON) | LASTCLOSED(ON));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -