/// <summary>Adds an arc between the given points using the pen point as a control point. /// Note that nothing will be seen until you call a fill or stroke method.</summary> public void arcTo(float x1, float y1, float x2, float y2, float radius) { VectorPoint previous = Path.LatestPathNode; float x0; float y0; if (previous == null) { x0 = 0f; y0 = 0f; } else { x0 = previous.X; y0 = previous.Y; } // How this works: // Line from 0->1. // Another from 2->1, thus forming a 'triangle'. // Slide a circle of the given radius in this triangle such that it's // as far in as it will go whilst just touching both lines. // Draw an arc from the two intersection points of the circle with the triangle. // What we need to find: // - Circle center. // - The start and end angles. Always clockwise. // Create the new arc node: ArcLinePoint arcNode = new ArcLinePoint(x2, y2); // Apply the radius: arcNode.Radius = radius; // First find the angle of 1 from +ve x. This is just an Atan2 call: float angleLine2 = (float)Math.Atan2(y0 - y1, x0 - x1); // Find the angle of 2 relative to 1. // As we know the angle of 1 relative to +ve x, we can find 1 relative to +ve x // and then grab the difference. float rotateThrough = (float)Math.Atan2(y2 - y1, x2 - x1) - angleLine2; // Find half of the angle: float halfAngle = rotateThrough / 2f; // What's the distance of point 1 to the circle center? float distanceToCenter = radius / (float)Math.Cos(halfAngle); // Resolve the x coordinate of the center: arcNode.CircleCenterX = (distanceToCenter * (float)Math.Cos(halfAngle - angleLine2)) + x1; // Resolve the y coordinate of the center: arcNode.CircleCenterY = (distanceToCenter * (float)Math.Sin(halfAngle - angleLine2)) + y1; // Apply the angles: arcNode.StartAngle = -rotateThrough - angleLine2; arcNode.EndAngle = arcNode.StartAngle - rotateThrough; float arcStartNodeX; float arcStartNodeY; arcNode.SampleAt(0f, out arcStartNodeX, out arcStartNodeY); float arcEndNodeX; float arcEndNodeY; arcNode.SampleAt(1f, out arcEndNodeX, out arcEndNodeY); if (Path.FirstPathNode == null) { // This occurs if the arc is the first thing we draw. No line is drawn to it. Path.MoveTo(arcStartNodeX, arcStartNodeY); } else if (x0 != arcStartNodeX || y0 != arcStartNodeY) { Path.LineTo(arcStartNodeX, arcStartNodeY); } // Apply the node location: arcNode.X = arcEndNodeX; arcNode.Y = arcEndNodeY; // Add the other end: Path.AddPathNode(arcNode); }
/// <summary>Creates an arc around the given circle center. Note that nothing will /// be seen until you call a fill or stroke method.</summary> public void arc(float centerX, float centerY, float radius, float sAngle, float eAngle, bool counterClockwise) { VectorPoint previous = Path.LatestPathNode; float x0; float y0; if (previous == null) { x0 = 0f; y0 = 0f; } else { x0 = previous.X; y0 = previous.Y; } // Clockwise eAngle > sAngle; counter clockwise otherwise. if (eAngle > sAngle) { if (counterClockwise) { // Get them both in range: eAngle = eAngle % (Mathf.PI * 2f); sAngle = sAngle % (Mathf.PI * 2f); // Reduce eAngle by a full rotation so it's smaller: eAngle -= (Mathf.PI * 2f); } } else if (!counterClockwise) { // Get them both in range: eAngle = eAngle % (Mathf.PI * 2f); sAngle = sAngle % (Mathf.PI * 2f); // Reduce sAngle by a full rotation so it's smaller: sAngle -= (Mathf.PI * 2f); } // First, figure out where the actual start is. // It's radius units to the right of center, then rotated through radius around center. // Thus we have a triangle with hyp length of 'radius' and an angle of sAngle: float startX = radius * (float)Math.Cos(sAngle); float startY = radius * (float)Math.Sin(sAngle); // Now find the end point, using exactly the same method: float endX = radius * (float)Math.Cos(eAngle); float endY = radius * (float)Math.Sin(eAngle); // We now have an arc from the current position to endX/endY. // The start and exit node angles are usefully just offset from the given ones. // This is because an sAngle of zero should create an arc which starts travelling downwards. // (Or upwards if it's counter clockwise): // Where does the arc start from? float arcStartX = centerX + startX; float arcStartY = centerY + startY; if (Path.FirstPathNode == null) { // This occurs if the arc is the first thing we draw. No line is drawn to it. Path.MoveTo(arcStartX, arcStartY); } else if (arcStartX != x0 || arcStartY != y0) { // Draw a line to this point: Path.LineTo(arcStartX, arcStartY); } // Create the new arc node: ArcLinePoint arcNode = new ArcLinePoint(centerX + endX, centerY + endY); // Apply the radius: arcNode.Radius = radius; // Apply the angles: arcNode.StartAngle = sAngle; arcNode.EndAngle = eAngle; // Apply the center: arcNode.CircleCenterX = centerX; arcNode.CircleCenterY = centerY; // Add the other end: Path.AddPathNode(arcNode); }