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