2

I am working on creating graphs with vertices and edges. The graph is directed, so the edges are represented as arrows. My problem is getting the correct coordinates for the arrows.

A Vertex has a Coordinate (see class below), while an Edge goes from a Vertex to another Vertex. The challenge is that a vertex is drawn with a fixed radius (see picture below). I'm having problems getting the arrow pointing to the correct place on the circles circumference. It seems like with the code I currently have, the arrow points to the top-left corner, not the closest point.

I have the following method for drawing the arrows:

public static void drawArrow(Graphics g, Color color, int size, 
        Coordinate from, Coordinate to, Coordinate offset) {
    Graphics2D g2 = (Graphics2D) g.create();

    g2.setColor(color);

    double dx = to.x - from.x, dy = to.y - from.y;
    double angle = Math.atan2(dy, dx);
    int len = (int) Math.sqrt(dx*dx + dy*dy);
    AffineTransform at = AffineTransform.getTranslateInstance(from.x + offset.x, from.y + offset.y);
    at.concatenate(AffineTransform.getRotateInstance(angle));
    g2.transform(at);

    // Draw horizontal arrow starting in (0, 0)
    g2.drawLine(0, 0, len, 0);
    g2.fillPolygon(new int[] {len, len-size, len-size, len},
            new int[] {0, -size, size, 0}, 4);
}

I got the essentials of the arrow code from an answer by aioobe, here.

I this method by overriding Edge's paintComponent function:

@Override
public void paintComponent(Graphics g) {
    double radius = this.from.getRadius();

    Coordinate vector = this.from.getPosition().clone();
    vector.normalize();
    vector.x = vector.x * radius; vector.y = vector.y * radius;

    Coordinate to = new Coordinate(this.to.getPosition().x - vector.x,
            this.to.getPosition().y - vector.y);

    GraphicsUtils.drawArrow(g, this.color, ARROW_SIZE,
            this.from.getPosition(), to, 
            new Coordinate(radius, radius));
}

As the drawArrow method does what it's supposed to, it draws an arrow from a to b, I want to change the way that I am calling it in the above method. For example, by using the offset parameter for the drawArrow method or something alike.

The Coordinate class:

public class Coordinate {

    public double x;
    public double y;
...
    public void normalize() {
        double length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
        this.x = this.x / length;
        this.y = this.y / length;
    }
...
}

A screenshot of my current output:

Directed graph

Note there are both arrows from D to E and E to D. The latter is not showing because the arrow head is behind D's circle.

Now to be clear, the problem is:

In the paintComponent-method, I am taking the radius of the circle and multiplying it with the normalized (see method) vector. This would give me a point of the circle's circumference, but it seems that always results in the top-left corner, which I don't get. I want to calculate the point on the circumference closest to the source vertex.

Like so:

http://www.onemotion.com/flash/sketch-paint/

Any suggestions?

Community
  • 1
  • 1
mfaerevaag
  • 730
  • 5
  • 29

1 Answers1

2

You can calculate the arrow endpoints from the coordinates of the vertex centers and the vertex image radius. If (xa, ya) and (xb, yb) are the centers of two vertices a and b, and the vertices are drawn with radius r, then the directed line from a to be can be represented as

x = xa + t*(xb - xa)
y = ya + t*(yb - ya)

for a parameter t that varies from 0 to 1. Since t == 1 corresponds to a distance of d = sqrt((xb - xa)2 + (yb - ya)2), you just need to evaluate the above for t = r / d and t = (d-r) / d. (No trig required.)

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • That is what I am doing. If you would look in the `paintComponent`-method, I am taking the radius of the circle and multiplying it with the normalized vector. This would give me a point of the circle's circumference, but it seems that always results in the top-left corner. I will clarify the question. – mfaerevaag Jun 05 '13 at 21:23
  • @7SLEVIN - I haven't yet worked through all your code, but one thing that looks suspect is that your offset is always along a diagonal (x and y are equal) regardless of where the two vertices are positioned. I would suggest calculating the arrow endpoints as I describe in my answer and then use aioobe's code as originally posted (since it doesn't deal with offsets). – Ted Hopp Jun 05 '13 at 21:33