/* ARGSUSED */ int TkThickPolyLineToArea( double *coordPtr, /* Points to an array of coordinates for the * polyline: x0, y0, x1, y1, ... */ int numPoints, /* Total number of points at *coordPtr. */ double width, /* Width of each line segment. */ int capStyle, /* How are end-points of polyline drawn? * CapRound, CapButt, or CapProjecting. */ int joinStyle, /* How are joints in polyline drawn? * JoinMiter, JoinRound, or JoinBevel. */ double *rectPtr) /* Rectangular area to check against. */ { double radius, poly[10]; int count; int changedMiterToBevel; /* Non-zero means that a mitered corner had to * be treated as beveled after all because the * angle was < 11 degrees. */ int inside; /* Tentative guess about what to return, based * on all points seen so far: one means * everything seen so far was inside the area; * -1 means everything was outside the area. * 0 means overlap has been found. */ radius = width/2.0; inside = -1; if ((coordPtr[0] >= rectPtr[0]) && (coordPtr[0] <= rectPtr[2]) && (coordPtr[1] >= rectPtr[1]) && (coordPtr[1] <= rectPtr[3])) { inside = 1; } /* * Iterate through all of the edges of the line, computing a polygon for * each edge and testing the area against that polygon. In addition, there * are additional tests to deal with rounded joints and caps. */ changedMiterToBevel = 0; for (count = numPoints; count >= 2; count--, coordPtr += 2) { /* * If rounding is done around the first point of the edge then test a * circular region around the point with the area. */ if (((capStyle == CapRound) && (count == numPoints)) || ((joinStyle == JoinRound) && (count != numPoints))) { poly[0] = coordPtr[0] - radius; poly[1] = coordPtr[1] - radius; poly[2] = coordPtr[0] + radius; poly[3] = coordPtr[1] + radius; if (TkOvalToArea(poly, rectPtr) != inside) { return 0; } } /* * Compute the polygonal shape corresponding to this edge, consisting * of two points for the first point of the edge and two points for * the last point of the edge. */ if (count == numPoints) { TkGetButtPoints(coordPtr+2, coordPtr, width, capStyle == CapProjecting, poly, poly+2); } else if ((joinStyle == JoinMiter) && !changedMiterToBevel) { poly[0] = poly[6]; poly[1] = poly[7]; poly[2] = poly[4]; poly[3] = poly[5]; } else { TkGetButtPoints(coordPtr+2, coordPtr, width, 0, poly, poly+2); /* * If the last joint was beveled, then also check a polygon * comprising the last two points of the previous polygon and the * first two from this polygon; this checks the wedges that fill * the beveled joint. */ if ((joinStyle == JoinBevel) || changedMiterToBevel) { poly[8] = poly[0]; poly[9] = poly[1]; if (TkPolygonToArea(poly, 5, rectPtr) != inside) { return 0; } changedMiterToBevel = 0; } } if (count == 2) { TkGetButtPoints(coordPtr, coordPtr+2, width, capStyle == CapProjecting, poly+4, poly+6); } else if (joinStyle == JoinMiter) { if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, (double) width, poly+4, poly+6) == 0) { changedMiterToBevel = 1; TkGetButtPoints(coordPtr, coordPtr+2, width, 0, poly+4, poly+6); } } else { TkGetButtPoints(coordPtr, coordPtr+2, width, 0, poly+4, poly+6); } poly[8] = poly[0]; poly[9] = poly[1]; if (TkPolygonToArea(poly, 5, rectPtr) != inside) { return 0; } } /* * If caps are rounded, check the cap around the final point of the line. */ if (capStyle == CapRound) { poly[0] = coordPtr[0] - radius; poly[1] = coordPtr[1] - radius; poly[2] = coordPtr[0] + radius; poly[3] = coordPtr[1] + radius; if (TkOvalToArea(poly, rectPtr) != inside) { return 0; } } return inside; }
/* ARGSUSED */ static double BLineToPoint(Tk_Canvas canvas, Tk_Item *itemPtr, double *pointPtr) { register BLineItem *linePtr = (BLineItem *) itemPtr; register double *coordPtr, *linePoints; double staticSpace[2*MAX_STATIC_POINTS]; double poly[10]; double bestDist, dist; int numPoints, count; int changedMiterToBevel; /* Non-zero means that a mitered corner * had to be treated as beveled after all * because the angle was < 11 degrees. */ bestDist = 1.0e40; /* * Handle smoothed lines by generating an expanded set of points * against which to do the check. */ numPoints = linePtr->numPoints; linePoints = linePtr->coordPtr; /* * The overall idea is to iterate through all of the edges of * the line, computing a polygon for each edge and testing the * point against that polygon. In addition, there are additional * tests to deal with rounded joints and caps. */ changedMiterToBevel = 0; for (count = numPoints, coordPtr = linePoints; count >= 2; count--, coordPtr += 2) { /* * If rounding is done around the first point then compute * the distance between the point and the point. */ if (((linePtr->capStyle == CapRound) && (count == numPoints)) || ((linePtr->joinStyle == JoinRound) && (count != numPoints))) { dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1]) - linePtr->width/2.0; if (dist <= 0.0) { bestDist = 0.0; goto done; } else if (dist < bestDist) { bestDist = dist; } } /* * Compute the polygonal shape corresponding to this edge, * consisting of two points for the first point of the edge * and two points for the last point of the edge. */ if (count == numPoints) { TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, linePtr->capStyle == CapProjecting, poly, poly+2); } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) { poly[0] = poly[6]; poly[1] = poly[7]; poly[2] = poly[4]; poly[3] = poly[5]; } else { TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, 0, poly, poly+2); /* * If this line uses beveled joints, then check the distance * to a polygon comprising the last two points of the previous * polygon and the first two from this polygon; this checks * the wedges that fill the mitered joint. */ if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) { poly[8] = poly[0]; poly[9] = poly[1]; dist = TkPolygonToPoint(poly, 5, pointPtr); if (dist <= 0.0) { bestDist = 0.0; goto done; } else if (dist < bestDist) { bestDist = dist; } changedMiterToBevel = 0; } } if (count == 2) { TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, linePtr->capStyle == CapProjecting, poly+4, poly+6); } else if (linePtr->joinStyle == JoinMiter) { if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, (double) linePtr->width, poly+4, poly+6) == 0) { changedMiterToBevel = 1; TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0, poly+4, poly+6); } } else { TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0, poly+4, poly+6); } poly[8] = poly[0]; poly[9] = poly[1]; dist = TkPolygonToPoint(poly, 5, pointPtr); if (dist <= 0.0) { bestDist = 0.0; goto done; } else if (dist < bestDist) { bestDist = dist; } } /* * If caps are rounded, check the distance to the cap around the * final end point of the line. */ if (linePtr->capStyle == CapRound) { dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1]) - linePtr->width/2.0; if (dist <= 0.0) { bestDist = 0.0; goto done; } else if (dist < bestDist) { bestDist = dist; } } done: if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) { ckfree((char *) linePoints); } return bestDist; }
/* ARGSUSED */ static int BLineToArea(Tk_Canvas canvas, Tk_Item *itemPtr, double *rectPtr) { register BLineItem *linePtr = (BLineItem *) itemPtr; register double *coordPtr; double staticSpace[2*MAX_STATIC_POINTS]; double *linePoints, poly[10]; double radius; int numPoints, count; int changedMiterToBevel; /* Non-zero means that a mitered corner * had to be treated as beveled after all * because the angle was < 11 degrees. */ int inside; /* Tentative guess about what to return, * based on all points seen so far: one * means everything seen so far was * inside the area; -1 means everything * was outside the area. 0 means overlap * has been found. */ radius = linePtr->width/2.0; inside = -1; numPoints = linePtr->numPoints; linePoints = linePtr->coordPtr; coordPtr = linePoints; if ((coordPtr[0] >= rectPtr[0]) && (coordPtr[0] <= rectPtr[2]) && (coordPtr[1] >= rectPtr[1]) && (coordPtr[1] <= rectPtr[3])) { inside = 1; } /* * Iterate through all of the edges of the line, computing a polygon * for each edge and testing the area against that polygon. In * addition, there are additional tests to deal with rounded joints * and caps. */ changedMiterToBevel = 0; for (count = numPoints; count >= 2; count--, coordPtr += 2) { /* * If rounding is done around the first point of the edge * then test a circular region around the point with the * area. */ if (((linePtr->capStyle == CapRound) && (count == numPoints)) || ((linePtr->joinStyle == JoinRound) && (count != numPoints))) { poly[0] = coordPtr[0] - radius; poly[1] = coordPtr[1] - radius; poly[2] = coordPtr[0] + radius; poly[3] = coordPtr[1] + radius; if (TkOvalToArea(poly, rectPtr) != inside) { inside = 0; goto done; } } /* * Compute the polygonal shape corresponding to this edge, * consisting of two points for the first point of the edge * and two points for the last point of the edge. */ if (count == numPoints) { TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, linePtr->capStyle == CapProjecting, poly, poly+2); } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) { poly[0] = poly[6]; poly[1] = poly[7]; poly[2] = poly[4]; poly[3] = poly[5]; } else { TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, 0, poly, poly+2); /* * If the last joint was beveled, then also check a * polygon comprising the last two points of the previous * polygon and the first two from this polygon; this checks * the wedges that fill the beveled joint. */ if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) { poly[8] = poly[0]; poly[9] = poly[1]; if (TkPolygonToArea(poly, 5, rectPtr) != inside) { inside = 0; goto done; } changedMiterToBevel = 0; } } if (count == 2) { TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, linePtr->capStyle == CapProjecting, poly+4, poly+6); } else if (linePtr->joinStyle == JoinMiter) { if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, (double) linePtr->width, poly+4, poly+6) == 0) { changedMiterToBevel = 1; TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0, poly+4, poly+6); } } else { TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0, poly+4, poly+6); } poly[8] = poly[0]; poly[9] = poly[1]; if (TkPolygonToArea(poly, 5, rectPtr) != inside) { inside = 0; goto done; } } /* * If caps are rounded, check the cap around the final point * of the line. */ if (linePtr->capStyle == CapRound) { poly[0] = coordPtr[0] - radius; poly[1] = coordPtr[1] - radius; poly[2] = coordPtr[0] + radius; poly[3] = coordPtr[1] + radius; if (TkOvalToArea(poly, rectPtr) != inside) { inside = 0; goto done; } } done: if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) { ckfree((char *) linePoints); } return inside; }
static void ComputeBLineBbox(Tk_Canvas canvas, BLineItem *linePtr) { register double *coordPtr; int i; coordPtr = linePtr->coordPtr; linePtr->header.x1 = linePtr->header.x2 = (int)coordPtr[0]; linePtr->header.y1 = linePtr->header.y2 = (int)coordPtr[1]; /* * Compute the bounding box of all the points in the line, * then expand in all directions by the line's width to take * care of butting or rounded corners and projecting or * rounded caps. This expansion is an overestimate (worst-case * is square root of two over two) but it's simple. Don't do * anything special for curves. This causes an additional * overestimate in the bounding box, but is faster. */ for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints; i++, coordPtr += 2) { TkIncludePoint((Tk_Item *) linePtr, coordPtr); } linePtr->header.x1 -= linePtr->width; linePtr->header.x2 += linePtr->width; linePtr->header.y1 -= linePtr->width; linePtr->header.y2 += linePtr->width; /* * For mitered lines, make a second pass through all the points. * Compute the locations of the two miter vertex points and add * those into the bounding box. */ if (linePtr->joinStyle == JoinMiter) { for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3; i--, coordPtr += 2) { double miter[4]; int j; if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, (double) linePtr->width, miter, miter+2)) { for (j = 0; j < 4; j += 2) { TkIncludePoint((Tk_Item *) linePtr, miter+j); } } } } /* * Add one more pixel of fudge factor just to be safe (e.g. * X may round differently than we do). */ linePtr->header.x1 -= 3; linePtr->header.x2 += 3; linePtr->header.y1 -= 3; linePtr->header.y2 += 3; }