/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g2d.elementclass.connection; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Path2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.List; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.element.ElementClass; import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.handler.BendsHandler; import org.simantics.g2d.element.handler.InternalSize; import org.simantics.g2d.element.handler.LifeCycle; import org.simantics.g2d.element.handler.Move; import org.simantics.g2d.element.handler.Outline; import org.simantics.g2d.element.handler.Parent; import org.simantics.g2d.element.handler.Transform; import org.simantics.g2d.element.handler.impl.ConfigurableEdgeVisuals; import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline; import org.simantics.g2d.element.handler.impl.FillColorImpl; import org.simantics.g2d.element.handler.impl.ParentImpl; import org.simantics.g2d.element.handler.impl.ShapePick; import org.simantics.g2d.element.handler.impl.SimpleElementLayers; import org.simantics.g2d.elementclass.PlainElementPropertySetter; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; /** * @author Toni Kalajainen */ public class EdgeClass { /** * A {@link Transform} and {@link Move} implementation suitable for edges * which are connected to nodes and cannot be moved by themselves. * *

* The {@link Transform} implementation is a simple one with support for a * parent element through {@link ElementHints#KEY_PARENT_ELEMENT}. The * {@link Move} implementation in turn is a stub which does nothing to make * edges immovable. * *

* FIXME: The more correct solution would be not to have a {@link Move} * handler at all but much the current participant code is very highly * dependent on having a single {@link Move} handler available that this * workaround seems better at this point. * * @author Tuukka Lehtonen */ public static class FixedTransform implements Transform, Move { private static final long serialVersionUID = 2287402413442694915L; public static final FixedTransform INSTANCE = new FixedTransform(); public static final AffineTransform IDENTITY = new AffineTransform(); @Override public AffineTransform getTransform(IElement e) { AffineTransform local = e.getHint(ElementHints.KEY_TRANSFORM); if (local == null) local = IDENTITY; Parent p = e.getElementClass().getAtMostOneItemOfClass(Parent.class); if (p == null) return local; IElement parentElement = p.getParent(e); if (parentElement == null) return local; AffineTransform parentTransform = parentElement.getElementClass().getSingleItem(Transform.class).getTransform(parentElement); if (parentTransform.isIdentity()) return local; AffineTransform result = new AffineTransform(local); result.preConcatenate(parentTransform); return result; } @Override public void setTransform(IElement e, AffineTransform at) { assert at != null; e.setHint(ElementHints.KEY_TRANSFORM, at); } @Override public Point2D getPosition(IElement e) { AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM); if (at == null) return new Point2D.Double(); return new Point2D.Double(at.getTranslateX(), at.getTranslateY()); } @Override public void moveTo(IElement e, double x, double y) { // Don't allow moving. } } // TODO scale, rotate, move, transform public static final ElementClass STRAIGHT = ElementClass.compile( EdgeSceneGraph.INSTANCE, EdgeHandler.INSTANCE, ConfigurableEdgeVisuals.DEFAULT, FillColorImpl.RED, FixedTransform.INSTANCE, ShapePick.INSTANCE, ConnectionSelectionOutline.INSTANCE, SimpleElementLayers.INSTANCE, ParentImpl.INSTANCE, new PlainElementPropertySetter(EdgeSceneGraph.KEY_SG_NODE) ).setId("EdgeClass.STRAIGHT"); public static class EdgeHandler implements BendsHandler, Outline, LifeCycle, InternalSize { private static final long serialVersionUID = -5949432471958957382L; public static final EdgeHandler INSTANCE = new EdgeHandler(); public static final Key KEY_PATH = new KeyOf(Path2D.class, "PATH"); public static final Key KEY_BOUNDS = new KeyOf(Rectangle2D.class, "BOUNDS"); public static final Key KEY_ANGLETYPE = new KeyOf(AngleType.class); public static final Key KEY_BENDS = new KeyOf(ArrayList.class, "BENDS"); public static class BendImpl implements Bend { Point2D pos; } /** * Reads bends, builds path, and writes it to KEY_PATH * @param e */ /* private void buildPath(IElement e) { ArrayList points = new ArrayList(); ElementUtils.getBends(e, points); Path2D path = GeometryUtils.buildPath(points); e.setHint(KEY_PATH, path); e.setHint(KEY_BOUNDS, path.getBounds2D()); }*/ @Override public void setAngleType(IElement e, AngleType angleType) { e.setHint(KEY_ANGLETYPE, angleType); } @Override public AngleType getAngleType(IElement e) { return e.getHint(KEY_ANGLETYPE); } @Override public Shape getElementShape(IElement e) { // Path2DOutlineShape no longer needed with ConnectionSelectionOutline // that uses Stroke.createStrokedShape. //return new Path2DOutlineShape((Path2D)e.getHint(KEY_PATH)); return e.getHint(KEY_PATH); } /* @Override public AffineTransform getTransform(IElement e) { AffineTransform at = GeometryUtils.IDENTITY; assert(at.isIdentity()); return at; } @Override public void setTransform(IElement e, AffineTransform at) { Path2D path = e.getHint(KEY_PATH); if (path==null) return; ArrayList list = e.getHint(KEY_BENDS); for (BendImpl bi : list) at.transform(bi.pos, bi.pos); buildPath(e); } @Override public Point2D getPosition(IElement e) { return new Point2D.Double(0, 0); } @Override public void moveTo(IElement e, double x, double y) { AffineTransform at = new AffineTransform(); at.setToTranslation(x, y); setTransform(e, at); } @Override public double getAngle(IElement e, ICanvasContext ctx) { return 0; } @Override public void rotate(IElement e, ICanvasContext ctx, double theta, Point2D origo) { AffineTransform at = new AffineTransform(); at.setToRotation(theta, origo.getX(), origo.getY()); setTransform(e, at); } @Override public Double getFixedAspectRatio(IElement e) { return null; } @Override public Point2D getMaximumScale(IElement e) { return null; } @Override public Point2D getMinimumScale(IElement e) { return null; } @Override public Point2D getScale(IElement e) { return new Point2D.Double(1, 1); } @Override public void setScale(IElement e, Point2D newScale) { AffineTransform at = new AffineTransform(); at.setToScale(newScale.getX(), newScale.getY()); setTransform(e, at); } */ @Override public void onElementActivated(IDiagram d, IElement e) { update(e); } @Override public void onElementCreated(IElement e) { e.setHint(KEY_PATH, new Path2D.Double(Path2D.WIND_NON_ZERO, 2)); e.setHint(KEY_ANGLETYPE, AngleType.RightAngled); e.setHint(KEY_BOUNDS, new Rectangle2D.Double()); e.setHint(KEY_BENDS, new ArrayList(2)); } @Override public void onElementDestroyed(IElement e) { // EdgeSGNode sg = e.getElementClass().getAtMostOneItemOfClass(EdgeSGNode.class); // if(sg != null) // sg.cleanup(e); } private void update(IElement e) { EdgeSceneGraph sg = e.getElementClass().getAtMostOneItemOfClass(EdgeSceneGraph.class); if(sg != null) sg.update(e); } @Override public void onElementDeactivated(IDiagram d, IElement e) { } @Override public Rectangle2D getBounds(IElement e, Rectangle2D size) { Rectangle2D rect = e.getHint(KEY_BOUNDS); if (size==null) size = new Rectangle2D.Double(); if (rect != null) size.setFrame(rect); return rect; } @Override public Bend addBend(IElement e, int index, Point2D pos) { ArrayList list = e.getHint(KEY_BENDS); BendImpl b = new BendImpl(); b.pos = new Point2D.Double(pos.getX(), pos.getY()); list.add(index, b); // buildPath(e); update(e); return b; } @Override public void getBendPosition(IElement e, Bend b, Point2D pos) { pos.setLocation( ((BendImpl)b).pos ); } @Override public int getBendsCount(IElement e) { ArrayList list = e.getHint(KEY_BENDS); return list.size(); } @Override public void getBends(IElement e, List bends) { ArrayList list = e.getHint(KEY_BENDS); bends.addAll(list); } @Override public boolean removeBend(IElement e, Bend b) { ArrayList list = e.getHint(KEY_BENDS); if (!list.remove(b)) return false; // buildPath(e); return true; } @Override public Path2D getPath(IElement e) { return e.getHint(KEY_PATH); } @Override public void setPath(IElement e, Path2D path) { e.setHint(KEY_PATH, path); e.setHint(KEY_BOUNDS, path.getBounds2D()); /* ArrayList list = e.getHint(KEY_BENDS); ArrayList positions = new ArrayList(); GeometryUtils.getPoints(path, positions); list.clear(); for (Point2D p : positions) { BendImpl bi = new BendImpl(); bi.pos = p; list.add(bi); }*/ update(e); } @Override public void moveBend(IElement e, Bend b, Point2D pos) { BendImpl bi = ((BendImpl)b); if (bi.pos.equals(pos)) return; bi.pos.setLocation(pos); // buildPath(e); update(e); } } public static class ControlPointKey extends Key { public final int index; final int hash; public ControlPointKey(int index) { super(); this.index = index; hash = getClass().hashCode() ^ index ^ 54392439; } @Override public boolean isValueAccepted(Object value) { return IElement.class.isInstance(value); } @Override public int hashCode() { return hash; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (getClass() != obj.getClass()) return false; ControlPointKey other = (ControlPointKey) obj; return other.index == index; } } }