--- /dev/null
+package net.mograsim.logic.ui.examples;
+
+import net.mograsim.logic.ui.SimpleLogicUIStandalone;
+import net.mograsim.logic.ui.model.ViewModelModifiable;
+import net.mograsim.logic.ui.model.components.GUIBitDisplay;
+import net.mograsim.logic.ui.model.components.GUIManualSwitch;
+import net.mograsim.logic.ui.model.components.TestSubmodelNANDComponent;
+import net.mograsim.logic.ui.model.wires.GUIWire;
+
+public class SubmodelExample
+{
+ public static void main(String[] args)
+ {
+ SimpleLogicUIStandalone.executeVisualisation(SubmodelExample::createSubmodelExample);
+ }
+
+ @SuppressWarnings("unused") // GUIWires being created
+ public static void createSubmodelExample(ViewModelModifiable model)
+ {
+ GUIManualSwitch swA = new GUIManualSwitch(model);
+ swA.moveTo(0, 0);
+ GUIManualSwitch swB = new GUIManualSwitch(model);
+ swB.moveTo(0, 25);
+ TestSubmodelNANDComponent nand = new TestSubmodelNANDComponent(model);
+ nand.moveTo(30, 10);
+ GUIBitDisplay bdY = new GUIBitDisplay(model);
+ bdY.moveTo(70, 12.5);
+
+ new GUIWire(model, swA.getOutputPin(), nand.getPins().get(0));
+ new GUIWire(model, swB.getOutputPin(), nand.getPins().get(1));
+ new GUIWire(model, nand.getPins().get(2), bdY.getInputPin());
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.logic.ui.model.components;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import net.haspamelodica.swt.helper.gcs.GCDefaultConfig;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.gcs.TranslatedGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+import net.mograsim.logic.ui.LogicUIRenderer;
+import net.mograsim.logic.ui.model.ViewModel;
+import net.mograsim.logic.ui.model.ViewModelModifiable;
+import net.mograsim.logic.ui.model.wires.Pin;
+
+public class SubmodelComponent extends GUIComponent
+{
+ protected final ViewModelModifiable submodelModifiable;
+ public final ViewModel submodel;
+ private final Map<PinMovable, PinMovable> submodelPinsPerSupermodelPin;
+ private final Map<Pin, Pin> submodelPinsPerSupermodelPinUnmodifiable;
+ private final Map<PinMovable, PinMovable> supermodelPinsPerSubmodelPin;
+ private final Map<Pin, Pin> supermodelPinsPerSubmodelPinUnmodifiable;
+ private final SubmodelInterface submodelInterface;
+
+ private double submodelScale;
+ private final LogicUIRenderer renderer;
+
+ public SubmodelComponent(ViewModelModifiable model)
+ {
+ super(model);
+ this.submodelModifiable = new ViewModelModifiable();
+ this.submodel = submodelModifiable;
+ this.submodelPinsPerSupermodelPin = new HashMap<>();
+ this.submodelPinsPerSupermodelPinUnmodifiable = Collections.unmodifiableMap(submodelPinsPerSupermodelPin);
+ this.supermodelPinsPerSubmodelPin = new HashMap<>();
+ this.supermodelPinsPerSubmodelPinUnmodifiable = Collections.unmodifiableMap(supermodelPinsPerSubmodelPin);
+ this.submodelInterface = new SubmodelInterface(submodelModifiable);
+
+ this.submodelScale = 1;
+ this.renderer = new LogicUIRenderer(submodelModifiable);
+
+ submodelModifiable.addRedrawListener(this::requestRedraw);
+ }
+
+ protected void setSubmodelScale(double submodelScale)
+ {
+ this.submodelScale = submodelScale;
+
+ for (Entry<PinMovable, PinMovable> e : supermodelPinsPerSubmodelPin.entrySet())
+ e.getKey().setRelPos(e.getValue().getRelX() * submodelScale, e.getValue().getRelY() * submodelScale);
+
+ requestRedraw();// needed if there is no submodel interface pin
+ }
+
+ /**
+ * Returns the submodel pin.
+ */
+ protected Pin addSubmodelInterface(int logicWidth, double relX, double relY)
+ {
+ PinMovable submodelPin = new PinMovable(submodelInterface, logicWidth, relX / submodelScale, relY / submodelScale);
+ submodelInterface.addPin(submodelPin);
+
+ PinMovable supermodelPin = new PinMovable(this, logicWidth, relX, relY);
+ addPin(supermodelPin);
+
+ submodelPinsPerSupermodelPin.put(supermodelPin, submodelPin);
+ supermodelPinsPerSubmodelPin.put(submodelPin, supermodelPin);
+
+ // no need to call requestRedraw() because addPin() will request a redraw
+ return submodelPin;
+ }
+
+ protected void moveSubmodelInterface(Pin supermodelPin, double relX, double relY)
+ {
+ PinMovable submodelPin = getSubmodelMovablePin(supermodelPin);
+ PinMovable supermodelPinMovable = getSupermodelMovablePin(submodelPin);
+
+ submodelPin.setRelPos(relX / submodelScale, relY / submodelScale);
+ supermodelPinMovable.setRelPos(relX, relY);
+
+ // no need to call requestRedraw() because setRelPos() will request a redraw
+ }
+
+ protected void removeSubmodelInterface(Pin supermodelPin)
+ {
+ removePin(supermodelPin);
+ Pin submodelPin = getSubmodelMovablePin(supermodelPin);
+ submodelInterface.removePin(submodelPin);
+
+ submodelPinsPerSupermodelPin.remove(supermodelPin);
+ supermodelPinsPerSubmodelPin.remove(submodelPin);
+
+ // no need to call requestRedraw() because removePin() will request a redraw
+ }
+
+ public Map<Pin, Pin> getSupermodelPinsPerSubmodelPin()
+ {
+ return supermodelPinsPerSubmodelPinUnmodifiable;
+ }
+
+ public Pin getSupermodelPin(Pin submodelPin)
+ {
+ return getSupermodelMovablePin(submodelPin);
+ }
+
+ protected PinMovable getSupermodelMovablePin(Pin submodelPin)
+ {
+ return supermodelPinsPerSubmodelPin.get(submodelPin);
+ }
+
+ public Map<Pin, Pin> getSubmodelPinsPerSupermodelPin()
+ {
+ return submodelPinsPerSupermodelPinUnmodifiable;
+ }
+
+ public Pin getSubmodelPin(Pin supermodelPin)
+ {
+ return getSubmodelMovablePin(supermodelPin);
+ }
+
+ protected PinMovable getSubmodelMovablePin(Pin supermodelPin)
+ {
+ return submodelPinsPerSupermodelPin.get(supermodelPin);
+ }
+
+ @Override
+ public void render(GeneralGC gc, Rectangle visibleRegion)
+ {
+ double posX = getBounds().x;
+ double posY = getBounds().y;
+
+ GCDefaultConfig conf = new GCDefaultConfig(gc);
+ TranslatedGC tgc = new TranslatedGC(gc, posX, posY, submodelScale, true);
+ conf.reset(tgc);
+ renderer.render(tgc, visibleRegion.translate(posX, posY, submodelScale));
+ conf.reset(gc);
+ // draw the "bounding box" after all other operations to make interface pins look better
+ gc.drawRectangle(getBounds());
+ }
+
+ private static class PinMovable extends Pin
+ {
+ public PinMovable(GUIComponent component, int logicWidth, double relX, double relY)
+ {
+ super(component, logicWidth, relX, relY);
+ }
+
+ @Override
+ protected void setRelPos(double relX, double relY)
+ {
+ super.setRelPos(relX, relY);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.logic.ui.model.components;
+
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+import net.mograsim.logic.ui.model.ViewModelModifiable;
+
+public class SubmodelInterface extends GUIComponent
+{
+ public SubmodelInterface(ViewModelModifiable model)
+ {
+ super(model);
+ }
+
+ @Override
+ public void render(GeneralGC gc, Rectangle visibleRegion)
+ {// nothing to do here
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.logic.ui.model.components;
+
+import net.mograsim.logic.ui.model.ViewModelModifiable;
+import net.mograsim.logic.ui.model.wires.GUIWire;
+import net.mograsim.logic.ui.model.wires.Pin;
+
+public class TestSubmodelNANDComponent extends SubmodelComponent
+{
+ public TestSubmodelNANDComponent(ViewModelModifiable model)
+ {
+ super(model);
+ setSize(30, 20);
+ setSubmodelScale(.5);
+ initSubmodelComponents();
+ }
+
+ @SuppressWarnings("unused") // GUIWires being created
+ private void initSubmodelComponents()
+ {
+ Pin A = addSubmodelInterface(1, 0, 5);
+ Pin B = addSubmodelInterface(1, 0, 15);
+ Pin Y = addSubmodelInterface(1, 30, 10);
+
+ GUIAndGate and = new GUIAndGate(submodelModifiable, 1);
+ and.moveTo(5, 10);
+ GUINotGate not = new GUINotGate(submodelModifiable, 1);
+ not.moveTo(30, 15);
+
+ new GUIWire(submodelModifiable, A, and.getInputPins().get(0));
+ new GUIWire(submodelModifiable, B, and.getInputPins().get(1));
+ new GUIWire(submodelModifiable, and.getOutputPin(), not.getInputPins().get(0));
+ new GUIWire(submodelModifiable, not.getOutputPin(), Y);
+ }
+}
\ No newline at end of file
package net.mograsim.logic.ui.modeladapter;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.mograsim.logic.ui.model.components.GUIComponent;
import net.mograsim.logic.ui.model.components.GUINotGate;
import net.mograsim.logic.ui.model.components.GUIOrGate;
+import net.mograsim.logic.ui.model.components.SubmodelComponent;
+import net.mograsim.logic.ui.model.components.SubmodelInterface;
import net.mograsim.logic.ui.model.wires.GUIWire;
import net.mograsim.logic.ui.model.wires.Pin;
import net.mograsim.logic.ui.model.wires.WireCrossPoint;
componentAdaptersModifiable.add(new ManualSwitchAdapter());
componentAdaptersModifiable.add(new BitDisplayAdapter());
componentAdaptersModifiable.add(new Am2901NANDBasedAdapter());
- // TODO list all "primitive" adapters here
+ // TODO list all adapters here
componentAdapters = Collections.unmodifiableMap(
componentAdaptersModifiable.stream().collect(Collectors.toMap(ComponentAdapter::getSupportedClass, Function.identity())));
}
// TODO replace Timeline with LogicModel as soon as it exists
Timeline timeline = new Timeline(10);
- Map<Pin, Wire> logicWiresPerPin = convertWires(
- viewModel.getComponents().stream().flatMap(component -> component.getPins().stream()).collect(Collectors.toSet()),
- viewModel.getWires(), params, timeline);
+ convert(viewModel, params, timeline, Map.of());
+
+ return timeline;
+ }
+
+ private static void convert(ViewModel viewModel, LogicModelParameters params, Timeline timeline, Map<Pin, Wire> externalWires)
+ {
+ Map<Pin, Wire> logicWiresPerPin = convertWires(getAllPins(viewModel), viewModel.getWires(), externalWires, params, timeline);
Map<Pin, Wire> logicWiresPerPinUnmodifiable = Collections.unmodifiableMap(logicWiresPerPin);
- Map<GUIComponent, Component> oneToOneComponents = new HashMap<>();
for (GUIComponent guiComp : viewModel.getComponents())
{
- if (!(guiComp instanceof WireCrossPoint))
- oneToOneComponents.put(guiComp, createAndLinkComponent(timeline, params, guiComp, logicWiresPerPinUnmodifiable,
- componentAdapters.get(guiComp.getClass())));
- else
+ if (guiComp instanceof SubmodelComponent)
+ {
+ SubmodelComponent guiCompCasted = (SubmodelComponent) guiComp;
+ Map<Pin, Pin> supermodelPinsPerSubmodelPin = guiCompCasted.getSupermodelPinsPerSubmodelPin();
+ Map<Pin, Wire> externalWiresForSubmodel = supermodelPinsPerSubmodelPin.entrySet().stream()
+ .collect(Collectors.toMap(Entry::getKey, e -> logicWiresPerPin.get(e.getValue())));
+ convert(guiCompCasted.submodel, params, timeline, externalWiresForSubmodel);
+ } else if (guiComp instanceof WireCrossPoint)
{
WireCrossPoint guiCompCasted = (WireCrossPoint) guiComp;
guiCompCasted.setLogicModelBinding(logicWiresPerPin.get(guiCompCasted.getPin()).createReadOnlyEnd());
- }
+ } else if (!(guiComp instanceof SubmodelInterface))// nothing to do for SubmodelInterfaces
+ createAndLinkComponent(timeline, params, guiComp, logicWiresPerPinUnmodifiable, componentAdapters.get(guiComp.getClass()));
}
+ }
- // TODO handle complex components
-
- List<Component> logicComponents = new ArrayList<>();
- // null means "no one to one mapping"
- oneToOneComponents.values().stream().filter(Objects::nonNull).forEach(logicComponents::add);
-
- return timeline;
+ private static Set<Pin> getAllPins(ViewModel viewModel)
+ {
+ return viewModel.getComponents().stream().flatMap(component -> component.getPins().stream()).collect(Collectors.toSet());
}
- private static Map<Pin, Wire> convertWires(Set<Pin> allPins, List<GUIWire> wires, LogicModelParameters params, Timeline timeline)
+ private static Map<Pin, Wire> convertWires(Set<Pin> allPins, List<GUIWire> wires, Map<Pin, Wire> externalWires,
+ LogicModelParameters params, Timeline timeline)
{
Map<Pin, Set<Pin>> connectedPinGroups = getConnectedPinGroups(allPins, wires);
- Map<Pin, Wire> logicWiresPerPin = createLogicWires(params, timeline, connectedPinGroups);
+ Map<Pin, Wire> logicWiresPerPin = createLogicWires(params, timeline, connectedPinGroups, externalWires);
setGUIWiresLogicModelBinding(wires, logicWiresPerPin);
return logicWiresPerPin;
}
- private static Map<Pin, Wire> createLogicWires(LogicModelParameters params, Timeline timeline, Map<Pin, Set<Pin>> connectedPinGroups)
+ private static Map<Pin, Wire> createLogicWires(LogicModelParameters params, Timeline timeline, Map<Pin, Set<Pin>> connectedPinGroups,
+ Map<Pin, Wire> externalWires)
{
Map<Pin, Wire> logicWiresPerPin = new HashMap<>();
Map<Set<Pin>, Wire> logicWiresPerPinGroup = new HashMap<>();
for (Entry<Pin, Set<Pin>> e : connectedPinGroups.entrySet())
- logicWiresPerPin.put(e.getKey(), logicWiresPerPinGroup.computeIfAbsent(e.getValue(),
- set -> new Wire(timeline, e.getKey().logicWidth, params.wireTravelTime)));
+ logicWiresPerPin.put(e.getKey(), logicWiresPerPinGroup.computeIfAbsent(e.getValue(), set ->
+ {
+ Wire externalWire = null;
+ for (Pin p : set)
+ {
+ Wire externalWireCandidate = externalWires.get(p);
+ if (externalWireCandidate != null)
+ if (externalWire == null)
+ externalWire = externalWireCandidate;
+ else
+ throw new IllegalArgumentException("Two pins to external wires can't be connected directly");
+ }
+ return new Wire(timeline, e.getKey().logicWidth, params.wireTravelTime);
+ }));
return logicWiresPerPin;
}