<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://dev.simantics.org/index.php?action=history&amp;feed=atom&amp;title=Scene_Graph%3A_Sysdyn-scenario</id>
	<title>Scene Graph: Sysdyn-scenario - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://dev.simantics.org/index.php?action=history&amp;feed=atom&amp;title=Scene_Graph%3A_Sysdyn-scenario"/>
	<link rel="alternate" type="text/html" href="https://dev.simantics.org/index.php?title=Scene_Graph:_Sysdyn-scenario&amp;action=history"/>
	<updated>2026-04-05T22:19:39Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://dev.simantics.org/index.php?title=Scene_Graph:_Sysdyn-scenario&amp;diff=2137&amp;oldid=prev</id>
		<title>Tuukka Lehtonen: Created page with &quot;In this scenario, diagram consists of nodes and arcs between them. Nodes are text boxes that scale according to the text inside them. Arcs go from the center of one node to anoth...&quot;</title>
		<link rel="alternate" type="text/html" href="https://dev.simantics.org/index.php?title=Scene_Graph:_Sysdyn-scenario&amp;diff=2137&amp;oldid=prev"/>
		<updated>2011-07-19T15:46:39Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;In this scenario, diagram consists of nodes and arcs between them. Nodes are text boxes that scale according to the text inside them. Arcs go from the center of one node to anoth...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;In this scenario, diagram consists of nodes and arcs between them. Nodes are text boxes that scale according to the text inside them. Arcs go from the center of one node to another so that it clipped to the edges of the nodes. An arrow is drawn in the head of the arc (at the point where the arc is clipped). The following editing operations are supported&lt;br /&gt;
