none
'Center' of ArcSegment

    Question

  • (I hope this isn't too much of a math problem) I am hoping there is something built in for this or some solution out there for this:

    Is it possible to find the center point of an ArcSegment? Edit: this is a circular arc segment where the size x and y are always the same.

    Say I have a Path, one PathFigure with one ArcSegment. PathFigure's StartPoint is 0,4.  ArcSegment's Point is 4,0. Size 2,2, IsLargeArc = false, SweepDirection = Clockwise.  In my head I know the centerpoint is 4,4.. but is there an easy way to solve this in silverlight or .net?

    The closest math forumula I found could find the two potential center points using the two points and radius.. but in silverlight we can specify the sweep and large part flag.. is there a solution to find a single center point then?

    Wednesday, July 23, 2008 4:20 PM

Answers

  • Hi

    It's a math problem. Please try following code to get the center:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Xml;
    using System.Windows.Media.Imaging;
    using System.Windows.Resources;
    using System.IO;
    using System.Windows.Browser;

    using System.ServiceModel;
    using System.Windows.Threading;
    using System.Threading;
    using System.Reflection;
    using System.Windows.Markup;
    using System.ServiceModel.Channels;

     


    namespace SilverlightApplication26
    {
       

        public partial class Page : UserControl
        {
          
          
            public Page()
            {
              

                         InitializeComponent();

                         this.Loaded += new RoutedEventHandler(Page_Loaded);
                 
                      
            }

            Point GetCircleCenter(Point p1, Point p2, double r, bool IsLargeArc, bool IsClockWise)
            {

                double x1 = p1.X, y1 = p1.Y, x2 = p2.X, y2 = p2.Y;
                  //Middle point
                    double xm = (x1 + x2) / 2, ym = (y1 + y2) / 2;
                double result_x_1,result_x_2, result_y_1, result_y_2;
                if ((Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2)) > Math.Pow(2 * r, 2))
                {
                    return new Point(xm, ym);
                }
                else
                {
                    if (Math.Abs(y1 - y2)<0.0000000000001)//corrected here
                    {
                        double k = (x2 - x1) / (y1 - y2);


                        //the center point's x axis value is the result of function ax^2+bx+c=0
                        double t = ym - k * xm;
                        double a = 1 + k * k;
                        double b = 2 * (k * t - x1 - y1 * k);
                        double c = x1 * x1 + t * t - 2 * t * y1 + y1 * y1 - r * r;
                        //Result
                        result_x_1 = (-1 * b + Math.Sqrt(b * b - 4 * a * c)) / (2 * a);
                        result_x_2 = (-1 * b - Math.Sqrt(b * b - 4 * a * c)) / (2 * a);
                        //Relevant y asix value
                        result_y_1 = k * result_x_1 + t;
                        result_y_2 = k * result_x_2 + t;
                    }
                    else
                    {
                        result_x_1 = result_x_2 = xm;
                        result_y_1 = y1 + Math.Sqrt(r * r - Math.Pow(result_x_1 - x1, 2));
                        result_y_2 = y1 - Math.Sqrt(r * r - Math.Pow(result_x_1 - x1, 2));
                    }

                    //Return

                    Vector v1_1 = new Vector(x1 - result_x_1, y1 - result_y_1);
                    Vector v1_2 = new Vector(x2 - result_x_1, y2 - result_y_1);
                    double b1 = Vector.AngleBetween(v1_1, v1_2);
                    bool result1_angle_larger_than_zero = b1 > 0;
                    Point result = new Point();
                    if (!IsClockWise)
                    {
                        if (IsLargeArc)
                        {
                            if (result1_angle_larger_than_zero)
                            {
                                result.X = result_x_1;
                                result.Y = result_y_1;
                            }
                            else
                            {
                                result.X = result_x_2;
                                result.Y = result_y_2;
                             
                            }
                        }
                        else
                        {
                            if (result1_angle_larger_than_zero)
                            {
                                result.X = result_x_2;
                                result.Y = result_y_2;
                            }
                            else
                            {

                                result.X = result_x_1;
                                result.Y = result_y_1;
                            }

                        }
                    }
                    else
                    {
                        if (IsLargeArc)
                        {
                            if (result1_angle_larger_than_zero)
                            {
                                result.X = result_x_2;
                                result.Y = result_y_2;
                            }
                            else
                            {
                                result.X = result_x_1;
                                result.Y = result_y_1;
                            }
                        }
                        else
                        {
                            if (result1_angle_larger_than_zero)
                            {
                                result.X = result_x_1;
                                result.Y = result_y_1;
                            }
                            else
                            {

                                result.X = result_x_2;
                                result.Y = result_y_2;
                            }

                        }
                    }
                    return result;
                }
            }
            Ellipse el = new Ellipse();
            void Page_Loaded(object sender, RoutedEventArgs e)
            {
                this.Canvas1.Children.Add(el);
                el.Width = 5;
                el.Height = 5;
          
                el.Fill = new SolidColorBrush(Colors.Red);


                //Make sure ArcSegment1 is a circle or else the result is wrong!
                Point p = GetCircleCenter(this.PathFigure1.StartPoint,this.ArcSegment1.Point,this.ArcSegment1.Size.Width,this.ArcSegment1.IsLargeArc,this.ArcSegment1.SweepDirection==SweepDirection.Clockwise);
                el.SetValue(Canvas.LeftProperty, p.X);
                el.SetValue(Canvas.TopProperty, p.Y);
              
            }

     


        }

        public struct Vector
        {
            internal double _x;
            internal double _y;
            public static bool operator ==(Vector vector1, Vector vector2)
            {
                return ((vector1.X == vector2.X) && (vector1.Y == vector2.Y));
            }

            public static bool operator !=(Vector vector1, Vector vector2)
            {
                return !(vector1 == vector2);
            }

            public static bool Equals(Vector vector1, Vector vector2)
            {
                return (vector1.X.Equals(vector2.X) && vector1.Y.Equals(vector2.Y));
            }

            public override bool Equals(object o)
            {
                if ((o == null) || !(o is Vector))
                {
                    return false;
                }
                Vector vector = (Vector)o;
                return Equals(this, vector);
            }

            public bool Equals(Vector value)
            {
                return Equals(this, value);
            }

            public override int GetHashCode()
            {
                return (this.X.GetHashCode() ^ this.Y.GetHashCode());
            }

       
            public double X
            {
                get
                {
                    return this._x;
                }
                set
                {
                    this._x = value;
                }
            }
            public double Y
            {
                get
                {
                    return this._y;
                }
                set
                {
                    this._y = value;
                }
            }
         
            public Vector(double x, double y)
            {
                this._x = x;
                this._y = y;
            }

            public double Length
            {
                get
                {
                    return Math.Sqrt((this._x * this._x) + (this._y * this._y));
                }
            }
            public double LengthSquared
            {
                get
                {
                    return ((this._x * this._x) + (this._y * this._y));
                }
            }
            public void Normalize()
            {
                this = (Vector)(this / Math.Max(Math.Abs(this._x), Math.Abs(this._y)));
                this = (Vector)(this / this.Length);
            }

            public static double CrossProduct(Vector vector1, Vector vector2)
            {
                return ((vector1._x * vector2._y) - (vector1._y * vector2._x));
            }

            public static double AngleBetween(Vector vector1, Vector vector2)
            {
                double y = (vector1._x * vector2._y) - (vector2._x * vector1._y);
                double x = (vector1._x * vector2._x) + (vector1._y * vector2._y);
                return (Math.Atan2(y, x) * 57.295779513082323);
            }

            public static Vector operator -(Vector vector)
            {
                return new Vector(-vector._x, -vector._y);
            }

            public void Negate()
            {
                this._x = -this._x;
                this._y = -this._y;
            }

            public static Vector operator +(Vector vector1, Vector vector2)
            {
                return new Vector(vector1._x + vector2._x, vector1._y + vector2._y);
            }

            public static Vector Add(Vector vector1, Vector vector2)
            {
                return new Vector(vector1._x + vector2._x, vector1._y + vector2._y);
            }

            public static Vector operator -(Vector vector1, Vector vector2)
            {
                return new Vector(vector1._x - vector2._x, vector1._y - vector2._y);
            }

            public static Vector Subtract(Vector vector1, Vector vector2)
            {
                return new Vector(vector1._x - vector2._x, vector1._y - vector2._y);
            }

       

            public static Vector operator *(Vector vector, double scalar)
            {
                return new Vector(vector._x * scalar, vector._y * scalar);
            }

            public static Vector Multiply(Vector vector, double scalar)
            {
                return new Vector(vector._x * scalar, vector._y * scalar);
            }

            public static Vector operator *(double scalar, Vector vector)
            {
                return new Vector(vector._x * scalar, vector._y * scalar);
            }

            public static Vector Multiply(double scalar, Vector vector)
            {
                return new Vector(vector._x * scalar, vector._y * scalar);
            }

            public static Vector operator /(Vector vector, double scalar)
            {
                return (Vector)(vector * (1.0 / scalar));
            }

            public static Vector Divide(Vector vector, double scalar)
            {
                return (Vector)(vector * (1.0 / scalar));
            }

            public static double operator *(Vector vector1, Vector vector2)
            {
                return ((vector1._x * vector2._x) + (vector1._y * vector2._y));
            }

            public static double Multiply(Vector vector1, Vector vector2)
            {
                return ((vector1._x * vector2._x) + (vector1._y * vector2._y));
            }

            public static double Determinant(Vector vector1, Vector vector2)
            {
                return ((vector1._x * vector2._y) - (vector1._y * vector2._x));
            }

            public static explicit operator Size(Vector vector)
            {
                return new Size(Math.Abs(vector._x), Math.Abs(vector._y));
            }

            public static explicit operator Point(Vector vector)
            {
                return new Point(vector._x, vector._y);
            }
        }


    }

     <Canvas x:Name="Canvas1" Width="500" Height="500" Background="Yellow">
                <Path Stroke="Black" StrokeThickness="2">
                    <Path.Data>
                        <PathGeometry>
                            <PathGeometry.Figures>
                                <PathFigureCollection>
                                    <PathFigure x:Name="PathFigure1" StartPoint="50,140">
                                        <PathFigure.Segments>
                                            <PathSegmentCollection>
                                                <ArcSegment x:Name="ArcSegment1" Size="40,40"  IsLargeArc="False"
    SweepDirection="Clockwise"  Point="80,140" />
                                            </PathSegmentCollection>
                                        </PathFigure.Segments>
                                    </PathFigure>
                                </PathFigureCollection>
                            </PathGeometry.Figures>
                        </PathGeometry>
                    </Path.Data>
                </Path>

            </Canvas>

     

    Please note even if it cannot create a circle with the given size (radius of the circle) it'll be auto stretched and create a semicircle. You can see I added some logic to handle this scenario.

    I haven't tested it throughly. Please help to debug.

    Friday, July 25, 2008 4:52 AM

