Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a curve between 2 points in 2D and get back Points that makes that curve every d distance?

I'm not good in math.

I have 2 points, A(x1, y1) and B(x2, y2) in 2D.

I need to create a virtual path from point A to B curved at R(radius), and then return an array of points which are describing this curved path, not all maybe every D(distance) from each other.

In Java I need a method like this:

private ArrayList<PointF> generateCurve(PointF pFrom,PointF pTo,float pRadius,float pMinDistance){

    ArrayList<PointF> pOutPut = new ArrayList<PointF>();
    // ...generate result to pOutPut 

    return pOutPut;
}

How to do this ?

like image 787
Paweł Avatar asked Jul 10 '11 15:07

Paweł


People also ask

How do you find the equation of a curve when given two points?

By taking the coordinates of the two points as (x1,y1) and (x2,y2) and substituting them into the equation y=mx+c, we will get the values of the parameters m and c, and hence the equation for the curve. Similarly, we can find out the equation for any other curve by substituting the coordinates.

How do you find the distance between 2 points?

Distance between two points is the length of the line segment that connects the two points in a plane. The formula to find the distance between the two points is usually given by d=√((x2 – x1)² + (y2 – y1)²). This formula is used to find the distance between any two points on a coordinate plane or x-y plane.

How do you find the arc length of a curve between two points?

If the arc is just a straight line between two points of coordinates (x1,y1), (x2,y2), its length can be found by the Pythagorean theorem: L = √ (∆x)2 + (∆y)2 , where ∆x = x2 − x1 and ∆y = y2 − y1.


2 Answers

This works:

private static double GetAngle(Point2D x, Point2D o, double R){
    double cosa = (x.getX()-o.getX())/R;
    double sina = (x.getY()-o.getY())/R;

    double angle = Math.acos(cosa);

    return Math.sin(angle)*sina >= 0 ? angle : 2*Math.PI - angle;
}

private static ArrayList<Point2D> generateCurve(Point2D pFrom,Point2D pTo,float pRadius,float pMinDistance){

    ArrayList<Point2D> pOutPut = new ArrayList<Point2D>();

    double dist = pFrom.distance(pTo);
    double h = Math.sqrt(pRadius * pRadius - (dist * dist / 4.0));
    double angleStep = pMinDistance/pRadius;

    if(2*pRadius <= dist)
        throw new Error("Radius is too small");

    //find center
    double x1 = pFrom.getX(), x2 = pFrom.getY();
    double y1 = pTo.getX(), y2 = pTo.getY();
    double m1 = (x1+y1)/2, m2 = (x2+y2)/2;
    double u1 = - (y2-x2)/dist, u2 = (y1-x1)/dist;
    double o1 = m1 + h * u1, o2 = m2 + h * u2;
    Point2D o = new Point2D.Double(o1, o2);

    double startAngle = GetAngle(pFrom, o, pRadius);
    double endAngle = GetAngle(pTo, o, pRadius);

    if(endAngle < startAngle)
        endAngle += 2 * Math.PI;        

    for(double a = startAngle; a < endAngle; a+=angleStep){
        pOutPut.add(new Point2D.Double(o1+pRadius*Math.cos(a), o2+pRadius*Math.sin(a)));
    }

    pOutPut.add(pTo);

    return pOutPut;
}

Here is what I get when I call it like this: generateCurve(new Point2D.Double(10,10), new Point2D.Double(400, 400), 300, 15)

enter image description here

like image 44
Petar Ivanov Avatar answered Sep 28 '22 07:09

Petar Ivanov


I didn't gave up and I've been working on it for a few more hours. And here is the result:

I created a method where you can specify if you want the shortest of the longest arc between the points.

Here are some calls to it, with the produced output:

generateCurve(pFrom, pTo, 100f, 7f, false, false);

generateCurve(pFrom, pTo, 100f, 7f, false, false);


generateCurve(pFrom, pTo, 100f, 7f, true, false);

generateCurve(pFrom, pTo, 100f, 7f, true, false);


generateCurve(pFrom, pTo, 100f, 7f, false, true);

generateCurve(pFrom, pTo, 100f, 7f, false, true);


generateCurve(pFrom, pTo, 100f, 7f, true, true);

generateCurve(pFrom, pTo, 100f, 7f, true, true);

As you can see, it is working like a charm. Here is the code:

package curve;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;

/**
 *
 * @author martijn
 */
