301cfdf092bdabb5ea368c763ada136908e196c6
[simantics/platform.git] / bundles / org.simantics.diagram.connection / src / org / simantics / diagram / connection / rendering / BasicConnectionStyle.java
1 /*******************************************************************************
2  * Copyright (c) 2011 Association for Decentralized Information Management in
3  * Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.diagram.connection.rendering;
13
14 import java.awt.Color;
15 import java.awt.Graphics2D;
16 import java.awt.RenderingHints;
17 import java.awt.Stroke;
18 import java.awt.geom.AffineTransform;
19 import java.awt.geom.Ellipse2D;
20 import java.awt.geom.Line2D;
21 import java.awt.geom.Path2D;
22 import java.awt.geom.PathIterator;
23 import java.io.Serializable;
24
25 /**
26  * @author Tuukka Lehtonen
27  */
28 public class BasicConnectionStyle implements ConnectionStyle, Serializable {
29
30     private static final long serialVersionUID = -5799681720482456895L;
31
32     // line thickness in millimeters.
33     final Color                     lineColor;
34     final Color                     branchPointColor;
35     final double                    branchPointRadius;
36     final Stroke                    lineStroke;
37     final Stroke                    routeLineStroke;
38     final double                    degenerateLineLength;
39     final double                    rounding;
40
41     transient Line2D          line             = new Line2D.Double();
42     transient Ellipse2D       ellipse          = new Ellipse2D.Double();
43
44     public BasicConnectionStyle(Color lineColor, Color branchPointColor, double branchPointRadius, Stroke lineStroke, Stroke routeLineStroke, double degenerateLineLength,
45             double rounding) {
46         this.lineColor = lineColor;
47         this.branchPointColor = branchPointColor;
48         this.branchPointRadius = branchPointRadius;
49         this.lineStroke = lineStroke;
50         this.routeLineStroke = routeLineStroke;
51         this.degenerateLineLength = degenerateLineLength;
52         this.rounding = rounding;
53     }
54     
55     public BasicConnectionStyle(Color lineColor, Color branchPointColor, double branchPointRadius, Stroke lineStroke, Stroke routeLineStroke, double degenerateLineLength) {
56         this(lineColor, branchPointColor, branchPointRadius, lineStroke, routeLineStroke, degenerateLineLength, 0.0);
57     }
58
59     public Color getLineColor() {
60         return lineColor;
61     }
62     
63     public Color getBranchPointColor() {
64         return branchPointColor;
65     }
66     
67     public double getBranchPointRadius() {
68         return branchPointRadius;
69     }
70     
71     public Stroke getLineStroke() {
72         return lineStroke;
73     }
74     
75     public Stroke getRouteLineStroke() {
76         return routeLineStroke;
77     }
78
79     @Override
80     public void drawLine(Graphics2D g, double x1, double y1, double x2,
81             double y2, boolean isTransient) {
82         if (lineColor != null)
83             g.setColor(lineColor);
84         if(isTransient) {
85             g.setStroke(lineStroke);
86             line.setLine(x1, y1, x2, y2);
87             g.draw(line);
88         } else {
89             g.setStroke(routeLineStroke);
90             line.setLine(x1, y1, x2, y2);
91             g.draw(line);
92         }
93     }
94
95     @Override
96     public void drawPath(Graphics2D g, Path2D path, boolean isTransient) {
97         if (lineColor != null)
98             g.setColor(lineColor);
99         if (lineStroke != null)
100             g.setStroke(lineStroke);
101         if(rounding > 0.0) {
102             Object oldRenderingHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
103             g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
104             g.draw(round(path));
105             g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldRenderingHint);
106         }
107         else
108             g.draw(path);
109     }
110     
111     private Path2D round(Path2D path) {
112         Path2D newPath = new Path2D.Double();
113         PathIterator it = path.getPathIterator(new AffineTransform());
114         double[] coords = new double[6];
115         double newX=0.0, newY=0.0;
116         double curX=0.0, curY=0.0;
117         double oldX=0.0, oldY=0.0;
118         int state = 0;
119         while(!it.isDone()) {
120             int type = it.currentSegment(coords);
121             if(type == PathIterator.SEG_LINETO) {
122                 newX = coords[0];
123                 newY = coords[1];
124                 if(state == 1) {
125                     double dx1 = curX-oldX;
126                     double dy1 = curY-oldY;
127                     double dx2 = curX-newX;
128                     double dy2 = curY-newY;
129                     double r1 = Math.sqrt(dx1*dx1 + dy1*dy1);
130                     double r2 = Math.sqrt(dx2*dx2 + dy2*dy2);
131                     double maxRadius = 0.5 * Math.min(r1, r2);
132                     double radius = Math.min(rounding, maxRadius);
133                     double dx1Normalized = r1 > 0 ? dx1 / r1 : 0;
134                     double dy1Normalized = r1 > 0 ? dy1 / r1 : 0;
135                     double dx2Normalized = r2 > 0 ? dx2 / r2 : 0;
136                     double dy2Normalized = r2 > 0 ? dy2 / r2 : 0;
137                     newPath.lineTo(curX - radius*dx1Normalized, curY - radius*dy1Normalized);
138                     newPath.curveTo(curX, curY,
139                                     curX, curY,
140                                     curX - radius*dx2Normalized, curY - radius*dy2Normalized);
141                 }
142                 else
143                     ++state;
144                 oldX = curX;
145                 oldY = curY;
146                 curX = newX;
147                 curY = newY;   
148             }
149             else {
150                 if(state > 0) {
151                     newPath.lineTo(curX, curY);
152                     state = 0;
153                 }
154                 switch(type) {
155                 case PathIterator.SEG_MOVETO:
156                     curX = coords[0];
157                     curY = coords[1];
158                     newPath.moveTo(curX, curY);
159                     break;
160                 case PathIterator.SEG_QUADTO:
161                     curX = coords[2];
162                     curY = coords[3];
163                     newPath.quadTo(coords[0], coords[1], coords[2], coords[3]);
164                     break;
165                 case PathIterator.SEG_CUBICTO:
166                     curX = coords[4];
167                     curY = coords[5];
168                     newPath.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
169                     break;
170                 case PathIterator.SEG_CLOSE:
171                     newPath.closePath();
172                     break;
173                 }
174             }
175             it.next();
176         }
177         if(state > 0)
178             newPath.lineTo(curX, curY);
179         return newPath;
180     }
181
182     @Override
183     public void drawBranchPoint(Graphics2D g, double x, double y) {
184         g.setColor(branchPointColor);
185         double r = branchPointRadius;
186         double d = 2*r;
187         ellipse.setFrame(x-r, y-r, d, d);
188         g.fill(ellipse);
189     }
190
191     @Override
192     public void drawDegeneratedLine(Graphics2D g, double x, double y,
193             boolean isHorizontal, boolean isTransient) {
194         double d = getDegeneratedLineLength()*0.5;
195         if(isHorizontal) {
196             line.setLine(x-d, y, x+d, y);
197             g.draw(line);
198         } else {
199             line.setLine(x, y-d, x, y+d);
200             g.draw(line);
201         }
202     }
203
204     @Override
205     public double getDegeneratedLineLength() {
206         return degenerateLineLength;
207     }
208
209     @Override
210     public int hashCode() {
211         final int prime = 31;
212         int result = 1;
213         result = prime * result + ((branchPointColor == null) ? 0 : branchPointColor.hashCode());
214         long temp;
215         temp = Double.doubleToLongBits(branchPointRadius);
216         result = prime * result + (int) (temp ^ (temp >>> 32));
217         temp = Double.doubleToLongBits(degenerateLineLength);
218         result = prime * result + (int) (temp ^ (temp >>> 32));
219         result = prime * result + ((lineColor == null) ? 0 : lineColor.hashCode());
220         result = prime * result + ((lineStroke == null) ? 0 : lineStroke.hashCode());
221         result = prime * result + ((routeLineStroke == null) ? 0 : routeLineStroke.hashCode());
222         return result;
223     }
224
225     @Override
226     public boolean equals(Object obj) {
227         if (this == obj)
228             return true;
229         if (obj == null)
230             return false;
231         if (getClass() != obj.getClass())
232             return false;
233         BasicConnectionStyle other = (BasicConnectionStyle) obj;
234         if (branchPointColor == null) {
235             if (other.branchPointColor != null)
236                 return false;
237         } else if (!branchPointColor.equals(other.branchPointColor))
238             return false;
239         if (Double.doubleToLongBits(branchPointRadius) != Double.doubleToLongBits(other.branchPointRadius))
240             return false;
241         if (Double.doubleToLongBits(degenerateLineLength) != Double.doubleToLongBits(other.degenerateLineLength))
242             return false;
243         if (lineColor == null) {
244             if (other.lineColor != null)
245                 return false;
246         } else if (!lineColor.equals(other.lineColor))
247             return false;
248         if (lineStroke == null) {
249             if (other.lineStroke != null)
250                 return false;
251         } else if (!lineStroke.equals(other.lineStroke))
252             return false;
253         if (routeLineStroke == null) {
254             if (other.routeLineStroke != null)
255                 return false;
256         } else if (!routeLineStroke.equals(other.routeLineStroke))
257             return false;
258         return true;
259     }
260
261     public double getRounding() {
262         return rounding;
263     }
264
265 }