public void CreateCap(VertexStore output, VertexDistance v0, VertexDistance v1, double len) { output.Clear(); double dx1 = (v1.y - v0.y) / len; double dy1 = (v1.x - v0.x) / len; double dx2 = 0; double dy2 = 0; dx1 *= m_width; dy1 *= m_width; if (m_line_cap != LineCap.Round) { if (m_line_cap == LineCap.Square) { dx2 = dy1 * m_width_sign; dy2 = dx1 * m_width_sign; } AddVertex(output, v0.x - dx1 - dx2, v0.y + dy1 - dy2); AddVertex(output, v0.x + dx1 - dx2, v0.y - dy1 - dy2); } else { double da = Math.Acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2; double a1; int i; int n = (int)(Math.PI / da); da = Math.PI / (n + 1); AddVertex(output, v0.x - dx1, v0.y + dy1); if (m_width_sign > 0) { a1 = Math.Atan2(dy1, -dx1); a1 += da; for (i = 0; i < n; i++) { AddVertex(output, v0.x + Math.Cos(a1) * m_width, v0.y + Math.Sin(a1) * m_width); a1 += da; } } else { a1 = Math.Atan2(-dy1, dx1); a1 -= da; for (i = 0; i < n; i++) { AddVertex(output, v0.x + Math.Cos(a1) * m_width, v0.y + Math.Sin(a1) * m_width); a1 -= da; } } AddVertex(output, v0.x + dx1, v0.y - dy1); } }
//-------------------------------------------------------calc_polygon_area public static double CalculatePolygonArea(VertexDistanceList st) { int i; double sum = 0.0; double x = st[0].x; double y = st[0].y; double xs = x; double ys = y; int j = st.Count; for (i = 1; i < j; i++) { VertexDistance v = st[i]; sum += x * v.y - y * v.x; x = v.x; y = v.y; } return((sum + x * ys - y * xs) * 0.5); }
public static void ShortenPath(VertexDistanceList vertexDistanceList, double s, bool closed) { if (s > 0.0 && vertexDistanceList.Count > 1) { double d; int n = (int)(vertexDistanceList.Count - 2); while (n != 0) { d = vertexDistanceList[n].dist; if (d > s) { break; } vertexDistanceList.RemoveLast(); s -= d; --n; } if (vertexDistanceList.Count < 2) { vertexDistanceList.Clear(); } else { n = (int)vertexDistanceList.Count - 1; VertexDistance prev = vertexDistanceList[n - 1]; VertexDistance last = vertexDistanceList[n]; d = (prev.dist - s) / prev.dist; double x = prev.x + (last.x - prev.x) * d; double y = prev.y + (last.y - prev.y) * d; last.x = x; last.y = y; if (!prev.IsEqual(last)) { vertexDistanceList.RemoveLast(); } vertexDistanceList.Close(closed); } } }
void CreateMiter(VertexStore output, VertexDistance v0, VertexDistance v1, VertexDistance v2, double dx1, double dy1, double dx2, double dy2, LineJoin lj, double mlimit, double dbevel) { double xi = v1.x; double yi = v1.y; double di = 1; double lim = m_width_abs * mlimit; bool miter_limit_exceeded = true; // Assume the worst bool intersection_failed = true; // Assume the worst if (AggMath.CalcIntersect(v0.x + dx1, v0.y - dy1, v1.x + dx1, v1.y - dy1, v1.x + dx2, v1.y - dy2, v2.x + dx2, v2.y - dy2, out xi, out yi)) { // Calculation of the intersection succeeded //--------------------- di = AggMath.calc_distance(v1.x, v1.y, xi, yi); if (di <= lim) { // Inside the miter limit //--------------------- AddVertex(output, xi, yi); miter_limit_exceeded = false; } intersection_failed = false; } else { // Calculation of the intersection failed, most probably // the three points lie one straight line. // First check if v0 and v2 lie on the opposite sides of vector: // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular // to the line determined by vertices v0 and v1. // This condition determines whether the next line segments continues // the previous one or goes back. //---------------- double x2 = v1.x + dx1; double y2 = v1.y - dy1; if ((AggMath.Cross(v0.x, v0.y, v1.x, v1.y, x2, y2) < 0.0) == (AggMath.Cross(v1.x, v1.y, v2.x, v2.y, x2, y2) < 0.0)) { // This case means that the next segment continues // the previous one (straight line) //----------------- AddVertex(output, v1.x + dx1, v1.y - dy1); miter_limit_exceeded = false; } } if (miter_limit_exceeded) { // Miter limit exceeded //------------------------ switch (lj) { case LineJoin.MiterRevert: // For the compatibility with SVG, PDF, etc, // we use a simple bevel join instead of // "smart" bevel //------------------- AddVertex(output, v1.x + dx1, v1.y - dy1); AddVertex(output, v1.x + dx2, v1.y - dy2); break; case LineJoin.MiterRound: CreateArc(output, v1.x, v1.y, dx1, -dy1, dx2, -dy2); break; default: // If no miter-revert, calculate new dx1, dy1, dx2, dy2 //---------------- if (intersection_failed) { mlimit *= m_width_sign; AddVertex(output, v1.x + dx1 + dy1 * mlimit, v1.y - dy1 + dx1 * mlimit); AddVertex(output, v1.x + dx2 - dy2 * mlimit, v1.y - dy2 - dx2 * mlimit); } else { double x1 = v1.x + dx1; double y1 = v1.y - dy1; double x2 = v1.x + dx2; double y2 = v1.y - dy2; di = (lim - dbevel) / (di - dbevel); AddVertex(output, x1 + (xi - x1) * di, y1 + (yi - y1) * di); AddVertex(output, x2 + (xi - x2) * di, y2 + (yi - y2) * di); } break; } } }
public void CreateJoin(VertexStore output, VertexDistance v0, VertexDistance v1, VertexDistance v2, double len1, double len2) { double dx1 = m_width * (v1.y - v0.y) / len1; double dy1 = m_width * (v1.x - v0.x) / len1; double dx2 = m_width * (v2.y - v1.y) / len2; double dy2 = m_width * (v2.x - v1.x) / len2; output.Clear(); double cp = AggMath.Cross(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y); if (cp != 0 && (cp > 0) == (m_width > 0)) { // Inner join //--------------- double limit = ((len1 < len2) ? len1 : len2) / m_width_abs; if (limit < m_inner_miter_limit) { limit = m_inner_miter_limit; } switch (m_inner_join) { default: // inner_bevel AddVertex(output, v1.x + dx1, v1.y - dy1); AddVertex(output, v1.x + dx2, v1.y - dy2); break; case InnerJoin.Miter: CreateMiter(output, v0, v1, v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0); break; case InnerJoin.Jag: case InnerJoin.Round: cp = (dx1 - dx2) * (dx1 - dx2) + (dy1 - dy2) * (dy1 - dy2); if (cp < len1 * len1 && cp < len2 * len2) { CreateMiter(output, v0, v1, v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0); } else { if (m_inner_join == InnerJoin.Jag) { AddVertex(output, v1.x + dx1, v1.y - dy1); AddVertex(output, v1.x, v1.y); AddVertex(output, v1.x + dx2, v1.y - dy2); } else { AddVertex(output, v1.x + dx1, v1.y - dy1); AddVertex(output, v1.x, v1.y); CreateArc(output, v1.x, v1.y, dx2, -dy2, dx1, -dy1); AddVertex(output, v1.x, v1.y); AddVertex(output, v1.x + dx2, v1.y - dy2); } } break; } } else { // Outer join //--------------- // Calculate the distance between v1 and // the central point of the bevel line segment //--------------- double dx = (dx1 + dx2) / 2; double dy = (dy1 + dy2) / 2; double dbevel = Math.Sqrt(dx * dx + dy * dy); if (m_line_join == LineJoin.Round || m_line_join == LineJoin.Bevel) { // This is an optimization that reduces the number of points // in cases of almost collinear segments. If there's no // visible difference between bevel and miter joins we'd rather // use miter join because it adds only one point instead of two. // // Here we calculate the middle point between the bevel points // and then, the distance between v1 and this middle point. // At outer joins this distance always less than stroke width, // because it's actually the height of an isosceles triangle of // v1 and its two bevel points. If the difference between this // width and this value is small (no visible bevel) we can // add just one point. // // The constant in the expression makes the result approximately // the same as in round joins and caps. You can safely comment // out this entire "if". //------------------- if (m_approx_scale * (m_width_abs - dbevel) < m_width_eps) { if (AggMath.CalcIntersect(v0.x + dx1, v0.y - dy1, v1.x + dx1, v1.y - dy1, v1.x + dx2, v1.y - dy2, v2.x + dx2, v2.y - dy2, out dx, out dy)) { AddVertex(output, dx, dy); } else { AddVertex(output, v1.x + dx1, v1.y - dy1); } return; } } switch (m_line_join) { case LineJoin.Miter: case LineJoin.MiterRevert: case LineJoin.MiterRound: CreateMiter(output, v0, v1, v2, dx1, dy1, dx2, dy2, m_line_join, m_miter_limit, dbevel); break; case LineJoin.Round: CreateArc(output, v1.x, v1.y, dx1, -dy1, dx2, -dy2); break; default: // Bevel join AddVertex(output, v1.x + dx1, v1.y - dy1); AddVertex(output, v1.x + dx2, v1.y - dy2); break; } } }