/******************************************************************************* * Copyright (c) 2011 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.diagram.connection.rendering; import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Path2D; import java.awt.geom.PathIterator; import java.io.Serializable; /** * @author Tuukka Lehtonen */ public class BasicConnectionStyle implements ConnectionStyle, Serializable { private static final long serialVersionUID = -5799681720482456895L; // line thickness in millimeters. final Color lineColor; final Color branchPointColor; final double branchPointRadius; final Stroke lineStroke; final Stroke routeLineStroke; final double degenerateLineLength; final double rounding; transient Line2D line = new Line2D.Double(); transient Ellipse2D ellipse = new Ellipse2D.Double(); public BasicConnectionStyle(Color lineColor, Color branchPointColor, double branchPointRadius, Stroke lineStroke, Stroke routeLineStroke, double degenerateLineLength, double rounding) { this.lineColor = lineColor; this.branchPointColor = branchPointColor; this.branchPointRadius = branchPointRadius; this.lineStroke = lineStroke; this.routeLineStroke = routeLineStroke; this.degenerateLineLength = degenerateLineLength; this.rounding = rounding; } public BasicConnectionStyle(Color lineColor, Color branchPointColor, double branchPointRadius, Stroke lineStroke, Stroke routeLineStroke, double degenerateLineLength) { this(lineColor, branchPointColor, branchPointRadius, lineStroke, routeLineStroke, degenerateLineLength, 0.0); } public Color getLineColor() { return lineColor; } public Color getBranchPointColor() { return branchPointColor; } public double getBranchPointRadius() { return branchPointRadius; } public Stroke getLineStroke() { return lineStroke; } public Stroke getRouteLineStroke() { return routeLineStroke; } @Override public void drawLine(Graphics2D g, double x1, double y1, double x2, double y2, boolean isTransient) { if (lineColor != null) g.setColor(lineColor); if(isTransient) { g.setStroke(lineStroke); line.setLine(x1, y1, x2, y2); g.draw(line); } else { g.setStroke(routeLineStroke); line.setLine(x1, y1, x2, y2); g.draw(line); } } @Override public void drawPath(Graphics2D g, Path2D path, boolean isTransient) { if (lineColor != null) g.setColor(lineColor); if (lineStroke != null) g.setStroke(lineStroke); if(rounding > 0.0) { Object oldRenderingHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.draw(round(path)); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldRenderingHint); } else g.draw(path); } private Path2D round(Path2D path) { Path2D newPath = new Path2D.Double(); PathIterator it = path.getPathIterator(new AffineTransform()); double[] coords = new double[6]; double newX=0.0, newY=0.0; double curX=0.0, curY=0.0; double oldX=0.0, oldY=0.0; int state = 0; while(!it.isDone()) { int type = it.currentSegment(coords); if(type == PathIterator.SEG_LINETO) { newX = coords[0]; newY = coords[1]; if(state == 1) { double dx1 = curX-oldX; double dy1 = curY-oldY; double dx2 = curX-newX; double dy2 = curY-newY; double r1 = Math.sqrt(dx1*dx1 + dy1*dy1); double r2 = Math.sqrt(dx2*dx2 + dy2*dy2); double maxRadius = 0.5 * Math.min(r1, r2); double radius = Math.min(rounding, maxRadius); double dx1Normalized = r1 > 0 ? dx1 / r1 : 0; double dy1Normalized = r1 > 0 ? dy1 / r1 : 0; double dx2Normalized = r2 > 0 ? dx2 / r2 : 0; double dy2Normalized = r2 > 0 ? dy2 / r2 : 0; newPath.lineTo(curX - radius*dx1Normalized, curY - radius*dy1Normalized); newPath.curveTo(curX, curY, curX, curY, curX - radius*dx2Normalized, curY - radius*dy2Normalized); } else ++state; oldX = curX; oldY = curY; curX = newX; curY = newY; } else { if(state > 0) { newPath.lineTo(curX, curY); state = 0; } switch(type) { case PathIterator.SEG_MOVETO: curX = coords[0]; curY = coords[1]; newPath.moveTo(curX, curY); break; case PathIterator.SEG_QUADTO: curX = coords[2]; curY = coords[3]; newPath.quadTo(coords[0], coords[1], coords[2], coords[3]); break; case PathIterator.SEG_CUBICTO: curX = coords[4]; curY = coords[5]; newPath.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); break; case PathIterator.SEG_CLOSE: newPath.closePath(); break; } } it.next(); } if(state > 0) newPath.lineTo(curX, curY); return newPath; } @Override public void drawBranchPoint(Graphics2D g, double x, double y) { g.setColor(branchPointColor); double r = branchPointRadius; double d = 2*r; ellipse.setFrame(x-r, y-r, d, d); g.fill(ellipse); } @Override public void drawDegeneratedLine(Graphics2D g, double x, double y, boolean isHorizontal, boolean isTransient) { double d = getDegeneratedLineLength()*0.5; if(isHorizontal) { line.setLine(x-d, y, x+d, y); g.draw(line); } else { line.setLine(x, y-d, x, y+d); g.draw(line); } } @Override public double getDegeneratedLineLength() { return degenerateLineLength; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((branchPointColor == null) ? 0 : branchPointColor.hashCode()); long temp; temp = Double.doubleToLongBits(branchPointRadius); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(degenerateLineLength); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + ((lineColor == null) ? 0 : lineColor.hashCode()); result = prime * result + ((lineStroke == null) ? 0 : lineStroke.hashCode()); result = prime * result + ((routeLineStroke == null) ? 0 : routeLineStroke.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BasicConnectionStyle other = (BasicConnectionStyle) obj; if (branchPointColor == null) { if (other.branchPointColor != null) return false; } else if (!branchPointColor.equals(other.branchPointColor)) return false; if (Double.doubleToLongBits(branchPointRadius) != Double.doubleToLongBits(other.branchPointRadius)) return false; if (Double.doubleToLongBits(degenerateLineLength) != Double.doubleToLongBits(other.degenerateLineLength)) return false; if (lineColor == null) { if (other.lineColor != null) return false; } else if (!lineColor.equals(other.lineColor)) return false; if (lineStroke == null) { if (other.lineStroke != null) return false; } else if (!lineStroke.equals(other.lineStroke)) return false; if (routeLineStroke == null) { if (other.routeLineStroke != null) return false; } else if (!routeLineStroke.equals(other.routeLineStroke)) return false; return true; } public double getRounding() { return rounding; } }