From 95d72ebf4ebc8b9aca649d3e604709fa9daaca24 Mon Sep 17 00:00:00 2001 From: Daniel Kirschten Date: Mon, 3 Jun 2019 22:11:22 +0200 Subject: [PATCH] Added SubmodelComponent --- .../logic/ui/examples/SubmodelExample.java | 33 ++++ .../model/components/SubmodelComponent.java | 156 ++++++++++++++++++ .../model/components/SubmodelInterface.java | 18 ++ .../components/TestSubmodelNANDComponent.java | 34 ++++ .../modeladapter/ViewLogicModelAdapter.java | 68 +++++--- 5 files changed, 285 insertions(+), 24 deletions(-) create mode 100644 net.mograsim.logic.ui/src/net/mograsim/logic/ui/examples/SubmodelExample.java create mode 100644 net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SubmodelComponent.java create mode 100644 net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SubmodelInterface.java create mode 100644 net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/TestSubmodelNANDComponent.java diff --git a/net.mograsim.logic.ui/src/net/mograsim/logic/ui/examples/SubmodelExample.java b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/examples/SubmodelExample.java new file mode 100644 index 00000000..3cba4db7 --- /dev/null +++ b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/examples/SubmodelExample.java @@ -0,0 +1,33 @@ +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 diff --git a/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SubmodelComponent.java b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SubmodelComponent.java new file mode 100644 index 00000000..9b0e41a3 --- /dev/null +++ b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SubmodelComponent.java @@ -0,0 +1,156 @@ +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 submodelPinsPerSupermodelPin; + private final Map submodelPinsPerSupermodelPinUnmodifiable; + private final Map supermodelPinsPerSubmodelPin; + private final Map 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 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 getSupermodelPinsPerSubmodelPin() + { + return supermodelPinsPerSubmodelPinUnmodifiable; + } + + public Pin getSupermodelPin(Pin submodelPin) + { + return getSupermodelMovablePin(submodelPin); + } + + protected PinMovable getSupermodelMovablePin(Pin submodelPin) + { + return supermodelPinsPerSubmodelPin.get(submodelPin); + } + + public Map 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 diff --git a/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SubmodelInterface.java b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SubmodelInterface.java new file mode 100644 index 00000000..8f448a80 --- /dev/null +++ b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/SubmodelInterface.java @@ -0,0 +1,18 @@ +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 diff --git a/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/TestSubmodelNANDComponent.java b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/TestSubmodelNANDComponent.java new file mode 100644 index 00000000..9cfdb266 --- /dev/null +++ b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/model/components/TestSubmodelNANDComponent.java @@ -0,0 +1,34 @@ +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 diff --git a/net.mograsim.logic.ui/src/net/mograsim/logic/ui/modeladapter/ViewLogicModelAdapter.java b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/modeladapter/ViewLogicModelAdapter.java index 3f05964e..80c2898a 100644 --- a/net.mograsim.logic.ui/src/net/mograsim/logic/ui/modeladapter/ViewLogicModelAdapter.java +++ b/net.mograsim.logic.ui/src/net/mograsim/logic/ui/modeladapter/ViewLogicModelAdapter.java @@ -1,13 +1,11 @@ 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; @@ -24,6 +22,8 @@ import net.mograsim.logic.ui.model.components.GUIAndGate; 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; @@ -45,7 +45,7 @@ public class ViewLogicModelAdapter 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()))); } @@ -55,48 +55,68 @@ public class ViewLogicModelAdapter // TODO replace Timeline with LogicModel as soon as it exists Timeline timeline = new Timeline(10); - Map 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 externalWires) + { + Map logicWiresPerPin = convertWires(getAllPins(viewModel), viewModel.getWires(), externalWires, params, timeline); Map logicWiresPerPinUnmodifiable = Collections.unmodifiableMap(logicWiresPerPin); - Map 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 supermodelPinsPerSubmodelPin = guiCompCasted.getSupermodelPinsPerSubmodelPin(); + Map 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 logicComponents = new ArrayList<>(); - // null means "no one to one mapping" - oneToOneComponents.values().stream().filter(Objects::nonNull).forEach(logicComponents::add); - - return timeline; + private static Set getAllPins(ViewModel viewModel) + { + return viewModel.getComponents().stream().flatMap(component -> component.getPins().stream()).collect(Collectors.toSet()); } - private static Map convertWires(Set allPins, List wires, LogicModelParameters params, Timeline timeline) + private static Map convertWires(Set allPins, List wires, Map externalWires, + LogicModelParameters params, Timeline timeline) { Map> connectedPinGroups = getConnectedPinGroups(allPins, wires); - Map logicWiresPerPin = createLogicWires(params, timeline, connectedPinGroups); + Map logicWiresPerPin = createLogicWires(params, timeline, connectedPinGroups, externalWires); setGUIWiresLogicModelBinding(wires, logicWiresPerPin); return logicWiresPerPin; } - private static Map createLogicWires(LogicModelParameters params, Timeline timeline, Map> connectedPinGroups) + private static Map createLogicWires(LogicModelParameters params, Timeline timeline, Map> connectedPinGroups, + Map externalWires) { Map logicWiresPerPin = new HashMap<>(); Map, Wire> logicWiresPerPinGroup = new HashMap<>(); for (Entry> 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; } -- 2.17.1