Sunday, May 30, 2004

 

兩點間畫有箭頭的線

我project裡面的片段程式
不知道有沒有更好的作法~
//畫箭頭
double x1 = fromPoint.getX();
double y1 = fromPoint.getY();
double x2 = toPoint.getX();
double y2 = toPoint.getY();
double distance = fromPoint.distance(toPoint);

double cita = Math.toDegrees(Math.asin(Math.abs(y1 - y2) / distance));
double subCitaSin = ARROW_LENGTH * Math.sin(Math.toRadians(cita - 30));
double subCitaCos = ARROW_LENGTH * Math.cos(Math.toRadians(cita - 30));
double addCitaSin = ARROW_LENGTH * Math.sin(Math.toRadians(cita + 30));
double addCitaCos = ARROW_LENGTH * Math.cos(Math.toRadians(cita + 30));
double up1, down1, up2, down2;
if(x1 > x2){
   //箭頭在第二象限
   if(y1 > y2){
      up1 = x2 + subCitaCos;
      down1 = y2 + subCitaSin;
      up2 = x2 + addCitaCos;
      down2 = y2 + addCitaSin;
   }
   //箭頭在第三象限
   else{
      up1 = x2 + subCitaCos;
      down1 = y2 - subCitaSin;
      up2 = x2 + addCitaCos;
      down2 = y2 - addCitaSin;
   }
}
else{
   //箭頭在第一象限
   if(y1 > y2){
      up1 = x2 - subCitaCos;
      down1 = y2 + subCitaSin;
      up2 = x2 - addCitaCos;
      down2 = y2 + addCitaSin;
   }
   //箭頭在第四象限
   else{
      up1 = x2 - subCitaCos;
      down1 = y2 - subCitaSin;
      up2 = x2 - addCitaCos;
      down2 = y2 - addCitaSin;
   }
}
g2d.draw(new Line2D.Double(new Point2D.Double(up1, down1), toPoint));
g2d.draw(new Line2D.Double(new Point2D.Double(up2, down2), toPoint));

由 swanky 發表於 May 30, 2004 11:56 PM
迴響

剛好路過, 覺得您的做法太繁瑣...
多多利用平移,旋轉將會讓您輕鬆許多.
野人獻曝,請多包含.

Here is my example:


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

/**
* @author T55555
* @version 1.0.0 2004-06-30
*/
public class DrawArrow extends JComponent {
public static void main(String[] args) {
JFrame f = new JFrame("DrawArrow ----- by T55555");
f.getContentPane().add(new DrawArrow());
f.setSize(275, 150);
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}

public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
double p = 10.0;
double q = 100.0;
double m = (p + q) / 2;

ciyawasay_drawArrow(g2d, new Point2D.Double(p, p), new Point2D.Double(q, q));
ciyawasay_drawArrow(g2d, new Point2D.Double(q, q), new Point2D.Double(p, p));
ciyawasay_drawArrow(g2d, new Point2D.Double(p, q), new Point2D.Double(q, p));
ciyawasay_drawArrow(g2d, new Point2D.Double(q, p), new Point2D.Double(p, q));
ciyawasay_drawArrow(g2d, new Point2D.Double(m, p), new Point2D.Double(m, q));
ciyawasay_drawArrow(g2d, new Point2D.Double(m, q), new Point2D.Double(m, p));
ciyawasay_drawArrow(g2d, new Point2D.Double(p, m), new Point2D.Double(q, m));
ciyawasay_drawArrow(g2d, new Point2D.Double(q, m), new Point2D.Double(p, m));

g2d.translate(150.0, 0.0);
t55555_drawArrow(g2d, new Point2D.Double(p, p), new Point2D.Double(q, q));
t55555_drawArrow(g2d, new Point2D.Double(q, q), new Point2D.Double(p, p));
t55555_drawArrow(g2d, new Point2D.Double(p, q), new Point2D.Double(q, p));
t55555_drawArrow(g2d, new Point2D.Double(q, p), new Point2D.Double(p, q));
t55555_drawArrow(g2d, new Point2D.Double(m, p), new Point2D.Double(m, q));
t55555_drawArrow(g2d, new Point2D.Double(m, q), new Point2D.Double(m, p));
t55555_drawArrow(g2d, new Point2D.Double(p, m), new Point2D.Double(q, m));
t55555_drawArrow(g2d, new Point2D.Double(q, m), new Point2D.Double(p, m));
g2d.translate(-150.0, 0.0);
}

