LMat619 0 Newbie Poster

Hey everyone. I am making an application where a user can draw shapes with their mouse. All the other shapes are implemented, but I am having difficulty with drawing arcs correctly. I got the program to draw arcs, but I am having some bugs in the code. Basically it's supposed to draw the arcs exactly like a 3-point arc in AutoCAD. So I specify the start and middle points, and then use the mouse to position the end point. My problem is that (for some reason) I can't invert the arc when it passes one side of the line made between the start and middle points of the arc. I am horrible at this kind of math so I am wondering if there is anyone out there who can help me out. Any help is appreciated. Thanks! Here is my code:

private void PreviewShape(MouseEventArgs e)
        {
            if (!line_drawn)
            {
                linept = new List<Point3D>();
                lineTwo = (Point3D)GetPoints(e);
                linept.Add(lineOne);
                linept.Add(lineTwo);
                LinesVisual3D line = new LinesVisual3D();
                line.Thickness = 2;
                line.Color = Colors.Blue;
                line.Points = linept;

                if (!skip_remove)
                    port.Children.RemoveAt(port.Children.Count - 1);
                port.Children.Add(line);
            }
            if (line_drawn)
            {
                Point3D currentPoint = (Point3D)GetPoints(e);
                Point3D center = GetCenterOfCircle(linept.ElementAt(0), linept.ElementAt(1), currentPoint);
                PieSliceVisual3D circle = new PieSliceVisual3D();
                double RadiusX = Math.Abs(lineOne.X - center.X);
                double RadiusY = Math.Abs(lineOne.Y - center.Y);
                circle.Center = center;
                if (RadiusX >= RadiusY)
                    circle.OuterRadius = RadiusX;
                else
                    circle.OuterRadius = RadiusY;
                circle.InnerRadius = circle.OuterRadius + 3;
                if (currentPoint.Y > linept.ElementAt(0).Y)
                {
                    circle.StartAngle = (Math.Atan2(linept.ElementAt(0).Y - center.Y, linept.ElementAt(0).X - center.X) * (180 / Math.PI));
                    if (circle.StartAngle < 0)
                        circle.StartAngle += 360;
                    circle.EndAngle = (Math.Atan2(currentPoint.Y - center.Y, currentPoint.X - center.X) * (180 / Math.PI));
                    if (circle.EndAngle < 0)
                        circle.EndAngle += 360;
                }
                else
                {
                    circle.StartAngle = (Math.Atan2(linept.ElementAt(0).Y - center.Y, linept.ElementAt(0).X - center.X) * (180 / Math.PI));
                    if (circle.StartAngle < 0)
                        circle.StartAngle += 360;
                    circle.EndAngle = (Math.Atan2(currentPoint.Y - center.Y, currentPoint.X - center.X) * (180 / Math.PI));
                    if (circle.EndAngle < 0)
                        circle.EndAngle += 360;
                }
                if (!skip_remove)
                    port.Children.RemoveAt(port.Children.Count - 1);
                port.Children.Add(circle);
            }
        }

        private void Select_Point(object sender, MouseButtonEventArgs e)
        {
            if (line_drawn)
                return;
            if (!firstPointSelected && !line_drawn)
            {
                lineOne = (Point3D)GetPoints(e);
                preview = true;
                firstPointSelected = true;
            }
            else if (firstPointSelected)
                firstPointSelected = false;
        }

        private Point3D? GetPoints(MouseEventArgs e)
        {
            var p = e.GetPosition(port);
            var ray = Viewport3DHelper.Point2DtoRay3D(port.Viewport, p);
            if (ray != null)
            {
                var pi = ray.PlaneIntersection(new Point3D(0, 0, .6), new Vector3D(0, 0, 1));
                if (pi.HasValue)
                    return pi;
            }
            return null;
        }

        private void Draw_Line(object sender, MouseButtonEventArgs e)
        {
            if (firstPointSelected)
                return;
            linept = new List<Point3D>();
            if (!line_drawn)
            {
                lineTwo = (Point3D)GetPoints(e);
                linept.Add(lineOne);
                linept.Add(lineTwo);
                LinesVisual3D line = new LinesVisual3D();
                line.Thickness = 2;
                line.Color = Colors.Blue;
                line.Points = linept;
                //port.Children.Add(line);
                line_drawn = true;
            }
            else if (line_drawn)
            {   
                Point3D center = GetCenterOfCircle(linept.ElementAt(0), linept.ElementAt(1), linept.ElementAt(2));
                circle = new PieSliceVisual3D();
                double RadiusX = Math.Abs(lineOne.X - center.X);
                double RadiusY = Math.Abs(lineOne.Y - center.Y);
                circle.Center = center;
                if (RadiusX >= RadiusY)
                    circle.OuterRadius = RadiusX;
                else
                    circle.OuterRadius = RadiusY;
                circle.InnerRadius = circle.OuterRadius + 3;
                if (linept.ElementAt(2).Y > linept.ElementAt(0).Y)
                {
                    circle.StartAngle = (Math.Atan2(linept.ElementAt(0).Y - center.Y, linept.ElementAt(0).X - center.X) * 180 / Math.PI);
                    if (circle.StartAngle < 0)
                        circle.StartAngle += 360;
                    circle.EndAngle = (Math.Atan2(linept.ElementAt(2).Y - center.Y, linept.ElementAt(2).X - center.X) * 180 / Math.PI);
                    if (circle.EndAngle < 0)
                        circle.EndAngle += 360;
                }
                else
                {
                    //the correct one
                    circle.StartAngle = (Math.Atan2(linept.ElementAt(0).Y - center.Y, linept.ElementAt(0).X - center.X) * 180 / Math.PI);
                    if (circle.StartAngle < 0)
                        circle.StartAngle += 360;
                    circle.EndAngle = (Math.Atan2(linept.ElementAt(2).Y - center.Y, linept.ElementAt(2).X - center.X) * 180 / Math.PI);
                    if (circle.EndAngle < 0)
                        circle.EndAngle += 360;
                }
                port.Children.RemoveAt(port.Children.Count - 1);
                port.Children.Add(circle);

                line_drawn = false;
                preview = false;
                skip_remove = true;
            }
        }

        private Point3D GetCenterOfCircle(Point3D startArc, Point3D middleArc, Point3D endArc)
        {
            double t = middleArc.X * middleArc.X + middleArc.Y * middleArc.Y;
            double bc = (startArc.X * startArc.X + startArc.Y * startArc.Y - t) / 2.0;
            double cd = (t - endArc.X * endArc.X - endArc.Y * endArc.Y) / 2.0;
            double det = (startArc.X - middleArc.X) * (middleArc.Y - endArc.Y) - (middleArc.X - endArc.X) * (startArc.Y - middleArc.Y);

            det = 1 / det;
            double x = (bc * (middleArc.Y - endArc.Y) - cd * (startArc.Y - middleArc.Y)) * det;
            double y = ((startArc.X - middleArc.X) * cd - (middleArc.X - endArc.X) * bc) * det;
            double r = Math.Sqrt((x - startArc.X) * (x - startArc.X) + (y - startArc.Y) * (y - startArc.Y));

            return new Point3D(x, y, .6);
        }

