Removed visitor pattern used for one test only
authorDaniel Kirschten <daniel.kirschten@gmx.de>
Tue, 25 Jun 2019 09:23:43 +0000 (11:23 +0200)
committerDaniel Kirschten <daniel.kirschten@gmx.de>
Tue, 25 Jun 2019 09:23:43 +0000 (11:23 +0200)
12 files changed:
net.mograsim.logic.ui.am2900/test/net/mograsim/logic/ui/am2900/TestableAm2901Impl.java
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/ModelVisitor.java [deleted file]
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/ViewModel.java
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/Visitable.java [deleted file]
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/GUIBitDisplay.java
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/GUIComponent.java
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/GUIManualSwitch.java
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SimpleRectangularGUIGate.java
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SimpleRectangularSubmodelComponent.java
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SubmodelInterface.java
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/wires/GUIWire.java
net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/wires/WireCrossPoint.java

index 6c7be71..d8934ce 100644 (file)
@@ -5,7 +5,9 @@ import static org.junit.jupiter.api.Assertions.fail;
 import java.lang.reflect.Field;\r
 import java.util.HashMap;\r
 import java.util.HashSet;\r
+import java.util.LinkedList;\r
 import java.util.Objects;\r
+import java.util.Queue;\r
 import java.util.Set;\r
 import java.util.TreeSet;\r
 \r
@@ -15,18 +17,14 @@ import net.mograsim.logic.core.timeline.Timeline;
 import net.mograsim.logic.core.types.Bit;\r
 import net.mograsim.logic.core.types.BitVector;\r
 import net.mograsim.logic.core.types.BitVector.BitVectorMutator;\r
-import net.mograsim.logic.ui.model.ModelVisitor;\r
 import net.mograsim.logic.ui.model.ViewModel;\r
 import net.mograsim.logic.ui.model.ViewModelModifiable;\r
 import net.mograsim.logic.ui.model.components.GUIBitDisplay;\r
 import net.mograsim.logic.ui.model.components.GUIComponent;\r
 import net.mograsim.logic.ui.model.components.GUIManualSwitch;\r
-import net.mograsim.logic.ui.model.components.SimpleRectangularGUIGate;\r
-import net.mograsim.logic.ui.model.components.SimpleRectangularSubmodelComponent;\r
-import net.mograsim.logic.ui.model.components.SubmodelInterface;\r
+import net.mograsim.logic.ui.model.components.SubmodelComponent;\r
 import net.mograsim.logic.ui.model.components.mi.nandbased.am2901.GUIAm2901;\r
 import net.mograsim.logic.ui.model.wires.GUIWire;\r
-import net.mograsim.logic.ui.model.wires.WireCrossPoint;\r
 import net.mograsim.logic.ui.modeladapter.LogicModelParameters;\r
 import net.mograsim.logic.ui.modeladapter.ViewLogicModelAdapter;\r
 \r
@@ -45,9 +43,6 @@ public class TestableAm2901Impl implements TestableAm2901
        private BitDisplay F_0, Cn_4, OVR, F3;\r
        private BitDisplay ORAMn, ORAMn_3, OQn, OQn_3;\r
 \r
-       private Set<GUIWire> allWires;\r
-       private Set<GUIComponent> allComponents;\r
-\r
        private Set<String> wireDebugChangeSet;\r
        private boolean debugWires = false;\r
        public int debugEventThreshold = 10_000;\r
@@ -136,11 +131,18 @@ public class TestableAm2901Impl implements TestableAm2901
                C.switchOff();\r
 \r
                // Debug code\r
