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