/*
* Copyright (C) 2007 Snorre Gylterud, Stein Magnus Jodal, Johannes Knutsen,
* Erik Bagge Ottesen, Ralf Bjarne Taraldset, and Iterate AS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
package no.ntnu.mmfplanner.ui.graph;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.geom.Line2D;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.util.PBounds;
import edu.umd.cs.piccolo.util.PPaintContext;
/**
* Draws a precursor arrow between two MmfNodes in the precursor graph.
*/
public class PrecursorNode extends PNode {
private static final long serialVersionUID = 1L;
private static final int[] END_DELTA = new int[] { 0, 7, 12, 16, 19, 21, 22 };
private static final BasicStroke STROKE_ARROW = new BasicStroke(2,
BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
private MmfNode source, target;
private Line2D line;
private Polygon arrowHead;
public PrecursorNode(MmfNode source, MmfNode target) {
super();
this.source = source;
this.target = target;
setPickable(false);
}
public void updateLine() {
PBounds sourceBounds = source.getBounds();
PBounds targetBounds = target.getBounds();
double x1, y1, x2, y2;
if (Math.abs(sourceBounds.getCenterX() - targetBounds.getCenterX()) < 1.0) {
// source and target on the same vertical position
x1 = x2 = sourceBounds.getCenterX();
y1 = sourceBounds.getMaxY();
y2 = targetBounds.getMinY();
y2 -= STROKE_ARROW.getLineWidth();
if (y1 > y2) {
y1 = sourceBounds.getMinY();
y2 = targetBounds.getMaxY();
y2 += STROKE_ARROW.getLineWidth();
}
} else {
// source and target in different vertical positions
x1 = sourceBounds.getMaxX();
y1 = sourceBounds.getCenterY();
x2 = targetBounds.getMinX();
y2 = targetBounds.getCenterY();
x2 -= STROKE_ARROW.getLineWidth();
if (x1 > x2) {
x1 = sourceBounds.getMinX();
x2 = targetBounds.getMaxX();
x2 += STROKE_ARROW.getLineWidth();
}
// move the ends a bit if we are not in the same vertical position
int ydi = (int) Math.round((y2 - y1)
/ (MmfNode.HEIGHT + MmfNode.PADDING_HEIGHT));
double yd = (ydi < 0 ? -1 : 1)
* END_DELTA[Math.min(Math.abs(ydi), END_DELTA.length - 1)];
y1 += yd;
y2 -= yd;
}
line = new Line2D.Double(x1, y1, x2, y2);
arrowHead = getArrowHead((int) x1, (int) y1, (int) x2, (int) y2);
Rectangle bounds = line.getBounds();
bounds.add(arrowHead.getBounds());
setBounds(bounds);
}
public MmfNode getSource() {
return this.source;
}
public MmfNode getTarget() {
return this.target;
}
@Override
protected void paint(PPaintContext pc) {
Graphics2D g2 = pc.getGraphics();
g2.setColor(Color.BLACK);
g2.setStroke(STROKE_ARROW);
g2.draw(line);
g2.draw(arrowHead);
g2.fill(arrowHead);
}
public static Polygon getArrowHead(int xFrom, int yFrom, int x, int y) {
double aDir = Math.atan2(xFrom - x, yFrom - y);
Polygon tmpPoly = new Polygon();
double i1 = 9; // + (int) (stroke * 2);
double i2 = 4.5; // + (int) stroke;
tmpPoly.addPoint(x, y); // arrow tip
tmpPoly.addPoint(x + xCor(i1, aDir + .5), y + yCor(i1, aDir + .5));
tmpPoly.addPoint(x + xCor(i2, aDir), y + yCor(i2, aDir));
tmpPoly.addPoint(x + xCor(i1, aDir - .5), y + yCor(i1, aDir - .5));
tmpPoly.addPoint(x, y); // arrow tip
return tmpPoly;
}
private static int yCor(double i2, double dir) {
return (int) (i2 * Math.cos(dir));
}
private static int xCor(double i2, double dir) {
return (int) (i2 * Math.sin(dir));
}
}