|
|
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.