Fixed problems in how GUIWires automatically choose a path
[Mograsim.git] / net.mograsim.logic.ui / src / net / mograsim / logic / ui / model / wires / GUIWire.java
1 package net.mograsim.logic.ui.model.wires;\r
2 \r
3 import java.util.ArrayList;\r
4 import java.util.Arrays;\r
5 import java.util.List;\r
6 \r
7 import net.haspamelodica.swt.helper.gcs.GeneralGC;\r
8 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;\r
9 import net.mograsim.logic.core.LogicObservable;\r
10 import net.mograsim.logic.core.LogicObserver;\r
11 import net.mograsim.logic.core.types.BitVectorFormatter;\r
12 import net.mograsim.logic.core.wires.Wire.ReadEnd;\r
13 import net.mograsim.logic.ui.ColorHelper;\r
14 import net.mograsim.logic.ui.model.ViewModelModifiable;\r
15 \r
16 public class GUIWire\r
17 {\r
18         private final ViewModelModifiable model;\r
19         public final int logicWidth;\r
20         private Pin pin1;\r
21         private Pin pin2;\r
22         private Point[] path;\r
23         private double[] effectivePath;\r
24 \r
25         private final List<Runnable> redrawListeners;\r
26 \r
27         private final LogicObserver logicObs;\r
28         private ReadEnd end;\r
29 \r
30         public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, WireCrossPoint pin2)\r
31         {\r
32                 this(model, pin1, pin2, (Point[]) null);\r
33         }\r
34 \r
35         public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, Pin pin2)\r
36         {\r
37                 this(model, pin1, pin2, (Point[]) null);\r
38         }\r
39 \r
40         public GUIWire(ViewModelModifiable model, Pin pin1, WireCrossPoint pin2)\r
41         {\r
42                 this(model, pin1, pin2, (Point[]) null);\r
43         }\r
44 \r
45         public GUIWire(ViewModelModifiable model, Pin pin1, Pin pin2)\r
46         {\r
47                 this(model, pin1, pin2, (Point[]) null);\r
48         }\r
49 \r
50         public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, WireCrossPoint pin2, Point... path)\r
51         {\r
52                 this(model, pin1.getPin(), pin2.getPin(), path);\r
53         }\r
54 \r
55         public GUIWire(ViewModelModifiable model, WireCrossPoint pin1, Pin pin2, Point... path)\r
56         {\r
57                 this(model, pin1.getPin(), pin2, path);\r
58         }\r
59 \r
60         public GUIWire(ViewModelModifiable model, Pin pin1, WireCrossPoint pin2, Point... path)\r
61         {\r
62                 this(model, pin1, pin2.getPin(), path);\r
63         }\r
64 \r
65         public GUIWire(ViewModelModifiable model, Pin pin1, Pin pin2, Point... path)\r
66         {\r
67                 logicObs = (i) -> callRedrawListeners();\r
68                 this.model = model;\r
69                 this.logicWidth = pin1.logicWidth;\r
70                 if (pin2.logicWidth != pin1.logicWidth)\r
71                         throw new IllegalArgumentException("Can't connect pins of different logic width");\r
72 \r
73                 this.pin1 = pin1;\r
74                 this.pin2 = pin2;\r
75 \r
76                 this.path = path == null ? null : Arrays.copyOf(path, path.length);\r
77 \r
78                 redrawListeners = new ArrayList<>();\r
79 \r
80                 pin1.addPinMovedListener(p -> pin1Moved());\r
81                 pin2.addPinMovedListener(p -> pin2Moved());\r
82 \r
83                 recalculateEffectivePath();\r
84 \r
85                 model.wireCreated(this);\r
86         }\r
87 \r
88         private void recalculateEffectivePath()\r
89         {\r
90                 Point pos1 = pin1.getPos(), pos2 = pin2.getPos();\r
91                 if (path == null)\r
92                         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
93                 else\r
94                 {\r
95                         effectivePath = new double[path.length * 2 + 4];\r
96                         effectivePath[0] = pos1.x;\r
97                         effectivePath[1] = pos1.y;\r
98                         for (int srcI = 0, dstI = 2; srcI < path.length; srcI++, dstI += 2)\r
99                         {\r
100                                 effectivePath[dstI + 0] = path[srcI].x;\r
101                                 effectivePath[dstI + 1] = path[srcI].y;\r
102                         }\r
103                         effectivePath[effectivePath.length - 2] = pos2.x;\r
104                         effectivePath[effectivePath.length - 1] = pos2.y;\r
105                 }\r
106         }\r
107 \r
108         private void pin1Moved()\r
109         {\r
110                 recalculateEffectivePath();\r
111                 callRedrawListeners();\r
112         }\r
113 \r
114         private void pin2Moved()\r
115         {\r
116                 recalculateEffectivePath();\r
117                 callRedrawListeners();\r
118         }\r
119 \r
120         public void destroy()\r
121         {\r
122                 model.wireDestroyed(this);\r
123         }\r
124 \r
125         public void render(GeneralGC gc)\r
126         {\r
127                 ColorHelper.executeWithDifferentForeground(gc, BitVectorFormatter.formatAsColor(end), () -> gc.drawPolyline(effectivePath));\r
128         }\r
129 \r
130         public void setLogicModelBinding(ReadEnd end)\r
131         {\r
132                 deregisterLogicObs(this.end);\r
133                 this.end = end;\r
134                 registerLogicObs(end);\r
135         }\r
136 \r
137         private void registerLogicObs(LogicObservable observable)\r
138         {\r
139                 if (observable != null)\r
140                         observable.registerObserver(logicObs);\r
141         }\r
142 \r
143         private void deregisterLogicObs(LogicObservable observable)\r
144         {\r
145                 if (observable != null)\r
146                         observable.deregisterObserver(logicObs);\r
147         }\r
148 \r
149         public Pin getPin1()\r
150         {\r
151                 return pin1;\r
152         }\r
153 \r
154         public Pin getPin2()\r
155         {\r
156                 return pin2;\r
157         }\r
158 \r
159         // @formatter:off\r
160         public void addRedrawListener   (Runnable listener) {redrawListeners         .add   (listener);}\r
161 \r
162         public void removeRedrawListener(Runnable listener) {redrawListeners         .remove(listener);}\r
163 \r
164         private void callRedrawListeners() {redrawListeners.forEach(l -> l.run());}\r
165         // @formatter:on\r
166 \r
167 }