All replies

  • I dared to ask on a Math forum and every other response was theory or accusations of homework fishing! I may have found a work around although it is not an exact method like I was after.

    Thursday, July 24, 2008 3:35 PM
  • Hi

    It's a math problem. Please try following code to get the center:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Xml;
    using System.Windows.Media.Imaging;
    using System.Windows.Resources;
    using System.IO;
    using System.Windows.Browser;

    using System.ServiceModel;
    using System.Windows.Threading;
    using System.Threading;
    using System.Reflection;
    using System.Windows.Markup;
    using System.ServiceModel.Channels;

     


    namespace SilverlightApplication26
    {
       

        public partial class Page : UserControl
        {
          
          
            public Page()
            {
              

                         InitializeComponent();

                         this.Loaded += new RoutedEventHandler(Page_Loaded);
                 
                      
            }

            Point GetCircleCenter(Point p1, Point p2, double r, bool IsLargeArc, bool IsClockWise)
            {

                double x1 = p1.X, y1 = p1.Y, x2 = p2.X, y2 = p2.Y;
                  //Middle point
                    double xm = (x1 + x2) / 2, ym = (y1 + y2) / 2;
                double result_x_1,result_x_2, result_y_1, result_y_2;
                if ((Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2)) > Math.Pow(2 * r, 2))
                {
                    return new Point(xm, ym);
                }
                else
                {
                    if (Math.Abs(y1 - y2)<0.0000000000001)//corrected here
                    {
                        double k = (x2 - x1) / (y1 - y2);


                        //the center point's x axis value is the result of function ax^2+bx+c=0
                        double t = ym - k * xm;
                        double a = 1 + k * k;
                        double b = 2 * (k * t - x1 - y1 * k);
                        double c = x1 * x1 + t * t - 2 * t * y1 + y1 * y1 - r * r;
                        //Result
                        result_x_1 = (-1 * b + Math.Sqrt(b * b - 4 * a * c)) / (2 * a);
                        result_x_2 = (-1 * b - Math.Sqrt(b * b - 4 * a * c)) / (2 * a);
                        //Relevant y asix value
                        result_y_1 = k * result_x_1 + t;
                        result_y_2 = k * result_x_2 + t;
                    }
                    else
                    {
                        result_x_1 = result_x_2 = xm;
                        result_y_1 = y1 + Math.Sqrt(r * r - Math.Pow(result_x_1 - x1, 2));
                        result_y_2 = y1 - Math.Sqrt(r * r - Math.Pow(result_x_1 - x1, 2));
                    }

                    //Return

                    Vector v1_1 = new Vector(x1 - result_x_1, y1 - result_y_1);
                    Vector v1_2 = new Vector(x2 - result_x_1, y2 - result_y_1);
                    double b1 = Vector.AngleBetween(v1_1, v1_2);
                    bool result1_angle_larger_than_zero = b1 > 0;
                    Point result = new Point();
                    if (!IsClockWise)
                    {
                        if (IsLargeArc)
                        {
                            if (result1_angle_larger_than_zero)
                            {
                                result.X = result_x_1;
                                result.Y = result_y_1;
                            }
                            else
                            {
                                result.X = result_x_2;
                                result.Y = result_y_2;
                             
                            }
                        }
                        else
                        {
                            if (result1_angle_larger_than_zero)
                            {
                                result.X = result_x_2;
                                result.Y = result_y_2;
                            }
                            else
                            {

                                result.X = result_x_1;
                                result.Y = result_y_1;
                            }

                        }
                    }
                    else
                    {
                        if (IsLargeArc)
                        {
                            if (result1_angle_larger_than_zero)
                            {
                                result.X = result_x_2;
                                result.Y = result_y_2;
                            }
                            else
                            {
                                result.X = result_x_1;
                                result.Y = result_y_1;
                            }
                        }
                        else
                        {
                            if (result1_angle_larger_than_zero)
                            {
                                result.X = result_x_1;
                                result.Y = result_y_1;
                            }
                            else
                            {

                                result.X = result_x_2;
                                result.Y = result_y_2;
                            }

                        }
                    }
                    return result;
                }
            }
            Ellipse el = new Ellipse();
            void Page_Loaded(object sender, RoutedEventArgs e)
            {
                this.Canvas1.Children.Add(el);
                el.Width = 5;
                el.Height = 5;
          
                el.Fill = new SolidColorBrush(Colors.Red);


                //Make sure ArcSegment1 is a circle or else the result is wrong!
                Point p = GetCircleCenter(this.PathFigure1.StartPoint,this.ArcSegment1.Point,this.ArcSegment1.Size.Width,this.ArcSegment1.IsLargeArc,this.ArcSegment1.SweepDirection==SweepDirection.Clockwise);
                el.SetValue(Canvas.LeftProperty, p.X);
                el.SetValue(Canvas.TopProperty, p.Y);
              
            }

     


        }

        public struct Vector
        {
            internal double _x;
            internal double _y;
            public static bool operator ==(Vector vector1, Vector vector2)
            {
                return ((vector1.X == vector2.X) && (vector1.Y == vector2.Y));
            }

            public static bool operator !=(Vector vector1, Vector vector2)
            {
                return !(vector1 == vector2);
            }

            public static bool Equals(Vector vector1, Vector vector2)
            {
                return (vector1.X.Equals(vector2.X) && vector1.Y.Equals(vector2.Y));
            }

            public override bool Equals(object o)
            {
                if ((o == null) || !(o is Vector))
                {
                    return false;
                }
                Vector vector = (Vector)o;
                return Equals(this, vector);
            }

            public bool Equals(Vector value)
            {
                return Equals(this, value);
            }

            public override int GetHashCode()
            {
                return (this.X.GetHashCode() ^ this.Y.GetHashCode());
            }

       
            public double X
            {
                get
                {
                    return this._x;
                }
                set
                {
                    this._x = value;
                }
            }
            public double Y
            {
                get
                {
                    return this._y;
                }
                set
                {
                    this._y = value;
                }
            }
         
            public Vector(double x, double y)
            {
                this._x = x;
                this._y = y;
            }

            public double Length
            {
                get
                {
                    return Math.Sqrt((this._x * this._x) + (this._y * this._y));
                }
            }
            public double LengthSquared
            {
                get
                {
                    return ((this._x * this._x) + (this._y * this._y));
                }
            }
            public void Normalize()
            {
                this = (Vector)(this / Math.Max(Math.Abs(this._x), Math.Abs(this._y)));
                this = (Vector)(this / this.Length);
            }

            public static double CrossProduct(Vector vector1, Vector vector2)
            {
                return ((vector1._x * vector2._y) - (vector1._y * vector2._x));
            }

            public static double AngleBetween(Vector vector1, Vector vector2)
            {
                double y = (vector1._x * vector2._y) - (vector2._x * vector1._y);
                double x = (vector1._x * vector2._x) + (vector1._y * vector2._y);
                return (Math.Atan2(y, x) * 57.295779513082323);
            }

            public static Vector operator -(Vector vector)
            {
                return new Vector(-vector._x, -vector._y);
            }

            public void Negate()
            {
                this._x = -this._x;
                this._y = -this._y;
            }

            public static Vector operator +(Vector vector1, Vector vector2)
            {
                return new Vector(vector1._x + vector2._x, vector1._y + vector2._y);
            }

            public static Vector Add(Vector vector1, Vector vector2)
            {
                return new Vector(vector1._x + vector2._x, vector1._y + vector2._y);
            }

            public static Vector operator -(Vector vector1, Vector vector2)
            {
                return new Vector(vector1._x - vector2._x, vector1._y - vector2._y);
            }

            public static Vector Subtract(Vector vector1, Vector vector2)
            {
                return new Vector(vector1._x - vector2._x, vector1._y - vector2._y);
            }

       

            public static Vector operator *(Vector vector, double scalar)
            {
                return new Vector(vector._x * scalar, vector._y * scalar);
            }

            public static Vector Multiply(Vector vector, double scalar)
            {
                return new Vector(vector._x * scalar, vector._y * scalar);
            }

            public static Vector operator *(double scalar, Vector vector)
            {
                return new Vector(vector._x * scalar, vector._y * scalar);
            }

            public static Vector Multiply(double scalar, Vector vector)
            {
                return new Vector(vector._x * scalar, vector._y * scalar);
            }

            public static Vector operator /(Vector vector, double scalar)
            {
                return (Vector)(vector * (1.0 / scalar));
            }

            public static Vector Divide(Vector vector, double scalar)
            {
                return (Vector)(vector * (1.0 / scalar));
            }

            public static double operator *(Vector vector1, Vector vector2)
            {
                return ((vector1._x * vector2._x) + (vector1._y * vector2._y));
            }

            public static double Multiply(Vector vector1, Vector vector2)
            {
                return ((vector1._x * vector2._x) + (vector1._y * vector2._y));
            }

            public static double Determinant(Vector vector1, Vector vector2)
            {
                return ((vector1._x * vector2._y) - (vector1._y * vector2._x));
            }

            public static explicit operator Size(Vector vector)
            {
                return new Size(Math.Abs(vector._x), Math.Abs(vector._y));
            }

            public static explicit operator Point(Vector vector)
            {
                return new Point(vector._x, vector._y);
            }
        }


    }

     <Canvas x:Name="Canvas1" Width="500" Height="500" Background="Yellow">
                <Path Stroke="Black" StrokeThickness="2">
                    <Path.Data>
                        <PathGeometry>
                            <PathGeometry.Figures>
                                <PathFigureCollection>
                                    <PathFigure x:Name="PathFigure1" StartPoint="50,140">
                                        <PathFigure.Segments>
                                            <PathSegmentCollection>
                                                <ArcSegment x:Name="ArcSegment1" Size="40,40"  IsLargeArc="False"
    SweepDirection="Clockwise"  Point="80,140" />
                                            </PathSegmentCollection>
                                        </PathFigure.Segments>
                                    </PathFigure>
                                </PathFigureCollection>
                            </PathGeometry.Figures>
                        </PathGeometry>
                    </Path.Data>
                </Path>

            </Canvas>

     

    Please note even if it cannot create a circle with the given size (radius of the circle) it'll be auto stretched and create a semicircle. You can see I added some logic to handle this scenario.

    I haven't tested it throughly. Please help to debug.

    Friday, July 25, 2008 4:52 AM
  • Allen, you are very generous for posting this code - so far it hasn't failed me, it finds the center everytime! Even if the arc's end point is moving.

    Friday, July 25, 2008 8:57 AM
  • I've broken the code a few times now, seems to be when Y values of p1 and p2 are VERY close together. The GetCircleCenter function returns a point with X, Y = NaN. My arc was Clockwise and LargeArc = true. If I can narrow it further I'll let you know.

    Friday, July 25, 2008 10:23 AM