From 4389d9d78dea4dcfa91efeea0122bbb19817a568 Mon Sep 17 00:00:00 2001 From: lehtonen Date: Mon, 21 Jun 2010 15:19:41 +0000 Subject: [PATCH] Desperately working towards a g2d version of the sysdyn diagram editor. True crap still. Connections are not created properly on the configuration side. PointerInteractor/ConnectTool needs to be replaced by something that works better for sysdyn models. Not quite sure how to model the connections. git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@16266 ac1ea38d-2e2b-0410-8846-a27921b304fc --- org.simantics.sysdyn.ui/adapters.xml | 2 +- org.simantics.sysdyn.ui/plugin.xml | 5 +- .../sysdyn/ui/editor/DiagramViewer.java | 26 +- .../sysdyn/ui/elements2/AuxiliaryFactory.java | 2 +- .../ConfigurationDiagramClassAdapter.java | 46 ++ .../ui/elements2/SysdynConnectionClass.java | 545 ++++++++++++++++++ .../SysdynConnectionEdgeFactory.java | 43 ++ .../ui/elements2/SysdynConnectionFactory.java | 57 ++ .../sysdyn/ui/elements2/SysdynEdgeClass.java | 196 +++++++ .../org/simantics/sysdyn/SysdynResource.java | 3 + sysdyn_ontologies/sysdyn.graph | 2 + 11 files changed, 920 insertions(+), 7 deletions(-) create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/ConfigurationDiagramClassAdapter.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionClass.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionEdgeFactory.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionFactory.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynEdgeClass.java diff --git a/org.simantics.sysdyn.ui/adapters.xml b/org.simantics.sysdyn.ui/adapters.xml index 863bfe88..8fb25f46 100644 --- a/org.simantics.sysdyn.ui/adapters.xml +++ b/org.simantics.sysdyn.ui/adapters.xml @@ -25,5 +25,5 @@ - + \ No newline at end of file diff --git a/org.simantics.sysdyn.ui/plugin.xml b/org.simantics.sysdyn.ui/plugin.xml index 2b48bcc7..4d1fce63 100644 --- a/org.simantics.sysdyn.ui/plugin.xml +++ b/org.simantics.sysdyn.ui/plugin.xml @@ -37,8 +37,9 @@ + class="org.simantics.sysdyn.ui.editor.OpenDiagramFromConfigurationAdapter" + groupId="org.simantics.diagramEditor.group" + priority="100"> terminals) { diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/ConfigurationDiagramClassAdapter.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/ConfigurationDiagramClassAdapter.java new file mode 100644 index 00000000..6e4a945a --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/ConfigurationDiagramClassAdapter.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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.sysdyn.ui.elements2; + +import org.simantics.db.AsyncReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.adaption.Adapter; +import org.simantics.db.procedure.AsyncProcedure; +import org.simantics.diagram.adapter.DiagramClassAdapter; +import org.simantics.g2d.diagram.DiagramClass; +import org.simantics.g2d.diagram.DiagramHints; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.diagram.handler.LifeCycle; +import org.simantics.g2d.routing.RouterFactory; + +/** + * @author Tuukka Lehtonen + */ +public class ConfigurationDiagramClassAdapter implements Adapter { + + @Override + public void adapt(AsyncReadGraph g, Resource r, AsyncProcedure procedure) { + procedure.execute(g, DiagramClassAdapter.INSTANCE.newClassWith( + Initializer.INSTANCE + )); + } + + static class Initializer extends LifeCycle.Stub { + public static final Initializer INSTANCE = new Initializer(); + + @Override + public void onDiagramCreated(IDiagram diagram) { + diagram.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(false, false)); + } + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionClass.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionClass.java new file mode 100644 index 00000000..0291a898 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionClass.java @@ -0,0 +1,545 @@ +/******************************************************************************* + * 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.sysdyn.ui.elements2; + +import java.awt.Composite; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.simantics.g2d.connection.ConnectionEntity; +import org.simantics.g2d.connection.ConnectionEntity.ConnectionEvent; +import org.simantics.g2d.connection.ConnectionEntity.ConnectionListener; +import org.simantics.g2d.connection.handler.ConnectionHandler; +import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy; +import org.simantics.g2d.diagram.handler.Topology.Connection; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.ElementHints; +import org.simantics.g2d.element.ElementUtils; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.handler.Children; +import org.simantics.g2d.element.handler.InternalSize; +import org.simantics.g2d.element.handler.Outline; +import org.simantics.g2d.element.handler.Pick; +import org.simantics.g2d.element.handler.Pick2; +import org.simantics.g2d.element.handler.SceneGraph; +import org.simantics.g2d.element.handler.SelectionOutline; +import org.simantics.g2d.element.handler.Transform; +import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline; +import org.simantics.g2d.element.handler.impl.ParentImpl; +import org.simantics.g2d.element.handler.impl.SimpleElementLayers; +import org.simantics.g2d.element.handler.impl.TextImpl; +import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform; +import org.simantics.g2d.utils.GeometryUtils; +import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.g2d.IG2DNode; +import org.simantics.scenegraph.g2d.nodes.SingleElementNode; +import org.simantics.utils.datastructures.ListenerList; +import org.simantics.utils.datastructures.hints.IHintContext.Key; +import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; + +/** + * An element class for single connection entity elements. A sysdyn connection + * entity consists of a single edge. Sysdyn connections can't be branched. + * + * @author Tuukka Lehtonen + */ +public class SysdynConnectionClass { + + public static final ElementClass CLASS = + ElementClass.compile( + TextImpl.INSTANCE, + FixedTransform.INSTANCE, + ConnectionPick.INSTANCE, + ConnectionBounds.INSTANCE, + ConnectionSelectionOutline.INSTANCE, + ConnectionHandlerImpl.INSTANCE, + ConnectionChildren.INSTANCE, + ParentImpl.INSTANCE, + ConnectionSceneGraph.INSTANCE, + SimpleElementLayers.INSTANCE + ).setId(SysdynConnectionClass.class.getSimpleName()); + + private static final ThreadLocal> perThreadElementList = new ThreadLocal>() { + @Override + protected java.util.List initialValue() { + return new ArrayList(); + } + }; + + static class ConnectionHandlerImpl implements ConnectionHandler { + + public static final ConnectionHandlerImpl INSTANCE = new ConnectionHandlerImpl(); + + private static final long serialVersionUID = 3267139233182458330L; + + @Override + public Collection getBranchPoints(IElement connection, Collection result) { + ConnectionEntity entity = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (entity == null) + return Collections.emptySet(); + return entity.getBranchPoints(result); + } + + @Override + public Collection getChildren(IElement connection, Collection result) { + ConnectionEntity entity = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (entity == null) + return Collections.emptySet(); + result = entity.getSegments(result); + return entity.getBranchPoints(result); + } + + @Override + public Collection getSegments(IElement connection, Collection result) { + ConnectionEntity entity = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (entity == null) + return Collections.emptySet(); + return entity.getSegments(result); + } + + @Override + public Collection getTerminalConnections(IElement connection, Collection result) { + ConnectionEntity entity = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (entity == null) + return Collections.emptySet(); + return entity.getTerminalConnections(result); + } + + @Override + public IElement newBranchPoint(IElement connection) { + ConnectionEntity entity = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (entity == null) + throw new IllegalArgumentException("element '" + connection + "' is not a connection element"); + return entity.newBranchPoint(); + } + + @Override + public IElement newEdge(IElement connection) { + ConnectionEntity entity = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (entity == null) + throw new IllegalArgumentException("element '" + connection + "' is not a connection element"); + return entity.newEdge(); + } + + @Override + public void removeBranchPoint(IElement connection, IElement branchPoint) { + ConnectionEntity entity = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (entity == null) + throw new IllegalArgumentException("element '" + connection + "' is not a connection element"); + entity.removeBranchPoint(branchPoint); + } + + @Override + public void removeEdge(IElement connection, IElement edge) { + ConnectionEntity entity = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (entity == null) + throw new IllegalArgumentException("element '" + connection + "' is not a connection element"); + entity.removeEdge(edge); + } + } + + static final class ConnectionSceneGraph implements SceneGraph { + + public static final ConnectionSceneGraph INSTANCE = new ConnectionSceneGraph(); + + private static final long serialVersionUID = 4232871859964883266L; + + @Override + public void init(IElement connection, G2DParentNode parent) { + ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (ce == null) + return; + + // Painting is single-threaded, it is OK to use a single thread-local collection here. + List children = perThreadElementList.get(); + children.clear(); + ce.getSegments(children); + ce.getBranchPoints(children); + //new Exception("painting connection entity " + ce.hashCode() + " with " + children.size() + " segments and branch points").printStackTrace(); + if (children.isEmpty()) + return; + + Set tmp = new HashSet(); + + int zIndex = 0; + for (IElement child : children) { + ElementClass ec = child.getElementClass(); + + Transform transform = child.getElementClass().getSingleItem(Transform.class); + assert (transform != null); + AffineTransform at2 = transform.getTransform(child); + if (at2 == null) + continue; + + SingleElementNode holder = child.getHint(ElementHints.KEY_SG_NODE); + if (holder == null) { + holder = parent.addNode(ElementUtils.generateNodeId(child), SingleElementNode.class); + child.setHint(ElementHints.KEY_SG_NODE, holder); + } + holder.setZIndex(++zIndex); + + Composite composite = child.getHint(ElementHints.KEY_COMPOSITE); + + holder.setTransform(at2); + holder.setComposite(composite); + holder.setVisible(true); + + // New node handler + for (SceneGraph n : ec.getItemsByClass(SceneGraph.class)) { + n.init(child, holder); + } + tmp.add(holder); + } + + // Hide unaccessed nodes (but don't remove) + for (IG2DNode node : parent.getNodes()) { + if (node instanceof SingleElementNode) { + if (!tmp.contains(node)) { + ((SingleElementNode)node).setVisible(false); + } + } else { + //System.out.println("WHAT IS THIS: "); + //NodeDebug.printSceneGraph(((Node) node)); + } + } + + // Don't leave dangling references behind. + children.clear(); + } + + @Override + public void cleanup(IElement e) { + } + } + + static final class ConnectionBounds implements InternalSize, Outline { + + public static final ConnectionBounds INSTANCE = new ConnectionBounds(); + + private static final long serialVersionUID = 4232871859964883266L; + + @Override + public Rectangle2D getBounds(IElement e, Rectangle2D size) { + if (size == null) + size = new Rectangle2D.Double(); + size.setFrame(0, 0, 0, 0); + + ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (ce == null) + return size; + + Collection parts = ce.getSegments(null); + if (parts.isEmpty()) + return size; + parts = ce.getBranchPoints(parts); + + Rectangle2D temp = null; + for (IElement part : parts) { + // Using on-diagram coordinates because neither connections nor + // edges have a non-identity transform which means that + // coordinates are always absolute. Therefore branch point + // bounds also need to be calculated in absolute coordinates. + ElementUtils.getElementBoundsOnDiagram(part, size); + //System.out.println("InternalSize BOUNDS: " + size + " for part " + part); + if (temp == null) { + temp = new Rectangle2D.Double(); + temp.setRect(size); + } else + Rectangle2D.union(temp, size, temp); + //System.out.println("InternalSize Combined BOUNDS: " + temp); + } + size.setRect(temp); + return size; + } + + private Shape getSelectionShape(IElement forPart) { + for (SelectionOutline so : forPart.getElementClass().getItemsByClass(SelectionOutline.class)) { + Shape shape = so.getSelectionShape(forPart); + if (shape != null) + return shape; + } + // Using on-diagram coordinates because neither connections nor + // edges have a non-identity transform which means that + // coordinates are always absolute. Therefore branch point + // shape also needs to be calculated in absolute coordinates. + Shape shape = ElementUtils.getElementShapeOrBoundsOnDiagram(forPart); + return shape; + //return shape.getBounds2D(); + } + + @Override + public Shape getElementShape(IElement e) { + ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (ce == null) + return new Rectangle2D.Double(); + + Collection parts = ce.getSegments(null); + if (parts.isEmpty()) + return new Rectangle2D.Double(); + parts = ce.getBranchPoints(parts); + + if (parts.size() == 1) { + Shape shape = getSelectionShape(parts.iterator().next()); + //System.out.println("Outline SHAPE: " + shape); + //System.out.println("Outline BOUNDS: " + shape.getBounds2D()); + return shape; + } + + //System.out.println("Outline: " + e); + Area area = new Area(); + for (IElement part : parts) { + //System.out.println(part); + + Shape shape = getSelectionShape(part); + + Rectangle2D bounds = shape.getBounds2D(); +// System.out.println(" shape: " + shape); +// System.out.println(" bounds: " + bounds); + + if (bounds.isEmpty()) { + double w = bounds.getWidth(); + double h = bounds.getHeight(); + if (w <= 0.0 && h <= 0.0) + continue; + + // Need to expand shape in either width or height to make it visible. + final double exp = 0.1; + if (w <= 0.0) + shape = GeometryUtils.expandRectangle(bounds, 0, 0, exp, exp); + else if (h <= 0.0) + shape = GeometryUtils.expandRectangle(bounds, exp, exp, 0, 0); + } + + //System.out.println(" final shape: " + shape); + //shape = bounds; + + Area a = null; + if (shape instanceof Area) + a = (Area) shape; + else + a = new Area(shape); + area.add(a); + } + + //System.out.println(" connection area outline: " + area); + //System.out.println(" connection area outline bounds: " + area.getBounds2D()); + return area; + } + } + + public static class ConnectionPick implements Pick2 { + + public final static ConnectionPick INSTANCE = new ConnectionPick(); + + private static final long serialVersionUID = 1L; + + @Override + public boolean pickTest(IElement e, Shape s, PickPolicy policy) { + ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (ce == null) + return false; + + // Primarily pick branch points and then edges. + Collection parts = ce.getBranchPoints(null); + parts = ce.getSegments(parts); + if (parts.isEmpty()) + return false; + + for (IElement part : parts) { + for (Pick pick : part.getElementClass().getItemsByClass(Pick.class)) { + //System.out.println("TESTING: " + part + " : " + s + " : " + policy); + if (pick.pickTest(part, s, policy)) { + //System.out.println(" HIT!"); + return true; + } + } + } + + return false; + } + + @Override + public int pick(IElement e, Shape s, PickPolicy policy, Collection result) { + int oldResultSize = result.size(); + + ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (ce == null) + return 0; + + // Primarily pick branch points and then edges. + List parts = perThreadElementList.get(); + parts.clear(); + + ce.getSegments(parts); + int edges = parts.size(); + ce.getBranchPoints(parts); + int branchPoints = parts.size() - edges; + + boolean singleEdge = branchPoints == 0 && edges == 1; + + if (parts.isEmpty()) + return 0; + + // See whether the whole connection is to be picked.. + boolean pickConnection = false; + wholeConnectionPick: + for (Outline outline : e.getElementClass().getItemsByClass(Outline.class)) { + Shape elementShape = outline.getElementShape(e); + if (elementShape == null) + continue; + + switch (policy) { + case PICK_CONTAINED_OBJECTS: + if (GeometryUtils.contains(s, elementShape)) { + pickConnection = true; + break wholeConnectionPick; + } + break; + case PICK_INTERSECTING_OBJECTS: + if (GeometryUtils.intersects(s, elementShape)) { + pickConnection = true; + break wholeConnectionPick; + } + break; + } + } + + ArrayList picks = null; + + // Pick connection segments + for (int i = 0; i < edges; ++i) { + IElement part = parts.get(i); + for (Pick pick : part.getElementClass().getItemsByClass(Pick.class)) { + //System.out.println("TESTING SEGMENT: " + part + " : " + s + " : " + policy); + if (pick.pickTest(part, s, policy)) { + //System.out.println(" HIT!"); + if (picks == null) + picks = new ArrayList(4); + picks.add(part); + break; + } + } + } + + // Pick the whole connection ? + if (pickConnection) { + if (picks == null) + picks = new ArrayList(4); + picks.add(e); + } + + // Pick branch/route points + for (int i = edges; i < parts.size(); ++i) { + IElement part = parts.get(i); + for (Pick pick : part.getElementClass().getItemsByClass(Pick.class)) { + //System.out.println("TESTING BRANCHPOINT: " + part + " : " + s + " : " + policy); + if (pick.pickTest(part, s, policy)) { + //System.out.println(" HIT!"); + if (picks == null) + picks = new ArrayList(4); + picks.add(part); + break; + } + } + } + + if (picks != null) { + // Add the discovered pickable children to the result after the + // parent to make the parent the primary pickable. + // Skip the children if there is only one child. + if (!singleEdge) { + result.addAll(picks); + } else { + result.add(e); + } + } + + return result.size() - oldResultSize; + } + } + + private static final Key CHILD_LISTENERS = new KeyOf(ListenerList.class, "CHILD_LISTENERS"); + + public static class ConnectionChildren implements Children, ConnectionListener { + + public final static ConnectionChildren INSTANCE = new ConnectionChildren(); + + private static final long serialVersionUID = 1L; + + @Override + public Collection getChildren(IElement element, Collection result) { + ConnectionEntity ce = element.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (ce == null) { + if (result == null) + result = new ArrayList(0); + return result; + } + result = ce.getSegments(result); + result = ce.getBranchPoints(result); + return result; + } + + @Override + public void addChildListener(IElement element, ChildListener listener) { + ListenerList ll = null; + synchronized (element) { + ll = element.getHint(CHILD_LISTENERS); + if (ll == null) { + ll = new ListenerList(ChildListener.class); + element.setHint(CHILD_LISTENERS, ll); + ConnectionEntity entity = element.getHint(ElementHints.KEY_CONNECTION_ENTITY); + entity.setListener(this); + } + } + ll.add(listener); + } + + @Override + public void removeChildListener(IElement element, ChildListener listener) { + synchronized (element) { + ListenerList ll = element.getHint(CHILD_LISTENERS); + if (ll == null) + return; + ll.remove(listener); + if (ll.isEmpty()) { + ConnectionEntity entity = element.getHint(ElementHints.KEY_CONNECTION_ENTITY); + entity.setListener(null); + } + } + } + + @Override + public void connectionChanged(ConnectionEvent event) { + fireChildrenChanged(event); + } + + private void fireChildrenChanged(ConnectionEvent event) { + ListenerList ll = event.connection.getHint(CHILD_LISTENERS); + if (ll == null) + return; + ChildEvent ce = new ChildEvent(event.connection, event.removedParts, event.addedParts); + for (ChildListener cl : ll.getListeners()) { + cl.elementChildrenChanged(ce); + } + } + + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionEdgeFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionEdgeFactory.java new file mode 100644 index 00000000..180a2349 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionEdgeFactory.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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.sysdyn.ui.elements2; + +import org.simantics.db.AsyncReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.procedure.AsyncProcedure; +import org.simantics.diagram.adapter.ElementFactoryAdapter; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.element.ElementClass; + +/** + * An element class factory for sysdyn connection edge segments. + * + * @author Tuukka Lehtonen + */ +public class SysdynConnectionEdgeFactory extends ElementFactoryAdapter { + + private static final ElementClass CLASS = SysdynEdgeClass.CLASS; + + @Override + public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType, + AsyncProcedure procedure) { + procedure.execute(graph, CLASS); + } + + @Override + public void getClass(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementResource, + AsyncProcedure procedure) { + throw new UnsupportedOperationException(); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionFactory.java new file mode 100644 index 00000000..3023e544 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynConnectionFactory.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * 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.sysdyn.ui.elements2; + +import org.simantics.db.AsyncReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.procedure.AsyncProcedure; +import org.simantics.diagram.adapter.ElementFactoryAdapter; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; +import org.simantics.g2d.elementclass.connection.ConnectionClass; + +/** + * An element class for single connection entity elements. A connection entity + * consists of connection edge segments and branch points as its children. + * + * @author Tuukka Lehtonen + */ +public class SysdynConnectionFactory extends ElementFactoryAdapter { + + public static final ElementClass CLASS = SysdynConnectionClass.CLASS; + + @Override + public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType, final AsyncProcedure procedure) { + DiagramResource dr = graph.getService(DiagramResource.class); + graph.forSingleType(elementType, dr.Connection, new AsyncProcedure() { + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + procedure.exception(graph, throwable); + } + @Override + public void execute(AsyncReadGraph graph, Resource connectionType) { + procedure.execute(graph, ConnectionClass.CLASS.newClassWith(false, new StaticObjectAdapter(connectionType))); + } + }); + } + + @Override + public void load(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementResource, + final IElement element, final AsyncProcedure procedure) { + procedure.execute(graph, element); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynEdgeClass.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynEdgeClass.java new file mode 100644 index 00000000..95ace230 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynEdgeClass.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * 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.sysdyn.ui.elements2; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collection; + +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.diagram.handler.Topology; +import org.simantics.g2d.diagram.handler.Topology.Connection; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.ElementUtils; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.SceneGraphNodeKey; +import org.simantics.g2d.element.handler.BendsHandler; +import org.simantics.g2d.element.handler.EdgeVisuals; +import org.simantics.g2d.element.handler.EdgeVisuals.ArrowType; +import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd; +import org.simantics.g2d.element.handler.Rotate; +import org.simantics.g2d.element.handler.SceneGraph; +import org.simantics.g2d.element.handler.TerminalLayout; +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.BranchPoint; +import org.simantics.g2d.elementclass.connection.EdgeClass.EdgeHandler; +import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform; +import org.simantics.g2d.utils.PathUtils; +import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.g2d.nodes.EdgeNode; +import org.simantics.utils.datastructures.hints.IHintContext.Key; + +/** + * @author Toni Kalajainen + */ +public class SysdynEdgeClass { + + // TODO scale, rotate, move, transform + public static final ElementClass CLASS = + ElementClass.compile( + SysdynEdgeSceneGraph.INSTANCE, + EdgeHandler.INSTANCE, + ConfigurableEdgeVisuals.DEFAULT, + FillColorImpl.BLACK, + FixedTransform.INSTANCE, + ShapePick.INSTANCE, + ConnectionSelectionOutline.INSTANCE, + SimpleElementLayers.INSTANCE, + ParentImpl.INSTANCE + ).setId("EdgeClass.STRAIGHT"); + + public static class SysdynEdgeSceneGraph implements SceneGraph { + + private static final long serialVersionUID = 2914383071126238996L; + + public static final SysdynEdgeSceneGraph INSTANCE = new SysdynEdgeSceneGraph(); + + public static final Stroke ARROW_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); + + public static final Key KEY_SG_NODE = new SceneGraphNodeKey(EdgeNode.class, "EDGE_NODE"); + + @Override + public void init(IElement e, G2DParentNode parent) { + ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE, "edge_" + e.hashCode(), EdgeNode.class); + update(e); + } + + @Override + public void cleanup(IElement e) { + ElementUtils.removePossibleNode(e, KEY_SG_NODE); + } + + public void update(final IElement e) { + EdgeNode node = e.getHint(KEY_SG_NODE); + if(node == null) return; + + EdgeVisuals vh = e.getElementClass().getSingleItem(EdgeVisuals.class); + ArrowType at1 = vh.getArrowType(e, EdgeEnd.Begin); + ArrowType at2 = vh.getArrowType(e, EdgeEnd.End); + Stroke stroke = vh.getStroke(e); + //StrokeType strokeType = vh.getStrokeType(e); + double as1 = vh.getArrowSize(e, EdgeEnd.Begin); + double as2 = vh.getArrowSize(e, EdgeEnd.End); + + Color c = ElementUtils.getFillColor(e, Color.BLACK); + + // Get terminal shape for clipping the painted edge to its bounds. + IDiagram diagram = ElementUtils.peekDiagram(e); + Shape beginTerminalShape = null; + Shape endTerminalShape = null; + if (diagram != null) { + Topology topology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class); + if (topology != null) { + Connection beginConnection = topology.getConnection(e, EdgeEnd.Begin); + Connection endConnection = topology.getConnection(e, EdgeEnd.End); + beginTerminalShape = getTerminalShape(beginConnection); + endTerminalShape = getTerminalShape(endConnection); + int beginBranchDegree = getBranchPointDegree(beginConnection, topology); + int endBranchDegree = getBranchPointDegree(endConnection, topology); + if (beginBranchDegree > 0 && beginBranchDegree < 3) { + at1 = ArrowType.None; + } + if (endBranchDegree > 0 && endBranchDegree < 3) { + at2 = ArrowType.None; + } + } + } + + // Read bends + BendsHandler bh = e.getElementClass().getSingleItem(BendsHandler.class); + Path2D line = bh.getPath(e); + + boolean drawArrows = at1 != ArrowType.None || at2 != ArrowType.None; + //line = clipLineEnds(line, beginTerminalShape, endTerminalShape); + + Point2D first = new Point2D.Double(); + Point2D dir1 = new Point2D.Double(); + Point2D last = new Point2D.Double(); + Point2D dir2 = new Point2D.Double(); + PathIterator pi = line.getPathIterator(null); + drawArrows &= PathUtils.getPathArrows(pi, first, dir1, last, dir2); + + EdgeNode.ArrowType pat1 = convert(at1); + EdgeNode.ArrowType pat2 = convert(at2); + + node.init(new GeneralPath(line), stroke, c, dir1, dir2, first, last, as1, as2, pat1, pat2, null, null); + } + + private static EdgeNode.ArrowType convert(ArrowType at) { + switch (at) { + case None: return EdgeNode.ArrowType.None; + case Stroke: return EdgeNode.ArrowType.Stroke; + case Fill: return EdgeNode.ArrowType.Fill; + default: + throw new IllegalArgumentException("unsupported arrow type: " + at); + } + } + + private static final Rectangle2D EMPTY = new Rectangle2D.Double(); + + private static Shape getTerminalShape(Connection connection) { + if (connection != null && connection.node != null && connection.terminal != null) { + TerminalLayout layout = connection.node.getElementClass().getAtMostOneItemOfClass(TerminalLayout.class); + if (layout != null) { + //return layout.getTerminalShape(connection.node, connection.terminal); + Shape shp = layout.getTerminalShape(connection.node, connection.terminal); + Rotate rotate = connection.node.getElementClass().getAtMostOneItemOfClass(Rotate.class); + if (rotate == null) + return shp; + + double theta = rotate.getAngle(connection.node); + return AffineTransform.getRotateInstance(theta).createTransformedShape(shp); + } + } + return null; + } + + private final Collection connectionsTemp = new ArrayList(); + private int getBranchPointDegree(Connection connection, Topology topology) { + if (connection != null && connection.node != null) { + if (connection.node.getElementClass().containsClass(BranchPoint.class)) { + connectionsTemp.clear(); + topology.getConnections(connection.node, connection.terminal, connectionsTemp); + int degree = connectionsTemp.size(); + connectionsTemp.clear(); + return degree; + } + } + return -1; + } + + } + +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/SysdynResource.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/SysdynResource.java index b745a584..c58bc230 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/SysdynResource.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/SysdynResource.java @@ -27,6 +27,7 @@ public class SysdynResource { public final Resource CloudSymbol; public final Resource Configuration; public final Resource ConfigurationDiagram; + public final Resource Connection; public final Resource ConstantExpression; public final Resource DelayExpression; public final Resource Dependency; @@ -93,6 +94,7 @@ public class SysdynResource { public static final String CloudSymbol = "http://www.simantics.org/Sysdyn-1.0/CloudSymbol"; public static final String Configuration = "http://www.simantics.org/Sysdyn-1.0/Configuration"; public static final String ConfigurationDiagram = "http://www.simantics.org/Sysdyn-1.0/ConfigurationDiagram"; + public static final String Connection = "http://www.simantics.org/Sysdyn-1.0/Connection"; public static final String ConstantExpression = "http://www.simantics.org/Sysdyn-1.0/ConstantExpression"; public static final String DelayExpression = "http://www.simantics.org/Sysdyn-1.0/DelayExpression"; public static final String Dependency = "http://www.simantics.org/Sysdyn-1.0/Dependency"; @@ -169,6 +171,7 @@ public class SysdynResource { CloudSymbol = getResourceOrNull(graph, URIs.CloudSymbol); Configuration = getResourceOrNull(graph, URIs.Configuration); ConfigurationDiagram = getResourceOrNull(graph, URIs.ConfigurationDiagram); + Connection = getResourceOrNull(graph, URIs.Connection); ConstantExpression = getResourceOrNull(graph, URIs.ConstantExpression); DelayExpression = getResourceOrNull(graph, URIs.DelayExpression); Dependency = getResourceOrNull(graph, URIs.Dependency); diff --git a/sysdyn_ontologies/sysdyn.graph b/sysdyn_ontologies/sysdyn.graph index 16f50640..afbd29a8 100644 --- a/sysdyn_ontologies/sysdyn.graph +++ b/sysdyn_ontologies/sysdyn.graph @@ -390,3 +390,5 @@ CloudSymbol