1

円弧の中心、「固定された」終点、および半径を決定することによって円弧のもう一方の端を与える 2 番目の点の 3 つの点で表される最小の円弧を描くのに問題があります。コサインの法則を使って弧の長さを決定し、開始度に atan を使用しようとしましたが、弧の開始位置がずれています。

象限 2 にあるときにアークをアンカー ポイント (x1,y1) にロックすることができましたが、これは象限 2 にある場合にのみ機能します。

私が見ることができる解決策はすべて、2つの点の位置を相対的に決定するためのifステートメントがたくさんあるのですが、単純なものを見落としているかどうか興味があります. どんな助けでも大歓迎です。

SSCCE:

import javax.swing.JComponent;
import javax.swing.JFrame;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.*;
import java.awt.*;
import java.util.*;

class Canvas extends JComponent {
    float circleX, circleY, x1, y1, x2, y2, dx, dy, dx2, dy2, radius, radius2;
    Random random = new Random();

    public Canvas() {

        //Setup. 

        x1 = random.nextInt(250);
        y1 = random.nextInt(250);

        //Cant have x2 == circleX
        while (x1 == 150 || y1 == 150)
        {
            x1 = random.nextInt(250);
            y1 = random.nextInt(250);
        }

        circleX = 150; //circle center is always dead center.
        circleY = 150;


        //Radius between the 2 points must be equal.
        dx = Math.abs(circleX-x1);
        dy = Math.abs(circleY-y1);

        //c^2 = a^2 + b^2 to solve for the radius
        radius = (float) Math.sqrt((float)Math.pow(dx, 2) + (float)Math.pow(dy, 2));

        //2nd random point
        x2 = random.nextInt(250);
        y2 = random.nextInt(250);

        //I need to push it out to radius length, because the radius is equal for both points.
        dx2 = Math.abs(circleX-x2);
        dy2 = Math.abs(circleY-y2);
        radius2 = (float) Math.sqrt((float)Math.pow(dx2, 2) + (float)Math.pow(dy2, 2));

        dx2 *= radius/radius2;
        dy2 *= radius/radius2;

        y2 = circleY+dy2;
        x2 = circleX+dx2;
        //Radius now equal for both points.
    }

    public void paintComponent(Graphics g2) {
        Graphics2D g = (Graphics2D) g2;
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_BEVEL));

        Arc2D.Float centerPoint = new Arc2D.Float(150-2,150-2,4,4, 0, 360, Arc2D.OPEN);
        Arc2D.Float point1 = new Arc2D.Float(x1-2, y1-2, 4, 4, 0, 360, Arc2D.OPEN);
        Arc2D.Float point2 = new Arc2D.Float(x2-2, y2-2, 4, 4, 0, 360, Arc2D.OPEN);

        //3 points drawn in black
        g.setColor(Color.BLACK);
        g.draw(centerPoint);
        g.draw(point1);
        g.draw(point2);

        float start = 0;
        float distance;

        //Form a right triangle to find the length of the hypotenuse.
        distance = (float) Math.sqrt(Math.pow(Math.abs(x2-x1),2) + Math.pow(Math.abs(y2-y1), 2));

        //Law of cosines to determine the internal angle between the 2 points.
        distance = (float) (Math.acos(((radius*radius) + (radius*radius) - (distance*distance)) / (2*radius*radius)) * 180/Math.PI);

        float deltaY = circleY - y1;
        float deltaX = circleX - x1;

        float deltaY2 = circleY - y2;
        float deltaX2 = circleX - x2;

        float angleInDegrees = (float) ((float) Math.atan((float) (deltaY / deltaX)) * 180 / Math.PI);
        float angleInDegrees2 = (float) ((float) Math.atan((float) (deltaY2 / deltaX2)) * 180 / Math.PI);

        start = angleInDegrees;

        //Q2 works.
        if (x1 < circleX)
        {
            if (y1 < circleY)
            {
                start*=-1;
                start+=180;
            } else if (y2 > circleX) {
                start+=180;
                start+=distance;
            }
        }

        //System.out.println("Start: " + start);
        //Arc drawn in blue
        g.setColor(Color.BLUE);
        Arc2D.Float arc = new Arc2D.Float(circleX-radius,  //Center x 
                                          circleY-radius,  //Center y Rotates around this point.
                                          radius*2,
                                          radius*2,
                                          start, //start degree
                                          distance, //distance to travel
                                          Arc2D.OPEN); //Type of arc.
        g.draw(arc);
    }
}

public class Angle implements MouseListener {

    Canvas view;
    JFrame window;

    public Angle() {
        window = new JFrame();
        view = new Canvas();
        view.addMouseListener(this);
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setBounds(30, 30, 400, 400);
        window.getContentPane().add(view);
        window.setVisible(true);
    }

    public static void main(String[] a) {
        new Angle();
    }

