2013-05-21 151 views
0

我無法繪製由3個點描述的最小弧:弧中心,一個「錨定」終點,以及第二個點通過確定半徑來給出弧的另一端。我使用餘弦定律來確定弧的長度,並嘗試使用atan作爲起始角度,但弧的起始位置已關閉。2點之間的Java繪製弧

我設法電弧鎖定在定位點(X1,Y1),當它在第二象限,但只會工作,當它在象限2

解決方案,我可以看到所有有一堆if語句來確定2點相對於彼此的位置,但我很好奇,如果我忽略了簡單的事情。任何幫助將不勝感激。

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 

    } 
} 
+0

我不明白你的問題;你問問圓弧長度是圓的圓周上的兩個點,給定一個任意的中心點? – cabbagery

+0

號我問如何在2個點之間繪製最小的弧,給定一個圓的中心和2個點在圓上。我真的在尋找起點 - 從哪裏開始繪製弧線,以及是否應該切換弧線的方向(順時針/逆時針)。 –

+1

我仍然在努力 - 乍一看看起來非常簡單,但在進一步的評論中顯得非常複雜,但我無法改變最終實際上相當簡單的感覺。無論如何,我已經確定了一些會讓事情變得棘手的場景。如果我能以可普遍的方式解決它們,我可能還沒有一個可行的答案。 – cabbagery

回答

1

也許這會有所幫助。它通過點擊和拖動測試來設置兩點而不是隨機數。這遠遠超過了你目前嘗試使用的其他解決方案。

注:

  • 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方法像這樣具有和點之間的曲線..

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

差不多。當我嘗試翻譯它時,我收到了令人不快的結果。它在-a-圈上給我2點,但不是由點確定的圈。 (爲了適應SSCCE我做了一些改動)我會稍微多玩一點。 –

1

好了,在這裏是,測試和工作。這些問題是基於我不使用圖形的事實,所以我必須提醒自己,座標系是落後的,事實上構造函數的Javadoc描述是非常殘酷的。

除了這些,我發現你的點創建(對於要連接的兩個點)在給定要求時效率非常低。我假設你實際上必須得到兩個任意點,然後然後計算它們的角度等,但根據你對Pastebin的設置,我們可以定義這兩點,但我們可以。這對我們有利。

無論如何,這裏是一個工作版本,沒有從之前的gobbledegook。簡化的代碼被簡化:

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)。 –