public void ciyawasay_drawArrow(Graphics2D g2d, Point2D fromPoint, Point2D toPoint) {
double ARROW_LENGTH = 10.0;
double x1 = fromPoint.getX();
double y1 = fromPoint.getY();
double x2 = toPoint.getX();
double y2 = toPoint.getY();
double distance = fromPoint.distance(toPoint);

double cita = Math.toDegrees(Math.asin(Math.abs(y1 - y2) / distance));
double subCitaSin = ARROW_LENGTH * Math.sin(Math.toRadians(cita - 30));
double subCitaCos = ARROW_LENGTH * Math.cos(Math.toRadians(cita - 30));
double addCitaSin = ARROW_LENGTH * Math.sin(Math.toRadians(cita + 30));
double addCitaCos = ARROW_LENGTH * Math.cos(Math.toRadians(cita + 30));
double up1, down1, up2, down2;
if(x1 > x2){
if(y1 > y2){
up1 = x2 + subCitaCos;
down1 = y2 + subCitaSin;
up2 = x2 + addCitaCos;
down2 = y2 + addCitaSin;
}
else{
up1 = x2 + subCitaCos;
down1 = y2 - subCitaSin;
up2 = x2 + addCitaCos;
down2 = y2 - addCitaSin;
}
}
else{
if(y1 > y2){
up1 = x2 - subCitaCos;
down1 = y2 + subCitaSin;
up2 = x2 - addCitaCos;
down2 = y2 + addCitaSin;
}
else{
up1 = x2 - subCitaCos;
down1 = y2 - subCitaSin;
up2 = x2 - addCitaCos;
down2 = y2 - addCitaSin;
}
}
g2d.draw(new Line2D.Double(new Point2D.Double(up1, down1), toPoint));
g2d.draw(new Line2D.Double(new Point2D.Double(up2, down2), toPoint));
g2d.draw(new Line2D.Double(fromPoint, toPoint));
}

// Note: pre-condition: toPoint != fromPoint
// ( if the 2 points are the same ===> distance == 0 and angle is indeterminate )
public void t55555_drawArrow(Graphics2D g2d, Point2D fromPoint, Point2D toPoint) {
double ARROW_LENGTH = 10.0;
double ARROW_ANGLE = Math.toRadians(30);
double ax = ARROW_LENGTH * Math.cos(ARROW_ANGLE);
double ay = ARROW_LENGTH * Math.sin(ARROW_ANGLE);

AffineTransform originTransform = g2d.getTransform();
g2d.translate(toPoint.getX(), toPoint.getY());
g2d.rotate((fromPoint.getY() > toPoint.getY() ? -1 : 1) * Math.acos((toPoint.getX() - fromPoint.getX()) / fromPoint.distance(toPoint)));
g2d.draw(new Line2D.Double(0, 0, -ax, -ay));
g2d.draw(new Line2D.Double(0, 0, -ax, ay));
g2d.setTransform(originTransform);
g2d.draw(new Line2D.Double(fromPoint, toPoint));
}

}

The main idea is translate the origin to the toPoint,
and rotate to make the axe X parallel to the vector (fromPoint, toPoint).

For the rotate angle, the "classic" way is try to using atan like:

if (x1 > x2) {
g2d.rotate(Math.PI + Math.atan((y1 - y2) / (x1 - x2)));
} else if (x1 y2) {
g2d.rotate( -Math.PI / 2 );
} else if (y1 g2d.rotate( Math.PI / 2);
} else {
return;
}
As you can see, there are many checking to do.
The example I show you do not using the atan.
I am using vector dot product formula.
A . B = |A| |B| cos(theda)
with A = vector (fromPoint, toPoint),
B = (1, 0) (axe X, unit vector)

In case if you want fill the arrow, with
3rd point that can define by the distance from the toPoint, the method I used will even simpler(compare to yours).

Posted by: T55555 發表於 2004-06-30 10:27 PM

Oops, it seems the atan checking have trouble to layout...
(my post code is not the same as showing on the page, bug on the post text re-formating ?)

Anyway, I do not know how to layout to post it correctly. And this is real simple algebra formula.


Posted by: T55555 發表於 2004-06-30 10:49 PM

太感謝了~
您的方法真是比我的好很多~ ^^"

Posted by: swanky 發表於 2004-06-30 10:59 PM

Comments: Post a Comment



<< Home