public class Main
{

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException
    {
        PointF pFrom = new PointF(-10f, 30.0f);
        PointF pTo = new PointF(-100f, 0.0f);
        List<PointF> points = generateCurve(pFrom, pTo, 100f, 7f, true, true);

        System.out.println(points);

        // Calculate the bounds of the curve
        Rectangle2D.Float bounds = new Rectangle2D.Float(points.get(0).x, points.get(0).y, 0, 0);
        for (int i = 1; i < points.size(); ++i) {
            bounds.add(points.get(i).x, points.get(i).y);
        }
        bounds.add(pFrom.x, pFrom.y);
        bounds.add(pTo.x, pTo.y);

        BufferedImage img = new BufferedImage((int) (bounds.width - bounds.x + 50), (int) (bounds.height - bounds.y + 50), BufferedImage.TYPE_4BYTE_ABGR_PRE);
        Graphics2D g = img.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g.translate(25.0f - bounds.getX(), 25.0f - bounds.getY());
        g.setStroke(new BasicStroke(1.0f));


        g.setColor(Color.DARK_GRAY);
        g.drawLine(-1000, 0, 1000, 0);
        g.drawLine(0, -1000, 0, 1000);

        g.setColor(Color.RED);
        for (int i = 0; i < points.size(); ++i) {
            if (i > 0) {
                Line2D.Float f = new Line2D.Float(points.get(i - 1).x, points.get(i - 1).y, points.get(i).x, points.get(i).y);
                System.out.println("Dist : " + f.getP1().distance(f.getP2()));
//                g.draw(f);
            }

            g.fill(new Ellipse2D.Float(points.get(i).x - 0.8f, points.get(i).y - 0.8f, 1.6f, 1.6f));

        }
        g.setColor(Color.BLUE);
        g.fill(new Ellipse2D.Float(pFrom.x - 1, pFrom.y - 1, 3, 3));
        g.fill(new Ellipse2D.Float(pTo.x - 1, pTo.y - 1, 3, 3));

        g.dispose();

        ImageIO.write(img, "PNG", new File("result.png"));
    }

    static class PointF
    {

        public float x, y;

        public PointF(float x, float y)
        {
            this.x = x;
            this.y = y;
        }

        @Override
        public String toString()
        {
            return "(" + x + "," + y + ")";
        }
    }

    private static List<PointF> generateCurve(PointF pFrom, PointF pTo, float pRadius, float pMinDistance, boolean shortest, boolean side)
    {

        List<PointF> pOutPut = new ArrayList<PointF>();

        // Calculate the middle of the two given points.
        PointF mPoint = new PointF(pFrom.x + pTo.x, pFrom.y + pTo.y);
        mPoint.x /= 2.0f;
        mPoint.y /= 2.0f;
        System.out.println("Middle Between From and To = " + mPoint);


        // Calculate the distance between the two points
        float xDiff = pTo.x - pFrom.x;
        float yDiff = pTo.y - pFrom.y;
        float distance = (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);
        System.out.println("Distance between From and To = " + distance);

        if (pRadius * 2.0f < distance) {
            throw new IllegalArgumentException("The radius is too small! The given points wont fall on the circle.");
        }

        // Calculate the middle of the expected curve.
        float factor = (float) Math.sqrt((pRadius * pRadius) / ((pTo.x - pFrom.x) * (pTo.x - pFrom.x) + (pTo.y - pFrom.y) * (pTo.y - pFrom.y)) - 0.25f);
        PointF circleMiddlePoint = new PointF(0, 0);
        if (side) {
            circleMiddlePoint.x = 0.5f * (pFrom.x + pTo.x) + factor * (pTo.y - pFrom.y);
            circleMiddlePoint.y = 0.5f * (pFrom.y + pTo.y) + factor * (pFrom.x - pTo.x);
        } else {
            circleMiddlePoint.x = 0.5f * (pFrom.x + pTo.x) - factor * (pTo.y - pFrom.y);
            circleMiddlePoint.y = 0.5f * (pFrom.y + pTo.y) - factor * (pFrom.x - pTo.x);
        }
        System.out.println("Middle = " + circleMiddlePoint);

        // Calculate the two reference angles
        float angle1 = (float) Math.atan2(pFrom.y - circleMiddlePoint.y, pFrom.x - circleMiddlePoint.x);
        float angle2 = (float) Math.atan2(pTo.y - circleMiddlePoint.y, pTo.x - circleMiddlePoint.x);

        // Calculate the step.
        float step = pMinDistance / pRadius;
        System.out.println("Step = " + step);

        // Swap them if needed
        if (angle1 > angle2) {
            float temp = angle1;
            angle1 = angle2;
            angle2 = temp;

        }
        boolean flipped = false;
        if (!shortest) {
            if (angle2 - angle1 < Math.PI) {
                float temp = angle1;
                angle1 = angle2;
                angle2 = temp;
                angle2 += Math.PI * 2.0f;
                flipped = true;
            }
        }
        for (float f = angle1; f < angle2; f += step) {
            PointF p = new PointF((float) Math.cos(f) * pRadius + circleMiddlePoint.x, (float) Math.sin(f) * pRadius + circleMiddlePoint.y);
            pOutPut.add(p);
        }
        if (flipped ^ side) {
            pOutPut.add(pFrom);
        } else {
            pOutPut.add(pTo);
        }

        return pOutPut;
    }
}

Enjoy!
PS: I created two questions on Mathematics to solve your question:

  • Analytic Geometry: Point coordinates, same distance from two points.
  • Trigonometry: Solve (1−cosα)2+sin2α=d2 for α
like image 163
Martijn Courteaux Avatar answered Sep 28 '22 07:09

Martijn Courteaux