# Changing the name of a node&lt;br /&gt;
# Moving a node. The arcs should be updated during the operation.&lt;br /&gt;
# Changing the curvature of a arc. This is done by dragging some point on the arc to other point.&lt;br /&gt;
See [http://www.simulationsite.net/~niemisto/sysdyn/]. The operations should be reponsive so an implementation that does not need server for updates during the operations is preferred.&lt;br /&gt;
&lt;br /&gt;
Specific questions:&lt;br /&gt;
* Can nodes refer to each other? For example, can arc be implemented as&lt;br /&gt;
 public class ArcNode extends G2DNode {&lt;br /&gt;
     protected IConnectable tail = null;&lt;br /&gt;
     protected IConnectable head = null;&lt;br /&gt;
     protected double angle = 0.0;&lt;br /&gt;
 &lt;br /&gt;
     @Override&lt;br /&gt;
     public void render(Graphics2D g) {&lt;br /&gt;
         calculate an arc from tail to head with given angle&lt;br /&gt;
         clip the arc to the clip bounds of the tail and the head&lt;br /&gt;
         (or check if connectables have been changed and update the arc if necessary)&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
where&lt;br /&gt;
 public interface IConnectable {&lt;br /&gt;
     double getX();&lt;br /&gt;
     double getY();&lt;br /&gt;
     Rectangle2D getClipBounds();&lt;br /&gt;
 }&lt;br /&gt;
and&lt;br /&gt;
 public class TextNode extends G2DNode implements IConnectable { ... }&lt;br /&gt;
...answer&lt;br /&gt;
&lt;br /&gt;
In theory, nodes could refer to each other. However, after serialization the references wouldn&amp;#039;t work, thus the references must be id based:&lt;br /&gt;
   public class ArcNode extends G2DNode {&lt;br /&gt;
     protected String tailId = null;&lt;br /&gt;
     protected String headId = null;&lt;br /&gt;
     protected double angle = 0.0;&lt;br /&gt;
 &lt;br /&gt;
     @SyncField(&amp;quot;tailId&amp;quot;)&lt;br /&gt;
     public void setTailId(String id) {&lt;br /&gt;
         this.tailId = id;&lt;br /&gt;
     }&lt;br /&gt;
     @SyncField(&amp;quot;headId&amp;quot;)&lt;br /&gt;
     public void setHeadId(String id) {&lt;br /&gt;
         this.headId = id;&lt;br /&gt;
     }&lt;br /&gt;
     @SyncField(&amp;quot;angle&amp;quot;)&lt;br /&gt;
     public void setAngle(double angle) {&lt;br /&gt;
         this.angle = angle;&lt;br /&gt;
     }&lt;br /&gt;
     @Override&lt;br /&gt;
     public void render(Graphics2D g) {&lt;br /&gt;
         calculate an arc from tail to head with given angle&lt;br /&gt;
         clip the arc to the clip bounds of the tail and the head&lt;br /&gt;
         (or check if connectables have been changed and update the arc if necessary)&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
* How derived fields in the nodes are updated when server updates the client-side node?&lt;br /&gt;
 public class TextNode extends G2DNode implements IConnectable {&lt;br /&gt;
     // The properties of the node&lt;br /&gt;
     protected double centerX = 0.0;&lt;br /&gt;
     protected double centerY = 0.0;&lt;br /&gt;
     protected String label = &amp;quot;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     // Auxiliary values computed in updateDerivedFields&lt;br /&gt;
     protected Rectangle2D clipBounds = null;	   &lt;br /&gt;
     protected float textX;&lt;br /&gt;
     protected float textY;&lt;br /&gt;
 &lt;br /&gt;
     private void updateDerivedFields() {&lt;br /&gt;
         ...&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
...answer:&lt;br /&gt;
&lt;br /&gt;
The methods in IConnectable should be actually implemented by every G2DNode (Hence, we might combine these interfaces). Notice that you cannot use serialization annotations with private methods.&lt;br /&gt;
 public class TextNode extends G2DNode implements IConnectable {&lt;br /&gt;
     // The properties of the node&lt;br /&gt;
     protected double centerX = 0.0;&lt;br /&gt;
     protected double centerY = 0.0;&lt;br /&gt;
     protected String label = &amp;quot;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     // Auxiliary values computed in updateDerivedFields&lt;br /&gt;
     protected Rectangle2D clipBounds = null;	   &lt;br /&gt;
     protected float textX;&lt;br /&gt;
     protected float textY;&lt;br /&gt;
 &lt;br /&gt;
     @SyncField({&amp;quot;clipBounds, textX, textY&amp;quot;})&lt;br /&gt;
     protected void updateDerivedFields() {&lt;br /&gt;
         // Update fields, but create clone of clipBounds to enable comparison between old and new value&lt;br /&gt;
     }&lt;br /&gt;
 } &lt;br /&gt;
&lt;br /&gt;
* How the operation 3 is implemented in client-side? For example, when an arc node notifies that it is being dragged, it should prevent other nodes from getting any mouse events. It should also have some place to store the state of the current operation so that this kind of transient structures do not have to be stored to the nodes. In the applet, this is implemented as:&lt;br /&gt;
    class Mover implements ActionHandler {&lt;br /&gt;
        public void handleDrag(double x, double y) {&lt;br /&gt;
            angle = Arcs.angleOfArc(tail.getX(), tail.getY(), x, y, head.getX(), head.getY());&lt;br /&gt;
        }&lt;br /&gt;
        public void handleRelease(double x, double y) {}        &lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    @Override&lt;br /&gt;
    public ActionHandler handlePress(double x, double y) {&lt;br /&gt;
        double dx = x-cx;&lt;br /&gt;
        double dy = y-cy;&lt;br /&gt;
        double dist = dx*dx + dy*dy;&lt;br /&gt;
        if(dist &amp;lt; (r+SELECTION_TOLERANCE)*(r+SELECTION_TOLERANCE) &amp;amp;&amp;amp;&lt;br /&gt;
            dist &amp;gt; (r-SELECTION_TOLERANCE)*(r-SELECTION_TOLERANCE)) {&lt;br /&gt;
            double angle = Arcs.normalizeAngle(Math.atan2(-dy, dx));&lt;br /&gt;
            if(Arcs.areClockwiseOrdered(angle0, angle, angle1))&lt;br /&gt;
                return new Mover();&lt;br /&gt;
        }&lt;br /&gt;
        return null;&lt;br /&gt;
    }&lt;br /&gt;
...answer&lt;br /&gt;
 public class ArcNode extends G2DNode {&lt;br /&gt;
    protected double angle;&lt;br /&gt;
    protected boolean drag = false;&lt;br /&gt;
 &lt;br /&gt;
    public void handleEvent(AWTEvent event) {&lt;br /&gt;
        if(!(event instanceof MouseEvent)) return;&lt;br /&gt;
        MouseEvent me = (MouseEvent)event;&lt;br /&gt;
        if(me.getID() == MouseEvent.MOUSE_PRESSED) {&lt;br /&gt;
            double dx = me.getPoint().getX()-cx;&lt;br /&gt;
            double dy = me.getPoint().getY()-cy;&lt;br /&gt;
            double dist = dx*dx + dy*dy;&lt;br /&gt;
            if(dist &amp;lt; (r+SELECTION_TOLERANCE)*(r+SELECTION_TOLERANCE) &amp;amp;&amp;amp;&lt;br /&gt;
                dist &amp;gt; (r-SELECTION_TOLERANCE)*(r-SELECTION_TOLERANCE)) {&lt;br /&gt;
                double angle = Arcs.normalizeAngle(Math.atan2(-dy, dx));&lt;br /&gt;
                if(Arcs.areClockwiseOrdered(angle0, angle, angle1))&lt;br /&gt;
                    drag = true;&lt;br /&gt;
            }&lt;br /&gt;
            me.consume();&lt;br /&gt;
        } else if(me.getID() == MouseEvent.MOUSE_DRAGGED &amp;amp;&amp;amp; drag == true) {&lt;br /&gt;
            G2DNode tail = findNode(tailId); // NOTE: findMode not implemented yet&lt;br /&gt;
            G2DNode head = findNode(headId);&lt;br /&gt;
            angle = Arcs.angleOfArc(tail.getX(), tail.getY(), me.getPoint().getX(), me.getPoint().getY(), head.getX(), head.getY());&lt;br /&gt;
            me.consume();&lt;br /&gt;
        } else if(me.getID() == MouseEvent.MOUSE_RELEASED) {&lt;br /&gt;
            drag = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Tuukka Lehtonen</name></author>
	</entry>
</feed>