From: niemisto Date: Wed, 18 Nov 2009 13:42:06 +0000 (+0000) Subject: git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@13137 ac1ea38d-2e2b... X-Git-Tag: simantics-1.0~123 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=5aca26b4239304dab5ae03d110583350bdf8c98d;p=simantics%2Fsysdyn.git git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@13137 ac1ea38d-2e2b-0410-8846-a27921b304fc --- diff --git a/org.simantics.sysdyn.ui/.classpath b/org.simantics.sysdyn.ui/.classpath new file mode 100644 index 00000000..8a8f1668 --- /dev/null +++ b/org.simantics.sysdyn.ui/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.simantics.sysdyn.ui/.hgignore b/org.simantics.sysdyn.ui/.hgignore new file mode 100644 index 00000000..73df90f6 --- /dev/null +++ b/org.simantics.sysdyn.ui/.hgignore @@ -0,0 +1,5 @@ +syntax: regexp +^bin/ + +syntax: glob +*.svn/* \ No newline at end of file diff --git a/org.simantics.sysdyn.ui/.project b/org.simantics.sysdyn.ui/.project new file mode 100644 index 00000000..2ebd5691 --- /dev/null +++ b/org.simantics.sysdyn.ui/.project @@ -0,0 +1,28 @@ + + + org.simantics.sysdyn.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.simantics.sysdyn.ui/.settings/org.eclipse.jdt.core.prefs b/org.simantics.sysdyn.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..e9cf5f10 --- /dev/null +++ b/org.simantics.sysdyn.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +#Tue Nov 10 13:35:16 EET 2009 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/org.simantics.sysdyn.ui/META-INF/MANIFEST.MF b/org.simantics.sysdyn.ui/META-INF/MANIFEST.MF new file mode 100644 index 00000000..6bd0fbf5 --- /dev/null +++ b/org.simantics.sysdyn.ui/META-INF/MANIFEST.MF @@ -0,0 +1,21 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Ui +Bundle-SymbolicName: org.simantics.sysdyn.ui;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Require-Bundle: org.simantics.h2d;bundle-version="1.0.0", + org.simantics.db;bundle-version="0.6.2", + org.simantics.db.management;bundle-version="0.6.2", + org.simantics.layer0.utils;bundle-version="0.6.2", + org.simantics.scenegraph;bundle-version="1.0.0", + org.junit4;bundle-version="4.5.0", + org.simantics.ui;bundle-version="1.0.0", + org.eclipse.ui;bundle-version="3.5.0", + org.eclipse.core.runtime;bundle-version="3.5.0", + org.simantics.objmap;bundle-version="0.1.0", + org.simantics.sysdyn;bundle-version="1.0.0", + org.simantics.project;bundle-version="1.0.0", + org.apache.log4j;bundle-version="1.2.15" +Bundle-Activator: org.simantics.sysdyn.ui.Activator +Bundle-ActivationPolicy: lazy diff --git a/org.simantics.sysdyn.ui/adapters.xml b/org.simantics.sysdyn.ui/adapters.xml new file mode 100644 index 00000000..3af512f2 --- /dev/null +++ b/org.simantics.sysdyn.ui/adapters.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/org.simantics.sysdyn.ui/build.properties b/org.simantics.sysdyn.ui/build.properties new file mode 100644 index 00000000..6f20375d --- /dev/null +++ b/org.simantics.sysdyn.ui/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/org.simantics.sysdyn.ui/doc/manual.mediawiki b/org.simantics.sysdyn.ui/doc/manual.mediawiki new file mode 100644 index 00000000..100ef978 --- /dev/null +++ b/org.simantics.sysdyn.ui/doc/manual.mediawiki @@ -0,0 +1,79 @@ +Possible connections between sysdyn elements: + +{| border="1" +|   +|   +| colspan="4", align="center" | To +|- +|   +|   +| Auxiliary +| Stock +| Valve +| Cloud +|- +| rowspan="4" | From +| Auxiliary +| dependency +| - +| dependency +| - +|- +| Stock +| dependency +| - +| flow / dependency +| - +|- +| Valve +| dependency +| flow +| dependency (?) +| flow +|- +| Cloud +| - +| - +| flow +| - +|} + +Connection actions between sysdyn elements: + +{| border="1" +|   +|   +| colspan="4", align="center" | To +|- +|   +|   +| Auxiliary +| Stock +| Valve +| Cloud +|- +| rowspan="4" | From +| Auxiliary +| dependency +| - +| dependency +| - +|- +| Stock +| dependency +| valve + 2 x flow +| flow / dependency +| valve + 2 x flow +|- +| Valve +| dependency +| flow +| dependency (?) +| flow +|- +| Cloud +| - +| valve + 2 x flow +| flow +| - +|} \ No newline at end of file diff --git a/org.simantics.sysdyn.ui/plugin.xml b/org.simantics.sysdyn.ui/plugin.xml new file mode 100644 index 00000000..6092710c --- /dev/null +++ b/org.simantics.sysdyn.ui/plugin.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/Activator.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/Activator.java new file mode 100644 index 00000000..15e8f4de --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/Activator.java @@ -0,0 +1,19 @@ +package org.simantics.sysdyn.ui; + +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.SimpleLayout; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +public class Activator extends AbstractUIPlugin { + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + ConsoleAppender appender = + new ConsoleAppender(new SimpleLayout()); + BasicConfigurator.configure(appender); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/TestDiagramEditor.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/TestDiagramEditor.java new file mode 100644 index 00000000..74e1bc9d --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/TestDiagramEditor.java @@ -0,0 +1,44 @@ +package org.simantics.sysdyn.ui; + +import javax.swing.JFrame; + +import org.simantics.h2d.canvas.EditorCanvas; +import org.simantics.h2d.diagram.Diagram; +import org.simantics.h2d.editor.impl.DiagramEditor; +import org.simantics.h2d.event.handler.DefaultEventHandlers; +import org.simantics.sysdyn.ui.actions.Connect; +import org.simantics.sysdyn.ui.actions.CreateAuxiliary; +import org.simantics.sysdyn.ui.actions.CreateCloud; +import org.simantics.sysdyn.ui.actions.CreateStock; +import org.simantics.sysdyn.ui.actions.CreateValve; + +public class TestDiagramEditor extends JFrame { + + public TestDiagramEditor() { + super("Test Canvas"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + Diagram diagram = new Diagram(); + final DiagramEditor editor = new DiagramEditor(getRootPane(), diagram); + + DefaultEventHandlers.configure(editor); + editor.addEventHandler(1, "key(A)", new CreateAuxiliary()); + editor.addEventHandler(1, "key(S)", new CreateStock()); + editor.addEventHandler(1, "key(V)", new CreateValve()); + editor.addEventHandler(1, "key(C)", new CreateCloud()); + editor.addEventHandler(1, "drag(alt+left)", new Connect()); + + getContentPane().add(new EditorCanvas(editor)); + + setSize(800, 600); + } + + public static void main(String[] args) { + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void run() { + new TestDiagramEditor().setVisible(true); + } + }); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/Connect.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/Connect.java new file mode 100644 index 00000000..6354acda --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/Connect.java @@ -0,0 +1,122 @@ +package org.simantics.sysdyn.ui.actions; + +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.simantics.h2d.editor.IDiagramEditor; +import org.simantics.h2d.element.IElement; +import org.simantics.h2d.element.handler.Connectable; +import org.simantics.h2d.event.DragEvent; +import org.simantics.h2d.event.handler.DragEventHandler; +import org.simantics.h2d.node.RectangleNode; +import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.sysdyn.ui.elements.AuxiliaryElement; +import org.simantics.sysdyn.ui.elements.CloudElement; +import org.simantics.sysdyn.ui.elements.DependencyElement; +import org.simantics.sysdyn.ui.elements.FlowElement; +import org.simantics.sysdyn.ui.elements.StockElement; +import org.simantics.sysdyn.ui.elements.ValveElement; + +public class Connect extends DragEventHandler { + + Connectable from; + Connectable to; + + RectangleNode fromNode = new RectangleNode(); + RectangleNode toNode = new RectangleNode(); + + @Override + protected boolean begin(IDiagramEditor editor, DragEvent event) { + for(IElement element : event.pickedElements) { + Connectable connectable = element.getInterface(Connectable.class); + if(connectable != null) { + this.from = connectable; + return true; + } + } + return false; + } + + @Override + protected void update(IDiagramEditor editor, DragEvent event) { + for(IElement element : editor.pickElements(event.current)) { + Connectable connectable = element.getInterface(Connectable.class); + if(connectable != null && connectable != from) { + if(connectable == to) + return; + to = connectable; + + Rectangle2D bounds = new Rectangle2D.Double(); + to.getBounds(bounds); + bounds.setFrame( + bounds.getX()-2.0, + bounds.getY()-2.0, + bounds.getWidth()+4.0, + bounds.getHeight()+4.0); + toNode.init(bounds); + + editor.requestRepaint(); + return; + } + } + to = null; + toNode.init(fromNode.getBounds()); + editor.requestRepaint(); + } + + @Override + protected void end(IDiagramEditor editor, DragEvent event) { + if(to != null) { + IElement newElement; + if(from instanceof ValveElement && to instanceof StockElement) + newElement = new FlowElement(from, to); + else if(from instanceof StockElement && to instanceof ValveElement) + newElement = new FlowElement(from, to); + else if( (from instanceof StockElement || from instanceof CloudElement) + && (to instanceof StockElement || to instanceof CloudElement)) { + Point2D fromOrigo = from.getOrigo(); + Point2D toOrigo = to.getOrigo(); + newElement = new ValveElement( + 0.5 * (fromOrigo.getX() + toOrigo.getX()), + 0.5 * (fromOrigo.getY() + toOrigo.getY()) + ); + editor.getDiagram().addElement(new FlowElement(from, (ValveElement)newElement)); + editor.getDiagram().addElement(new FlowElement(to, (ValveElement)newElement)); + } + else if(from instanceof ValveElement && to instanceof AuxiliaryElement) + newElement = new DependencyElement(to, from); + else if(from instanceof AuxiliaryElement && to instanceof StockElement) + newElement = new DependencyElement(to, from); + else + newElement = new DependencyElement(from, to); + editor.getDiagram().addElement(newElement); + } + + from = null; + to = null; + } + + @Override + public void init(G2DParentNode parent) { + fromNode = parent.addNode(RectangleNode.class); + toNode = parent.addNode(RectangleNode.class); + + Rectangle2D bounds = new Rectangle2D.Double(); + from.getBounds(bounds); + bounds.setFrame( + bounds.getX()-2.0, + bounds.getY()-2.0, + bounds.getWidth()+4.0, + bounds.getHeight()+4.0); + + fromNode.init(bounds); + toNode.init(bounds); + } + + @Override + public void remove() { + fromNode.remove(); + toNode.remove(); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateAuxiliary.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateAuxiliary.java new file mode 100644 index 00000000..3e6977ed --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateAuxiliary.java @@ -0,0 +1,24 @@ +package org.simantics.sysdyn.ui.actions; + +import org.simantics.h2d.editor.IDiagramEditor; +import org.simantics.h2d.event.IEvent; +import org.simantics.h2d.event.ILocatableEvent; +import org.simantics.h2d.event.handler.IEventHandler; +import org.simantics.sysdyn.ui.elements.AuxiliaryElement; +import org.simantics.sysdyn.ui.elements.StockElement; + +public class CreateAuxiliary implements IEventHandler { + + @Override + public boolean handle(IDiagramEditor editor, IEvent _event) { + ILocatableEvent event = (ILocatableEvent)_event; + AuxiliaryElement element = new AuxiliaryElement("Auxiliary", + event.getLocation().getX(), + event.getLocation().getY()); + editor.getDiagram().addElement(element); + element.beginRenameAction(editor); + editor.requestRepaint(); + return true; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateCloud.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateCloud.java new file mode 100644 index 00000000..b201f375 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateCloud.java @@ -0,0 +1,22 @@ +package org.simantics.sysdyn.ui.actions; + +import org.simantics.h2d.editor.IDiagramEditor; +import org.simantics.h2d.event.IEvent; +import org.simantics.h2d.event.ILocatableEvent; +import org.simantics.h2d.event.handler.IEventHandler; +import org.simantics.sysdyn.ui.elements.CloudElement; + +public class CreateCloud implements IEventHandler { + + @Override + public boolean handle(IDiagramEditor editor, IEvent _event) { + ILocatableEvent event = (ILocatableEvent)_event; + CloudElement element = new CloudElement( + event.getLocation().getX(), + event.getLocation().getY()); + editor.getDiagram().addElement(element); + editor.requestRepaint(); + return true; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateStock.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateStock.java new file mode 100644 index 00000000..99238f11 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateStock.java @@ -0,0 +1,23 @@ +package org.simantics.sysdyn.ui.actions; + +import org.simantics.h2d.editor.IDiagramEditor; +import org.simantics.h2d.event.IEvent; +import org.simantics.h2d.event.ILocatableEvent; +import org.simantics.h2d.event.handler.IEventHandler; +import org.simantics.sysdyn.ui.elements.StockElement; + +public class CreateStock implements IEventHandler { + + @Override + public boolean handle(IDiagramEditor editor, IEvent _event) { + ILocatableEvent event = (ILocatableEvent)_event; + StockElement element = new StockElement("Stock", + event.getLocation().getX(), + event.getLocation().getY()); + editor.getDiagram().addElement(element); + element.beginRenameAction(editor); + editor.requestRepaint(); + return true; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateValve.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateValve.java new file mode 100644 index 00000000..88050538 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/actions/CreateValve.java @@ -0,0 +1,21 @@ +package org.simantics.sysdyn.ui.actions; + +import org.simantics.h2d.editor.IDiagramEditor; +import org.simantics.h2d.event.IEvent; +import org.simantics.h2d.event.ILocatableEvent; +import org.simantics.h2d.event.handler.IEventHandler; +import org.simantics.sysdyn.ui.elements.ValveElement; + +public class CreateValve implements IEventHandler { + + @Override + public boolean handle(IDiagramEditor editor, IEvent _event) { + ILocatableEvent event = (ILocatableEvent)_event; + editor.getDiagram().addElement(new ValveElement( + event.getLocation().getX(), + event.getLocation().getY())); + editor.requestRepaint(); + return true; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/SysdynDiagramEditor.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/SysdynDiagramEditor.java new file mode 100644 index 00000000..006093c5 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/SysdynDiagramEditor.java @@ -0,0 +1,210 @@ +package org.simantics.sysdyn.ui.editor; + +import java.awt.Frame; +import java.util.ArrayList; + +import javax.swing.SwingUtilities; + +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.awt.SWT_AWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.PartInitException; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.h2d.canvas.EditorCanvas; +import org.simantics.h2d.diagram.IDiagram; +import org.simantics.h2d.diagram.IDiagramListener; +import org.simantics.h2d.editor.ISelection; +import org.simantics.h2d.editor.ISelectionListener; +import org.simantics.h2d.editor.impl.DiagramEditor; +import org.simantics.h2d.element.IElement; +import org.simantics.h2d.element.IElementListener; +import org.simantics.h2d.event.handler.DefaultEventHandlers; +import org.simantics.objmap.IMapping; +import org.simantics.objmap.IMappingListener; +import org.simantics.objmap.MappingException; +import org.simantics.objmap.Mappings; +import org.simantics.sysdyn.ui.actions.Connect; +import org.simantics.sysdyn.ui.actions.CreateAuxiliary; +import org.simantics.sysdyn.ui.actions.CreateCloud; +import org.simantics.sysdyn.ui.actions.CreateStock; +import org.simantics.sysdyn.ui.actions.CreateValve; +import org.simantics.ui.SimanticsUI; +import org.simantics.ui.workbench.ResourceEditorPart; +import org.simantics.utils.ui.jface.ActiveSelectionProvider; + +public class SysdynDiagramEditor extends ResourceEditorPart { + + EditorCanvas canvas; + IDiagram diagram; + IMapping mapping; + Frame frame; + + IElementListener elementUpdateListener = new IElementListener() { + + @Override + public void elementUpdated(IElement element) { + mapping.rangeModified(element); + } + + @Override + public void elementRemoved(IElement element) { + } + }; + + protected void readDiagram(ReadGraph g) throws DatabaseException { + SysdynDiagramSchema schema = new SysdynDiagramSchema(g); + mapping = Mappings.createWithListening(schema); + + try { + diagram = (IDiagram)mapping.map(g, getInputResource()); + } catch (MappingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + final Session session = g.getSession(); + mapping.addMappingListener(new IMappingListener() { + + @Override + public void rangeModified() { + session.asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) + throws DatabaseException { + try { + mapping.updateDomain(graph); + } catch(Exception e) { + e.printStackTrace(); + } + } + + }); + } + + @Override + public void domainModified() { + session.asyncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph graph) throws DatabaseException { + try { + mapping.updateRange(graph); + } catch(Exception e) { + e.printStackTrace(); + } + } + }); + } + + }); + + diagram.addDiagramListener(new IDiagramListener() { + + @Override + public void elementAdded(IElement element) { + mapping.rangeModified(diagram); + element.addListener(elementUpdateListener); + } + + @Override + public void elementRemoved(IElement element) { + mapping.rangeModified(diagram); + } + + }); + } + + @Override + public void init(IEditorSite site, IEditorInput input) + throws PartInitException { + super.init(site, input); + + try { + SimanticsUI.getSession().syncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph g) throws DatabaseException { + readDiagram(g); + } + + }); + } catch (DatabaseException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + public void createPartControl(Composite parent) { + final Composite composite = new Composite(parent, + SWT.NO_BACKGROUND | SWT.EMBEDDED); + frame = SWT_AWT.new_Frame(composite); + + final ActiveSelectionProvider selectionProvider = new ActiveSelectionProvider(); + getSite().setSelectionProvider(selectionProvider); + + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + DiagramEditor editor = new DiagramEditor(null, diagram); + + DefaultEventHandlers.configure(editor); + editor.addEventHandler(1, "key(A)", new CreateAuxiliary()); + editor.addEventHandler(1, "key(S)", new CreateStock()); + editor.addEventHandler(1, "key(V)", new CreateValve()); + editor.addEventHandler(1, "key(C)", new CreateCloud()); + editor.addEventHandler(1, "drag(alt+left)", new Connect()); + + canvas = new EditorCanvas(editor); + frame.add(canvas); + + canvas.requestFocus(); + + editor.getSelection().addSelectionListener(new ISelectionListener() { + + @Override + public void selectionChanged(ISelection selection) { + final ArrayList resources = new ArrayList(selection.size()); + for(IElement element : selection) + resources.add(mapping.inverseGet(element)); + composite.getDisplay().asyncExec(new Runnable() { + + @Override + public void run() { + selectionProvider.setSelection(new StructuredSelection(resources)); + } + + }); + } + + }); + } + + }); + } + + @Override + public void setFocus() { + if(canvas != null) + canvas.requestFocus(); + } + + @Override + public void dispose() { + mapping.dispose(); + frame.dispose(); + + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/SysdynDiagramSchema.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/SysdynDiagramSchema.java new file mode 100644 index 00000000..bdcf5689 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/SysdynDiagramSchema.java @@ -0,0 +1,54 @@ +package org.simantics.sysdyn.ui.editor; + +import java.util.Collection; + +import org.simantics.db.Builtins; +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.h2d.diagram.Diagram; +import org.simantics.objmap.rules.MappedElementsRule; +import org.simantics.objmap.rules.domain.RelatedObjectsAccessor; +import org.simantics.objmap.rules.range.FieldAccessor; +import org.simantics.objmap.schema.MappingSchemas; +import org.simantics.objmap.schema.SimpleLinkType; +import org.simantics.objmap.schema.SimpleSchema; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.ui.elements.AuxiliaryElement; +import org.simantics.sysdyn.ui.elements.CloudElement; +import org.simantics.sysdyn.ui.elements.DependencyElement; +import org.simantics.sysdyn.ui.elements.FlowElement; +import org.simantics.sysdyn.ui.elements.StockElement; +import org.simantics.sysdyn.ui.elements.ValveElement; + +public class SysdynDiagramSchema extends SimpleSchema { + + public SysdynDiagramSchema(ReadGraph g) throws DatabaseException { + Builtins b = g.getBuiltins(); + SysdynResource sr = SysdynResource.getInstance(g); + try { + { + SimpleLinkType linkType = + new SimpleLinkType(sr.Configuration, Diagram.class); + linkType.addRule(new MappedElementsRule( + new RelatedObjectsAccessor(b.ConsistsOf), + new FieldAccessor>(Diagram.class.getField("elements")) + )); + addLinkType(linkType); + } + + addLinkType(MappingSchemas.fromAnnotations(g, AuxiliaryElement.class)); + addLinkType(MappingSchemas.fromAnnotations(g, StockElement.class)); + addLinkType(MappingSchemas.fromAnnotations(g, CloudElement.class)); + addLinkType(MappingSchemas.fromAnnotations(g, ValveElement.class)); + addLinkType(MappingSchemas.fromAnnotations(g, DependencyElement.class)); + addLinkType(MappingSchemas.fromAnnotations(g, FlowElement.class)); + } catch(NoSuchFieldException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/Arcs.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/Arcs.java new file mode 100644 index 00000000..89d1dc08 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/Arcs.java @@ -0,0 +1,154 @@ +package org.simantics.sysdyn.ui.elements; + +import java.awt.geom.Rectangle2D; + +public class Arcs { + + public static final double PI2 = Math.PI*2.0; + + /** + * Returns angle + 2PI * n such that the + * result is between -PI and PI. + */ + public static double normalizeAngle(double angle) { + return Math.IEEEremainder(angle, PI2); + } + + /** + * Returns true, if three normalized angles are clockwise oriented. + */ + public static boolean areClockwiseOrdered(double angle1, double angle2, double angle3) { + //System.out.println(angle1 + " " + angle2 + " " + angle3); + return angle1 < angle2 + ? (angle2 < angle3 || angle3 < angle1) + : (angle2 < angle3 && angle3 < angle1) + ; + } + + /** + * Returns an angle in radians between straight line from (x0,y0) to (x2,y2) + * and an arc from (x0,y0) to (x2,y2) thru (x1,y1). The angle + * is measured at (x0,y0) and is between -PI and PI. + */ + public static double angleOfArc( + double x0, double y0, + double x1, double y1, + double x2, double y2) { + double dx0 = x1-x0; + double dy0 = y1-y0; + double dx1 = x1-x2; + double dy1 = y1-y2; + double dx = x2-x0; + double dy = y2-y0; + // Length of cross product (p1-p0)x(p2-p0) + double dd = dx0*dy - dy0*dx; + + if(Math.abs(dd) < 1e-6) // Points are (almost) collinear + return 0.0; + else { + // (p1-p0)*(p1-p2) / dd + double offset = (dx0*dx1 + dy0*dy1) / dd; + double angle = Math.PI*0.5 - Math.atan(offset); + if(dd > 0.0) + angle = angle-Math.PI; + return angle; + + } + } + + private static double updateBestNextAngle(double curAngle, double bestAngle, double newAngle) { + if(newAngle < curAngle) + newAngle += PI2; + if(newAngle < bestAngle) + return newAngle; + return bestAngle; + } + + private static double updateBestPrevAngle(double curAngle, double bestAngle, double newAngle) { + if(newAngle > curAngle) + newAngle -= PI2; + if(newAngle > bestAngle) + return newAngle; + return bestAngle; + } + + public static double nextIntersectingAngle(double cx, double cy, double r, + double curAngle, Rectangle2D rect, boolean dir) { + if(!dir) { + double bestAngle = curAngle + PI2; + { + double dx = rect.getMinX() - cx; + if(Math.abs(dx) < r) { + double angle = normalizeAngle(Math.acos(dx / r)); + bestAngle = updateBestNextAngle(curAngle, bestAngle, angle); + bestAngle = updateBestNextAngle(curAngle, bestAngle, -angle); + } + } + { + double dx = rect.getMaxX() - cx; + if(Math.abs(dx) < r) { + double angle = normalizeAngle(Math.acos(dx / r)); + bestAngle = updateBestNextAngle(curAngle, bestAngle, angle); + bestAngle = updateBestNextAngle(curAngle, bestAngle, -angle); + } + } + { + double dy = cy - rect.getMinY(); + if(Math.abs(dy) < r) { + double angle = Math.asin(dy / r); + bestAngle = updateBestNextAngle(curAngle, bestAngle, angle); + bestAngle = updateBestNextAngle(curAngle, bestAngle, + normalizeAngle(Math.PI-angle)); + } + } + { + double dy = cy - rect.getMaxY(); + if(Math.abs(dy) < r) { + double angle = Math.asin(dy / r); + bestAngle = updateBestNextAngle(curAngle, bestAngle, angle); + bestAngle = updateBestNextAngle(curAngle, bestAngle, + normalizeAngle(Math.PI-angle)); + } + } + return normalizeAngle(bestAngle); + } + else { + double bestAngle = curAngle - PI2; + { + double dx = rect.getMinX() - cx; + if(Math.abs(dx) < r) { + double angle = normalizeAngle(Math.acos(dx / r)); + bestAngle = updateBestPrevAngle(curAngle, bestAngle, angle); + bestAngle = updateBestPrevAngle(curAngle, bestAngle, -angle); + } + } + { + double dx = rect.getMaxX() - cx; + if(Math.abs(dx) < r) { + double angle = normalizeAngle(Math.acos(dx / r)); + bestAngle = updateBestPrevAngle(curAngle, bestAngle, angle); + bestAngle = updateBestPrevAngle(curAngle, bestAngle, -angle); + } + } + { + double dy = cy - rect.getMinY(); + if(Math.abs(dy) < r) { + double angle = Math.asin(dy / r); + bestAngle = updateBestPrevAngle(curAngle, bestAngle, angle); + bestAngle = updateBestPrevAngle(curAngle, bestAngle, + normalizeAngle(Math.PI-angle)); + } + } + { + double dy = cy - rect.getMaxY(); + if(Math.abs(dy) < r) { + double angle = Math.asin(dy / r); + bestAngle = updateBestPrevAngle(curAngle, bestAngle, angle); + bestAngle = updateBestPrevAngle(curAngle, bestAngle, + normalizeAngle(Math.PI-angle)); + } + } + return normalizeAngle(bestAngle); + } + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/AuxiliaryElement.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/AuxiliaryElement.java new file mode 100644 index 00000000..e10f97f6 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/AuxiliaryElement.java @@ -0,0 +1,15 @@ +package org.simantics.sysdyn.ui.elements; + +import org.simantics.objmap.annotations.GraphType; + +@GraphType("http://www.simantics.org/Sysdyn#Auxiliary") +public class AuxiliaryElement extends TextElement { + + public AuxiliaryElement() { + } + + public AuxiliaryElement(String label, double x, double y) { + super(label, x, y); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/CloudElement.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/CloudElement.java new file mode 100644 index 00000000..97c974d1 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/CloudElement.java @@ -0,0 +1,71 @@ +package org.simantics.sysdyn.ui.elements; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.geom.Path2D; + +import org.simantics.h2d.element.handler.Connectable; +import org.simantics.h2d.node.ShapeNode; +import org.simantics.objmap.annotations.GraphType; +import org.simantics.scenegraph.g2d.G2DParentNode; + +@GraphType("http://www.simantics.org/Sysdyn#Cloud") +public class CloudElement extends RectangularElement implements Connectable { + + public static final double CLOUD_SIZE_X = 5.0; + public static final double CLOUD_SIZE_Y = 3.0; + + public static final double CLOUD_CURVES = 7; + + ShapeNode cloudNode; + boolean rotated = false; + + public CloudElement() { + } + + public CloudElement(double x, double y) { + this.posX = x; + this.posY = y; + } + + @Override + public void init(G2DParentNode parent) { + cloudNode = parent.addNode(ShapeNode.class); + cloudNode.setColor(Color.BLACK); + cloudNode.setScaleStroke(true); + cloudNode.setStroke(new BasicStroke(1)); + update(); + } + + @Override + public void remove() { + cloudNode.remove(); + } + + @Override + protected void update() { + //bounds.setFrame(posX-CLOUD_SIZE_X, posY-CLOUD_SIZE_Y, 2.0*CLOUD_SIZE_X, 2.0*CLOUD_SIZE_Y); + Path2D path = new Path2D.Double(); + double ox = CLOUD_SIZE_X; + double oy = 0.0; + path.moveTo(posX+ox, posY+oy); + for(int i=1;i 0.0); + double extent = angle1-angle0; + //double arcAngle = angle0; + if(angle < 0.0) { + double temp = angle0; + angle0 = angle1; + angle1 = temp; + extent = -extent; + } + if(extent < 0) + extent += Math.PI*2.0; + else if(extent >= 360.0) + extent -= Math.PI*2.0; + Shape shape = new Arc2D.Double(cx-r, cy-r, 2*r, 2*r, + Math.toDegrees(angle0), + Math.toDegrees(extent), + Arc2D.OPEN); + arcNode.setShape(shape); + + double xx = Math.cos(angle > 0.0 ? angle1 : angle0); + double yy = -Math.sin(angle > 0.0 ? angle1 : angle0); + Shape arrowShape = createArrow(cx + r*xx, cy + r*yy, + angle < 0.0 ? -yy : yy, + angle > 0.0 ? -xx : xx); + arrowNode.setShape(arrowShape); + } + + public void update() { + if(arcNode != null) + updateSceneGraph(); + super.update(); + } + + @Override + public void getBounds(Rectangle2D bounds) { + bounds.setFrame(arcNode.getBounds()); + } + + @Override + public boolean hitTest(double x, double y, double tolerance) { + double dx = x-cx; + double dy = y-cy; + double dist = dx*dx + dy*dy; + if(dist < (r+tolerance)*(r+tolerance) && + dist > (r-tolerance)*(r-tolerance)) { + double angle = Arcs.normalizeAngle(Math.atan2(-dy, dx)); + if(Arcs.areClockwiseOrdered(angle0, angle, angle1)) + return true; + } + return false; + } + + class EventHandler extends DragEventHandler { + @Override + protected boolean begin(IDiagramEditor editor, DragEvent event) { + return event.startModifiers.equals("left"); + } + + @Override + protected void update(IDiagramEditor editor, DragEvent event) { + angle = Arcs.angleOfArc( + tail.getOrigo().getX(), tail.getOrigo().getY(), + event.current.getX(), event.current.getY(), + head.getOrigo().getX(), head.getOrigo().getY() + ); + DependencyElement.this.update(); + editor.requestRepaint(); + } + } + + @Override + public T getInterface(Class clazz) { + if(clazz == IEventHandler.class) + return (T)new EventHandler(); + return super.getInterface(clazz); + } + + @Override + public void elementRemoved(IElement element) { + remove(); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/FlowElement.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/FlowElement.java new file mode 100644 index 00000000..2642aa52 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/FlowElement.java @@ -0,0 +1,183 @@ +package org.simantics.sysdyn.ui.elements; + +import java.awt.Shape; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; + +import org.simantics.h2d.element.Element; +import org.simantics.h2d.element.IElement; +import org.simantics.h2d.element.IElementListener; +import org.simantics.h2d.element.handler.Connectable; +import org.simantics.h2d.node.FilledShapeNode; +import org.simantics.h2d.node.ShapeNode; +import org.simantics.objmap.annotations.GraphType; +import org.simantics.objmap.annotations.RelatedElement; +import org.simantics.scenegraph.g2d.G2DParentNode; + +@GraphType("http://www.simantics.org/Sysdyn#Flow") +public class FlowElement extends Element implements IElementListener { + + public static double ARROW_LENGTH1 = 0.2; + public static double ARROW_LENGTH2 = 3.0; + public static double ARROW_WIDTH = 1.8; + + static final double OFFSET = 1.5; + + /* + * Total length of the arrow is ARROW_LENGTH1 + ARROW_LENGTH2 + */ + @RelatedElement("http://www.simantics.org/Sysdyn#HasTail") + Connectable tail; + @RelatedElement("http://www.simantics.org/Sysdyn#HasHead") + Connectable head; + double angle = 0.1; + + // Auxiliary + double angle0; + double angle1; + double cx; + double cy; + double r; + + // Scene graph + ShapeNode lineNode1; + ShapeNode lineNode2; + FilledShapeNode arrowNode; + + public FlowElement() { + } + + public FlowElement(Connectable tail, Connectable head) { + super(); + this.tail = tail; + this.head = head; + } + + @Override + public void elementUpdated(IElement element) { + update(); + } + + @Override + public void remove() { + lineNode1.remove(); + lineNode2.remove(); + tail.removeListener(this); + head.removeListener(this); + } + + @Override + public void init(G2DParentNode parent) { + tail.addListener(this); + head.addListener(this); + + lineNode1 = parent.addNode(ShapeNode.class); + lineNode1.setScaleStroke(true); + lineNode2 = parent.addNode(ShapeNode.class); + lineNode2.setScaleStroke(true); + + //arrowNode = parent.addNode(FilledShapeNode.class); + update(); + } + + protected Shape createArrow(double x, double y, double dx, double dy) { + Path2D path = new Path2D.Double(); + path.moveTo(x+ARROW_LENGTH1*dx, y+ARROW_LENGTH1*dy); + x -= ARROW_LENGTH2*dx; + y -= ARROW_LENGTH2*dy; + path.lineTo(x-ARROW_WIDTH*dy, y+ARROW_WIDTH*dx); + path.lineTo(x+ARROW_WIDTH*dy, y-ARROW_WIDTH*dx); + path.closePath(); + return path; + } + + // TODO + + private void draw(boolean vertical, double ... coordinates) { + lineNode1.setShape(Flows.createOffsetLine(vertical, OFFSET, coordinates)); + lineNode2.setShape(Flows.createOffsetLine(vertical, -OFFSET, coordinates)); + } + + private void draw(Connectable valve, Connectable node) { + double x0 = valve.getOrigo().getX(); + double y0 = valve.getOrigo().getY(); + double x1 = node.getOrigo().getX(); + double y1 = node.getOrigo().getY(); + + Rectangle2D rect = new Rectangle2D.Double(); + node.getBounds(rect); + if( ((ValveElement)valve).rotated ) { + if(y1 > y0) + y0 += OFFSET; + else + y0 -= OFFSET; + if(rect.getMinX() <= x0 && rect.getMaxX() >= x0) { + if(y1 > y0) + draw(true, y0, x0, rect.getMinY()); + else + draw(true, y0, x0, rect.getMaxY()); + } + else { + if(x1 > x0) + draw(true, y0, x0, y1, rect.getMinX()); + else + draw(true, y0, x0, y1, rect.getMaxX()); + } + } + else { + if(x1 > x0) + x0 += OFFSET; + else + x0 -= OFFSET; + if(rect.getMinY() <= y0 && rect.getMaxY() >= y0) { + if(x1 > x0) + draw(false, x0, y0, rect.getMinX()); + else + draw(false, x0, y0, rect.getMaxX()); + } + else { + if(y1 > y0) + draw(false, x0, y0, x1, rect.getMinY()); + else + draw(false, x0, y0, x1, rect.getMaxY()); + } + } + } + + protected void updateSceneGraph() { + if(tail instanceof ValveElement) + draw(tail, head); + else if(head instanceof ValveElement) + draw(head, tail); + } + + // TODO + + public void update() { + if(lineNode1 != null) + updateSceneGraph(); + super.update(); + } + + @Override + public void getBounds(Rectangle2D bounds) { + bounds.setFrame(lineNode1.getBounds()); + } + + @Override + public boolean hitTest(double x, double y, double tolerance) { + // TODO + return false; + } + + @Override + public T getInterface(Class clazz) { + return super.getInterface(clazz); + } + + @Override + public void elementRemoved(IElement element) { + remove(); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/Flows.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/Flows.java new file mode 100644 index 00000000..2c9c23ef --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/Flows.java @@ -0,0 +1,33 @@ +package org.simantics.sysdyn.ui.elements; + +import java.awt.geom.Path2D; + +public class Flows { + public static Path2D createLine(boolean vertical, double ... coordinates) { + Path2D path = new Path2D.Double(); + if(vertical) + path.moveTo(coordinates[1], coordinates[0]); + else + path.moveTo(coordinates[0], coordinates[1]); + for(int i=2;i