    @Override
    public void mouseClicked(MouseEvent arg0) {
        window.getContentPane().remove(view);
        view = new Canvas();
        window.getContentPane().add(view);
        view.addMouseListener(this);
        view.revalidate();
        view.repaint();
    }

    @Override
    public void mouseEntered(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseExited(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mousePressed(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseReleased(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }
}
4

3 に答える 3

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

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

このように generateCurve メソッドを使用して、始点と終点の間に曲線を作成します。

generateCurve(pFrom, pTo, 100f, 7f, true, false);
于 2013-05-23T13:23:10.327 に答える
1

さて、これでテストと作業が完了しました。問題は、私がグラフィックスをあまり使用しないという事実に基づいていたので、座標系が後方にあることを思い出さなければならず、Arc2Dコンストラクターの Javadoc 記述がひどいという事実に基づいていました。

これらに加えて、(2 つのポイントを接続するための) ポイントの作成は、要件を考えると非常に非効率的であることがわかりました。実際には任意の 2 点を受け取り、それらの角度など計算する必要があると想定していましたが、Pastebin に入力した内容に基づいて、2 点を自由に定義できます。これは私たちに利益をもたらします。

とにかく、これは動作中のバージョンで、以前のゴブルデグックはありません。簡略化されたコードは簡略化されています。

import javax.swing.JComponent;
import java.awt.geom.*;
import java.awt.*;
import java.util.*;

public class Canvas extends JComponent {
    double circleX, circleY, x1, y1, x2, y2, dx, dy, dx2, dy2, radius, radius2;
    Random random = new Random();
    double distance;
    private static double theta1;
    private static double theta2;
    private static double theta;
    // private static double radius;
    private Point2D point1;
    private Point2D point2;
    private Point2D center;
    private static int direction;
    private static final int CW = -1;
    private static final int CCW = 1;

public Canvas() {
    /*
     * You want two random points on a circle, so let's start correctly,
     * by setting a random *radius*, and then two random *angles*.
     * 
     * This has the added benefit of giving us the angles without having to calculate them
     */

    radius = random.nextInt(175);   //your maximum radius is higher, but we only have 200 pixels in each cardinal direction
    theta1 = random.nextInt(360);   //angle to first point (absolute measurement)
    theta2 = random.nextInt(360);   //angle to second point

    //build the points
    center = new Point2D.Double(200, 200);  //your frame is actually 400 pixels on a side
    point1 = new Point2D.Double(radius * Math.cos(toRadians(theta1)) + center.getX(), center.getY() - radius * Math.sin(toRadians(theta1)));
    point2 = new Point2D.Double(radius * Math.cos(toRadians(theta2)) + center.getX(), center.getY() - radius * Math.sin(toRadians(theta2)));

    theta = Math.abs(theta1 - theta2) <= 180 ? Math.abs(theta1 - theta2) : 360 - (Math.abs(theta1 - theta2));

    if ((theta1 + theta) % 360 == theta2) {
        direction = CCW;
    } else {
        direction = CW;
    }

    System.out.println("theta1: " + theta1 + "; theta2: " + theta2 + "; theta: " + theta + "; direction: " + (direction == CCW ? "CCW" : "CW"));
    System.out.println("point1: (" + (point1.getX() - center.getX()) + ", " + (center.getY() - point1.getY()) + ")");
    System.out.println("point2: (" + (point2.getX() - center.getX()) + ", " + (center.getY() - point2.getY()) + ")");

    // Radius now equal for both points.
}

public double toRadians(double angle) {
    return angle * Math.PI / 180;
}

public double toDegrees(double angle) {
    return angle * 180 / Math.PI;
}

public void paintComponent(Graphics g2) {
    Graphics2D g = (Graphics2D) g2;
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    g.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_BUTT,
            BasicStroke.JOIN_BEVEL));

    //centerpoint should be based on the actual center point
    Arc2D.Double centerPoint = new Arc2D.Double(center.getX() - 2, center.getY() - 2, 4, 4, 0,
            360, Arc2D.OPEN);
    //likewise these points
    Arc2D.Double point11 = new Arc2D.Double(point1.getX() - 2, point1.getY() - 2, 4, 4, 0, 360,
            Arc2D.OPEN);
    Arc2D.Double point22 = new Arc2D.Double(point2.getX() - 2, point2.getY() - 2, 4, 4, 0, 360,
            Arc2D.OPEN);

    // 3 points drawn in black
    g.setColor(Color.BLACK);
    g.draw(centerPoint);
    g.draw(point11);
    g.draw(point22);

    // Arc drawn in blue
    g.setColor(Color.BLUE);
    g.draw(new Arc2D.Double(center.getX() - radius, center.getY() - radius, 2 * radius, 2 * radius, theta1, theta * direction, Arc2D.OPEN));
}

}

于 2013-05-23T15:09:49.287 に答える