--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2011 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.connection.rendering.arrows;\r
+\r
+import java.awt.Color;\r
+import java.awt.Graphics2D;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Path2D;\r
+import java.io.Serializable;\r
+import java.util.StringTokenizer;\r
+\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class ArrowLineEndStyle implements ILineEndStyle, Serializable {\r
+\r
+ private static final long serialVersionUID = -346653685430483245L;\r
+\r
+ public static enum ArrowType { None, Stroke, Fill }\r
+\r
+ public static final double DEFAULT_LENGTH = 8.0;\r
+ public static final double DEFAULT_WIDTH = 4.0;\r
+ public static final double DEFAULT_SPACE = 0.0;\r
+\r
+ protected ArrowType type;\r
+ protected double lineEndLength;\r
+ protected double length;\r
+ protected double width;\r
+ protected double space;\r
+ protected Color color;\r
+\r
+ protected transient Path2D path;\r
+\r
+ public ArrowLineEndStyle() {\r
+ this(DEFAULT_LENGTH, DEFAULT_WIDTH, DEFAULT_SPACE, null);\r
+ }\r
+\r
+ public ArrowLineEndStyle(double length, double width, double space) {\r
+ this(length, width, space, null);\r
+ }\r
+\r
+ public ArrowLineEndStyle(double length, double width, double space, Color color) {\r
+ this.type = ArrowType.Fill;\r
+ this.length = length;\r
+ this.width = width;\r
+ this.space = space;\r
+ this.color = color;\r
+ this.path = arrow(length, width, space);\r
+ }\r
+\r
+ public ArrowLineEndStyle(String desc) {\r
+ this.type = ArrowType.None;\r
+ this.lineEndLength = 0.0;\r
+\r
+ double l = DEFAULT_LENGTH;\r
+ double w = DEFAULT_WIDTH;\r
+ double s = DEFAULT_SPACE;\r
+\r
+ StringTokenizer tokenizer = new StringTokenizer(desc);\r
+ if (tokenizer.hasMoreTokens()) {\r
+ String type = tokenizer.nextToken();\r
+ this.type = parseType(type);\r
+\r
+ if (tokenizer.hasMoreTokens()) {\r
+ String ls = tokenizer.nextToken();\r
+ l = parseSize(ls, length);\r
+\r
+ if (tokenizer.hasMoreTokens()) {\r
+ String ws = tokenizer.nextToken();\r
+ w = parseSize(ws, width);\r
+\r
+ if (tokenizer.hasMoreTokens()) {\r
+ String ss = tokenizer.nextToken();\r
+ s = parseSize(ss, space);\r
+ }\r
+ }\r
+ }\r
+ if (this.type != ArrowType.None) {\r
+ this.path = arrow(l, w, s);\r
+ lineEndLength = l+s;\r
+ }\r
+ }\r
+\r
+ this.width = w;\r
+ this.length = l;\r
+ this.space = s;\r
+ }\r
+\r
+ @Override\r
+ public void render(Graphics2D g, double x, double y, int dir) {\r
+ if (type == ArrowType.None || path == null)\r
+ return;\r
+\r
+ AffineTransform old = g.getTransform();\r
+ g.translate(x, y);\r
+ g.rotate(dir*Math.PI*0.5);\r
+ if(color != null)\r
+ g.setColor(color);\r
+\r
+ switch (type) {\r
+ case Fill:\r
+ g.fill(path);\r
+ break;\r
+ case Stroke:\r
+ g.draw(path);\r
+ break;\r
+ }\r
+\r
+ g.setTransform(old);\r
+ }\r
+\r
+ @Override\r
+ public double getLineEndLength(int direction) {\r
+ return lineEndLength;\r
+ }\r
+\r
+ private static Path2D arrow(double length, double width, double space) {\r
+ Path2D.Double path = new Path2D.Double();\r
+ path.moveTo(-space, 0);\r
+ path.lineTo(-length-space, -width);\r
+ path.lineTo(-length-space, +width);\r
+ path.closePath();\r
+ return path;\r
+ }\r
+\r
+ private static double parseSize(String size, double defaultValue) {\r
+ try {\r
+ return Double.parseDouble(size);\r
+ } catch (NumberFormatException e) {\r
+ return defaultValue;\r
+ }\r
+ }\r
+\r
+ private static ArrowType parseType(String type) {\r
+ String lower = type.toLowerCase();\r
+ if ("none".equals(lower))\r
+ return ArrowType.None;\r
+ if ("stroke".equals(lower))\r
+ return ArrowType.Stroke;\r
+ if ("fill".equals(lower))\r
+ return ArrowType.Fill;\r
+ throw new IllegalArgumentException("unrecognized arrow type: " + type);\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return getClass().getSimpleName() + "@" + System.identityHashCode(this) + "[" + type + ", w=" + width + ", l=" + length + ", s=" + space + ", lel=" + lineEndLength + "]";\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = 1;\r
+ long temp;\r
+ temp = Double.doubleToLongBits(length);\r
+ result = prime * result + (int) (temp ^ (temp >>> 32));\r
+ temp = Double.doubleToLongBits(lineEndLength);\r
+ result = prime * result + (int) (temp ^ (temp >>> 32));\r
+ temp = Double.doubleToLongBits(space);\r
+ result = prime * result + (int) (temp ^ (temp >>> 32));\r
+ result = prime * result + ((type == null) ? 0 : type.hashCode());\r
+ temp = Double.doubleToLongBits(width);\r
+ result = prime * result + (int) (temp ^ (temp >>> 32));\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj)\r
+ return true;\r
+ if (obj == null)\r
+ return false;\r
+ if (getClass() != obj.getClass())\r
+ return false;\r
+ ArrowLineEndStyle other = (ArrowLineEndStyle) obj;\r
+ if (Double.doubleToLongBits(length) != Double.doubleToLongBits(other.length))\r
+ return false;\r
+ if (Double.doubleToLongBits(lineEndLength) != Double.doubleToLongBits(other.lineEndLength))\r
+ return false;\r
+ if (Double.doubleToLongBits(space) != Double.doubleToLongBits(other.space))\r
+ return false;\r
+ if (type != other.type)\r
+ return false;\r
+ if (Double.doubleToLongBits(width) != Double.doubleToLongBits(other.width))\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+}\r