-               allWires = new HashSet<>();\r
-               allComponents = new HashSet<>();\r
-               ModelAccumulator accumulator = new ModelAccumulator();\r
-               accumulator.visit(viewModel);\r
-               allWires.forEach(w -> w.addRedrawListener(() ->\r
+               HashSet<GUIWire> wiresIncludingSubmodels = new HashSet<>();\r
+               Queue<ViewModel> modelsToIterate = new LinkedList<>();\r
+               modelsToIterate.add(viewModel);\r
+               while (modelsToIterate.size() > 0)\r
+               {\r
+                       ViewModel model = modelsToIterate.poll();\r
+                       wiresIncludingSubmodels.addAll(model.getWires());\r
+                       for (GUIComponent comp : model.getComponents())\r
+                               if (comp instanceof SubmodelComponent)\r
+                                       modelsToIterate.offer(((SubmodelComponent) comp).submodel);\r
+               }\r
+               wiresIncludingSubmodels.forEach(w -> w.addRedrawListener(() ->\r
                {\r
                        if (debugWires)\r
                        {\r
@@ -345,58 +347,4 @@ public class TestableAm2901Impl implements TestableAm2901
                }\r
                return mutator.toBitVector();\r
        }\r
-\r
-       class ModelAccumulator implements ModelVisitor\r
-       {\r
-               @Override\r
-               public void visit(GUIWire w)\r
-               {\r
-                       allWires.add(w);\r
-               }\r
-\r
-               @Override\r
-               public void visit(SimpleRectangularGUIGate simpleRectangularGUIGate)\r
-               {\r
-                       allComponents.add(simpleRectangularGUIGate);\r
-               }\r
-\r
-               @Override\r
-               public void visit(SimpleRectangularSubmodelComponent simpleRectangularSubmodelComponent)\r
-               {\r
-                       allComponents.add(simpleRectangularSubmodelComponent);\r
-                       simpleRectangularSubmodelComponent.getWires().forEach(w -> w.accept(this));\r
-                       simpleRectangularSubmodelComponent.getComponents().forEach(w -> w.accept(this));\r
-               }\r
-\r
-               @Override\r
-               public void visit(WireCrossPoint wireCrossPoint)\r
-               {\r
-                       // nothing\r
-               }\r
-\r
-               @Override\r
-               public void visit(GUIBitDisplay guiBitDisplay)\r
-               {\r
-                       allComponents.add(guiBitDisplay);\r
-               }\r
-\r
-               @Override\r
-               public void visit(GUIManualSwitch guiManualSwitch)\r
-               {\r
-                       allComponents.add(guiManualSwitch);\r
-               }\r
-\r
-               @Override\r
-               public void visit(SubmodelInterface submodelInterface)\r
-               {\r
-                       // nothing\r
-               }\r
-\r
-               @Override\r
-               public void visit(ViewModel viewModel)\r
-               {\r
-                       viewModel.getWires().forEach(w -> w.accept(this));\r
-                       viewModel.getComponents().forEach(w -> w.accept(this));\r
-               }\r
-       }\r
 }\r
diff --git a/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/ModelVisitor.java b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/ModelVisitor.java
deleted file mode 100644 (file)
index a11d2fc..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.mograsim.logic.ui.model;\r
-\r
-import net.mograsim.logic.ui.model.components.GUIBitDisplay;\r
-import net.mograsim.logic.ui.model.components.GUIManualSwitch;\r
-import net.mograsim.logic.ui.model.components.SimpleRectangularGUIGate;\r
-import net.mograsim.logic.ui.model.components.SimpleRectangularSubmodelComponent;\r
-import net.mograsim.logic.ui.model.components.SubmodelInterface;\r
-import net.mograsim.logic.ui.model.wires.GUIWire;\r
-import net.mograsim.logic.ui.model.wires.WireCrossPoint;\r
-\r
-public interface ModelVisitor\r
-{\r
-       void visit(GUIWire w);\r
-\r
-       void visit(SimpleRectangularGUIGate simpleRectangularGUIGate);\r
-\r
-       void visit(SimpleRectangularSubmodelComponent simpleRectangularSubmodelComponent);\r
-\r
-       void visit(WireCrossPoint wireCrossPoint);\r
-\r
-       void visit(GUIBitDisplay guiBitDisplay);\r
-\r
-       void visit(GUIManualSwitch guiManualSwitch);\r
-\r
-       void visit(SubmodelInterface submodelInterface);\r
-\r
-       void visit(ViewModel viewModel);\r
-}\r
index 445810a..1f69acf 100644 (file)
@@ -8,7 +8,7 @@ import java.util.function.Consumer;
 import net.mograsim.logic.ui.model.components.GUIComponent;
 import net.mograsim.logic.ui.model.wires.GUIWire;
 
-public class ViewModel implements Visitable
+public class ViewModel
 {
        private final List<GUIComponent> components;
        private final List<GUIComponent> componentsUnmodifiable;
@@ -105,12 +105,6 @@ public class ViewModel implements Visitable
                return wiresUnmodifiable;
        }
 
-       @Override
-       public void accept(ModelVisitor mv)
-       {
-               mv.visit(this);
-       }
-
        // @formatter:off
        public void addComponentAddedListener     (Consumer<? super GUIComponent> listener) {componentAddedListeners  .add   (listener);}
        public void addComponentRemovedListener   (Consumer<? super GUIComponent> listener) {componentRemovedListeners.add   (listener);}
diff --git a/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/Visitable.java b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/Visitable.java
deleted file mode 100644 (file)
index 3cbe9c5..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-package net.mograsim.logic.ui.model;\r
-\r
-public interface Visitable\r
-{\r
-       void accept(ModelVisitor mv);\r
-}\r
index c4d8df3..82b3bbb 100644 (file)
@@ -8,7 +8,6 @@ import net.mograsim.logic.core.LogicObservable;
 import net.mograsim.logic.core.LogicObserver;
 import net.mograsim.logic.core.components.BitDisplay;
 import net.mograsim.logic.core.types.BitVectorFormatter;
-import net.mograsim.logic.ui.model.ModelVisitor;
 import net.mograsim.logic.ui.model.ViewModelModifiable;
 import net.mograsim.logic.ui.model.wires.Pin;
 import net.mograsim.logic.ui.modeladapter.ViewLogicModelAdapter;
@@ -77,12 +76,6 @@ public class GUIBitDisplay extends GUIComponent
                return inputPin;
        }
 
-       @Override
-       public void accept(ModelVisitor mv)
-       {
-               mv.visit(this);
-       }
-
        static
        {
                ViewLogicModelAdapter.addComponentAdapter(new BitDisplayAdapter());
index 2c8324a..6e566c4 100644 (file)
@@ -12,7 +12,6 @@ import java.util.function.Supplier;
 import net.haspamelodica.swt.helper.gcs.GeneralGC;
 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
 import net.mograsim.logic.ui.model.ViewModelModifiable;
-import net.mograsim.logic.ui.model.Visitable;
 import net.mograsim.logic.ui.model.wires.Pin;
 
 /**
@@ -21,7 +20,7 @@ import net.mograsim.logic.ui.model.wires.Pin;
  * 
  * @author Daniel Kirschten
  */
-public abstract class GUIComponent implements Visitable
+public abstract class GUIComponent
 {
        /**
         * The model this component is a part of.
index 563f928..43c7199 100644 (file)
@@ -9,7 +9,6 @@ import net.mograsim.logic.core.LogicObserver;
 import net.mograsim.logic.core.components.ManualSwitch;
 import net.mograsim.logic.core.types.BitVectorFormatter;
 import net.mograsim.logic.core.wires.Wire.ReadEnd;
-import net.mograsim.logic.ui.model.ModelVisitor;
 import net.mograsim.logic.ui.model.ViewModelModifiable;
 import net.mograsim.logic.ui.model.wires.Pin;
 import net.mograsim.logic.ui.modeladapter.ViewLogicModelAdapter;
@@ -90,12 +89,6 @@ public class GUIManualSwitch extends GUIComponent
                return outputPin;
        }
 
-       @Override
-       public void accept(ModelVisitor mv)
-       {
-               mv.visit(this);
-       }
-
        static
        {
                ViewLogicModelAdapter.addComponentAdapter(new ManualSwitchAdapter());
index 7beafe1..d263f6d 100644 (file)
@@ -8,7 +8,6 @@ import net.haspamelodica.swt.helper.gcs.GeneralGC;
 import net.haspamelodica.swt.helper.swtobjectwrappers.Font;
 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
-import net.mograsim.logic.ui.model.ModelVisitor;
 import net.mograsim.logic.ui.model.ViewModelModifiable;
 import net.mograsim.logic.ui.model.wires.MovablePin;
 import net.mograsim.logic.ui.model.wires.Pin;
@@ -83,10 +82,4 @@ public class SimpleRectangularGUIGate extends GUIComponent
                m.put(kLogicWidth, logicWidth);
                return m;
        }
-
-       @Override
-       public void accept(ModelVisitor mv)
-       {
-               mv.visit(this);
-       }
 }
\ No newline at end of file
index 2fd94c6..59d800a 100644 (file)
@@ -12,7 +12,6 @@ import net.haspamelodica.swt.helper.gcs.GeneralGC;
 import net.haspamelodica.swt.helper.swtobjectwrappers.Font;
 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
-import net.mograsim.logic.ui.model.ModelVisitor;
 import net.mograsim.logic.ui.model.ViewModelModifiable;
 import net.mograsim.logic.ui.model.wires.MovablePin;
 import net.mograsim.logic.ui.model.wires.Pin;
@@ -156,10 +155,4 @@ public class SimpleRectangularSubmodelComponent extends SubmodelComponent
                throw new UnsupportedOperationException(
                                "Can't set the size of a SimpleRectangularSubmodelComponent directly, call setInputPins / setOutputPins instead");
        }
-
-       @Override
-       public void accept(ModelVisitor mv)
-       {
-               mv.visit(this);
-       }
 }
\ No newline at end of file
index d08c937..8f448a8 100644 (file)
@@ -2,7 +2,6 @@ package net.mograsim.logic.ui.model.components;
 
 import net.haspamelodica.swt.helper.gcs.GeneralGC;
 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
-import net.mograsim.logic.ui.model.ModelVisitor;
 import net.mograsim.logic.ui.model.ViewModelModifiable;
 
 public class SubmodelInterface extends GUIComponent
@@ -16,10 +15,4 @@ public class SubmodelInterface extends GUIComponent
        public void render(GeneralGC gc, Rectangle visibleRegion)
        {// nothing to do here
        }
-
-       @Override
-       public void accept(ModelVisitor mv)
-       {
-               mv.visit(this);
-       }
 }
\ No newline at end of file
index a79054c..08b1d42 100644 (file)
-package net.mograsim.logic.ui.model.wires;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.List;\r
-\r
-import org.eclipse.swt.SWT;\r
-\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-import net.mograsim.logic.core.LogicObserver;\r
-import net.mograsim.logic.core.types.BitVectorFormatter;\r
-import net.mograsim.logic.core.wires.Wire.ReadEnd;\r
-import net.mograsim.logic.ui.ColorHelper;\r
-import net.mograsim.logic.ui.model.ModelVisitor;\r
-import net.mograsim.logic.ui.model.ViewModelModifiable;\r
-import net.mograsim.logic.ui.model.Visitable;\r
-\r
-/**\r
- * A wire connecting exactly two {@link Pin}s.\r
- * \r
- * @author Daniel Kirschten\r
- */\r
-public class GUIWire implements Visitable\r
-{\r
-       /**\r
-        * The model this wire is a part of.\r
-        */\r
-       private final ViewModelModifiable model;\r
-       /**\r
-        * The logical width of this wire. Is equal to the logical with of {@link #pin1} and {@link #pin2}.\r
-        */\r
-       public final int logicWidth;\r
-       /**\r
-        * The {@link Pin} on one side of this wire, usually the signal source.\r
-        */\r
-       private Pin pin1;\r
-       /**\r
-        * The {@link Pin} on one side of this wire, usually the signal target.\r
-        */\r
-       private Pin pin2;\r
-       /**\r
-        * The user-defined path between {@link #pin1} and {@link #pin2}.<br>\r
-        * Special cases: <code>null</code> means "choose an interpolation as fits", and an empty array means "direct connection without any\r
-        * interpolation".\r
-        */\r
-       private Point[] path;\r
-       /**\r
-        * The bounds of this wire, excluding line width (and line joins, if the line join is {@link SWT#JOIN_MITER})\r
-        */\r
-       private final Rectangle bounds;\r
-       /**\r
-        * The effective path of this wire, including automatic interpolation and the position of both {@link Pin}s. Is never null.\r
-        */\r
-       private double[] effectivePath;\r
-\r
-       private final List<Runnable> redrawListeners;\r
-\r
-       /**\r
-        * A LogicObserver calling redrawListeners. Used for logic model bindings.\r
-        */\r
-       private final LogicObserver logicObs;\r
-       /**\r
-        * A ReadEnd of the logic wire this GUI wire currently is bound to.\r
-        */\r
-       private ReadEnd end;\r
-\r
-       // creation and destruction\r
-\r
-       /**\r
-        * Creates a new {@link GUIWire} with automatic interpolation.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, WireCrossPoint pin2)\r
-       {\r
-               this(model, pin1, pin2, (Point[]) null);\r
-       }\r
-\r
-       /**\r
-        * Creates a new {@link GUIWire} with automatic interpolation.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, Pin pin2)\r
-       {\r
-               this(model, pin1, pin2, (Point[]) null);\r
-       }\r
-\r
-       /**\r
-        * Creates a new {@link GUIWire} with automatic interpolation.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public GUIWire(ViewModelModifiable model, Pin pin1, WireCrossPoint pin2)\r
-       {\r
-               this(model, pin1, pin2, (Point[]) null);\r
-       }\r
-\r
-       /**\r
-        * Creates a new {@link GUIWire} with automatic interpolation.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public GUIWire(ViewModelModifiable model, Pin pin1, Pin pin2)\r
-       {\r
-               this(model, pin1, pin2, (Point[]) null);\r
-       }\r
-\r
-       /**\r
-        * Creates a new {@link GUIWire} without automatic interpolation.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, WireCrossPoint pin2, Point... path)\r
-       {\r
-               this(model, pin1.getPin(), pin2.getPin(), path);\r
-       }\r
-\r
-       /**\r
-        * Creates a new {@link GUIWire} without automatic interpolation.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, Pin pin2, Point... path)\r
-       {\r
-               this(model, pin1.getPin(), pin2, path);\r
-       }\r
-\r
-       /**\r
-        * Creates a new {@link GUIWire} without automatic interpolation.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public GUIWire(ViewModelModifiable model, Pin pin1, WireCrossPoint pin2, Point... path)\r
-       {\r
-               this(model, pin1, pin2.getPin(), path);\r
-       }\r
-\r
-       /**\r
-        * Creates a new {@link GUIWire} without automatic interpolation.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public GUIWire(ViewModelModifiable model, Pin pin1, Pin pin2, Point... path)\r
-       {\r
-               logicObs = (i) -> callRedrawListeners();\r
-               this.model = model;\r
-               this.logicWidth = pin1.logicWidth;\r
-               if (pin2.logicWidth != pin1.logicWidth)\r
-                       throw new IllegalArgumentException("Can't connect pins of different logic width");\r
-\r
-               this.pin1 = pin1;\r
-               this.pin2 = pin2;\r
-\r
-               this.path = path == null ? null : Arrays.copyOf(path, path.length);\r
-               this.bounds = new Rectangle(0, 0, -1, -1);\r
-\r
-               redrawListeners = new ArrayList<>();\r
-\r
-               pin1.addPinMovedListener(p -> pinMoved());\r
-               pin2.addPinMovedListener(p -> pinMoved());\r
-\r
-               recalculateEffectivePath();\r
-\r
-               model.wireCreated(this);\r
-       }\r
-\r
-       /**\r
-        * Destroys this wire. This method implicitly calls {@link ViewModelModifiable#wireDestroyed(GUIWire) wireDestroyed()} for the model\r
-        * this component is a part of.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public void destroy()\r
-       {\r
-               model.wireDestroyed(this);\r
-       }\r
-\r
-       // pins\r
-\r
-       /**\r
-        * Returns the {@link Pin} on one side of this wire, usually the signal source.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public Pin getPin1()\r
-       {\r
-               return pin1;\r
-       }\r
-\r
-       /**\r
-        * Returns the {@link Pin} on one side of this wire, usually the signal target.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public Pin getPin2()\r
-       {\r
-               return pin2;\r
-       }\r
-\r
-       /**\r
-        * Called when {@link #pin1} or {@link #pin2} were moved.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       private void pinMoved()\r
-       {\r
-               recalculateEffectivePath();\r
-               callRedrawListeners();\r
-       }\r
-\r
-       // "graphical" operations\r
-\r
-       /**\r
-        * Recalculates {@link #effectivePath} "from scratch". Also updates {@link #bounds}.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       private void recalculateEffectivePath()\r
-       {\r
-               Point pos1 = pin1.getPos(), pos2 = pin2.getPos();\r
-\r
-               double boundsX1 = Math.min(pos1.x, pos2.x);\r
-               double boundsY1 = Math.min(pos1.y, pos2.y);\r
-               double boundsX2 = Math.max(pos1.x, pos2.x);\r
-               double boundsY2 = Math.max(pos1.y, pos2.y);\r
-\r
-               if (path == null)\r
-                       effectivePath = new double[] { pos1.x, pos1.y, (pos1.x + pos2.x) / 2, pos1.y, (pos1.x + pos2.x) / 2, pos2.y, pos2.x, pos2.y };\r
-               else\r
-               {\r
-                       effectivePath = new double[path.length * 2 + 4];\r
-                       effectivePath[0] = pos1.x;\r
-                       effectivePath[1] = pos1.y;\r
-                       for (int srcI = 0, dstI = 2; srcI < path.length; srcI++, dstI += 2)\r
-                       {\r
-                               double pathX = path[srcI].x;\r
-                               double pathY = path[srcI].y;\r
-                               effectivePath[dstI + 0] = pathX;\r
-                               effectivePath[dstI + 1] = pathY;\r
-                               if (pathX < boundsX1)\r
-                                       boundsX1 = pathX;\r
-                               if (pathX > boundsX2)\r
-                                       boundsX2 = pathX;\r
-                               if (pathY < boundsY1)\r
-                                       boundsY1 = pathY;\r
-                               if (pathY > boundsY2)\r
-                                       boundsY2 = pathY;\r
-                       }\r
-                       effectivePath[effectivePath.length - 2] = pos2.x;\r
-                       effectivePath[effectivePath.length - 1] = pos2.y;\r
-               }\r
-\r
-               bounds.x = boundsX1;\r
-               bounds.y = boundsY1;\r
-               bounds.width = boundsX2 - boundsX1;\r
-               bounds.height = boundsY2 - boundsY1;\r
-       }\r
-\r
-       /**\r
-        * Returns the bounds of this wire, excluding line width (and line joins, if the line join is {@link SWT#JOIN_MITER})\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public Rectangle getBounds()\r
-       {\r
-               return new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);\r
-       }\r
-\r
-       /**\r
-        * Render this wire to the given gc, in absoulute coordinates.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public void render(GeneralGC gc)\r
-       {\r
-               ColorHelper.executeWithDifferentForeground(gc, BitVectorFormatter.formatAsColor(end), () -> gc.drawPolyline(effectivePath));\r
-       }\r
-\r
-       /**\r
-        * The user-defined path between {@link #pin1} and {@link #pin2}. Note that this is not neccessarily equal to the effective path drawn\r
-        * in {@link #render(GeneralGC)}.<br>\r
-        * Special cases: <code>null</code> means "choose an interpolation as fits", and an empty array means "direct connection without any\r
-        * interpolation".\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public Point[] getPath()\r
-       {\r
-               return path == null ? null : path.clone();\r
-       }\r
-\r
-       // logic model binding\r
-\r
-       /**\r
-        * Binds this {@link GUIWire} to the given {@link ReadEnd}: The color of this {@link GUIWire} will now depend on the state of the given\r
-        * {@link ReadEnd}, and further changes of the given {@link ReadEnd} will result in readrawListeners being called.<br>\r
-        * The argument can be null, in which case the old binding is stopped.\r
-        * \r
-        * @author Daniel Kirschten\r
-        */\r
-       public void setLogicModelBinding(ReadEnd end)\r
-       {\r
-               if (this.end != null)\r
-                       this.end.deregisterObserver(logicObs);\r
-               this.end = end;\r
-               if (end != null)\r
-                       end.registerObserver(logicObs);\r
-       }\r
-\r
-       // listeners\r
-\r
-       // @formatter:off\r
-       public void addRedrawListener   (Runnable listener) {redrawListeners         .add   (listener);}\r
-\r
-       public void removeRedrawListener(Runnable listener) {redrawListeners         .remove(listener);}\r
-\r
-       private void callRedrawListeners() {redrawListeners.forEach(l -> l.run());}\r
-       // @formatter:on\r
-\r
-       @Override\r
-       public String toString()\r
-       {\r
-               return "GUIWire [" + pin1 + "---" + pin2 + ", value=" + (end == null ? "null" : end.getValues()) + "]";\r
-       }\r
-\r
-       @Override\r
-       public void accept(ModelVisitor mv)\r
-       {\r
-               mv.visit(this);\r
-       }\r
+package net.mograsim.logic.ui.model.wires;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+import net.mograsim.logic.core.LogicObserver;
+import net.mograsim.logic.core.types.BitVectorFormatter;
+import net.mograsim.logic.core.wires.Wire.ReadEnd;
+import net.mograsim.logic.ui.ColorHelper;
+import net.mograsim.logic.ui.model.ViewModelModifiable;
+
+/**
+ * A wire connecting exactly two {@link Pin}s.
+ * 
+ * @author Daniel Kirschten
+ */
+public class GUIWire
+{
+       /**
+        * The model this wire is a part of.
+        */
+       private final ViewModelModifiable model;
+       /**
+        * The logical width of this wire. Is equal to the logical with of {@link #pin1} and {@link #pin2}.
+        */
+       public final int logicWidth;
+       /**
+        * The {@link Pin} on one side of this wire, usually the signal source.
+        */
+       private Pin pin1;
+       /**
+        * The {@link Pin} on one side of this wire, usually the signal target.
+        */
+       private Pin pin2;
+       /**
+        * The user-defined path between {@link #pin1} and {@link #pin2}.<br>
+        * Special cases: <code>null</code> means "choose an interpolation as fits", and an empty array means "direct connection without any
+        * interpolation".
+        */
+       private Point[] path;
+       /**
+        * The bounds of this wire, excluding line width (and line joins, if the line join is {@link SWT#JOIN_MITER})
+        */
+       private final Rectangle bounds;
+       /**
+        * The effective path of this wire, including automatic interpolation and the position of both {@link Pin}s. Is never null.
+        */
+       private double[] effectivePath;
+
+       private final List<Runnable> redrawListeners;
+
+       /**
+        * A LogicObserver calling redrawListeners. Used for logic model bindings.
+        */
+       private final LogicObserver logicObs;
+       /**
+        * A ReadEnd of the logic wire this GUI wire currently is bound to.
+        */
+       private ReadEnd end;
+
+       // creation and destruction
+
+       /**
+        * Creates a new {@link GUIWire} with automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, WireCrossPoint pin2)
+       {
+               this(model, pin1, pin2, (Point[]) null);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} with automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, Pin pin2)
+       {
+               this(model, pin1, pin2, (Point[]) null);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} with automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, Pin pin1, WireCrossPoint pin2)
+       {
+               this(model, pin1, pin2, (Point[]) null);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} with automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, Pin pin1, Pin pin2)
+       {
+               this(model, pin1, pin2, (Point[]) null);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} without automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, WireCrossPoint pin2, Point... path)
+       {
+               this(model, pin1.getPin(), pin2.getPin(), path);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} without automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, Pin pin2, Point... path)
+       {
+               this(model, pin1.getPin(), pin2, path);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} without automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, Pin pin1, WireCrossPoint pin2, Point... path)
+       {
+               this(model, pin1, pin2.getPin(), path);
+       }
+
+       /**
+        * Creates a new {@link GUIWire} without automatic interpolation.
+        * 
+        * @author Daniel Kirschten
+        */
+       public GUIWire(ViewModelModifiable model, Pin pin1, Pin pin2, Point... path)
+       {
+               logicObs = (i) -> callRedrawListeners();
+               this.model = model;
+               this.logicWidth = pin1.logicWidth;
+               if (pin2.logicWidth != pin1.logicWidth)
+                       throw new IllegalArgumentException("Can't connect pins of different logic width");
+
+               this.pin1 = pin1;
+               this.pin2 = pin2;
+
+               this.path = path == null ? null : Arrays.copyOf(path, path.length);
+               this.bounds = new Rectangle(0, 0, -1, -1);
+
+               redrawListeners = new ArrayList<>();
+
+               pin1.addPinMovedListener(p -> pinMoved());
+               pin2.addPinMovedListener(p -> pinMoved());
+
+               recalculateEffectivePath();
+
+               model.wireCreated(this);
+       }
+
+       /**
+        * Destroys this wire. This method implicitly calls {@link ViewModelModifiable#wireDestroyed(GUIWire) wireDestroyed()} for the model
+        * this component is a part of.
+        * 
+        * @author Daniel Kirschten
+        */
+       public void destroy()
+       {
+               model.wireDestroyed(this);
+       }
+
+       // pins
+
+       /**
+        * Returns the {@link Pin} on one side of this wire, usually the signal source.
+        * 
+        * @author Daniel Kirschten
+        */
+       public Pin getPin1()
+       {
+               return pin1;
+       }
+
+       /**
+        * Returns the {@link Pin} on one side of this wire, usually the signal target.
+        * 
+        * @author Daniel Kirschten
+        */
+       public Pin getPin2()
+       {
+               return pin2;
+       }
+
+       /**
+        * Called when {@link #pin1} or {@link #pin2} were moved.
+        * 
+        * @author Daniel Kirschten
+        */
+       private void pinMoved()
+       {
+               recalculateEffectivePath();
+               callRedrawListeners();
+       }
+
+       // "graphical" operations
+
+       /**
+        * Recalculates {@link #effectivePath} "from scratch". Also updates {@link #bounds}.
+        * 
+        * @author Daniel Kirschten
+        */
+       private void recalculateEffectivePath()
+       {
+               Point pos1 = pin1.getPos(), pos2 = pin2.getPos();
+
+               double boundsX1 = Math.min(pos1.x, pos2.x);
+               double boundsY1 = Math.min(pos1.y, pos2.y);
+               double boundsX2 = Math.max(pos1.x, pos2.x);
+               double boundsY2 = Math.max(pos1.y, pos2.y);
+
+               if (path == null)
+                       effectivePath = new double[] { pos1.x, pos1.y, (pos1.x + pos2.x) / 2, pos1.y, (pos1.x + pos2.x) / 2, pos2.y, pos2.x, pos2.y };
+               else
+               {
+                       effectivePath = new double[path.length * 2 + 4];
+                       effectivePath[0] = pos1.x;
+                       effectivePath[1] = pos1.y;
+                       for (int srcI = 0, dstI = 2; srcI < path.length; srcI++, dstI += 2)
+                       {
+                               double pathX = path[srcI].x;
+                               double pathY = path[srcI].y;
+                               effectivePath[dstI + 0] = pathX;
+                               effectivePath[dstI + 1] = pathY;
+                               if (pathX < boundsX1)
+                                       boundsX1 = pathX;
+                               if (pathX > boundsX2)
+                                       boundsX2 = pathX;
+                               if (pathY < boundsY1)
+                                       boundsY1 = pathY;
+                               if (pathY > boundsY2)
+                                       boundsY2 = pathY;
+                       }
+                       effectivePath[effectivePath.length - 2] = pos2.x;
+                       effectivePath[effectivePath.length - 1] = pos2.y;
+               }
+
+               bounds.x = boundsX1;
+               bounds.y = boundsY1;
+               bounds.width = boundsX2 - boundsX1;
+               bounds.height = boundsY2 - boundsY1;
+       }
+
+       /**
+        * Returns the bounds of this wire, excluding line width (and line joins, if the line join is {@link SWT#JOIN_MITER})
+        * 
+        * @author Daniel Kirschten
+        */
+       public Rectangle getBounds()
+       {
+               return new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
+       }
+
+       /**
+        * Render this wire to the given gc, in absoulute coordinates.
+        * 
+        * @author Daniel Kirschten
+        */
+       public void render(GeneralGC gc)
+       {
+               ColorHelper.executeWithDifferentForeground(gc, BitVectorFormatter.formatAsColor(end), () -> gc.drawPolyline(effectivePath));
+       }
+
+       /**
+        * The user-defined path between {@link #pin1} and {@link #pin2}. Note that this is not neccessarily equal to the effective path drawn
+        * in {@link #render(GeneralGC)}.<br>
+        * Special cases: <code>null</code> means "choose an interpolation as fits", and an empty array means "direct connection without any
+        * interpolation".
+        * 
+        * @author Daniel Kirschten
+        */
+       public Point[] getPath()
+       {
+               return path == null ? null : path.clone();
+       }
+
+       // logic model binding
+
+       /**
+        * Binds this {@link GUIWire} to the given {@link ReadEnd}: The color of this {@link GUIWire} will now depend on the state of the given
+        * {@link ReadEnd}, and further changes of the given {@link ReadEnd} will result in readrawListeners being called.<br>
+        * The argument can be null, in which case the old binding is stopped.
+        * 
+        * @author Daniel Kirschten
+        */
+       public void setLogicModelBinding(ReadEnd end)
+       {
+               if (this.end != null)
+                       this.end.deregisterObserver(logicObs);
+               this.end = end;
+               if (end != null)
+                       end.registerObserver(logicObs);
+       }
+
+       // listeners
+
+       // @formatter:off
+       public void addRedrawListener   (Runnable listener) {redrawListeners         .add   (listener);}
+
+       public void removeRedrawListener(Runnable listener) {redrawListeners         .remove(listener);}
+
+       private void callRedrawListeners() {redrawListeners.forEach(l -> l.run());}
+       // @formatter:on
+
+       @Override
+       public String toString()
+       {
+               return "GUIWire [" + pin1 + "---" + pin2 + ", value=" + (end == null ? "null" : end.getValues()) + "]";
+       }
 }
\ No newline at end of file
index 2b07238..f79cf9a 100644 (file)
@@ -1,92 +1,85 @@
-package net.mograsim.logic.ui.model.wires;\r
-\r
-import java.util.Map;\r
-\r
-import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;\r
-import net.mograsim.logic.core.LogicObservable;\r
-import net.mograsim.logic.core.LogicObserver;\r
-import net.mograsim.logic.core.types.BitVectorFormatter;\r
-import net.mograsim.logic.core.wires.Wire.ReadEnd;\r
-import net.mograsim.logic.ui.ColorHelper;\r
-import net.mograsim.logic.ui.model.ModelVisitor;\r
-import net.mograsim.logic.ui.model.ViewModelModifiable;\r
-import net.mograsim.logic.ui.model.components.GUIComponent;\r
-import net.mograsim.logic.ui.model.components.SimpleRectangularGUIGate;\r
-\r
-public class WireCrossPoint extends GUIComponent\r
-{\r
-       private static final int CIRCLE_RADIUS = 1;\r
-       private static final int CIRCLE_DIAM = CIRCLE_RADIUS * 2;\r
-\r
-       private final Pin pin;\r
-       private final int logicWidth;\r
-\r
-       private final LogicObserver logicObs;\r
-       private ReadEnd end;\r
-\r
-       public WireCrossPoint(ViewModelModifiable model, int logicWidth)\r
-       {\r
-               super(model);\r
-               logicObs = (i) -> requestRedraw();\r
-\r
-               this.logicWidth = logicWidth;\r
-               setSize(CIRCLE_DIAM, CIRCLE_DIAM);\r
-               addPin(this.pin = new Pin(this, "", logicWidth, CIRCLE_RADIUS, CIRCLE_RADIUS));\r
-       }\r
-\r
-       public void moveCenterTo(double x, double y)\r
-       {\r
-               moveTo(x - CIRCLE_RADIUS, y - CIRCLE_RADIUS);\r
-       }\r
-\r
-       @Override\r
-       public void render(GeneralGC gc, Rectangle visibleRegion)\r
-       {\r
-               ColorHelper.executeWithDifferentBackground(gc, BitVectorFormatter.formatAsColor(end),\r
-                               () -> gc.fillOval(getPosX(), getPosY(), CIRCLE_DIAM, CIRCLE_DIAM));\r
-       }\r
-\r
-       public void setLogicModelBinding(ReadEnd end)\r
-       {\r
-               deregisterLogicObs(this.end);\r
-               this.end = end;\r
-               registerLogicObs(end);\r
-       }\r
-\r
-       private void registerLogicObs(LogicObservable observable)\r
-       {\r
-               if (observable != null)\r
-                       observable.registerObserver(logicObs);\r
-       }\r
-\r
-       private void deregisterLogicObs(LogicObservable observable)\r
-       {\r
-               if (observable != null)\r
-                       observable.deregisterObserver(logicObs);\r
-       }\r
-\r
-       public int getLogicWidth()\r
-       {\r
-               return logicWidth;\r
-       }\r
-\r
-       public Pin getPin()\r
-       {\r
-               return pin;\r
-       }\r
-\r
-       @Override\r
-       public Map<String, Object> getInstantiationParameters()\r
-       {\r
-               Map<String, Object> m = super.getInstantiationParameters();\r
-               m.put(SimpleRectangularGUIGate.kLogicWidth, logicWidth);\r
-               return m;\r
-       }\r
-\r
-       @Override\r
-       public void accept(ModelVisitor mv)\r
-       {\r
-               mv.visit(this);\r
-       }\r
+package net.mograsim.logic.ui.model.wires;
+
+import java.util.Map;
+
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+import net.mograsim.logic.core.LogicObservable;
+import net.mograsim.logic.core.LogicObserver;
+import net.mograsim.logic.core.types.BitVectorFormatter;
+import net.mograsim.logic.core.wires.Wire.ReadEnd;
+import net.mograsim.logic.ui.ColorHelper;
+import net.mograsim.logic.ui.model.ViewModelModifiable;
+import net.mograsim.logic.ui.model.components.GUIComponent;
+import net.mograsim.logic.ui.model.components.SimpleRectangularGUIGate;
+
+public class WireCrossPoint extends GUIComponent
+{
+       private static final int CIRCLE_RADIUS = 1;
+       private static final int CIRCLE_DIAM = CIRCLE_RADIUS * 2;
+
+       private final Pin pin;
+       private final int logicWidth;
+
+       private final LogicObserver logicObs;
+       private ReadEnd end;
+
+       public WireCrossPoint(ViewModelModifiable model, int logicWidth)
+       {
+               super(model);
+               logicObs = (i) -> requestRedraw();
+
+               this.logicWidth = logicWidth;
+               setSize(CIRCLE_DIAM, CIRCLE_DIAM);
+               addPin(this.pin = new Pin(this, "", logicWidth, CIRCLE_RADIUS, CIRCLE_RADIUS));
+       }
+
+       public void moveCenterTo(double x, double y)
+       {
+               moveTo(x - CIRCLE_RADIUS, y - CIRCLE_RADIUS);
+       }
+
+       @Override
+       public void render(GeneralGC gc, Rectangle visibleRegion)
+       {
+               ColorHelper.executeWithDifferentBackground(gc, BitVectorFormatter.formatAsColor(end),
+                               () -> gc.fillOval(getPosX(), getPosY(), CIRCLE_DIAM, CIRCLE_DIAM));
+       }
+
+       public void setLogicModelBinding(ReadEnd end)
+       {
+               deregisterLogicObs(this.end);
+               this.end = end;
+               registerLogicObs(end);
+       }
+
+       private void registerLogicObs(LogicObservable observable)
+       {
+               if (observable != null)
+                       observable.registerObserver(logicObs);
+       }
+
+       private void deregisterLogicObs(LogicObservable observable)
+       {
+               if (observable != null)
+                       observable.deregisterObserver(logicObs);
+       }
+
+       public int getLogicWidth()
+       {
+               return logicWidth;
+       }
+
+       public Pin getPin()
+       {
+               return pin;
+       }
+
+       @Override
+       public Map<String, Object> getInstantiationParameters()
+       {
+               Map<String, Object> m = super.getInstantiationParameters();
+               m.put(SimpleRectangularGUIGate.kLogicWidth, logicWidth);
+               return m;
+       }
 }
\ No newline at end of file