--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in 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.scenegraph.g2d.nodes;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.Composite;\r
+import java.awt.Graphics2D;\r
+import java.awt.Shape;\r
+import java.awt.Stroke;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.GeneralPath;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.simantics.scenegraph.g2d.G2DNode;\r
+import org.simantics.scenegraph.utils.InitValueSupport;\r
+\r
+public class EdgeNode extends G2DNode implements InitValueSupport {\r
+\r
+ private static final long serialVersionUID = 1294351381209071074L;\r
+\r
+ public static enum ArrowType { None, Stroke, Fill, Both }\r
+\r
+ protected Color color = null;\r
+ protected Stroke stroke = null;\r
+ protected Shape shape = null;\r
+ protected Point2D firstdir = null;\r
+ protected Point2D lastdir = null;\r
+ protected Point2D first = null;\r
+ protected Point2D last = null;\r
+ protected double firstsize = 0;\r
+ protected double lastsize = 0;\r
+ protected ArrowType first_at = null;\r
+ protected ArrowType last_at = null;\r
+ protected Shape firstShape = null;\r
+ protected Shape lastShape = null;\r
+\r
+ private transient Rectangle2D bounds;\r
+\r
+ @SyncField({"color", "stroke", "shape", "firstdir", "lastdir", "first", "last", "firstsize", "lastsize", "first_at", "last_at"})\r
+ public void init(Shape shape, Stroke stroke, Color color, Point2D firstdir, Point2D lastdir, Point2D first, Point2D last, double firstsize, double lastsize, ArrowType first_at, ArrowType last_at, Shape firstShape, Shape lastShape) {\r
+ this.color = color;\r
+ this.stroke = stroke;\r
+ this.shape = shape;\r
+ this.firstdir = firstdir;\r
+ this.lastdir = lastdir;\r
+ this.first = first;\r
+ this.last = last;\r
+ this.firstsize = firstsize;\r
+ this.lastsize = lastsize;\r
+ this.first_at = first_at;\r
+ this.last_at = last_at;\r
+ this.firstShape = firstShape;\r
+ this.lastShape = lastShape;\r
+\r
+ if (shape != null) {\r
+ this.bounds = shape.getBounds2D();\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void render(Graphics2D g) {\r
+ if(color != null) g.setColor(color);\r
+ if(stroke == null || shape == null) return;\r
+\r
+ if(alphaComposite != null) {\r
+ g.setComposite(alphaComposite);\r
+ }\r
+\r
+ Stroke effectiveStroke = stroke;\r
+ if(dynamicStroke != null) {\r
+ effectiveStroke = dynamicStroke;\r
+ }\r
+\r
+ Color effectiveColor = color;\r
+ if(dynamicColor != null) {\r
+ effectiveColor = dynamicColor;\r
+ }\r
+\r
+ g.setStroke(effectiveStroke);\r
+\r
+ // NICENESS\r
+ //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\r
+\r
+ //g.draw(shape);\r
+\r
+ // Draw line "halo"\r
+// float f = ((BasicStroke)effectiveStroke).getLineWidth() * 3f;\r
+// Color background = Color.WHITE; // FIXME\r
+// g.setColor(background);\r
+// g.setStroke(new BasicStroke(f));\r
+// g.draw(shape);\r
+\r
+ // Draw line\r
+ g.setColor(effectiveColor);\r
+ g.setStroke(effectiveStroke);\r
+ g.draw(shape);\r
+\r
+ // Draw line ends if necessary.\r
+ boolean drawArrows = first_at != ArrowType.None || last_at != ArrowType.None;\r
+ if (!drawArrows)\r
+ return;\r
+\r
+ AffineTransform at = g.getTransform();\r
+\r
+ g.setStroke(ARROW_STROKE);\r
+\r
+ if (first_at != ArrowType.None) {\r
+ double theta = Math.atan2(firstdir.getY(), firstdir.getX()) - Math.PI/2;\r
+ g.translate(first.getX(), first.getY());\r
+ g.rotate(theta);\r
+ g.scale(firstsize, firstsize);\r
+\r
+ if (first_at == ArrowType.Fill)\r
+ g.fill(FILLED_ARROW);\r
+ else if (first_at == ArrowType.Stroke)\r
+ g.draw(NORMAL_ARROW);\r
+\r
+ g.setTransform(at);\r
+ }\r
+\r
+ if (last_at != ArrowType.None) {\r
+ double theta = Math.atan2(lastdir.getY(), lastdir.getX()) - Math.PI/2;\r
+ g.translate(last.getX(), last.getY());\r
+ g.rotate(theta);\r
+ g.scale(lastsize, lastsize);\r
+\r
+ if (last_at == ArrowType.Fill)\r
+ g.fill(FILLED_ARROW);\r
+ else if (last_at == ArrowType.Stroke)\r
+ g.draw(NORMAL_ARROW);\r
+\r
+ g.setTransform(at);\r
+ }\r
+ }\r
+\r
+\r
+ public transient final static GeneralPath NORMAL_ARROW;\r
+ public transient final static GeneralPath FILLED_ARROW;\r
+ public transient static final Stroke ARROW_STROKE = new BasicStroke(1.0f);\r
+\r
+ static {\r
+ FILLED_ARROW = new GeneralPath();\r
+ FILLED_ARROW.moveTo(-0.5f, 1f);\r
+ FILLED_ARROW.lineTo( 0f, 0f);\r
+ FILLED_ARROW.lineTo( 0.5f, 1f);\r
+ FILLED_ARROW.closePath();\r
+\r
+ NORMAL_ARROW = new GeneralPath();\r
+ NORMAL_ARROW.moveTo(-0.5f, 1f);\r
+ NORMAL_ARROW.lineTo( 0f, 0f);\r
+ NORMAL_ARROW.lineTo( 0.5f, 1f);\r
+ }\r
+\r
+ @Override\r
+ public Rectangle2D getBoundsInLocal() {\r
+ return bounds;\r
+ }\r
+\r
+ protected Composite alphaComposite = null;\r
+ protected Stroke dynamicStroke = null;\r
+ protected Color dynamicColor = null;\r
+\r
+ @PropertySetter("alpha")\r
+ @SyncField("alphaComposite")\r
+ public void setAlphaComposite(Composite alphaComposite) {\r
+ this.alphaComposite = alphaComposite;\r
+ }\r
+\r
+ @PropertySetter("width")\r
+ @SyncField("dynamicStroke")\r
+ public void setDynamicStroke(Stroke stroke) {\r
+ this.dynamicStroke = stroke;\r
+ }\r
+\r
+ @PropertySetter("color")\r
+ @SyncField("dynamicColor")\r
+ public void setDynamicColor(Color color) {\r
+ this.dynamicColor = color;\r
+ }\r
+\r
+// public void setValue(String key, Object value) {\r
+//\r
+// if ("alpha".equals(key)) {\r
+// Float val = Float.parseFloat((String)value);\r
+// alphaComposite = AlphaComposite.getInstance(AlphaComposite. SRC_OVER, val);\r
+// } else if ("width".equals(key)) {\r
+// dynamicStroke = new BasicStroke(Float.parseFloat((String)value));\r
+// } else if ("color".equals(key)) {\r
+// try {\r
+// dynamicColor = new Color(Integer.parseInt(value.toString(), 16));\r
+// } catch (Throwable t) {\r
+// t.printStackTrace();\r
+// }\r
+// }\r
+// }\r
+\r
+ @Override\r
+ public void initValues() {\r
+ dynamicStroke = null;\r
+ dynamicColor = null;\r
+ alphaComposite = null;\r
+ }\r
+\r
+}\r