Improved submodule rendering
[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.Map;
6 import java.util.Map.Entry;
7
8 import net.haspamelodica.swt.helper.gcs.GCDefaultConfig;
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.ui.LogicUIRenderer;
13 import net.mograsim.logic.ui.model.ViewModel;
14 import net.mograsim.logic.ui.model.ViewModelModifiable;
15 import net.mograsim.logic.ui.model.wires.Pin;
16
17 public class SubmodelComponent extends GUIComponent
18 {
19         protected final ViewModelModifiable submodelModifiable;
20         public final ViewModel submodel;
21         private final Map<PinMovable, PinMovable> submodelPinsPerSupermodelPin;
22         private final Map<Pin, Pin> submodelPinsPerSupermodelPinUnmodifiable;
23         private final Map<PinMovable, PinMovable> supermodelPinsPerSubmodelPin;
24         private final Map<Pin, Pin> supermodelPinsPerSubmodelPinUnmodifiable;
25         private final SubmodelInterface submodelInterface;
26
27         private double submodelScale;
28         private double maxVisibleRegionFillRatioForAlpha0;
29         private double minVisibleRegionFillRatioForAlpha1;
30         private final LogicUIRenderer renderer;
31
32         public SubmodelComponent(ViewModelModifiable model)
33         {
34                 super(model);
35                 this.submodelModifiable = new ViewModelModifiable();
36                 this.submodel = submodelModifiable;
37                 this.submodelPinsPerSupermodelPin = new HashMap<>();
38                 this.submodelPinsPerSupermodelPinUnmodifiable = Collections.unmodifiableMap(submodelPinsPerSupermodelPin);
39                 this.supermodelPinsPerSubmodelPin = new HashMap<>();
40                 this.supermodelPinsPerSubmodelPinUnmodifiable = Collections.unmodifiableMap(supermodelPinsPerSubmodelPin);
41                 this.submodelInterface = new SubmodelInterface(submodelModifiable);
42
43                 this.submodelScale = 1;
44                 this.maxVisibleRegionFillRatioForAlpha0 = 0.1;
45                 this.minVisibleRegionFillRatioForAlpha1 = 0.8;
46                 this.renderer = new LogicUIRenderer(submodelModifiable);
47
48                 submodelModifiable.addRedrawListener(this::requestRedraw);
49         }
50
51         protected void setSubmodelScale(double submodelScale)
52         {
53                 this.submodelScale = submodelScale;
54
55                 for (Entry<PinMovable, PinMovable> e : supermodelPinsPerSubmodelPin.entrySet())
56                         e.getKey().setRelPos(e.getValue().getRelX() * submodelScale, e.getValue().getRelY() * submodelScale);
57
58                 requestRedraw();// needed if there is no submodel interface pin
59         }
60
61         /**
62          * Returns the submodel pin.
63          */
64         protected Pin addSubmodelInterface(int logicWidth, double relX, double relY)
65         {
66                 PinMovable submodelPin = new PinMovable(submodelInterface, logicWidth, relX / submodelScale, relY / submodelScale);
67                 submodelInterface.addPin(submodelPin);
68
69                 PinMovable supermodelPin = new PinMovable(this, logicWidth, relX, relY);
70                 addPin(supermodelPin);
71
72                 submodelPinsPerSupermodelPin.put(supermodelPin, submodelPin);
73                 supermodelPinsPerSubmodelPin.put(submodelPin, supermodelPin);
74
75                 // no need to call requestRedraw() because addPin() will request a redraw
76                 return submodelPin;
77         }
78
79         protected void moveSubmodelInterface(Pin supermodelPin, double relX, double relY)
80         {
81                 PinMovable submodelPin = getSubmodelMovablePin(supermodelPin);
82                 PinMovable supermodelPinMovable = getSupermodelMovablePin(submodelPin);
83
84                 submodelPin.setRelPos(relX / submodelScale, relY / submodelScale);
85                 supermodelPinMovable.setRelPos(relX, relY);
86
87                 // no need to call requestRedraw() because setRelPos() will request a redraw
88         }
89
90         protected void removeSubmodelInterface(Pin supermodelPin)
91         {
92                 removePin(supermodelPin);
93                 Pin submodelPin = getSubmodelMovablePin(supermodelPin);
94                 submodelInterface.removePin(submodelPin);
95
96                 submodelPinsPerSupermodelPin.remove(supermodelPin);
97                 supermodelPinsPerSubmodelPin.remove(submodelPin);
98
99                 // no need to call requestRedraw() because removePin() will request a redraw
100         }
101
102         public Map<Pin, Pin> getSupermodelPinsPerSubmodelPin()
103         {
104                 return supermodelPinsPerSubmodelPinUnmodifiable;
105         }
106
107         public Pin getSupermodelPin(Pin submodelPin)
108         {
109                 return getSupermodelMovablePin(submodelPin);
110         }
111
112         protected PinMovable getSupermodelMovablePin(Pin submodelPin)
113         {
114                 return supermodelPinsPerSubmodelPin.get(submodelPin);
115         }
116
117         public Map<Pin, Pin> getSubmodelPinsPerSupermodelPin()
118         {
119                 return submodelPinsPerSupermodelPinUnmodifiable;
120         }
121
122         public Pin getSubmodelPin(Pin supermodelPin)
123         {
124                 return getSubmodelMovablePin(supermodelPin);
125         }
126
127         protected PinMovable getSubmodelMovablePin(Pin supermodelPin)
128         {
129                 return submodelPinsPerSupermodelPin.get(supermodelPin);
130         }
131
132         @Override
133         public void render(GeneralGC gc, Rectangle visibleRegion)
134         {
135                 double posX = getBounds().x;
136                 double posY = getBounds().y;
137
138                 GCDefaultConfig conf = new GCDefaultConfig(gc);
139                 TranslatedGC tgc = new TranslatedGC(gc, posX, posY, submodelScale, true);
140                 conf.reset(tgc);
141                 double visibleRegionFillRatio = Math.max(getBounds().width / visibleRegion.width, getBounds().height / visibleRegion.height);
142                 double alphaFactor = map(visibleRegionFillRatio, maxVisibleRegionFillRatioForAlpha0, minVisibleRegionFillRatioForAlpha1, 0, 1);
143                 alphaFactor = Math.max(0, Math.min(1, alphaFactor));
144                 // we need to take the old alpha into account to support nested submodules better.
145                 gc.setAlpha(Math.max(0, Math.min(255, (int) (gc.getAlpha() * alphaFactor))));
146                 renderer.render(tgc, visibleRegion.translate(posX, posY, submodelScale));
147                 conf.reset(gc);
148                 // draw the "bounding box" after all other operations to make interface pins look better
149                 gc.drawRectangle(getBounds());
150         }
151
152         private static double map(double val, double valMin, double valMax, double mapMin, double mapMax)
153         {
154                 return mapMin + (val - valMin) * (mapMax - mapMin) / (valMax - valMin);
155         }
156
157         private static class PinMovable extends Pin
158         {
159                 public PinMovable(GUIComponent component, int logicWidth, double relX, double relY)
160                 {
161                         super(component, logicWidth, relX, relY);
162                 }
163
164                 @Override
165                 protected void setRelPos(double relX, double relY)
166                 {
167                         super.setRelPos(relX, relY);
168                 }
169         }
170 }