void AddRecursiveBezier(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, int level) { if (level > Curves.CURVE_RECURSION_LIMIT) { return; } // Calculate all the mid-points of the line segments //---------------------- double x12 = (x1 + x2) / 2; double y12 = (y1 + y2) / 2; double x23 = (x2 + x3) / 2; double y23 = (y2 + y3) / 2; double x34 = (x3 + x4) / 2; double y34 = (y3 + y4) / 2; double x123 = (x12 + x23) / 2; double y123 = (y12 + y23) / 2; double x234 = (x23 + x34) / 2; double y234 = (y23 + y34) / 2; double x1234 = (x123 + x234) / 2; double y1234 = (y123 + y234) / 2; // Try to approximate the full cubic curve by a single straight line //------------------ double dx = x4 - x1; double dy = y4 - y1; double d2 = Math.Abs(((x2 - x4) * dy - (y2 - y4) * dx)); double d3 = Math.Abs(((x3 - x4) * dy - (y3 - y4) * dx)); double da1, da2, k; int SwitchCase = 0; if (d2 > Curves.CURVE_COLLINEARITY_EPSILON) { SwitchCase = 2; } if (d3 > Curves.CURVE_COLLINEARITY_EPSILON) { SwitchCase++; } switch (SwitchCase) { case 0: // All collinear OR p1==p4 //---------------------- k = dx * dx + dy * dy; if (k == 0) { d2 = AggMath.calc_sq_distance(x1, y1, x2, y2); d3 = AggMath.calc_sq_distance(x4, y4, x3, y3); } else { k = 1 / k; da1 = x2 - x1; da2 = y2 - y1; d2 = k * (da1 * dx + da2 * dy); da1 = x3 - x1; da2 = y3 - y1; d3 = k * (da1 * dx + da2 * dy); if (d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) { // Simple collinear case, 1---2---3---4 // We can leave just two endpoints return; } if (d2 <= 0) { d2 = AggMath.calc_sq_distance(x2, y2, x1, y1); } else if (d2 >= 1) { d2 = AggMath.calc_sq_distance(x2, y2, x4, y4); } else { d2 = AggMath.calc_sq_distance(x2, y2, x1 + d2 * dx, y1 + d2 * dy); } if (d3 <= 0) { d3 = AggMath.calc_sq_distance(x3, y3, x1, y1); } else if (d3 >= 1) { d3 = AggMath.calc_sq_distance(x3, y3, x4, y4); } else { d3 = AggMath.calc_sq_distance(x3, y3, x1 + d3 * dx, y1 + d3 * dy); } } if (d2 > d3) { if (d2 < m_distance_tolerance_square) { m_points.AddVertex(new Vector2(x2, y2)); return; } } else { if (d3 < m_distance_tolerance_square) { m_points.AddVertex(new Vector2(x3, y3)); return; } } break; case 1: // p1,p2,p4 are collinear, p3 is significant //---------------------- if (d3 * d3 <= m_distance_tolerance_square * (dx * dx + dy * dy)) { if (m_angle_tolerance < Curves.CURVE_ANGLE_TOLERANCE_EPSILON) { m_points.AddVertex(new Vector2(x23, y23)); return; } // Angle Condition //---------------------- da1 = Math.Abs(Math.Atan2(y4 - y3, x4 - x3) - Math.Atan2(y3 - y2, x3 - x2)); if (da1 >= Math.PI) { da1 = 2 * Math.PI - da1; } if (da1 < m_angle_tolerance) { m_points.AddVertex(new Vector2(x2, y2)); m_points.AddVertex(new Vector2(x3, y3)); return; } if (m_cusp_limit != 0.0) { if (da1 > m_cusp_limit) { m_points.AddVertex(new Vector2(x3, y3)); return; } } } break; case 2: // p1,p3,p4 are collinear, p2 is significant //---------------------- if (d2 * d2 <= m_distance_tolerance_square * (dx * dx + dy * dy)) { if (m_angle_tolerance < Curves.CURVE_ANGLE_TOLERANCE_EPSILON) { m_points.AddVertex(new Vector2(x23, y23)); return; } // Angle Condition //---------------------- da1 = Math.Abs(Math.Atan2(y3 - y2, x3 - x2) - Math.Atan2(y2 - y1, x2 - x1)); if (da1 >= Math.PI) { da1 = 2 * Math.PI - da1; } if (da1 < m_angle_tolerance) { m_points.AddVertex(new Vector2(x2, y2)); m_points.AddVertex(new Vector2(x3, y3)); return; } if (m_cusp_limit != 0.0) { if (da1 > m_cusp_limit) { m_points.AddVertex(new Vector2(x2, y2)); return; } } } break; case 3: // Regular case //----------------- if ((d2 + d3) * (d2 + d3) <= m_distance_tolerance_square * (dx * dx + dy * dy)) { // If the curvature doesn't exceed the distance_tolerance value // we tend to finish subdivisions. //---------------------- if (m_angle_tolerance < Curves.CURVE_ANGLE_TOLERANCE_EPSILON) { m_points.AddVertex(new Vector2(x23, y23)); return; } // Angle & Cusp Condition //---------------------- k = Math.Atan2(y3 - y2, x3 - x2); da1 = Math.Abs(k - Math.Atan2(y2 - y1, x2 - x1)); da2 = Math.Abs(Math.Atan2(y4 - y3, x4 - x3) - k); if (da1 >= Math.PI) { da1 = 2 * Math.PI - da1; } if (da2 >= Math.PI) { da2 = 2 * Math.PI - da2; } if (da1 + da2 < m_angle_tolerance) { // Finally we can stop the recursion //---------------------- m_points.AddVertex(new Vector2(x23, y23)); return; } if (m_cusp_limit != 0.0) { if (da1 > m_cusp_limit) { m_points.AddVertex(new Vector2(x2, y2)); return; } if (da2 > m_cusp_limit) { m_points.AddVertex(new Vector2(x3, y3)); return; } } } break; } // Continue subdivision //---------------------- AddRecursiveBezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1); AddRecursiveBezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1); }
private void AddRecursiveBezier(double x1, double y1, double x2, double y2, double x3, double y3, int level) { if (level > Curves.CURVE_RECURSION_LIMIT) { return; } // Calculate all the mid-points of the line segments //---------------------- double x12 = (x1 + x2) / 2; double y12 = (y1 + y2) / 2; double x23 = (x2 + x3) / 2; double y23 = (y2 + y3) / 2; double x123 = (x12 + x23) / 2; double y123 = (y12 + y23) / 2; double dx = x3 - x1; double dy = y3 - y1; double d = Math.Abs(((x2 - x3) * dy - (y2 - y3) * dx)); double da; if (d > Curves.CURVE_COLLINEARITY_EPSILON) { // Regular case //----------------- if (d * d <= m_distance_tolerance_square * (dx * dx + dy * dy)) { // If the curvature doesn't exceed the distance_tolerance value // we tend to finish subdivisions. //---------------------- if (m_angle_tolerance < Curves.CURVE_ANGLE_TOLERANCE_EPSILON) { m_points.AddVertex(new Vector2(x123, y123)); return; } // Angle & Cusp Condition //---------------------- da = Math.Abs(Math.Atan2(y3 - y2, x3 - x2) - Math.Atan2(y2 - y1, x2 - x1)); if (da >= Math.PI) { da = 2 * Math.PI - da; } if (da < m_angle_tolerance) { // Finally we can stop the recursion //---------------------- m_points.AddVertex(new Vector2(x123, y123)); return; } } } else { // Collinear case //------------------ da = dx * dx + dy * dy; if (da == 0) { d = AggMath.calc_sq_distance(x1, y1, x2, y2); } else { d = ((x2 - x1) * dx + (y2 - y1) * dy) / da; if (d > 0 && d < 1) { // Simple collinear case, 1---2---3 // We can leave just two endpoints return; } if (d <= 0) { d = AggMath.calc_sq_distance(x2, y2, x1, y1); } else if (d >= 1) { d = AggMath.calc_sq_distance(x2, y2, x3, y3); } else { d = AggMath.calc_sq_distance(x2, y2, x1 + d * dx, y1 + d * dy); } } if (d < m_distance_tolerance_square) { m_points.AddVertex(new Vector2(x2, y2)); return; } } // Continue subdivision //---------------------- AddRecursiveBezier(x1, y1, x12, y12, x123, y123, level + 1); AddRecursiveBezier(x123, y123, x23, y23, x3, y3, level + 1); }