Search code examples
c#wpfcanvasmatinkcanvas

Draw arc between two lines. I need to calculate points


Lines and arc

I can't find a way to drawing ARC between two lines. My constraint is : I have to calculate this Arc stroke points. Because i am using InkCanvas and i have to draw this arc point by point, i can't put any object to screen or canvas. So I know i can draw any arc with PATH object and use ArcSegment. With this method yes i can draw arc but it isn't stroke point on the Canvas. For this reason i cannot delete or save it. Anyway i need calculate this arch point by point.

I have code for drawing circle on canvas like this :

Stroke GetCircleStroke(int centerX, int centerY, int radiusX, int radiusY,double angletoDraw=2.0)
        {
            StylusPointCollection strokePoints = new StylusPointCollection();

            int numTotalSteps = 180;

            for (int i = 0; i <= numTotalSteps; i++)
            {
                double angle = angletoDraw * Math.PI * (double)i / (double)numTotalSteps;
                StylusPoint sp = new StylusPoint();
                //compute x and y points
                sp.X = centerX + Math.Cos(angle) * radiusX;
                sp.Y = centerY - Math.Sin(angle) * radiusY;

                //add to the collection
                strokePoints.Add(sp);
            }

            Stroke newStroke = new Stroke(strokePoints);
            return newStroke;

        }

I can draw circle easly, but i couldn't find a way to draw an arc :(

We know center point X,Y and we know Line1 and Line2 coordinates. I just don't know what is that arc..

Could you please help me for calculate arc points like this way ?


Solution

  • You have a few concepts flying around like Line/Segment, Point, Circle, etc. Instead of making a mess of hard to understand code, let's try to breakdown the problem into smaller parts that are easier to digest.

    You have a notion of Point, ok, lets implement one:

    public struct Point2D //omitted equality logic
    {
        public double X { get; }
        public double Y { get; }
    
        public Point2D(double x, double y)
        {
            X = x;
            Y = y;
        }
    
        public override string ToString() => $"{X:N3}; {Y:N3}";
    }
    

    Ok, we also have a notion of Segment or a delimitted Line:

    public struct Segment2D
    {
        public Point2D Start { get; }
        public Point2D End { get; }
        public double Argument => Math.Atan2(End.Y - Start.Y , End.X - Start.X);
    
        public Segment2D(Point2D start, Point2D end)
        {
            Start = start;
            End = end;
        }
    }
    

    And last, but not least, we have the notion of Circle:

    public struct Circle2D
    {
        private const double FullCircleAngle = 2 * Math.PI;
        public Point2D Center { get; }
        public double Radius { get; }
    
        public Circle2D(Point2D center, double radius)
        {
            if (radius <= 0)
                throw new ArgumentOutOfRangeException(nameof(radius));
    
            Center = center;
            Radius = radius;
        }
    
        public IEnumerable<Point2D> GetPointsOfArch(int numberOfPoints, double startAngle, double endAngle)
        {
            double normalizedEndAngle;
    
            if (startAngle < endAngle)
            {
                normalizedEndAngle = endAngle;
            }
            else
            {
                normalizedEndAngle = endAngle + FullCircleAngle;
            }
    
            var angleRange = normalizedEndAngle - startAngle;
            angleRange = angleRange > FullCircleAngle ? FullCircleAngle : angleRange;
            var step = angleRange / numberOfPoints;
            var currentAngle = startAngle;
    
            while (currentAngle <= normalizedEndAngle)
            {
                var x = Center.X + Radius * Math.Cos(currentAngle);
                var y = Center.Y + Radius * Math.Sin(currentAngle);
                yield return new Point2D(x, y);
                currentAngle += step;
            }
        }
    
        public IEnumerable<Point2D> GetPoints(int numberOfPoints)
            => GetPointsOfArch(numberOfPoints, 0, FullCircleAngle);
    }
    

    Study the implementation of GetPointsOfArch, it shouldn't be too hard to understand.

    And now, to solve your problem, you would do:

    var myCircle = new Circle2D(new Point2D(centerX, centerY), radius);
    var line1 = ....
    var line2 = ....
    var archPoints = myCircle.GetPointsOfArch(number, line2.Argument, line1.Argument);
    

    Isn't that much easier to read, follow and understand?