Dani AI

Generated

Short version: the bug is not the circle math but the orientation test. Comparing Y coordinates to decide which arc to draw is brittle — instead compute the circumcenter, get angles for start/mid/end around that center, then pick the end angle so the arc sweep actually passes through the middle point. That handles wrap-around and automatic inversion when the mouse crosses the chord between start and middle. This fixes the exact case you described, .

Here is a compact, robust approach (C#) that (a) computes a proper circumcenter, (b) normalizes angles, and (c) chooses an end angle that guarantees the middle point lies on the arc:

Point3D GetCircumcenter(Point3D A, Point3D B, Point3D C)
{
    double x1=A.X,y1=A.Y,x2=B.X,y2=B.Y,x3=C.X,y3=C.Y;
    double d = 2*(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2));
    if (Math.Abs(d) < 1e-12) throw new InvalidOperationException("points are colinear");
    double a1 = x1*x1 + y1*y1, a2 = x2*x2 + y2*y2, a3 = x3*x3 + y3*y3;
    double ux = (a1*(y2-y3) + a2*(y3-y1) + a3*(y1-y2)) / d;
    double uy = (a1*(x3-x2) + a2*(x1-x3) + a3*(x2-x1)) / d;
    return new Point3D(ux, uy, A.Z);
}

double Norm360(double ang) { ang %= 360; if (ang < 0) ang += 360; return ang; }

bool MidBetween(double start, double end, double mid)
{
    double delta = Norm360(end - start);
    double midDelta = Norm360(mid - start);
    return midDelta > 0 && midDelta < delta;
}

// call this to get StartAngle and EndAngle so the arc goes through 'middle'
void ComputeArcAngles(Point3D startPt, Point3D middlePt, Point3D endPt, out double startAngle, out double endAngle)
{
    var c = GetCircumcenter(startPt, middlePt, endPt);
    startAngle = Norm360(Math.Atan2(startPt.Y - c.Y, startPt.X - c.X) * 180/Math.PI);
    double midAngle = Norm360(Math.Atan2(middlePt.Y - c.Y, middlePt.X - c.X) * 180/Math.PI);
    double rawEnd = Norm360(Math.Atan2(endPt.Y - c.Y, endPt.X - c.X) * 180/Math.PI);

    if (MidBetween(startAngle, rawEnd, midAngle)) endAngle = rawEnd;
    else if (MidBetween(startAngle, rawEnd + 360, midAngle)) endAngle = rawEnd + 360;
    else endAngle = rawEnd - 360; // fallback to other sweep
}

Practical notes: compute radius as distance(center, start) rather than taking X/Y differences; handle the colinear case (no valid circle) explicitly and draw a chord if needed; for debugging draw the center and radial lines to start/mid/end to confirm orientation. Also, set inner/outer radii so inner < outer (your code used outer+3 which will always make inner larger). This angle-normalization approach will reliably invert the arc when the mouse crosses the chord and produce the same behavior as a 3‑point AutoCAD arc.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.