2013-05-21 2 views
0

3 점으로 표시된 가장 작은 호를 그리는 데 문제가 있습니다. 호 중심, "정박 된"종점 및 반경을 결정하여 호의 다른 쪽 끝을 제공하는 두 번째 점 . 나는 코사인의 법칙을 사용하여 호의 길이를 결정하고 시작 각도로 atan을 사용해 보았지만 호의 시작 위치는 꺼져 있습니다. 자바 그리기 아크 2 점 사이

가 나는 쿼드런트 2 때 (X1, Y1) 앵커 포인트에 잠 호를 얻을 수 있었다, 그러나 그것은 단지 그것을 사분면에있을 때 2.

솔루션 나는 모든이를 볼 수 있습니다 작동합니다 if-statements의 뭉치는 서로 상대적인 2 점의 위치를 ​​결정하지만, 내가 간단한 것을 간과한다면 궁금합니다. 어떤 도움이라도 대단히 감사하겠습니다. 여기에서이 같은 generateCurve 방법로부터 포인트 간의 곡선을 갖도록

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 

    } 
} 
+0

; 임의의 중심점이 주어 졌을 때 원의 원주에있는 두 점에 대해 호 길이를 묻는 것입니까? – cabbagery

+0

No.나는 원의 중심과 원 위에 놓인 2 점을 주어서 2 점 사이에서 가장 작은 호를 그리는 방법을 묻습니다. 나는 원점 그리기를 시작할 곳과 아크의 방향 (시계 방향/반 시계 방향)을 바꿔야 하는지를 정말로 찾고있다. –

+1

나는 아직도 그것을 연구 중이다. 그것은 처음에는 눈에 띄게 단순 해 보이지만, 더 심도있게 살펴보면 상당히 복잡해 보이지만 궁극적으로는 상당히 단순하다는 느낌을 떨칠 수 없다. 어쨌든, 나는 까다로운 일이 될 수있는 특정 시나리오를 발견했습니다. 일반화 할 수있는 방법으로 문제를 풀 수 있다면 아직 해결할 수있는 대답을 찾을 수 있습니다. – cabbagery

답변

1

아마도이 도움이 될 것입니다. 클릭하고 드래그하여 테스트하여 임의의 숫자가 아닌 2 개의 포인트를 설정합니다. 지금까지 게시 한 내용과 다른 솔루션보다 훨씬 간단합니다.

주 :

  • Math.atan2()이 같은 문제의 친구입니다.
  • 거의 도움이되지 않는 함수를 사용하면 코드를 쉽게 추론 할 수 있습니다.
  • 독립 변수 값만 인스턴스 변수를 사용하고 로컬 변수의 종속 값을 계산하는 것이 가장 좋습니다.
  • 내 코드는 주 스레드에서 Swing 함수를 호출하는 것과 같은 일부 Swing 사용 문제를 수정합니다.

코드는 다음과 같습니다 : 나는 당신의 질문을 이해하지

import java.awt.*; 
import java.awt.event.*; 
import java.awt.geom.*; 
import javax.swing.*; 
import javax.swing.event.MouseInputAdapter; 

class TestCanvas extends JComponent { 

    float x0 = 150f, y0 = 150f; // Arc center. Subscript 0 used for center throughout. 
    float xa = 200f, ya = 150f; // Arc anchor point. Subscript a for anchor. 
    float xd = 150f, yd = 50f; // Point determining arc angle. Subscript d for determiner. 

    // Return the distance from any point to the arc center. 
    float dist0(float x, float y) { 
     return (float)Math.sqrt(sqr(x - x0) + sqr(y - y0)); 
    } 

    // Return polar angle of any point relative to arc center. 
    float angle0(float x, float y) { 
     return (float)Math.toDegrees(Math.atan2(y0 - y, x - x0)); 
    } 

    @Override 
    protected void paintComponent(Graphics g0) { 
     Graphics2D g = (Graphics2D) g0; 

     // Can always draw the center point. 
     dot(g, x0, y0); 

     // Get radii of anchor and det point. 
     float ra = dist0(xa, ya); 
     float rd = dist0(xd, yd); 

     // If either is zero there's nothing else to draw. 
     if (ra == 0 || rd == 0) { return; } 

     // Get the angles from center to points. 
     float aa = angle0(xa, ya); 
     float ad = angle0(xd, yd); // (xb, yb) would work fine, too. 

     // Draw the arc and other dots. 
     g.draw(new Arc2D.Float(x0 - ra, y0 - ra, // box upper left 
       2 * ra, 2 * ra,     // box width and height 
       aa, angleDiff(aa, ad),   // angle start, extent 
       Arc2D.OPEN)); 
     dot(g, xa, ya); 

     // Use similar triangles to get the second dot location. 
     float xb = x0 + (xd - x0) * ra/rd; 
     float yb = y0 + (yd - y0) * ra/rd; 
     dot(g, xb, yb); 
    } 

