有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何在2D中的2个点之间创建一条曲线,并获得使该曲线每隔d距离的返回点?

我的数学不好

我有两个点,A(x1, y1)B(x2, y2)在2D中

我需要创建一个从点A到在R(半径)处弯曲的B的虚拟路径,然后返回一个描述这个弯曲路径的点数组,可能不是每个点之间都有D(距离)

在Java中,我需要这样一种方法:

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

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

    return pOutPut;
}

如何做到这一点


共 (2) 个答案

  1. # 1 楼答案

    这项工作:

    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;
    }
    

    下面是我这样称呼它时得到的:generateCurve(new Point2D.Double(10,10), new Point2D.Double(400, 400), 300, 15)

    enter image description here

  2. # 2 楼答案

    我没有放弃,我已经为此工作了几个小时。结果如下:

    我创建了一个方法,您可以指定是否需要点之间最长弧中的最短弧

    下面是对它的一些调用,以及生成的输出:

    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);

    正如你所看到的,它就像一个魅力。代码如下:

    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;
        }
    }
    

    享受吧
    PS:我创建了两个数学问题来解决您的问题: