Rich
Rich

Reputation: 4170

Finding Points on a Quadratic Bezier Curve Path

I am attempting to create a shape like the following:

Spiral

For the curves of the spiral, I am utilizing Quadratic Bezier Segments.

    PathGeometry pg1 = new PathGeometry();
    PathFigure pf1 = new PathFigure()
    {
        StartPoint = new Point(Convert.ToDouble(middle) + 500, Convert.ToDouble(middle) + 500)
    };
    PathSegmentCollection psc1= new PathSegmentCollection();
    QuadraticBezierSegment arcs1 = new QuadraticBezierSegment()
    {
        Point1 = new Point(100, 560),
        Point2 = new Point(pf.StartPoint.X - 300, pf.StartPoint.Y + 200)
    };
    psc1.Add(arcs1);
    pf1.Segments = psc1;
    pg1.Figures.Add(pf1);
    Path spiral1 = new Path()
    {
        Data = pg1,
        Stroke = Brushes.White,
        StrokeThickness = 1.5
    };
    MainScrn.Children.Add(spiral1);

Which outputs an appropriate curve for one of the Paths: animation path displayed

I'm sure I marked this up wrong, but here is where and how the variables above are associated with the Bezier Curve.

Quadratic Bezier Curves Points

Now what I want is the points along the curve.

Path with points on segment

And I can't receive that from the object. I'm trying to collect these points so that I can animate movement of objects along the path of the Bezier Curve and have them stop at different points of the curve. How can I achieve this?

Upvotes: 4

Views: 2510

Answers (2)

Rich
Rich

Reputation: 4170

Here's the complete answer. I calculated the total length of the path with 30 points spread across the path itself (thanks for @samgak for that). I found the average spaced pixel length by dividing the length by the amount of segments, then comparing it to an array of incremented calculations(avg vs next point) between each point.

Here's the output.

proper curve

Here's the code.

            PathGeometry pg1 = new PathGeometry();
            PathFigure pf1 = new PathFigure()
            {
                StartPoint = new Point(Convert.ToDouble(middle) + 500, Convert.ToDouble(middle) + 500)
            };
            PathSegmentCollection psc1= new PathSegmentCollection();
            QuadraticBezierSegment arcs1 = new QuadraticBezierSegment()
            {
                //Point1 = new Point(100, 560),
                Point1 = new Point(150, 480),
                Point2 = new Point(pf.StartPoint.X - 300, pf.StartPoint.Y + 200)
            };
            psc1.Add(arcs1);
            pf1.Segments = psc1;
            pg1.Figures.Add(pf1);
            Path spiral1 = new Path()
            {
                Data = pg1,
                Stroke = Brushes.White,
                StrokeThickness = 1.5
            };
            MainScrn.Children.Add(spiral1);
            Rectangle[] pnt = new Rectangle[30];
            float growth = (float)1 / (float)30;
            float loc = 0;
            MessageBox.Show(growth.ToString());
            double lenOfpath = 0;
            Point pntA = new Point(0, 0);
            int segments = 8; segments++;
            float avgspace = 0;
            for (int length = 0; length < pnt.Count(); length++)
            {
                pnt[length] = new Rectangle();
                pnt[length].Fill = Brushes.Red;
                pnt[length].Width = 10;
                pnt[length].Height = 10;
                double t = loc;
                double left = (1 - t) * (1 - t) * pf.StartPoint.X + 2 * (1 - t) * t * arcs1.Point1.X + t * t * arcs1.Point2.X;
                double top = (1 - t) * (1 - t) * pf.StartPoint.Y + 2 * (1 - t) * t * arcs1.Point1.Y + t * t * arcs1.Point2.Y;
                MainScrn.Children.Add(pnt[length]);
                Canvas.SetLeft(pnt[length], left);
                Canvas.SetTop(pnt[length], top);
                loc = loc + growth;
                if (length > 0)
                {
                    double x10 = Canvas.GetLeft(pnt[length - 1]);
                    double x20 = Canvas.GetLeft(pnt[length]);
                    double y10 = Canvas.GetTop(pnt[length - 1]);
                    double y20 = Canvas.GetTop(pnt[length]);
                    lenOfpath = lenOfpath + Math.Sqrt(Math.Pow(x20 - x10, 2) + Math.Pow(y20 - y10, 2));
                    avgspace = ((float)lenOfpath / (float)segments);
                }
            }
            for (int length = 1; length < pnt.Count(); length++)
            {

                double total = 0;
                double[] smallestpos = new double[pnt.Count()-1];
                for (int digger = length + 1; digger < pnt.Count(); digger++)
                {
                    double x11 = Canvas.GetLeft(pnt[length]);
                    double x22 = Canvas.GetLeft(pnt[digger]);
                    double y11 = Canvas.GetTop(pnt[length]);
                    double y22 = Canvas.GetTop(pnt[digger]);
                    smallestpos[digger-1] = Math.Sqrt(Math.Pow(x22 - x11, 2) + Math.Pow(y22 - y11, 2));
                }
                int takeposition = FindClosest(avgspace, smallestpos);
                double min = smallestpos[takeposition];
                while (length < (takeposition+1))
                {
                    pnt[length].Visibility = System.Windows.Visibility.Hidden;
                    length++;
                }
            }    
        }
        public static int FindClosest(float given_number, double[] listofflts)
        {
            // Start min_delta with first element because it's safer
            int min_index = 0;
            double min_delta = listofflts[0] - given_number;
            // Take absolute value of the min_delta
            if (min_delta < 0)
            {
                min_delta = min_delta * (-1);
            }

            // Iterate through the list of integers to find the minimal delta
            // Skip first element because min_delta is set with first element's
            for (int index = 1; index < listofflts.Count(); index++)
            {
                float cur_delta = (float)listofflts[index] - (float)given_number;
                // Take absolute value of the current delta
                if (cur_delta < 0)
                {
                    cur_delta = cur_delta * (-1);
                }

                // Update the minimum delta and save the index
                if (cur_delta < min_delta)
                {
                    min_delta = cur_delta;
                    min_index = index;
                }
            }
            return min_index;
        }

With Octagons:

completed.

Once all sides are completed:

enter image description here

Upvotes: 3

samgak
samgak

Reputation: 24427

Suppose you want to compute a point one fifth (or any amount) of the way along the cubic Bezier curve from pf1.StartPoint (P1) to arcs1.Point2 (P3) with the control point arcs1.Point1 (P2).

You do it like this:

  • Compute a point one fifth of the way along the straight line from P1 to P2, and call that A
  • Compute a point one fifth of the way along the straight line from P2 to P3, and call that B
  • Compute a point one fifth of the way along the straight line from A to B, and that's your answer.

You can reduce this to a polynomial formula that you can just plug values into and get your answer out of, but this is perhaps more geometrically intuitive and you can probably implement it easily with built in functions in the Point class.

Upvotes: 3

Related Questions