365
:h2.Font "Hint" Segments
367
:h3.Hint() - A Font 'Hint' Segment
369
This is temporary code while we experiment with hints.
372
/*SHARED LINE(S) ORIGINATED HERE*/
374
Hint(struct XYspace *S, float ref, float width,
375
char orientation, char hinttype, char adjusttype, char direction,
378
/* added reference field of 1 to hintsegment template below 3-26-91 PNM */
379
static struct hintsegment template = { HINTTYPE, 0, 1, sizeof(struct hintsegment), 0,
380
NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 },
381
' ', ' ', ' ', ' ', 0};
383
register struct hintsegment *r;
385
r = (struct hintsegment *)Allocate(sizeof(struct hintsegment), &template, 0);
387
r->orientation = orientation;
388
if (width == 0.0) width = 1.0;
390
if (orientation == 'h') {
391
(*S->convert)(&r->ref, S, 0.0, ref);
392
(*S->convert)(&r->width, S, 0.0, width);
394
else if (orientation == 'v') {
395
(*S->convert)(&r->ref, S, ref, 0.0);
396
(*S->convert)(&r->width, S, width, 0.0);
399
return((struct hintsegment *)ArgErr("Hint: orient not 'h' or 'v'", NULL, NULL));
400
if (r->width.x < 0) r->width.x = - r->width.x;
401
if (r->width.y < 0) r->width.y = - r->width.y;
402
r->hinttype = hinttype;
403
r->adjusttype = adjusttype;
404
r->direction = direction;
406
r->last = (struct segment *) r;
414
342
/*SHARED LINE(S) ORIGINATED HERE*/
671
:h2.Reversing the Direction of a Path
673
This turned out to be more difficult than I thought at first. The
674
trickiness was due to the fact that closed paths must remain closed,
677
We need three subroutines:
680
/* break a path at any point */
681
static struct segment *SplitPath ( struct segment *anchor,
682
struct segment *before );
683
/* breaks a path after first sub-path */
684
static struct segment *DropSubPath ( struct segment *p0 );
685
/* reverses a single sub-path */
686
static struct segment *ReverseSubPath ( struct segment *p );
689
:h3.Reverse() - User Operator to Reverse a Path
691
This operator reverses the entire path.
695
Reverse(struct segment *p) /* full path to reverse */
697
register struct segment *r; /* output path built here */
698
register struct segment *nextp; /* contains next sub-path */
703
ARGCHECK(!ISPATHANCHOR(p), "Reverse: invalid path", p, NULL, (0), struct segment *);
705
if (p->type == TEXTTYPE)
712
nextp = DropSubPath(p);
713
p = ReverseSubPath(p);
723
:h4.ReverseSubPath() - Subroutine to Reverse a Single Sub-Path
726
static struct segment *
727
ReverseSubPath(struct segment *p) /* input path */
729
register struct segment *r; /* reversed path will be created here */
730
register struct segment *nextp; /* temporary variable used in loop */
731
register int wasclosed; /* flag, path was closed */
736
wasclosed = ISCLOSED(p->flag);
741
First we reverse the direction of this segment and clean up its flags:
743
p->dest.x = - p->dest.x; p->dest.y = - p->dest.y;
744
p->flag &= ~(ISCLOSED(ON) | LASTCLOSED(ON));
755
The logic of this is that the new M point (stored relative to the new
756
beginning) is (M - C). However, C ("dest") has already been reversed
757
So, we add "dest" instead of subtracting it:
759
register struct conicsegment *cp = (struct conicsegment *) p;
761
cp->M.x += cp->dest.x; cp->M.y += cp->dest.y;
767
register struct beziersegment *bp = (struct beziersegment *) p;
769
bp->B.x += bp->dest.x; bp->B.y += bp->dest.y;
770
bp->C.x += bp->dest.x; bp->C.y += bp->dest.y;
776
register struct hintsegment *hp = (struct hintsegment *) p;
778
hp->ref.x = -hp->ref.x; hp->ref.y = -hp->ref.y;
783
Abort("Reverse: bad path segment");
786
We need to reverse the order of segments too, so we break this segment
787
off of the input path, and tack it on the front of the growing path
794
CONCAT(p,r); /* leaves result in 'p'... not what we want */
796
p = nextp; /* advance to next segment in input path */
807
:h4.DropSubPath() - Drops the First Sub-Path Off a Path
809
This subroutine returns the remaining sub-path(s). While doing so, it
810
breaks the input path after the first sub-path so that a pointer to
811
the original path now contains the first sub-path only.
814
static struct segment *
815
DropSubPath(struct segment *p0) /* original path */
817
register struct segment *p; /* returned remainder here */
819
for (p = p0; p->link != NULL; p = p->link) {
820
if (p->link->type == MOVETYPE)
824
return(SplitPath(p0, p));
827
static struct segment *
828
SplitPath(struct segment *anchor, struct segment *before)
830
register struct segment *r;
832
if (before == anchor->last)
836
r->last = anchor->last;
837
anchor->last = before;
844
UnClose(struct segment *p0)
846
register struct segment *p;
848
for (p=p0; p->link->link != NULL; p=p->link) { ; }
850
if (!LASTCLOSED(p->link->flag))
851
Abort("UnClose: no LASTCLOSED");
853
Free(SplitPath(p0, p));
854
p0->flag &= ~ISCLOSED(ON);
858
:h3.ReverseSubPaths() - Reverse the Direction of Sub-paths Within a Path
860
This user operator reverses the sub-paths in a path, but leaves the
861
'move' segments unchanged. It builds on top of the subroutines
866
ReverseSubPaths(struct segment *p) /* input path */
868
register struct segment *r; /* reversed path will be created here */
869
register struct segment *nextp; /* temporary variable used in loop */
870
int wasclosed; /* flag; subpath was closed */
871
register struct segment *nomove; /* the part of sub-path without move segment */
872
struct fractpoint delta;
877
ARGCHECK(!ISPATHANCHOR(p), "ReverseSubPaths: invalid path", p, NULL, (0), struct segment *);
879
if (p->type == TEXTTYPE)
881
if (p->type != MOVETYPE)
882
p = JoinSegment(NULL, MOVETYPE, 0, 0, p);
889
nextp = DropSubPath(p);
890
wasclosed = ISCLOSED(p->flag);
894
nomove = SplitPath(p, p);
897
PathDelta(nomove, &delta);
899
nomove = ReverseSubPath(nomove);
900
p->dest.x += delta.x;
901
p->dest.y += delta.y;
903
nextp->dest.x += delta.x;
904
nextp->dest.y += delta.y;
907
nomove = ClosePath(nomove);
908
nextp->dest.x -= delta.x;
909
nextp->dest.y -= delta.y;
920
598
:h2.Transforming and Putting Handles on Paths
1087
747
UnConvert(S, &P->dest, xP, yP);
1090
:h3.QueryPath() - Find Out the Type of Segment at the Head of a Path
1092
This is a very simple routine that looks at the first segment of a
1093
path and tells the caller what it is, as well as returning the control
1094
point(s) of the path segment. Different path segments have different
1095
number of control points. If the caller knows that the segment is
1096
a move segment, for example, he only needs to pass pointers to return
1101
QueryPath(struct segment *path, /* path to check */
1102
int *typeP, /* return the type of path here */
1103
struct segment **Bp, /* return location of first point */
1104
struct segment **Cp, /* return location of second point */
1105
struct segment **Dp, /* return location of third point */
1106
double *fP) /* return Conic sharpness */
1108
register int coerced = FALSE; /* did I coerce a text path? */
1114
if (!ISPATHANCHOR(path)) {
1115
ArgErr("QueryPath: arg not a valid path", path, NULL);
1117
if (path->type == TEXTTYPE) {
1118
path = CoerceText(path);
1122
switch (path->type) {
1126
*Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
1130
*typeP = (LASTCLOSED(path->flag)) ? 4 : 1;
1131
*Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
1136
register struct conicsegment *cp = (struct conicsegment *) path;
1139
*Bp = PathSegment(MOVETYPE, cp->M.x, cp->M.y);
1140
*Cp = PathSegment(MOVETYPE, cp->dest.x, cp->dest.y);
1141
*fP = cp->roundness;
1147
register struct beziersegment *bp = (struct beziersegment *) path;
1150
*Bp = PathSegment(MOVETYPE, bp->B.x, bp->B.y);
1151
*Cp = PathSegment(MOVETYPE, bp->C.x, bp->C.y);
1152
*Dp = PathSegment(MOVETYPE, bp->dest.x, bp->dest.y);
1161
Abort("QueryPath: unknown segment");
1167
:h3.QueryBounds() - Return the Bounding Box of a Path
1169
Returns the bounding box by setting the user's variables.
1173
QueryBounds(struct segment *p0, /* object to check for bound */
1174
struct XYspace *S, /* coordinate space of returned values */
1175
double *xminP, /* lower left hand corner (set by routine) */
1177
double *xmaxP, /* upper right hand corner (set by routine) */
1180
register struct segment *path; /* loop variable for path segments */
1181
register fractpel lastx,lasty; /* loop variables: previous endingpoint */
1182
register fractpel x,y; /* loop variables: current ending point */
1183
struct fractpoint min; /* registers to keep lower left hand corner */
1184
struct fractpoint max; /* registers to keep upper right hand corner */
1185
int coerced = FALSE; /* we have coerced the path from another object */
1186
double x1,y1,x2,y2,x3,y3,x4,y4; /* corners of rectangle in space X */
1188
if (S->type != SPACETYPE) {
1189
ArgErr("QueryBounds: bad XYspace", S, NULL);
1193
min.x = min.y = max.x = max.y = 0;
1195
if (!ISPATHANCHOR(p0)) {
1197
case STROKEPATHTYPE:
1198
/* replaced DupStrokePath() with Dup() 3-26-91 PNM */
1199
p0 = (struct segment *) DoStroke(Dup(p0));
1200
/* no break here, we have a region in p0 */
1202
p0 = RegionBounds((struct region *)p0);
1206
p0 = PictureBounds(p0);
1210
ArgErr("QueryBounds: bad object", p0, NULL);
1215
if (p0->type == TEXTTYPE) {
1216
/* replaced CopyPath() with Dup() 3-26-91 PNM */
1217
p0 = (struct segment *)CoerceText(Dup(p0)); /* there are faster ways */
1220
if (p0->type == MOVETYPE) {
1221
min.x = max.x = p0->dest.x;
1222
min.y = max.y = p0->dest.y;
1227
for (path = p0; path != NULL; path = path->link) {
1229
x = lastx + path->dest.x;
1230
y = lasty + path->dest.y;
1232
switch (path->type) {
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;
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;
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;
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;
1270
* We can't risk adding trailing Moves to the
1273
if (path->link == NULL)
1274
goto done; /* God forgive me */
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;
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;
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;
1300
Abort("QueryBounds: unknown type");
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;
1308
lastx = x; lasty = y;
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);
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;
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;
1340
BoxPath(struct XYspace *S, int h, int w)
1342
struct segment *path;
1344
path = Join( Line(ILoc(S, w, 0)), Line(ILoc(S, 0, h)) );
1345
path = JoinSegment(path, LINETYPE, -path->dest.x, -path->dest.y, NULL);
1346
return(ClosePath(path));
1350
:h3.DropSegment() - Drop the First Segment in a Path
1352
This routine takes the path and returns a new path that is one segment
1353
shorter. It can be used in conjunction with QueryPath(), for example,
1354
to ask about an entire path.
1358
DropSegment(struct segment *path)
1360
if (path != NULL && path->type == STROKEPATHTYPE)
1361
path = CoercePath(path);
1362
ARGCHECK((path == NULL || !ISPATHANCHOR(path)),
1363
"DropSegment: arg not a non-null path", path, path, (0), struct segment *);
1364
if (path->type == TEXTTYPE)
1365
path = CoerceText(path);
1366
path = UniquePath(path);
1372
:h3.HeadSegment() - Return the First Segment in a Path
1374
This routine takes the path and returns a new path consists of the
1379
HeadSegment(struct segment *path) /* input path */
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);
1390
if (path->link != NULL)
1391
KillPath(path->link);
1398
:h2.Path Debug Routines
1400
:h3.DumpPath() - Display a Path on the Trace File
1404
DumpPath(struct segment *p)