08e7d1af1d539a803367b166fd05f17295d0c53d
[Mograsim.git] / net.mograsim.logic.ui / src / net / mograsim / logic / ui / model / components / SubmodelComponent.java
1 package net.mograsim.logic.ui.model.components;
2
3 import java.util.Collections;
4 import java.util.HashMap;
5 import java.util.Iterator;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Map.Entry;
9
10 import net.haspamelodica.swt.helper.gcs.GCConfig;
11 import net.haspamelodica.swt.helper.gcs.GeneralGC;
12 import net.haspamelodica.swt.helper.gcs.TranslatedGC;
13 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
14 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
15 import net.mograsim.logic.ui.LogicUIRenderer;
16 import net.mograsim.logic.ui.model.ViewModel;
17 import net.mograsim.logic.ui.model.ViewModelModifiable;
18 import net.mograsim.logic.ui.model.components.SubmodelComponentParams.ComponentCompositionParams;
19 import net.mograsim.logic.ui.model.components.SubmodelComponentParams.ComponentCompositionParams.InnerComponentParams;
20 import net.mograsim.logic.ui.model.components.SubmodelComponentParams.InnerPinParams;
21 import net.mograsim.logic.ui.model.components.SubmodelComponentParams.InnerWireParams;
22 import net.mograsim.logic.ui.model.components.SubmodelComponentParams.InterfacePinParams;
23 import net.mograsim.logic.ui.model.wires.GUIWire;
24 import net.mograsim.logic.ui.model.wires.MovablePin;
25 import net.mograsim.logic.ui.model.wires.Pin;
26
27 /**
28  * A {@link GUIComponent} consisting of another model. A <code>SubmodelComponent</code> can have so-called "interface pins" connecting the
29  * inner and outer models.
30  */
31 public abstract class SubmodelComponent extends GUIComponent
32 {
33         /**
34          * A modifiable view of {@link #submodel}.
35          */
36         protected final ViewModelModifiable submodelModifiable;
37         /**
38          * The model this {@link SubmodelComponent} consists of.
39          */
40         public final ViewModel submodel;
41         /**
42          * The list of all submodel interface pins of this {@link SubmodelComponent} on the submodel side.
43          */
44         private final Map<String, MovablePin> submodelPins;
45         /**
46          * An unmodifiable view of {@link #submodelPins}.
47          */
48         private final Map<String, MovablePin> submodelMovablePinsUnmodifiable;
49         /**
50          * An unmodifiable view of {@link #submodelPins} where pins are not movable.
51          */
52         private final Map<String, Pin> submodelUnmovablePinsUnmodifiable;
53         /**
54          * The list of all submodel interface pins of this {@link SubmodelComponent} on the supermodel side.
55          */
56         private final Map<String, MovablePin> supermodelPins;
57         /**
58          * An unmodifiable view of {@link #supermodelPins}.
59          */
60         private final Map<String, MovablePin> supermodelMovablePinsUnmodifiable;
61         /**
62          * An unmodifiable view of {@link #supermodelPins} where pins are not movable.
63          */
64         private final Map<String, Pin> supermodelUnmovablePinsUnmodifiable;
65         /**
66          * A pseudo-component containing all submodel interface pins on the submodel side.
67          */
68         private final SubmodelInterface submodelInterface;
69
70         /**
71          * The factor by which the submodel is scaled when rendering.
72          */
73         private double submodelScale;
74         /**
75          * If this {@link SubmodelComponent} fills at least this amount of the visible region vertically or horizontally, the submodel starts to
76          * be visible.
77          */
78         private double maxVisibleRegionFillRatioForAlpha0;
79         /**
80          * If this {@link SubmodelComponent} fills at least this amount of the visible region vertically or horizontally, the submodel is fully
81          * visible.
82          */
83         private double minVisibleRegionFillRatioForAlpha1;
84         /**
85          * The renderer used for rendering the submodel.
86          */
87         private final LogicUIRenderer renderer;
88
89         // creation and destruction
90
91         public SubmodelComponent(ViewModelModifiable model)
92         {
93                 super(model);
94                 this.submodelModifiable = new ViewModelModifiable();
95                 this.submodel = submodelModifiable;
96                 this.submodelPins = new HashMap<>();
97                 this.submodelMovablePinsUnmodifiable = Collections.unmodifiableMap(submodelPins);
98                 this.submodelUnmovablePinsUnmodifiable = Collections.unmodifiableMap(submodelPins);
99                 this.supermodelPins = new HashMap<>();
100                 this.supermodelMovablePinsUnmodifiable = Collections.unmodifiableMap(supermodelPins);
101                 this.supermodelUnmovablePinsUnmodifiable = Collections.unmodifiableMap(supermodelPins);
102                 this.submodelInterface = new SubmodelInterface(submodelModifiable);
103
104                 this.submodelScale = 1;
105                 this.maxVisibleRegionFillRatioForAlpha0 = 0.4;
106                 this.minVisibleRegionFillRatioForAlpha1 = 0.8;
107                 this.renderer = new LogicUIRenderer(submodelModifiable);
108
109                 submodelModifiable.addRedrawListener(this::requestRedraw);
110         }
111
112         // pins
113
114         /**
115          * Adds a new submodel interface pin.
116          * 
117          * @param supermodelPin the submodel interface pin on the supermodel side
118          * 
119          * @return the submodel interface pin on the submodel side
120          * 
121          * @author Daniel Kirschten
122          */
123         protected Pin addSubmodelInterface(MovablePin supermodelPin)
124         {
125                 super.addPin(supermodelPin);// do this first to be fail-fast if the supermodel does not belong to this component
126
127                 String name = supermodelPin.name;
128                 MovablePin submodelPin = new MovablePin(submodelInterface, name, supermodelPin.logicWidth, supermodelPin.getRelX() / submodelScale,
129                                 supermodelPin.getRelY() / submodelScale);
130
131                 submodelPin.addPinMovedListener(p ->
132                 {
133                         double newRelX = p.getRelX() * submodelScale;
134                         double newRelY = p.getRelY() * submodelScale;
135                         if (supermodelPin.getRelX() != newRelX || supermodelPin.getRelY() != newRelY)
136                                 supermodelPin.setRelPos(newRelX, newRelY);
137                 });
138                 supermodelPin.addPinMovedListener(p ->
139                 {
140                         double newRelX = p.getRelX() / submodelScale;
141                         double newRelY = p.getRelY() / submodelScale;
142                         if (submodelPin.getRelX() != newRelX || submodelPin.getRelY() != newRelY)
143                                 submodelPin.setRelPos(newRelX, newRelY);
144                 });
145
146                 submodelInterface.addPin(submodelPin);
147
148                 submodelPins.put(name, submodelPin);
149                 supermodelPins.put(name, supermodelPin);
150
151                 // no need to call requestRedraw() because addPin() will request a redraw
152                 return submodelPin;
153         }
154
155         /**
156          * Removes a submodel interface pin.
157          * 
158          * @author Daniel Kirschten
159          */
160         protected void removeSubmodelInterface(String name)
161         {
162                 super.removePin(name);// do this first to be fail-fast if this component doesn't have a pin with the given name
163                 Pin submodelPin = submodelPins.remove(name);
164                 submodelInterface.removePin(submodelPin.name);
165                 supermodelPins.remove(name);
166
167                 // no need to call requestRedraw() because removePin() will request a redraw
168         }
169
170         /**
171          * Returns a collection of submodel interface pins on the submodel side of this component.
172          * 
173          * @author Daniel Kirschten
174          */
175         public Map<String, Pin> getSubmodelPins()
176         {
177                 return submodelUnmovablePinsUnmodifiable;
178         }
179
180         /**
181          * Returns the submodel interface pin with the given name on the submodel side of this component.
182          * 
183          * @author Daniel Kirschten
184          */
185         public Pin getSubmodelPin(String name)
186         {
187                 return getSubmodelMovablePin(name);
188         }
189
190         /**
191          * Returns a collection of movable submodel interface pins on the submodel side of this component.
192          * 
193          * @author Daniel Kirschten
194          */
195         protected Map<String, MovablePin> getSubmodelMovablePins()
196         {
197                 return submodelMovablePinsUnmodifiable;
198         }
199
200         /**
201          * Returns the movable submodel interface pin with the given name on the submodel side of this component.
202          * 
203          * @author Daniel Kirschten
204          */
205         protected MovablePin getSubmodelMovablePin(String name)
206         {
207                 return submodelPins.get(name);
208         }
209
210         /**
211          * Returns a collection of submodel interface pins on the supermodel side of this component.
212          * 
213          * @author Daniel Kirschten
214          */
215         public Map<String, Pin> getSupermodelPins()
216         {
217                 return supermodelUnmovablePinsUnmodifiable;
218         }
219
220         /**
221          * Returns the submodel interface pin with the given name on the supermodel side of this component.
222          * 
223          * @author Daniel Kirschten
224          */
225         public Pin getSupermodelPin(String name)
226         {
227                 return getSupermodelMovablePin(name);
228         }
229
230         /**
231          * Returns a collection of movable submodel interface pins on the supermodel side of this component.
232          * 
233          * @author Daniel Kirschten
234          */
235         protected Map<String, MovablePin> getSupermodelMovablePins()
236         {
237                 return supermodelMovablePinsUnmodifiable;
238         }
239
240         /**
241          * Returns the movable submodel interface pin with the given name on the supermodel side of this component.
242          * 
243          * @author Daniel Kirschten
244          */
245         protected MovablePin getSupermodelMovablePin(String name)
246         {
247                 return supermodelPins.get(name);
248         }
249
250         // "graphical" operations
251
252         /**
253          * Sets the factor by which the submodel is scaled when rendering and calls redrawListeners. Note that the submodel interface pins will
254          * stay at their position relative to the supermodel, which means they will move relative to the submodel.
255          * 
256          * @author Daniel Kirschten
257          */
258         protected void setSubmodelScale(double submodelScale)
259         {
260                 this.submodelScale = submodelScale;
261
262                 for (Entry<String, MovablePin> e : supermodelPins.entrySet())
263                         getSubmodelMovablePin(e.getKey()).setRelPos(e.getValue().getRelX() * submodelScale, e.getValue().getRelY() * submodelScale);
264
265                 requestRedraw();// needed if there is no submodel interface pin
266         }
267
268         /**
269          * Returns the current factor by which the submodel is scaled when rendering.
270          * 
271          * @author Daniel Kirschten
272          */
273         protected double getSubmodelScale()
274         {
275                 return submodelScale;
276         }
277
278         @Override
279         public void render(GeneralGC gc, Rectangle visibleRegion)
280         {
281                 GCConfig conf = new GCConfig(gc);
282                 TranslatedGC tgc = new TranslatedGC(gc, getPosX(), getPosY(), submodelScale, true);
283                 conf.reset(tgc);
284                 double visibleRegionFillRatio = Math.max(getWidth() / visibleRegion.width, getHeight() / visibleRegion.height);
285                 double alphaFactor = map(visibleRegionFillRatio, maxVisibleRegionFillRatioForAlpha0, minVisibleRegionFillRatioForAlpha1, 0, 1);
286                 alphaFactor = Math.max(0, Math.min(1, alphaFactor));
287                 // we need to take the old alpha into account to support nested submodules better.
288                 int oldAlpha = gc.getAlpha();
289                 int submodelAlpha = Math.max(0, Math.min(255, (int) (oldAlpha * alphaFactor)));
290                 int labelAlpha = Math.max(0, Math.min(255, (int) (oldAlpha * (1 - alphaFactor))));
291                 if (submodelAlpha != 0)
292                 {
293                         gc.setAlpha(submodelAlpha);
294                         renderer.render(tgc, visibleRegion.translate(getPosX() / submodelScale, getPosY() / submodelScale, 1 / submodelScale));
295                 }
296                 if (labelAlpha != 0)
297                 {
298                         gc.setAlpha(labelAlpha);
299                         renderSymbol(gc, visibleRegion);
300                 }
301                 conf.reset(gc);
302                 // draw the outline after all other operations to make interface pins look better
303                 renderOutline(gc, visibleRegion);
304         }
305
306         protected abstract void renderOutline(GeneralGC gc, Rectangle visibleRegion);
307
308         protected abstract void renderSymbol(GeneralGC gc, Rectangle visibleRegion);
309
310         private static double map(double val, double valMin, double valMax, double mapMin, double mapMax)
311         {
312                 return mapMin + (val - valMin) * (mapMax - mapMin) / (valMax - valMin);
313         }
314
315         @Override
316         public boolean clicked(double x, double y)
317         {
318                 double scaledX = (x - getPosX()) / submodelScale;
319                 double scaledY = (y - getPosY()) / submodelScale;
320                 for (GUIComponent component : submodel.getComponents())
321                         if (component.getBounds().contains(scaledX, scaledY) && component.clicked(scaledX, scaledY))
322                                 return true;
323                 return false;
324         }
325
326         // serializing
327
328         /**
329          * @return {@link SubmodelComponentParams}, which describe this {@link SubmodelComponent}.
330          */
331         public SubmodelComponentParams calculateParams()
332         {
333                 SubmodelComponentParams params = new SubmodelComponentParams();
334                 params.type = SubmodelComponent.class.getSimpleName();
335                 params.composition = calculateCompositionParams();
336
337                 params.width = getWidth();
338                 params.height = getHeight();
339
340                 InterfacePinParams[] iPins = new InterfacePinParams[getPins().size()];
341                 int i = 0;
342                 for (Pin p : getPins().values())
343                 {
344                         InterfacePinParams iPinParams = new InterfacePinParams();
345                         iPins[i] = iPinParams;
346                         iPinParams.location = p.getRelPos();
347                         iPinParams.name = p.name;
348                         iPinParams.logicWidth = p.logicWidth;
349                         i++;
350                 }
351                 params.interfacePins = iPins;
352                 return params;
353         }
354
355         protected ComponentCompositionParams calculateCompositionParams()
356         {
357                 ComponentCompositionParams params = new ComponentCompositionParams();
358                 params.innerScale = getSubmodelScale();
359
360                 List<GUIComponent> compList = submodelModifiable.getComponents();
361                 Iterator<GUIComponent> componentIt = compList.iterator();
362                 componentIt.next(); // Skip inner SubmodelInterface
363                 InnerComponentParams[] comps = new InnerComponentParams[compList.size() - 1];
364                 int i = 0;
365                 while (componentIt.hasNext())
366                 {
367                         GUIComponent component = componentIt.next();
368                         InnerComponentParams inner = new InnerComponentParams();
369                         comps[i] = inner;
370                         inner.params = component.getInstantiationParameters();
371                         inner.pos = new Point(getPosX(), getPosY());
372                         inner.type = component.getIdentifier();
373                         i++;
374                 }
375                 params.subComps = comps;
376
377                 List<GUIWire> wireList = submodelModifiable.getWires();
378                 InnerWireParams wires[] = new InnerWireParams[wireList.size()];
379                 i = 0;
380                 for (GUIWire wire : wireList)
381                 {
382                         InnerWireParams inner = new InnerWireParams();
383                         wires[i] = inner;
384                         InnerPinParams pin1Params = new InnerPinParams(), pin2Params = new InnerPinParams();
385
386                         pin1Params.pinName = wire.getPin1().name;
387                         pin1Params.compId = compList.indexOf(wire.getPin1().component);
388                         pin2Params.pinName = wire.getPin2().name;
389                         pin2Params.compId = compList.indexOf(wire.getPin2().component);
390                         inner.pin1 = pin1Params;
391                         inner.pin2 = pin2Params;
392                         inner.path = wire.getPath();
393                         i++;
394                 }
395                 params.innerWires = wires;
396                 return params;
397         }
398
399         // operations no longer supported
400
401         @Override
402         protected void addPin(Pin pin)
403         {
404                 throw new UnsupportedOperationException("Can't add pins to a SubmodelComponent directly, call addSubmodelInterface instead");
405         }
406
407         @Override
408         protected void removePin(String name)
409         {
410                 throw new UnsupportedOperationException("Can't remove pins of a SubmodelComponent directly, call removeSubmodelInterface instead");
411         }
412 }