1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g2d.elementclass.connection;
14 import java.awt.Shape;
15 import java.awt.geom.AffineTransform;
16 import java.awt.geom.Path2D;
17 import java.awt.geom.Point2D;
18 import java.awt.geom.Rectangle2D;
19 import java.util.ArrayList;
20 import java.util.List;
22 import org.simantics.g2d.diagram.IDiagram;
23 import org.simantics.g2d.element.ElementClass;
24 import org.simantics.g2d.element.ElementHints;
25 import org.simantics.g2d.element.IElement;
26 import org.simantics.g2d.element.handler.BendsHandler;
27 import org.simantics.g2d.element.handler.InternalSize;
28 import org.simantics.g2d.element.handler.LifeCycle;
29 import org.simantics.g2d.element.handler.Move;
30 import org.simantics.g2d.element.handler.Outline;
31 import org.simantics.g2d.element.handler.Parent;
32 import org.simantics.g2d.element.handler.Transform;
33 import org.simantics.g2d.element.handler.impl.ConfigurableEdgeVisuals;
34 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
35 import org.simantics.g2d.element.handler.impl.FillColorImpl;
36 import org.simantics.g2d.element.handler.impl.ParentImpl;
37 import org.simantics.g2d.element.handler.impl.ShapePick;
38 import org.simantics.g2d.element.handler.impl.SimpleElementLayers;
39 import org.simantics.g2d.elementclass.PlainElementPropertySetter;
40 import org.simantics.utils.datastructures.hints.IHintContext.Key;
41 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
44 * @author Toni Kalajainen
46 public class EdgeClass {
49 * A {@link Transform} and {@link Move} implementation suitable for edges
50 * which are connected to nodes and cannot be moved by themselves.
53 * The {@link Transform} implementation is a simple one with support for a
54 * parent element through {@link ElementHints#KEY_PARENT_ELEMENT}. The
55 * {@link Move} implementation in turn is a stub which does nothing to make
59 * FIXME: The more correct solution would be not to have a {@link Move}
60 * handler at all but much the current participant code is very highly
61 * dependent on having a single {@link Move} handler available that this
62 * workaround seems better at this point.
64 * @author Tuukka Lehtonen
66 public static class FixedTransform implements Transform, Move {
68 private static final long serialVersionUID = 2287402413442694915L;
70 public static final FixedTransform INSTANCE = new FixedTransform();
71 public static final AffineTransform IDENTITY = new AffineTransform();
74 public AffineTransform getTransform(IElement e) {
75 AffineTransform local = e.getHint(ElementHints.KEY_TRANSFORM);
79 Parent p = e.getElementClass().getAtMostOneItemOfClass(Parent.class);
83 IElement parentElement = p.getParent(e);
84 if (parentElement == null)
87 AffineTransform parentTransform = parentElement.getElementClass().getSingleItem(Transform.class).getTransform(parentElement);
88 if (parentTransform.isIdentity())
91 AffineTransform result = new AffineTransform(local);
92 result.preConcatenate(parentTransform);
97 public void setTransform(IElement e, AffineTransform at) {
99 e.setHint(ElementHints.KEY_TRANSFORM, at);
103 public Point2D getPosition(IElement e) {
104 AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM);
106 return new Point2D.Double();
107 return new Point2D.Double(at.getTranslateX(), at.getTranslateY());
111 public void moveTo(IElement e, double x, double y) {
112 // Don't allow moving.
116 // TODO scale, rotate, move, transform
117 public static final ElementClass STRAIGHT =
118 ElementClass.compile(
119 EdgeSceneGraph.INSTANCE,
120 EdgeHandler.INSTANCE,
121 ConfigurableEdgeVisuals.DEFAULT,
123 FixedTransform.INSTANCE,
125 ConnectionSelectionOutline.INSTANCE,
126 SimpleElementLayers.INSTANCE,
128 new PlainElementPropertySetter(EdgeSceneGraph.KEY_SG_NODE)
129 ).setId("EdgeClass.STRAIGHT");
132 public static class EdgeHandler implements BendsHandler, Outline, LifeCycle, InternalSize {
134 private static final long serialVersionUID = -5949432471958957382L;
136 public static final EdgeHandler INSTANCE = new EdgeHandler();
138 public static final Key KEY_PATH = new KeyOf(Path2D.class, "PATH");
139 public static final Key KEY_BOUNDS = new KeyOf(Rectangle2D.class, "BOUNDS");
140 public static final Key KEY_ANGLETYPE = new KeyOf(AngleType.class);
141 public static final Key KEY_BENDS = new KeyOf(ArrayList.class, "BENDS");
143 public static class BendImpl implements Bend {
148 * Reads bends, builds path, and writes it to KEY_PATH
152 private void buildPath(IElement e)
154 ArrayList<Point2D> points = new ArrayList<Point2D>();
155 ElementUtils.getBends(e, points);
156 Path2D path = GeometryUtils.buildPath(points);
157 e.setHint(KEY_PATH, path);
158 e.setHint(KEY_BOUNDS, path.getBounds2D());
162 public void setAngleType(IElement e, AngleType angleType) {
163 e.setHint(KEY_ANGLETYPE, angleType);
167 public AngleType getAngleType(IElement e) {
168 return e.getHint(KEY_ANGLETYPE);
172 public Shape getElementShape(IElement e) {
173 // Path2DOutlineShape no longer needed with ConnectionSelectionOutline
174 // that uses Stroke.createStrokedShape.
175 //return new Path2DOutlineShape((Path2D)e.getHint(KEY_PATH));
176 return e.getHint(KEY_PATH);
180 public AffineTransform getTransform(IElement e) {
181 AffineTransform at = GeometryUtils.IDENTITY;
182 assert(at.isIdentity());
187 public void setTransform(IElement e, AffineTransform at) {
188 Path2D path = e.getHint(KEY_PATH);
189 if (path==null) return;
190 ArrayList<BendImpl> list = e.getHint(KEY_BENDS);
191 for (BendImpl bi : list)
192 at.transform(bi.pos, bi.pos);
197 public Point2D getPosition(IElement e) {
198 return new Point2D.Double(0, 0);
202 public void moveTo(IElement e, double x, double y) {
203 AffineTransform at = new AffineTransform();
204 at.setToTranslation(x, y);
209 public double getAngle(IElement e, ICanvasContext ctx) {
214 public void rotate(IElement e, ICanvasContext ctx, double theta, Point2D origo) {
215 AffineTransform at = new AffineTransform();
216 at.setToRotation(theta, origo.getX(), origo.getY());
221 public Double getFixedAspectRatio(IElement e) {
226 public Point2D getMaximumScale(IElement e) {
231 public Point2D getMinimumScale(IElement e) {
236 public Point2D getScale(IElement e) {
237 return new Point2D.Double(1, 1);
241 public void setScale(IElement e, Point2D newScale) {
242 AffineTransform at = new AffineTransform();
243 at.setToScale(newScale.getX(), newScale.getY());
248 public void onElementActivated(IDiagram d, IElement e) {
253 public void onElementCreated(IElement e) {
254 e.setHint(KEY_PATH, new Path2D.Double(Path2D.WIND_NON_ZERO, 2));
255 e.setHint(KEY_ANGLETYPE, AngleType.RightAngled);
256 e.setHint(KEY_BOUNDS, new Rectangle2D.Double());
257 e.setHint(KEY_BENDS, new ArrayList<BendImpl>(2));
260 public void onElementDestroyed(IElement e) {
261 // EdgeSGNode sg = e.getElementClass().getAtMostOneItemOfClass(EdgeSGNode.class);
266 private void update(IElement e) {
267 EdgeSceneGraph sg = e.getElementClass().getAtMostOneItemOfClass(EdgeSceneGraph.class);
273 public void onElementDeactivated(IDiagram d, IElement e) {
277 public Rectangle2D getBounds(IElement e, Rectangle2D size) {
278 Rectangle2D rect = e.getHint(KEY_BOUNDS);
279 if (size==null) size = new Rectangle2D.Double();
286 public Bend addBend(IElement e, int index, Point2D pos) {
287 ArrayList<BendImpl> list = e.getHint(KEY_BENDS);
288 BendImpl b = new BendImpl();
289 b.pos = new Point2D.Double(pos.getX(), pos.getY());
297 public void getBendPosition(IElement e, Bend b, Point2D pos) {
298 pos.setLocation( ((BendImpl)b).pos );
302 public int getBendsCount(IElement e) {
303 ArrayList<BendImpl> list = e.getHint(KEY_BENDS);
308 public void getBends(IElement e, List<Bend> bends) {
309 ArrayList<BendImpl> list = e.getHint(KEY_BENDS);
314 public boolean removeBend(IElement e, Bend b) {
315 ArrayList<BendImpl> list = e.getHint(KEY_BENDS);
316 if (!list.remove(b)) return false;
322 public Path2D getPath(IElement e) {
323 return e.getHint(KEY_PATH);
327 public void setPath(IElement e, Path2D path) {
328 e.setHint(KEY_PATH, path);
329 e.setHint(KEY_BOUNDS, path.getBounds2D());
331 ArrayList<BendImpl> list = e.getHint(KEY_BENDS);
332 ArrayList<Point2D> positions = new ArrayList<Point2D>();
333 GeometryUtils.getPoints(path, positions);
335 for (Point2D p : positions) {
336 BendImpl bi = new BendImpl();
344 public void moveBend(IElement e, Bend b, Point2D pos) {
345 BendImpl bi = ((BendImpl)b);
346 if (bi.pos.equals(pos)) return;
347 bi.pos.setLocation(pos);
353 public static class ControlPointKey extends Key {
354 public final int index;
356 public ControlPointKey(int index)
360 hash = getClass().hashCode() ^ index ^ 54392439;
363 public boolean isValueAccepted(Object value) {
364 return IElement.class.isInstance(value);
367 public int hashCode() {
371 public boolean equals(Object obj) {
374 if (getClass() != obj.getClass())
376 ControlPointKey other = (ControlPointKey) obj;
377 return other.index == index;