|
|
1.1 root 1: /***********************************************************
2: Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
3: and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
4:
5: All Rights Reserved
6:
7: Permission 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 names of Digital or MIT not be
12: used in advertising or publicity pertaining to distribution of the
13: software without specific, written prior permission.
14:
15: DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16: ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17: DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18: ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19: WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20: ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21: SOFTWARE.
22:
23: ******************************************************************/
24: /* $Header: milines.c,v 1.45 87/09/11 07:20:09 toddb Exp $ */
25:
26: /*
27: *
28: * Written by Todd Newman
29: * loosely based on long ago code and recent advice
30: * from Brian Kelleher, John Danskin
31: *
32: * Draw fat lines using a convex polygon routine.
33: */
34:
35: #include <stdio.h>
36: extern double hypot();
37: #include "X.h"
38: #include "windowstr.h"
39: #include "Xprotostr.h"
40: #include "gcstruct.h"
41: #include "scrnintstr.h"
42: #include "miscstruct.h"
43: #include "pixmapstr.h"
44: #include "mifpoly.h"
45: #include "mi.h"
46:
47: #define GCVALSALU 0
48: #define GCVALSFORE 1
49: #define GCVALSBACK 2
50: #define GCVALSWIDTH 3
51: #define GCVALSCAPSTYLE 4
52: #define GCVALSJOINSTYLE 5
53: #define GCVALSARCMODE 6
54: static int gcvals[] = {GXcopy, 1, 0, 0, 0, 0, ArcChord};
55:
56: #define SAMESIGN(a, b) ((((a) >= 0) && ((b) >= 0)) ||\
57: (((a) <= 0) && ((b) <= 0)))
58:
59: SppPointRec IntersectLines();
60:
61: /* MIWIDELINE - Public entry for PolyLine call
62: * handles 1 segment wide lines specially. Then it sets up the GC/Drawable
63: * based on the raster op. If the raster op is one that doesn't look at
64: * the destination, then we can draw directly onto the drawable. If the
65: * raster op does look at the destination (e.g., Xor), then we will draw
66: * with GXcopy onto a side bitmap and then squeegee the appropriate pattern
67: * (foreground, or tile, or stipple) onto the real drawable.
68: * We call one of two helpers (miMiter or miNonMiter) to actually draw
69: * the line segments. Which helper we call is determined by the device
70: * specific GC validator (e.g., mfbValidateGC).
71: */
72: void
73: miWideLine(pDrawable, pGC, mode, npt, pPts)
74: DrawablePtr pDrawable;
75: GCPtr pGC;
76: int mode;
77: int npt;
78: DDXPointPtr pPts;
79: {
80: int width = pGC->lineWidth;
81: SppPointPtr pSppPts, pSppT;
82: int gcflags, i, xOrg, yOrg, fTricky, arcmode;
83: int xMin, xMax, yMin, yMax, dx, dy;
84: DrawablePtr pDrawTo;
85: GCPtr pGCTo;
86:
87:
88: /* make everything absolute */
89: if (mode == CoordModePrevious)
90: {
91: DDXPointPtr pptTmp;
92: int nptTmp;
93:
94: pptTmp = pPts + 1;
95: nptTmp = npt - 1;
96: while (nptTmp--)
97: {
98: pptTmp->x += (pptTmp-1)->x;
99: pptTmp->y += (pptTmp-1)->y;
100: pptTmp++;
101: }
102: }
103: switch(pGC->alu)
104: {
105: case GXclear: /* 0 */
106: case GXcopy: /* src */
107: case GXcopyInverted: /* NOT src */
108: case GXset: /* 1 */
109: fTricky = FALSE;
110: xOrg = yOrg = 0;
111: pDrawTo = pDrawable;
112: pGCTo = pGC;
113: arcmode = pGCTo->arcMode;
114: if(arcmode != ArcChord)
115: {
116: DoChangeGC(pGCTo, GCArcMode, &gcvals[GCVALSARCMODE], 0);
117: ValidateGC(pDrawTo, pGCTo);
118: }
119:
120: break;
121: case GXand: /* src AND dst */
122: case GXandReverse: /* src AND NOT dst */
123: case GXandInverted: /* NOT src AND dst */
124: case GXnoop: /* dst */
125: case GXxor: /* src XOR dst */
126: case GXor : /* src OR dst */
127: case GXnor: /* NOT src AND NOT dst */
128: case GXequiv: /* NOT src XOR dst */
129: case GXinvert: /* NOT dst */
130: case GXorReverse: /* src OR NOT dst */
131: case GXorInverted: /* NOT src OR dst */
132: case GXnand: /* NOT src OR NOT dst */
133: fTricky = TRUE;
134: yMin = yMax = pPts[0].y;
135: xMin = xMax = pPts[0].x;
136:
137: for (i = 1; i < npt; i++)
138: {
139: xMin = min(xMin, pPts[i].x);
140: xMax = max(xMax, pPts[i].x);
141: yMin = min(yMin, pPts[i].y);
142: yMax = max(yMax, pPts[i].y);
143: }
144: xOrg = xMin - (width + 1)/2;
145: yOrg = yMin - (width + 1)/2;
146: dx = xMax - xMin + width + 1;
147: dy = yMax - yMin + width + 1;
148: pDrawTo = (DrawablePtr) (*pDrawable->pScreen->CreatePixmap)
149: (pDrawable->pScreen, dx, dy, 1, XYBitmap);
150: pGCTo = GetScratchGC(1, pDrawable->pScreen);
151: gcvals[GCVALSWIDTH] = width;
152: gcvals[GCVALSCAPSTYLE] = pGC->capStyle;
153: gcvals[GCVALSJOINSTYLE] = pGC->joinStyle;
154: gcflags = GCFunction|GCForeground|GCBackground|GCLineWidth |
155: GCCapStyle | GCJoinStyle;
156: if(pGCTo->arcMode != ArcChord)
157: gcflags |= GCArcMode;
158: arcmode = ArcChord;
159: DoChangeGC(pGCTo, gcflags, gcvals, 0);
160: ValidateGC(pDrawTo, pGCTo);
161: miClearDrawable(pDrawTo, pGCTo);
162:
163: }
164: pSppPts = (SppPointPtr) Xalloc((npt + 1) * sizeof(SppPointRec));
165: pSppT = pSppPts;
166: /* convert points to DblPts */
167: for(i = 0; i < npt; i++)
168: {
169: pSppT->x = (double) pPts->x;
170: pSppT++->y = (double) pPts++->y;
171:
172: }
173:
174: /* Draw the lines with the appropriate join style.
175: * We always wants caps drawn. */
176: (*pGCTo->LineHelper) (pDrawTo, pGCTo, TRUE, npt, pSppPts, xOrg, yOrg);
177:
178: if(arcmode != ArcChord)
179: {
180: DoChangeGC(pGCTo, GCArcMode, &arcmode, 0);
181: ValidateGC(pDrawTo, pGCTo);
182: }
183: if(fTricky)
184: {
185: if (pGC->miTranslate && (pDrawable->type == DRAWABLE_WINDOW) )
186: {
187: xOrg += ((WindowPtr)pDrawable)->absCorner.x;
188: yOrg += ((WindowPtr)pDrawable)->absCorner.y;
189: }
190:
191: (*pGC->PushPixels)(pGC, pDrawTo, pDrawable, dx, dy, xOrg, yOrg);
192: (*pDrawable->pScreen->DestroyPixmap)(pDrawTo);
193: FreeScratchGC(pGCTo);
194: }
195: }
196:
197: /* MIONESEGWIDE -- private helper for wide line code
198: * Draw wide line which only has 1 segment. This is so easy that it's worth
199: * special casing. Regardless of the raster op, this can't self-intersect.
200: * Also handles fluke cases where we're called with only 1 point
201: */
202: static
203: void
204: miOneSegWide(pDrawable, pGC, npt, pPts, caps, xOrg, yOrg)
205: DrawablePtr pDrawable;
206: GCPtr pGC;
207: int npt;
208: SppPointPtr pPts;
209: int caps, xOrg, yOrg;
210: {
211: SppPointRec PolyPoints[4];
212: int width = pGC->lineWidth;
213:
214: if(npt == 1)
215: {
216: Xrealloc(pPts, 2 * sizeof(SppPointRec));
217: pPts[1] = pPts[0];
218: }
219: if(caps && pGC->capStyle == CapProjecting)
220: {
221: if(PtEqual(pPts[0], pPts[1]))
222: {
223: pPts[0].y -= width/2.0;
224: pPts[1].y += width/2.0;
225: }
226: else
227: {
228: pPts[0] = miExtendSegment(pPts[0], pPts[1], width/2);
229: pPts[1] = miExtendSegment(pPts[1], pPts[0], width/2);
230: }
231: }
232: miGetPts(pPts[0], pPts[1], &PolyPoints[0],
233: &PolyPoints[1], &PolyPoints[2], &PolyPoints[3], width);
234: miFillSppPoly(pDrawable, pGC, 4, PolyPoints, -xOrg, -yOrg);
235: if(caps && pGC->capStyle == CapRound)
236: {
237: miRoundCap(pDrawable, pGC, pPts[0], pPts[1], PolyPoints[0],
238: PolyPoints[3], FirstEnd, xOrg, yOrg);
239: miRoundCap(pDrawable, pGC, pPts[1], pPts[0], PolyPoints[2],
240: PolyPoints[1], SecondEnd, xOrg, yOrg);
241: }
242: }
243:
244: /* MIMITER -- helper for wide line code, stuffed into GC.lineHelper by
245: * validation routine
246: * draw wide polylines by finding the polygon that outlines
247: * each wide line segment, and drawing that. Uses GC's fill polygon routine.
248: *
249: * internal vertexes are joined with a mitre as in PostScript.
250: * vertices at the beginning and end of the polyline
251: * are given the appropriate end unless they meet at the same point in which
252: * case they are mitred together. */
253:
254: void
255: miMiter (pDraw, pGC, caps, npt, pPts, xOrg, yOrg)
256: DrawablePtr pDraw;
257: GCPtr pGC;
258: int caps;
259: int npt;
260: SppPointPtr pPts;
261: int xOrg, yOrg;
262: {
263: int width = pGC->lineWidth;
264: SppPointRec PolyPoints[4], FirstEdge[2];
265: SppPointRec p1, p2, p3, p4, p5, p6, p7, p8;
266: int edges_match, i,
267: capStyle = pGC->capStyle;
268:
269: /*
270: * special case length 1 polyline
271: */
272: if(npt <= 2)
273: {
274: miOneSegWide(pDraw, pGC, npt, pPts, caps, xOrg, yOrg);
275: return;
276: }
277: miGetPts(pPts[0], pPts[1], &p1, &p2, &p3, &p4, width);
278:
279: /*
280: * start on first edge of first point
281: */
282: if (PtEqual(pPts[0], pPts[npt-1]))
283: {
284: /*
285: * edges match so mitre them
286: */
287: edges_match = 1;
288: miGetPts(pPts[npt-2], pPts[npt-1], &p5, &p6, &p7, &p8, width);
289:
290: PolyPoints[3] = IntersectLines(p1, p2, p5, p6);
291: PolyPoints[2] = IntersectLines(p3, p4, p7, p8);
292:
293: FirstEdge[0] = PolyPoints[3];
294: FirstEdge[1] = PolyPoints[2];
295: }
296: else
297: {
298: edges_match = 0;
299: PolyPoints[3] = p1;
300: PolyPoints[2] = p4;
301: if (caps && capStyle == CapProjecting)
302: {
303: pPts[0] = miExtendSegment(pPts[0], pPts[1], width/2);
304: miGetPts(pPts[0], pPts[1], &p1, &p2, &p3, &p4, width);
305: pPts[npt-1] = miExtendSegment(pPts[npt-1], pPts[npt-2], width/2);
306: }
307: else if (caps && capStyle == CapRound)
308: {
309: miRoundCap(pDraw, pGC, pPts[0], pPts[1], p1, p4, FirstEnd,
310: xOrg, yOrg);
311: miGetPts(pPts[npt-1], pPts[npt-2], &p5, &p6, &p7, &p8, width);
312: miRoundCap(pDraw, pGC, pPts[npt-1], pPts[npt-2], p5, p8, FirstEnd,
313: xOrg, yOrg);
314: }
315: }
316:
317: for (i = 1; i < (npt - 1); i++)
318: {
319: /*
320: * find the next edge
321: * build a polygon out of the next edge and the last one
322: * send the polygon
323: * bump to next
324: */
325: miGetPts(pPts[i], pPts[i+1], &p5, &p6, &p7, &p8, width);
326:
327: PolyPoints[0] = PolyPoints[3];
328: PolyPoints[1] = PolyPoints[2];
329: PolyPoints[2] = IntersectLines(p4, p3, p7, p8);
330: PolyPoints[3] = IntersectLines(p1, p2, p5, p6);
331:
332: miFillSppPoly(pDraw, pGC, 4, PolyPoints, -xOrg, -yOrg);
333:
334: p1 = p5; p2 = p6; p3 = p7; p4 = p8;
335:
336: }
337:
338: PolyPoints[0] = PolyPoints[3];
339: PolyPoints[1] = PolyPoints[2];
340:
341: if (edges_match)
342: {
343: PolyPoints[2] = FirstEdge[1];
344: PolyPoints[3] = FirstEdge[0];
345: }
346: else
347: {
348: miGetPts(pPts[npt-2], pPts[npt-1], &p5, &p6, &p7, &p8, width);
349: PolyPoints[2] = p7;
350: PolyPoints[3] = p6;
351: }
352:
353: miFillSppPoly(pDraw, pGC, 4, PolyPoints, -xOrg, -yOrg);
354: }
355:
356: /* MINOTMITER -- helper for wide line code, stuffed into GC.lineHelper by
357: * validation routine
358: *
359: * internal vertexes are joined with a bevel or rounded joint as in PostScript.
360: * vertices at the beginning and end of the polyline
361: * are given the appropriate end unless they meet at the same point in which
362: * case they are drawn with the appropriate join. */
363:
364: typedef struct {
365: int c;
366: SppPointRec v0, v1, v2, v3, v4;
367: SppPointRec p1, p2, p3, p4;
368: } POLYLINEINFO;
369:
370: void
371: miNotMiter (pDraw, pGC, caps, npt, pPts, xOrg, yOrg)
372: DrawablePtr pDraw;
373: GCPtr pGC;
374: int caps;
375: int npt;
376: SppPointPtr pPts;
377: int xOrg, yOrg;
378: {
379: int i, c,
380: width = pGC->lineWidth,
381: capStyle = pGC->capStyle,
382: joinStyle = pGC->joinStyle;
383: Bool edges_match;
384: SppPointRec FirstEdge[4], /* 2 extra for special npt == 2 */
385: wedge[3],
386: p, p1, p2, p3, p4, p5, p6, p7, p8, center;
387: POLYLINEINFO *plinfo;
388:
389: /* special case length 1 polyline */
390: if(npt <= 2)
391: {
392: miOneSegWide(pDraw, pGC, npt, pPts, caps, xOrg, yOrg);
393: return;
394: }
395: if(!(plinfo = (POLYLINEINFO *) ALLOCATE_LOCAL(npt * sizeof(POLYLINEINFO))))
396: return;
397: miGetPts(pPts[0], pPts[1], &p1, &p2, &p3, &p4, width);
398: /* save the points, in case this is hard */
399: plinfo[0].p1 = p1;
400: plinfo[0].p2 = p2;
401: plinfo[0].p3 = p3;
402: plinfo[0].p4 = p4;
403:
404: /*
405: * If ends meet, calculate angle of final intersection and points
406: * of intersection for it.
407: */
408: if(PtEqual(pPts[0], pPts[npt-1]))
409: {
410: edges_match = TRUE;
411: miGetPts(pPts[npt-2], pPts[npt-1], &p5, &p6, &p7, &p8, width);
412:
413: c = (pPts[0].x - pPts[npt-2].x)*(pPts[1].y - pPts[0].y) -
414: (pPts[1].x - pPts[0].x)*(pPts[0].y - pPts[npt-2].y);
415: plinfo[0].c = c;
416:
417: if(c > 0) /* it's a clockwise turn */
418: {
419: p = IntersectLines(p1, p2, p5, p6);
420: plinfo[0].v0 = p4;
421: plinfo[0].v1 = p7;
422: plinfo[0].v2 = p;
423: FirstEdge[0] = p;
424: FirstEdge[1] = p7;
425: if(joinStyle == JoinRound)
426: {
427: center.x = (p4.x + p7.x)/2;
428: center.y = (p4.y + p7.y)/2;
429: miRoundCap(pDraw, pGC, center, p, p4, p7, NotEnd, xOrg, yOrg);
430: }
431: }
432: else if (c < 0) /* counterclockwise */
433: {
434: p = IntersectLines(p3, p4, p7, p8);
435: plinfo[0].v0 = p;
436: plinfo[0].v1 = p6;
437: plinfo[0].v2 = p1;
438: FirstEdge[0] = p6;
439: FirstEdge[1] = p;
440: if(joinStyle == JoinRound)
441: {
442: center.x = (p6.x + p1.x)/2;
443: center.y = (p6.y + p1.y)/2;
444: miRoundCap(pDraw, pGC, center, p, p1, p6, NotEnd, xOrg, yOrg);
445: }
446: }
447: else /* straight line intersection */
448: {
449: plinfo[0].v1 = p4;
450: plinfo[0].v2 = p1;
451: FirstEdge[0] = p6;
452: FirstEdge[1] = p7;
453:
454: }
455: }
456: else /* Ends don't meet */
457: {
458: if(caps)
459: {
460: if(capStyle == CapProjecting)
461: {
462: pPts[0] = miExtendSegment(pPts[0], pPts[1], width/2);
463: miGetPts(pPts[0], pPts[1], &p1, &p2, &p3, &p4, width);
464: plinfo[0].p1 = p1;
465: plinfo[0].p2 = p2;
466: plinfo[0].p3 = p3;
467: plinfo[0].p4 = p4;
468: pPts[npt-1] =
469: miExtendSegment(pPts[npt-1], pPts[npt-2], width/2);
470: } else if (capStyle == CapRound)
471: {
472: miRoundCap(pDraw, pGC, pPts[0], pPts[1], p1, p4, FirstEnd,
473: xOrg, yOrg);
474: miGetPts(pPts[npt-1], pPts[npt-2], &p5, &p6, &p7, &p8, width);
475: miRoundCap(pDraw, pGC, pPts[npt-1], pPts[npt-2], p5, p8,
476: FirstEnd, xOrg, yOrg);
477: }
478: }
479: edges_match = FALSE;
480: plinfo[0].c = 0; /* treat no connection as straight connection */
481: plinfo[0].v1 = p4;
482: plinfo[0].v2 = p1;
483:
484: }
485:
486: for (i = 1; i < (npt - 1); i++)
487: {
488: /* Find the point of intersection with the previous segment.
489: * Make sure it's on this segment */
490: if(plinfo[i-1].c > 0)
491: {
492: p = plinfo[i-1].v2;
493: }
494: else if (plinfo[i-1].c < 0)
495: {
496: p = plinfo[i-1].v0;
497: }
498: miGetPts(pPts[i], pPts[i+1], &p5, &p6, &p7, &p8, width);
499:
500: plinfo[i].p1 = p5;
501: plinfo[i].p2 = p6;
502: plinfo[i].p3 = p7;
503: plinfo[i].p4 = p8;
504:
505: /* Figure out whether this angle turns clockwise or not by taking
506: * the total cross product of the three vectors formed by drawing
507: * vectors between the start of the previous segment and the start
508: * and end of this one */
509: if(i == npt - 1)
510: c = 0;
511: else
512: c = (pPts[i].x - pPts[i-1].x)*(pPts[i + 1].y - pPts[i].y) -
513: (pPts[i + 1].x - pPts[i].x)*(pPts[i].y - pPts[i-1].y);
514: plinfo[i].c = c;
515:
516:
517: if(c > 0) /* counter-clockwise */
518: {
519: p = IntersectLines(p1, p2, p5, p6);
520: plinfo[i].v0 = p8;
521: plinfo[i].v1 = p3;
522: plinfo[i].v2 = p;
523:
524: plinfo[i-1].v3 = p;
525: plinfo[i-1].v4 = p3;
526: if(joinStyle == JoinRound)
527: {
528: center.x = (p8.x + p3.x)/2;
529: center.y = (p8.y + p3.y)/2;
530: miRoundCap(pDraw, pGC, center, p, p8, p3, NotEnd, xOrg, yOrg);
531: }
532: }
533: else if (c < 0)
534: {
535: p = IntersectLines(p3, p4, p7, p8);
536: plinfo[i].v0 = p;
537: plinfo[i].v1 = p2;
538: plinfo[i].v2 = p5;
539:
540: plinfo[i-1].v3 = p2;
541: plinfo[i-1].v4 = p;
542: if(joinStyle == JoinRound)
543: {
544: center.x = (p2.x + p5.x)/2;
545: center.y = (p2.y + p5.y)/2;
546: miRoundCap(pDraw, pGC, center, p, p2, p5, NotEnd, xOrg, yOrg);
547: }
548: }
549: else /* straight line intersection */
550: {
551: plinfo[i].v1 = p2;
552: plinfo[i].v2 = p3;
553: plinfo[i-1].v3 = p2;
554: plinfo[i-1].v4 = p3;
555:
556: }
557:
558: p1 = p5; p2 = p6; p3 = p7; p4 = p8;
559: }
560:
561: /* Special case for final segment --
562: */
563: if(plinfo[i-1].c > 0)
564: {
565: p = plinfo[i-1].v2;
566: }
567: else if (plinfo[i-1].c < 0)
568: {
569: p = plinfo[i-1].v0;
570: }
571:
572: if (edges_match)
573: {
574: plinfo[i-1].v3 = FirstEdge[0];
575: plinfo[i-1].v4 = FirstEdge[1];
576: if(plinfo[i-1].c > 0)
577: {
578: p = plinfo[i-1].v2;
579: }
580: else if (plinfo[i-1].c < 0)
581: {
582: p = plinfo[i-1].v0;
583: }
584: }
585: else
586: {
587: miGetPts(pPts[npt-2], pPts[npt-1], &p5, &p6, &p7, &p8, width);
588: plinfo[i-1].v3 = p6;
589: plinfo[i-1].v4 = p7;
590: }
591: /*
592: * plinfo[i].c is 0 if there is no notch, either because the lines meet
593: * at 180 degrees, or the lines don't meet.
594: * Since we've set the alu to GCCopy, we don't worry about drawing
595: * points twice.
596: */
597: for(i = 0; i < (npt - 1); i++)
598: {
599: if(plinfo[i].c < 0)
600: {
601: wedge[1] = plinfo[i].p1;
602: wedge[2] = i ? plinfo[i - 1].p2 : plinfo[npt-2].p2;
603: }
604: else if (plinfo[i].c > 0)
605: {
606: wedge[1] = plinfo[i].p4;
607: wedge[2] = i ? plinfo[i-1].p3 : plinfo[npt-2].p3;
608: }
609:
610: if(plinfo[i].c)
611: {
612: wedge[0] = pPts[i];
613: /* Check that points aren't colinear */
614: if(!(wedge[0].x == wedge[1].x && wedge[1].x == wedge[2].x) &&
615: !(wedge[0].y == wedge[1].y && wedge[1].y == wedge[2].y))
616: {
617: miFillSppPoly(pDraw, pGC, 3, wedge, -xOrg, -yOrg);
618: }
619: }
620: miFillSppPoly(pDraw, pGC, 4, &plinfo[i].p1, -xOrg, -yOrg);
621: }
622: DEALLOCATE_LOCAL(plinfo);
623: }
624:
625: /* MIGETPTS -- helper function
626: * Get the corners of the polygon formed for a line with the endpoints
627: * p1In, p2In and the given width stuff the corners into *p1Out, *p2Out ...
628: * p1Out and p4Out are always on the same end as p1In.
629: */
630: void
631: miGetPts(p1In, p2In, p1Out, p2Out, p3Out, p4Out, width)
632: SppPointRec p1In, p2In;
633: SppPointPtr p1Out, p2Out, p3Out, p4Out;
634: int width;
635: {
636: double hyp; /* true distance of line (hypotenuse) */
637: double dx, dy;
638: double tmpdx, tmpdy;
639:
640: tmpdx = (double)(p1In.x - p2In.x);
641: tmpdy = (double)(p1In.y - p2In.y);
642:
643: hyp = hypot(tmpdx, tmpdy);
644: if (!ISZERO(hyp))
645: {
646: /* NOTE: the division by 2 must be done in floating point */
647: dy = (tmpdx * width/2.0) / hyp;
648: dx = (tmpdy * width/2.0) / hyp;
649:
650: p1Out->x = (p1In.x + dx);
651: p1Out->y = (p1In.y - dy);
652:
653: p2Out->x = (p2In.x + dx);
654: p2Out->y = (p2In.y - dy);
655:
656: p3Out->x = (p2In.x - dx);
657: p3Out->y = (p2In.y + dy);
658:
659: p4Out->x = (p1In.x - dx);
660: p4Out->y = (p1In.y + dy);
661: }
662: else
663: {
664: /* Bizarre answer, but it's what the spec calls for */
665: p1Out->x = p2Out->x = p1In.x - width/2.0;
666: p3Out->x = p4Out->x = p1In.x + width/2.0;
667: p1Out->y = p2Out->y = p3Out->y = p4Out->y = p1In.y;
668: }
669: }
670:
671: /* INTERSECTLINES -- private helper for line code
672: * Return the intersection of the line from p1 to p2 with
673: * the line from p3 to p4.
674: *
675: * First, reduce the lines to: y = m1x + b, and y = m2x + c.
676: * Then do the standard math.
677: */
678: static
679: SppPointRec
680: IntersectLines(p1, p2, p3, p4)
681: SppPointRec p1, p2, p3, p4;
682: {
683: SppPointRec i;
684: double m1, m2;
685: double b1, b2;
686: double dx1, dx2;
687: double tx;
688:
689: dx1 = p2.x - p1.x;
690: dx2 = p4.x - p3.x;
691:
692: if (ISZERO(dx1))
693: {
694: if (! ISZERO(dx2))
695: {
696: i.y = p3.y + ((p4.y - p3.y) * (p1.x - p3.x)) / (p4.x - p3.x);
697: i.x = p1.x;
698: }
699: else
700: i = p2;
701: }
702: else if (ISZERO(dx2))
703: {
704: i.y = p1.y + ((p2.y - p1.y) * (p3.x - p1.x)) / (p2.x - p1.x);
705: i.x = p3.x;
706: }
707: else
708: {
709: m1 = (p2.y - p1.y) / dx1;
710: m2 = (p4.y - p3.y) / dx2;
711:
712: b1 = p2.y - (m1 * p2.x);
713: b2 = p4.y - (m2 * p4.x);
714:
715: if (! ISEQUAL(m1, m2))
716: i.x = (tx = ((b2-b1) / (m1-m2)));
717: else
718: i.x = (tx = p2.x);
719:
720: i.y = m1 * tx + b1;
721: }
722: return(i);
723: }
724:
725:
726: /* MIEXTENDSEGMENT -- a private helper function
727: * Calcuate point p3 made by extending the segment backward (away from p2)
728: * by a length d */
729:
730: SppPointRec
731: miExtendSegment(p1, p2, d)
732: SppPointRec p1, p2;
733: int d;
734: {
735: SppPointRec p3;
736: double l, dx, dy, hypot();
737:
738: dx = p2.x - p1.x;
739: dy = p2.y - p1.y;
740: l = hypot(dx, dy);
741: if(!ISZERO(l))
742: {
743: p3.x = (p2.x - (l + d)/l * dx);
744: p3.y = (p2.y - (l + d)/l * dy);
745: }
746: else
747: {
748: ErrorF("ExtendSegment called with coincident points.\n");
749: p3 = p2;
750: }
751: return(p3);
752: }
753:
754: /* MIROUNDCAP -- a private helper function
755: * Put Rounded cap on end. pCenter is the center of this end of the line
756: * pEnd is the center of the other end of the line. pCorner is one of the
757: * two corners at this end of the line. It doesn't matter which of the two
758: * corners you give it. pOtherCorner is the other one.
759: */
760: void
761: miRoundCap(pDraw, pGC, pCenter, pEnd, pCorner, pOtherCorner, fLineEnd,
762: xOrg, yOrg)
763: DrawablePtr pDraw;
764: GCPtr pGC;
765: SppPointRec pCenter, pEnd;
766: SppPointRec pCorner, pOtherCorner;
767: int fLineEnd, xOrg, yOrg;
768: {
769: int c, i, signc, cpt;
770: double width;
771: double dx, dy, hypot();
772: xArc arc;
773: DDXPointPtr pArcPts;
774: SppPointPtr pPts;
775: int arcmode = pGC->arcMode;
776:
777: if(arcmode != ArcChord)
778: {
779: DoChangeGC(pGC, GCArcMode, &gcvals[GCVALSARCMODE], 0);
780: ValidateGC(pDraw, pGC);
781: }
782: if (fLineEnd == NotEnd)
783: {
784: dx = pCenter.x - pCorner.x;
785: dy = pCenter.y - pCorner.y;
786: width = hypot(dx, dy);
787: }
788: else
789: width = pGC->lineWidth;
790: if(PtEqual(pCenter, pEnd))
791: {
792: signc = 1;
793: }
794: else
795: {
796: c = (pCenter.x - pEnd.x) * (pCorner.y - pCenter.y) -
797: (pCorner.x - pCenter.x) * (pCenter.y - pEnd.y);
798: signc = -sign(c);
799: }
800:
801: /* These come back scaled by 64 and all ready to use */
802: arc.x = ROUNDTOINT(pCenter.x - width/2);
803: arc.y = ROUNDTOINT(pCenter.y - width/2);
804: arc.width = ROUNDTOINT(width);
805: arc.height = arc.width;
806: arc.angle1 = -PtToAngle(pCenter, pCorner);
807: arc.angle2 = -signc * 180 * 64;
808: pArcPts = (DDXPointPtr)NULL;
809: if( cpt = miGetArcPts(&arc, 0, &pArcPts))
810: {
811: /* by drawing with miFillSppPoly and setting the endpoints of the arc
812: * to be the corners, we assure that the cap will meet up with the
813: * rest of the line */
814: pPts = (SppPointPtr) Xalloc(cpt * sizeof(SppPointRec));
815: for(i = 1; i < cpt -1; i++)
816: {
817: pPts[i].x = (double) pArcPts[i].x;
818: pPts[i].y = (double) pArcPts[i].y;
819: }
820: pPts[0] = pCorner;
821: pPts[cpt - 1] = pOtherCorner;
822: miFillSppPoly(pDraw, pGC, cpt, pPts, -xOrg, -yOrg);
823: Xfree((char *)pArcPts);
824: Xfree((char *)pPts);
825: if(arcmode != ArcChord)
826: {
827: DoChangeGC(pGC, GCArcMode, &arcmode, 0);
828: ValidateGC(pDraw, pGC);
829: }
830: }
831: }
832:
833: /* these are from <math.h>, but I'm told some systems don't have math.h Psi! */
834: extern double sqrt(), cos(), sin(), atan();
835: #define M_PI 3.14159265358979323846
836: #define M_PI_2 1.57079632679489661923
837:
838: /* PTTOANGLE -- a private helper function
839: * Given two points, compute the slope of the line segment
840: * Result is in radians * 64
841: */
842: static
843: int
844: PtToAngle(c, p)
845: SppPointRec c, p;
846: {
847: double dx, dy, theta;
848:
849: dx = p.x - c.x;
850: dy = p.y - c.y;
851: if(dx == 0)
852: {
853: theta = (dy > 0) ? M_PI_2 : -M_PI_2;
854: }
855: else
856: theta =(atan(dy/dx));
857: if(dx < 0)
858: theta += M_PI;
859: theta = (theta * 64 * 180/M_PI);
860: return(ROUNDTOINT(theta));
861: }
862:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.