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