    // Some helper functions. 

    // Draw a small dot with the current color. 
    static void dot(Graphics2D g, float x, float y) { 
     final int rad = 2; 
     g.fill(new Ellipse2D.Float(x - rad, y - rad, 2 * rad, 2 * rad)); 
    } 

    // Return the square of a float. 
    static float sqr(float x) { return x * x; } 

    // Find the angular difference between a and b, -180 <= diff < 180. 
    static float angleDiff(float a, float b) { 
     float d = b - a; 
     while (d >= 180f) { d -= 360f; } 
     while (d < -180f) { d += 360f; } 
     return d; 
    } 

    // Construct a test canvas with mouse handling. 
    TestCanvas() { 
     addMouseListener(mouseListener); 
     addMouseMotionListener(mouseListener); 
    } 

    // Listener changes arc parameters with click and drag. 
    MouseInputAdapter mouseListener = new MouseInputAdapter() { 
     boolean mouseDown = false; // Is left mouse button down? 

     @Override 
     public void mousePressed(MouseEvent e) { 
      if (e.getButton() == MouseEvent.BUTTON1) { 
       mouseDown = true; 
       xa = xd = e.getX(); 
       ya = yd = e.getY(); 
       repaint(); 
      } 
     } 

     @Override 
     public void mouseReleased(MouseEvent e) { 
      if (e.getButton() == MouseEvent.BUTTON1) { 
       mouseDown = false; 
      } 
     } 

     @Override 
     public void mouseDragged(MouseEvent e) { 
      if (mouseDown) { 
       xd = e.getX(); 
       yd = e.getY(); 
       repaint(); 
      } 
     } 
    }; 
} 

public class Test extends JFrame { 

    public Test() { 
     setSize(400, 400); 
     setLocationRelativeTo(null); 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     getContentPane().add(new TestCanvas()); 
    } 

    public static void main(String[] args) { 
     // Swing code must run in the UI thread, so 
     // must invoke setVisible rather than just calling it. 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       new Test().setVisible(true); 
      } 
     }); 
    } 
} 
+0

이것은 정확히 내가 찾고있는 것입니다. 내가 취한 것은 arcTan 계산이 올바르지 않은 것입니다. 이제 내 프로그램이 훌륭하게 작동합니다. 고맙습니다. –

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(pFrom, pTo, 100f, 7f, true, false); 
+0

거의. 내가 그것을 번역하려고 할 때 나는 급상승 한 결과를 얻고있다. 그것은 나에게 2 점을 준다 .- 원, 그러나 그 점에 의해 정의 된 원은 아니다. (나는 그것을 SSCCE에 맞추기 위해 조금 변경했다.) 조금 더 조금 더 할 것이다. –

1

좋아 :

SSCCE

테스트하고 일하고 있습니다. 문제는 내가 그래픽을 많이 사용하지 않는다는 사실에 기반을 두었 기 때문에 좌표계가 뒤로 가고 Arc2D 생성자에 대한 Javadoc 설명이 잔인하다는 사실을 상기시켜야합니다.

이외에도 요구 사항을 고려할 때 포인트 생성 (두 지점 연결)이 매우 비효율적이라는 사실을 발견했습니다. 나는 실제로 당신이 2 개의 임의의 점을 받아야한다고 가정 했었고 다음에 등의 각도를 계산했지만, Pastebin을 넣은 것에 따라 우리는 두 점을 정의 할 수 있습니다. 이것은 우리에게 도움이됩니다.

어쨌든, 이전 버전과의 호환성이없는 작동 버전이 있습니다. 단순화 된 코드가 간단 해집니다 :

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

}

+0

번역이 끝났지 만 제대로 작동하지 않습니다. http://pastebin.com/XXXGWa12 –

+0

적어도 적절한 모양의 호를 그렸습니까? – cabbagery

+0

아니요. 모든 것이 꺼져 있고, 때로는 호가 화면에서 그려지는 경우와 거꾸로 된 경우가 있습니다. 왜 센터가 꺼져 있는지 모르겠다. 나는 이것을 알아 내려고 노력하고있다. 중심은 (150,150)으로 초기화 된 상수입니다. –