/**
*
*/
package org.seqcode.viz.graphs;
import java.util.*;
import java.awt.*;
import java.awt.geom.*;
/**
* @author Timothy Danford
*/
public class EdgeView extends ObjectView {
private GraphView graph;
private NodeView start, finish;
public EdgeView(GraphView g, NodeView s, NodeView f) {
super();
graph = g;
start = s; finish = f;
if(start.equals(finish)){setOption("self", new Boolean(true));}
else{setOption("self", new Boolean(false)); setOption("selfAngle", new Double(Math.PI));}
setOption("directed", "false");
setOption("arrowSize", 10);
}
public EdgeView(ObjectView defs, GraphView g, NodeView s, NodeView f) {
super(defs);
graph = g;
start = s; finish = f;
if(start.equals(finish)){setOption("self", new Boolean(true));}
else{setOption("self", new Boolean(false));setOption("selfAngle", new Double(Math.PI));}
setOption("directed", "false");
setOption("arrowSize", 10);
}
public GraphView getGraph() { return graph; }
public NodeView getStart() { return start; }
public NodeView getFinish() { return finish; }
public boolean hasDynamicAttributes() {
return containsOption("dynamicAttrs");
}
public void setDynamicAttributes() {
setOption("dynamicAttrs", "true");
if(isDirected()) {
int sx = start.getX(), ex = finish.getX();
int sy = start.getY(), ey = finish.getY();
double dx = (double)(sx - ex), dy = (double)(sy - ey);
double length = Math.sqrt((dx * dx) + (dy * dy));
double rot = 0.0;
if(dy >= 0.0) {
rot = Math.acos(dx/length);
} else {
rot = -Math.acos(dx/length);
}
setOption("arrowRotation", rot);
int ewidth = finish.containsOption("width") ? (Integer)finish.getOption("width") : 10;
int arrowSize = 10;
double rotdiff = Math.PI / 20.0;
Point p1 = rotate(new Point(ewidth, 0), rot);
Point p2 = rotate(new Point(ewidth+arrowSize,0), rot+rotdiff);
Point p3 = rotate(new Point(ewidth+arrowSize,0), rot-rotdiff);
p1.translate(ex, ey);
p2.translate(ex, ey);
p3.translate(ex, ey);
setOption("arrowPoint1", p1);
setOption("arrowPoint2", p2);
setOption("arrowPoint3", p3);
}
}
private Point rotate(Point p, double theta) {
double cosTheta = Math.cos(theta), sinTheta = Math.sin(theta);
double xp = (double)p.x * cosTheta - (double)p.y * sinTheta;
double yp = (double)p.x * sinTheta + (double)p.y * cosTheta;
return new Point((int)Math.round(xp), (int)Math.round(yp));
}
public void clearDynamicAttributes() {
clearOption("dynamicAttrs");
if(isDirected()) {
clearOption("arrowRotation");
clearOption("arrowPoint1");
clearOption("arrowPoint2");
clearOption("arrowPoint3");
}
}
public void setDirected(boolean directed) {
setOption("directed", String.valueOf(directed));
}
public boolean isDirected() {
String value = getOption("directed").toString();
if(!value.equals("true") && !value.equals("false")) {
throw new IllegalStateException(String.format("option 'directed' has illegal value '%s'",
value));
}
return value.equals("true");
}
public void twistSelfEdge(double angle){
double currAngle = (Double)getOption("selfAngle");
setOption("selfAngle", currAngle+angle);
}
public void paintView(Graphics2D g2) {
if(containsOption("self") && (Boolean)getOption("self")==true) {
Double angle = (Double)getOption("selfAngle");
if(angle == null) { angle = Math.PI / 2.0; }
paintSelfEdge(g2, angle);
}else if(containsOption("curved")&& (Boolean)getOption("curved")==true) {
paintCurvedEdge(g2);
} else {
paintEdge(g2);
}
}
public void paintEdge(Graphics2D g2) {
NodeView first = getStart(), last = getFinish();
int sr = first.getWidth()/2;
int er = last.getWidth()/2;
int sx = first.getX(), sy = first.getY();
int ex = last.getX(), ey = last.getY();
double omega = Math.atan2((sy-ey),(sx-ex));
int textOffset=10;
Stroke oldStroke = g2.getStroke();
if(containsOption("edgeWidth")) {
int ewidth = (Integer)getOption("edgeWidth");
g2.setStroke(new BasicStroke((float)ewidth));
sx = first.getX() -(new Double((ewidth/2)*Math.cos(omega)).intValue());
ex = last.getX() +(new Double((ewidth/2)*Math.cos(omega)).intValue());
sy = first.getY() -(new Double((ewidth/2)*Math.sin(omega)).intValue());
ey = last.getY() +(new Double((ewidth/2)*Math.sin(omega)).intValue());
}
g2.setColor(getColor());
g2.drawLine(sx, sy, ex, ey);
int[] xs = new int[3], ys = new int[3];
if(isDirected() && (ex != sx || ey != sy)) {
if(!hasDynamicAttributes()) {
setDynamicAttributes();
}
//Tim's way of drawing arrows:
/*Point p1 = (Point)ev.getOption("arrowPoint1");
Point p2 = (Point)ev.getOption("arrowPoint2");
Point p3 = (Point)ev.getOption("arrowPoint3");
xs[0] = p1.x; xs[1] = p2.x; xs[2] = p3.x;
ys[0] = p1.y; ys[1] = p2.y; ys[2] = p3.y;
g2.fillPolygon(xs, ys, 3);
if(ev.containsOption("name")){
String edName = (String)ev.getOption("name");
g2.setColor(Color.black);
g2.drawString(edName, xs[2]+textOffset, ys[2]);
g2.setColor(ev.getColor());
}*/
//Shaun's way of drawing arrows:
int c0 = (int)Math.round(((ex+sx)/2));
int c1 = (int)Math.round(((ey+sy)/2));
GraphPaintable.drawArrow(g2, (Integer)getOption("arrowSize"),
new Point(c0, c1), omega+Math.PI);
if(containsOption("name")){
String edName = (String)getOption("name");
g2.setColor(Color.black);
g2.drawString(edName, c0+textOffset, c1);
g2.setColor(getColor());
}
}
g2.setStroke(oldStroke);
}
public void paintCurvedEdge(Graphics2D g2) {
NodeView first = getStart(), last = getFinish();
int sx = first.getX(), sy = first.getY();
int ex = last.getX(), ey = last.getY();
double omega = Math.atan2((sy-ey),(sx-ex));
int ewidth=10;
Stroke oldStroke = g2.getStroke();
if(containsOption("edgeWidth")) {
ewidth = (Integer)getOption("edgeWidth");
g2.setStroke(new BasicStroke((float)ewidth));
}
g2.setColor(getColor());
int curveBend = options.containsKey("curve-bend") ?
(Integer)options.get("curve-bend") : 250;
AffineTransform currTrans = g2.getTransform();
Point po1 = new Point(sx, sy);
Point po2 = new Point(ex, ey);
double vx = 0;
double vy = curveBend; //Controls how "bent" the curve is
int textOffset=10;
g2.translate(sx+(ex-sx)/2, sy+(ey-sy)/2);
Point nP = rotate(new Point(new Double(vx).intValue(),new Double(vy).intValue()), omega);
QuadCurve2D.Double curve =
new QuadCurve2D.Double(-(ex-sx)/2, -(ey-sy)/2, new Double(nP.getX()).intValue(), new Double(nP.getY()).intValue(), (ex-sx)/2, (ey-sy)/2);
g2.draw(curve);
if(isDirected()){
PathIterator itr = curve.getPathIterator(null);
double[] coords = new double[6];
double[] prev = new double[6];
do {
int type = itr.currentSegment(coords);
switch(type) {
case PathIterator.SEG_MOVETO:
break;
case PathIterator.SEG_LINETO:
break;
case PathIterator.SEG_CUBICTO:
case PathIterator.SEG_QUADTO:
int c0 = (int)Math.round((coords[0]+coords[4])/2);
int c1 = (int)Math.round((coords[1]+coords[5])/2);
GraphPaintable.drawArrow(g2, (Integer)getOption("arrowSize"),
new Point(c0, c1), omega+Math.PI);
if(containsOption("name")){
String edName = (String)getOption("name");
g2.setColor(Color.black);
g2.drawString(edName, c0+textOffset, c1);
g2.setColor(getColor());
}
break;
case PathIterator.SEG_CLOSE:
break;
}
for(int i = 0; i < coords.length; i++) {
prev[i] = coords[i];
}
itr.next();
} while(!itr.isDone());
}
g2.setTransform(currTrans);
g2.setStroke(oldStroke);
}
public void paintSelfEdge(Graphics2D g2, double angle) {
double offset = 200;
double offsetAngle = Math.toRadians(50);
int textOffset=10;
double omega = angle;
NodeView first = getStart(), last = getFinish();
int sx = first.getX(), sy = first.getY();
int ex = last.getX(), ey = last.getY();
Stroke oldStroke = g2.getStroke();
if(containsOption("edgeWidth")) {
int ewidth = (Integer)getOption("edgeWidth");
g2.setStroke(new BasicStroke((float)ewidth));
}
g2.setColor(getColor());
AffineTransform currTrans = g2.getTransform();
g2.translate(sx, sy);
Point cp1 = rotate(new Point(0,new Double(offset).intValue()), omega-offsetAngle);
Point cp2 = rotate(new Point(0,new Double(offset).intValue()), omega+offsetAngle);
CubicCurve2D.Double curve =
new CubicCurve2D.Double(0, 0, new Double(cp1.getX()).intValue(), new Double(cp1.getY()).intValue(), new Double(cp2.getX()).intValue(), new Double(cp2.getY()).intValue(), 0, 0);
g2.draw(curve);
if(isDirected()){
PathIterator itr = curve.getPathIterator(null);
double[] coords = new double[6];
double[] prev = new double[6];
do {
int type = itr.currentSegment(coords);
switch(type) {
case PathIterator.SEG_MOVETO:
break;
case PathIterator.SEG_LINETO:
break;
case PathIterator.SEG_QUADTO:
case PathIterator.SEG_CUBICTO:
int c0 = (int)Math.round((coords[0]+coords[2])/2);
int c1 = (int)Math.round((coords[1]+coords[3])/2);
int c2 = (int)Math.round(3*c0/4);
int c3 = (int)Math.round(3*c1/4);
GraphPaintable.drawArrow(g2, (Integer)getOption("arrowSize"),
new Point(c2, c3), omega+Math.PI);
if(containsOption("name")){
String edName = (String)getOption("name");
g2.setColor(Color.black);
g2.drawString(edName, c2+textOffset, c3);
g2.setColor(getColor());
}
break;
case PathIterator.SEG_CLOSE:
break;
}
for(int i = 0; i < coords.length; i++) {
prev[i] = coords[i];
}
itr.next();
} while(!itr.isDone());
}
g2.setTransform(currTrans);g2.setColor(Color.red); g2.fillOval(sx, sy, 4, 4);
g2.setStroke(oldStroke);
}
}