From: Fabian Stemmler Date: Wed, 29 May 2019 16:07:52 +0000 (+0200) Subject: Merge branch 'development' of X-Git-Url: https://mograsim.net/gitweb/?a=commitdiff_plain;h=10a75c8a10c2508210efbd2f2925a514465a5457;hp=50082bc2126d3f408acbdf4103bb4fdf29ac1d4f;p=Mograsim.git Merge branch 'development' of https://gitlab.lrz.de/lrr-tum/students/eragp-misim-2019 into development # Conflicts: # LogicUI/src/era/mi/gui/LogicUIStandalone.java --- diff --git a/LogicUI/src/era/mi/gui/LogicUICanvas.java b/LogicUI/src/era/mi/gui/LogicUICanvas.java index 2fbb45d1..c516e0c6 100644 --- a/LogicUI/src/era/mi/gui/LogicUICanvas.java +++ b/LogicUI/src/era/mi/gui/LogicUICanvas.java @@ -8,6 +8,7 @@ import org.eclipse.swt.widgets.Event; import era.mi.gui.model.ViewModel; import era.mi.gui.model.components.GUIComponent; +import era.mi.gui.model.wires.GUIWire; import era.mi.gui.model.wires.Pin; import net.haspamelodica.swt.helper.gcs.GeneralGC; import net.haspamelodica.swt.helper.swtobjectwrappers.Point; @@ -40,30 +41,34 @@ public class LogicUICanvas extends ZoomableCanvas p.removePinMovedListener(redrawConsumer); redrawThreadsafe(); }; - model.addComponentAddedListener(c -> + Consumer componentAddedListener = c -> { - c.addComponentChangedListener(redrawConsumer); + c.addComponentLookChangedListener(redrawConsumer); c.addComponentMovedListener(redrawConsumer); c.addPinAddedListener(pinAddedListener); c.addPinRemovedListener(pinRemovedListener); redrawThreadsafe(); - }); + }; + model.addComponentAddedListener(componentAddedListener); + model.getComponents().forEach(componentAddedListener); model.addComponentRemovedListener(c -> { - c.removeComponentChangedListener(redrawConsumer); + c.removeComponentLookChangedListener(redrawConsumer); c.removeComponentMovedListener(redrawConsumer); c.removePinAddedListener(pinAddedListener); c.removePinRemovedListener(pinRemovedListener); redrawThreadsafe(); }); - model.addWireAddedListener(w -> + Consumer wireAddedListener = w -> { - w.addWireChangedListener(redrawConsumer); + w.addWireLookChangedListener(redrawConsumer); redrawThreadsafe(); - }); + }; + model.addWireAddedListener(wireAddedListener); + model.getWires().forEach(wireAddedListener); model.addWireRemovedListener(w -> { - w.removeWireChangedListener(redrawConsumer); + w.removeWireLookChangedListener(redrawConsumer); redrawThreadsafe(); }); diff --git a/LogicUI/src/era/mi/gui/LogicUIStandalone.java b/LogicUI/src/era/mi/gui/LogicUIStandalone.java index 8bbfb1c5..8e37be8b 100644 --- a/LogicUI/src/era/mi/gui/LogicUIStandalone.java +++ b/LogicUI/src/era/mi/gui/LogicUIStandalone.java @@ -8,6 +8,9 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import era.mi.gui.model.ViewModel; +import era.mi.gui.modeladapter.LogicModelParameters; +import era.mi.gui.modeladapter.ViewLogicModelAdapter; +import era.mi.logic.timeline.Timeline; import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasOverlay; import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput; @@ -19,6 +22,7 @@ import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInpu public class LogicUIStandalone { private ViewModel model; + private Timeline timeline; private final Display display; private final Shell shell; @@ -37,6 +41,12 @@ public class LogicUIStandalone userInput.buttonZoom = 2; userInput.enableUserInput(); new ZoomableCanvasOverlay(ui, null).enableScale(); + + // TODO don't do this here + LogicModelParameters params = new LogicModelParameters(); + params.gateProcessTime = 50; + params.wireTravelTime = 10; + timeline = ViewLogicModelAdapter.convert(model, params); } public LogicUICanvas getLogicUICanvas() @@ -50,34 +60,33 @@ public class LogicUIStandalone public void run() { AtomicBoolean running = new AtomicBoolean(true); -// Thread simulationThread = new Thread(() -> -// { -// while (running.get()) -// { -// // always execute to keep timeline from "hanging behind" for too long -// timeline.executeUntil(timeline.laterThan(System.currentTimeMillis()), System.currentTimeMillis() + 10); -// model.timeline.executeUpTo(System.currentTimeMillis(), System.currentTimeMillis() + 10); -// long sleepTime; -// if (model.timeline.hasNext()) -// sleepTime = model.timeline.nextEventTime() - System.currentTimeMillis(); -// else -// sleepTime = 10; -// try -// { -// if (sleepTime > 0) -// Thread.sleep(sleepTime); -// } -// catch (InterruptedException e) -// { -// } // it is normal execution flow to be interrupted -// } -// }); -// simulationThread.start(); -// model.timeline.addEventAddedListener(event -> -// { -// if (event.getTiming() <= System.currentTimeMillis()) -// simulationThread.interrupt(); -// }); + Thread simulationThread = new Thread(() -> + { + while (running.get()) + { + // always execute to keep timeline from "hanging behind" for too long + timeline.executeUntil(timeline.laterThan(System.currentTimeMillis()), System.currentTimeMillis() + 10); + long sleepTime; + if (timeline.hasNext()) + sleepTime = timeline.nextEventTime() - System.currentTimeMillis(); + else + sleepTime = 10; + try + { + if (sleepTime > 0) + Thread.sleep(sleepTime); + } + catch (InterruptedException e) + { + } // it is normal execution flow to be interrupted + } + }); + simulationThread.start(); + timeline.addEventAddedListener(event -> + { + if (event.getTiming() <= System.currentTimeMillis()) + simulationThread.interrupt(); + }); shell.open(); while (!shell.isDisposed()) diff --git a/LogicUI/src/era/mi/gui/examples/Playground.java b/LogicUI/src/era/mi/gui/examples/Playground.java index 78524b7d..cf9f1e07 100644 --- a/LogicUI/src/era/mi/gui/examples/Playground.java +++ b/LogicUI/src/era/mi/gui/examples/Playground.java @@ -11,23 +11,27 @@ import net.haspamelodica.swt.helper.swtobjectwrappers.Point; public class Playground { - private static final int WIRE_DELAY = 10; - private static final int OR_DELAY = 50; - private static final int NOT_DELAY = 50; - public static void main(String[] args) { ViewModel model = new ViewModel(); + GUIAndGate andGate = new GUIAndGate(model, 1); + andGate.moveTo(10, 10); + GUINotGate notGate = new GUINotGate(model, 1); + notGate.moveTo(10, 40); + + new GUIWire(model, andGate.getPins().get(0), notGate.getPins().get(1), new Point(20, 50)); + LogicUIStandalone ui = new LogicUIStandalone(model); - addComponentsAndWires(ui, model); + + ui.getLogicUICanvas().addListener(SWT.KeyDown, e -> notGate.moveTo(150, 10)); ui.run(); } public static void addComponentsAndWires(LogicUIStandalone ui, ViewModel model) { - GUIAndGate andGate = new GUIAndGate(model); + GUIAndGate andGate = new GUIAndGate(model, 1); andGate.moveTo(10, 10); - GUINotGate notGate = new GUINotGate(model); + GUINotGate notGate = new GUINotGate(model, 1); notGate.moveTo(10, 40); new GUIWire(model, andGate.getPins().get(0), notGate.getPins().get(1), new Point(20, 50)); diff --git a/LogicUI/src/era/mi/gui/model/components/GUIAndGate.java b/LogicUI/src/era/mi/gui/model/components/GUIAndGate.java index 4f69d36d..65f1db4e 100644 --- a/LogicUI/src/era/mi/gui/model/components/GUIAndGate.java +++ b/LogicUI/src/era/mi/gui/model/components/GUIAndGate.java @@ -2,11 +2,11 @@ package era.mi.gui.model.components; import era.mi.gui.model.ViewModel; -public class GUIAndGate extends RectangularShapedGUIGate +public class GUIAndGate extends SimpleRectangularGUIGate { - public GUIAndGate(ViewModel model) + public GUIAndGate(ViewModel model, int logicWidth) { - super(model, "&", false); - setInputCount(2); + super(model, logicWidth, "&", false); + setInputCount(2);// TODO make variable } } \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/model/components/GUIComponent.java b/LogicUI/src/era/mi/gui/model/components/GUIComponent.java index dc064617..ccb24a22 100644 --- a/LogicUI/src/era/mi/gui/model/components/GUIComponent.java +++ b/LogicUI/src/era/mi/gui/model/components/GUIComponent.java @@ -17,7 +17,7 @@ public abstract class GUIComponent private final List pins; protected final List pinsUnmodifiable; - private final List> componentChangedListeners; + private final List> componentLookChangedListeners; private final List> componentMovedListeners; private final List> pinAddedListeners; private final List> pinRemovedListeners; @@ -29,7 +29,7 @@ public abstract class GUIComponent this.pins = new ArrayList<>(); this.pinsUnmodifiable = Collections.unmodifiableList(pins); - this.componentChangedListeners = new ArrayList<>(); + this.componentLookChangedListeners = new ArrayList<>(); this.componentMovedListeners = new ArrayList<>(); this.pinAddedListeners = new ArrayList<>(); this.pinRemovedListeners = new ArrayList<>(); @@ -76,20 +76,20 @@ public abstract class GUIComponent } // @formatter:off - public void addComponentChangedListener (Consumer listener) {componentChangedListeners.add (listener);} - public void addComponentMovedListener (Consumer listener) {componentMovedListeners .add (listener);} - public void addPinAddedListener (Consumer listener) {pinAddedListeners .add (listener);} - public void addPinRemovedListener (Consumer listener) {pinRemovedListeners .add (listener);} - - public void removeComponentChangedListener(Consumer listener) {componentChangedListeners.remove(listener);} - public void removeComponentMovedListener (Consumer listener) {componentMovedListeners .remove(listener);} - public void removePinAddedListener (Consumer listener) {pinAddedListeners .remove(listener);} - public void removePinRemovedListener (Consumer listener) {pinRemovedListeners .remove(listener);} - - protected void callComponentChangedListeners( ) {componentChangedListeners.forEach(l -> l.accept(this));} - private void callComponentMovedListeners ( ) {componentMovedListeners .forEach(l -> l.accept(this));} - private void callPinAddedListeners (Pin p) {pinAddedListeners .forEach(l -> l.accept(p ));} - private void callPinRemovedListeners (Pin p) {pinRemovedListeners .forEach(l -> l.accept(p ));} + public void addComponentLookChangedListener (Consumer listener) {componentLookChangedListeners.add (listener);} + public void addComponentMovedListener (Consumer listener) {componentMovedListeners .add (listener);} + public void addPinAddedListener (Consumer listener) {pinAddedListeners .add (listener);} + public void addPinRemovedListener (Consumer listener) {pinRemovedListeners .add (listener);} + + public void removeComponentLookChangedListener(Consumer listener) {componentLookChangedListeners.remove(listener);} + public void removeComponentMovedListener (Consumer listener) {componentMovedListeners .remove(listener);} + public void removePinAddedListener (Consumer listener) {pinAddedListeners .remove(listener);} + public void removePinRemovedListener (Consumer listener) {pinRemovedListeners .remove(listener);} + + protected void callComponentLookChangedListeners( ) {componentLookChangedListeners.forEach(l -> l.accept(this));} + private void callComponentMovedListeners ( ) {componentMovedListeners .forEach(l -> l.accept(this));} + private void callPinAddedListeners (Pin p) {pinAddedListeners .forEach(l -> l.accept(p ));} + private void callPinRemovedListeners (Pin p) {pinRemovedListeners .forEach(l -> l.accept(p ));} // @form atter:on /** @@ -101,7 +101,7 @@ public abstract class GUIComponent { bounds.width = width; bounds.height = height; - callComponentChangedListeners(); + callComponentLookChangedListeners(); } protected void addPin(Pin pin) diff --git a/LogicUI/src/era/mi/gui/model/components/GUIManualSwitch.java b/LogicUI/src/era/mi/gui/model/components/GUIManualSwitch.java index 870a55a4..6008329c 100644 --- a/LogicUI/src/era/mi/gui/model/components/GUIManualSwitch.java +++ b/LogicUI/src/era/mi/gui/model/components/GUIManualSwitch.java @@ -23,7 +23,7 @@ public class GUIManualSwitch extends GUIComponent { super(model); setSize(width, height); - addPin(new Pin(this, width, height / 2)); + addPin(new Pin(this, 1, width, height / 2)); } @Override @@ -44,7 +44,7 @@ public class GUIManualSwitch extends GUIComponent this.logicSwitch = logicSwitch; this.end = end; // TODO when ManualSwitch supports it, add listeners - end.addObserver((i, o) -> callComponentChangedListeners()); + end.addObserver((i, o) -> callComponentLookChangedListeners()); } @Override diff --git a/LogicUI/src/era/mi/gui/model/components/GUINotGate.java b/LogicUI/src/era/mi/gui/model/components/GUINotGate.java index 5f41592b..c22d9b28 100644 --- a/LogicUI/src/era/mi/gui/model/components/GUINotGate.java +++ b/LogicUI/src/era/mi/gui/model/components/GUINotGate.java @@ -2,11 +2,11 @@ package era.mi.gui.model.components; import era.mi.gui.model.ViewModel; -public class GUINotGate extends RectangularShapedGUIGate +public class GUINotGate extends SimpleRectangularGUIGate { - public GUINotGate(ViewModel model) + public GUINotGate(ViewModel model, int logicWidth) { - super(model, "1", true); + super(model, logicWidth, "1", true); setInputCount(1); } } \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/model/components/GUIOrGate.java b/LogicUI/src/era/mi/gui/model/components/GUIOrGate.java index df64bba1..a732509b 100644 --- a/LogicUI/src/era/mi/gui/model/components/GUIOrGate.java +++ b/LogicUI/src/era/mi/gui/model/components/GUIOrGate.java @@ -2,11 +2,11 @@ package era.mi.gui.model.components; import era.mi.gui.model.ViewModel; -public class GUIOrGate extends RectangularShapedGUIGate +public class GUIOrGate extends SimpleRectangularGUIGate { - public GUIOrGate(ViewModel model) + public GUIOrGate(ViewModel model, int logicWidth) { - super(model, "\u22651", false);// ">=1" + super(model, logicWidth, "\u22651", false);// ">=1" setInputCount(2); } } \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/model/components/RectangularShapedGUIGate.java b/LogicUI/src/era/mi/gui/model/components/RectangularShapedGUIGate.java deleted file mode 100644 index c7d0b346..00000000 --- a/LogicUI/src/era/mi/gui/model/components/RectangularShapedGUIGate.java +++ /dev/null @@ -1,74 +0,0 @@ -package era.mi.gui.model.components; - -import java.util.ArrayList; -import java.util.List; - -import era.mi.gui.model.ViewModel; -import era.mi.gui.model.wires.MovablePin; -import era.mi.gui.model.wires.Pin; -import net.haspamelodica.swt.helper.gcs.GeneralGC; -import net.haspamelodica.swt.helper.swtobjectwrappers.Font; -import net.haspamelodica.swt.helper.swtobjectwrappers.Point; -import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle; - -public class RectangularShapedGUIGate extends GUIComponent -{ - private static final double width = 20; - private static final double pinDistance = 10; - private static final double fontHeight = 5; - private static final double invertedCircleDiam = 3.5; - - private final String label; - private final boolean isInverted; - private final double rectWidth; - - private MovablePin outputPin; - private final List inputPins; - - protected RectangularShapedGUIGate(ViewModel model, String label, boolean isInverted) - { - super(model); - this.label = label; - this.isInverted = isInverted; - this.rectWidth = width - (isInverted ? invertedCircleDiam : 0); - this.outputPin = new MovablePin(this, width, 0); - addPin(outputPin); - this.inputPins = new ArrayList<>(); - setInputCount(1); - } - - protected void setInputCount(int inputCount) - { - int oldInputCount = inputPins.size(); - setSize(width, inputCount * pinDistance); - if (oldInputCount > inputCount) - while (inputPins.size() > inputCount) - removePin(inputPins.get(inputCount)); - else if (oldInputCount < inputCount) - for (int i = oldInputCount; i < inputCount; i++) - { - Pin pin = new Pin(this, 0, pinDistance / 2 + i * pinDistance); - inputPins.add(pin); - addPin(pin); - } - outputPin.setRelPos(width, inputCount * pinDistance / 2); - } - - @Override - public void render(GeneralGC gc, Rectangle visibleRegion) - { - double posX = getBounds().x; - double posY = getBounds().y; - - double height = inputPins.size() * pinDistance; - gc.drawRectangle(posX, posY, rectWidth, height); - Font oldFont = gc.getFont(); - Font labelFont = new Font(oldFont.getName(), fontHeight, oldFont.getStyle()); - gc.setFont(labelFont); - Point textExtent = gc.textExtent(label); - gc.drawText(label, posX + (rectWidth - textExtent.x) / 2, posY + (height - textExtent.y) / 2, true); - gc.setFont(oldFont); - if (isInverted) - gc.drawOval(posX + rectWidth, posY + (height - invertedCircleDiam) / 2, invertedCircleDiam, invertedCircleDiam); - } -} \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/model/components/SimpleRectangularGUIGate.java b/LogicUI/src/era/mi/gui/model/components/SimpleRectangularGUIGate.java new file mode 100644 index 00000000..eb396cca --- /dev/null +++ b/LogicUI/src/era/mi/gui/model/components/SimpleRectangularGUIGate.java @@ -0,0 +1,89 @@ +package era.mi.gui.model.components; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import era.mi.gui.model.ViewModel; +import era.mi.gui.model.wires.MovablePin; +import era.mi.gui.model.wires.Pin; +import net.haspamelodica.swt.helper.gcs.GeneralGC; +import net.haspamelodica.swt.helper.swtobjectwrappers.Font; +import net.haspamelodica.swt.helper.swtobjectwrappers.Point; +import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle; + +public class SimpleRectangularGUIGate extends GUIComponent +{ + private static final double width = 20; + private static final double pinDistance = 10; + private static final double fontHeight = 5; + private static final double invertedCircleDiam = 3.5; + + private final String label; + protected final int logicWidth; + private final boolean isInverted; + private final double rectWidth; + + private MovablePin outputPin; + private final List inputPins; + private final List inputPinsUnmodifiable; + + protected SimpleRectangularGUIGate(ViewModel model, int logicWidth, String label, boolean isInverted) + { + super(model); + this.label = label; + this.logicWidth = logicWidth; + this.isInverted = isInverted; + this.rectWidth = width - (isInverted ? invertedCircleDiam : 0); + this.outputPin = new MovablePin(this, logicWidth, width, 0); + addPin(outputPin); + this.inputPins = new ArrayList<>(); + this.inputPinsUnmodifiable = Collections.unmodifiableList(inputPins); + setInputCount(1); + } + + protected void setInputCount(int inputCount) + { + int oldInputCount = inputPins.size(); + setSize(width, inputCount * pinDistance); + if (oldInputCount > inputCount) + while (inputPins.size() > inputCount) + removePin(inputPins.get(inputCount)); + else if (oldInputCount < inputCount) + for (int i = oldInputCount; i < inputCount; i++) + { + Pin pin = new Pin(this, logicWidth, 0, pinDistance / 2 + i * pinDistance); + inputPins.add(pin); + addPin(pin); + } + outputPin.setRelPos(width, inputCount * pinDistance / 2); + } + + public Pin getOutputPin() + { + return outputPin; + } + + public List getInputPins() + { + return inputPinsUnmodifiable; + } + + @Override + public void render(GeneralGC gc, Rectangle visibleRegion) + { + double posX = getBounds().x; + double posY = getBounds().y; + + double height = inputPins.size() * pinDistance; + gc.drawRectangle(posX, posY, rectWidth, height); + Font oldFont = gc.getFont(); + Font labelFont = new Font(oldFont.getName(), fontHeight, oldFont.getStyle()); + gc.setFont(labelFont); + Point textExtent = gc.textExtent(label); + gc.drawText(label, posX + (rectWidth - textExtent.x) / 2, posY + (height - textExtent.y) / 2, true); + gc.setFont(oldFont); + if (isInverted) + gc.drawOval(posX + rectWidth, posY + (height - invertedCircleDiam) / 2, invertedCircleDiam, invertedCircleDiam); + } +} \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/model/wires/GUIWire.java b/LogicUI/src/era/mi/gui/model/wires/GUIWire.java index c2f03ee5..6bcbd1c5 100644 --- a/LogicUI/src/era/mi/gui/model/wires/GUIWire.java +++ b/LogicUI/src/era/mi/gui/model/wires/GUIWire.java @@ -14,17 +14,21 @@ import net.haspamelodica.swt.helper.swtobjectwrappers.Point; public class GUIWire { private final ViewModel model; + public final int logicWidth; private Pin pin1; private Pin pin2; private double[] path; - private final List> wireChangedListeners; + private final List> wireLookChangedListeners; private ReadEnd end; public GUIWire(ViewModel model, Pin pin1, Pin pin2, Point... path) { this.model = model; + this.logicWidth = pin1.logicWidth; + if (pin2.logicWidth != pin1.logicWidth) + throw new IllegalArgumentException("Can't connect pins of different logic width"); this.path = new double[path.length * 2 + 4]; for (int srcI = 0, dstI = 2; srcI < path.length; srcI++, dstI += 2) { @@ -35,7 +39,7 @@ public class GUIWire this.pin1 = pin1; this.pin2 = pin2; - wireChangedListeners = new ArrayList<>(); + wireLookChangedListeners = new ArrayList<>(); pin1.addPinMovedListener(p -> pin1Moved()); pin2.addPinMovedListener(p -> pin2Moved()); @@ -72,15 +76,25 @@ public class GUIWire public void setLogicModelBinding(ReadEnd end) { this.end = end; - end.addObserver((i, o) -> callWireChangedListeners()); + end.addObserver((i, o) -> callWireLookChangedListeners()); + } + + public Pin getPin1() + { + return pin1; + } + + public Pin getPin2() + { + return pin2; } // @formatter:off - public void addWireChangedListener (Consumer listener) {wireChangedListeners.add (listener);} + public void addWireLookChangedListener (Consumer listener) {wireLookChangedListeners.add (listener);} - public void removeWireChangedListener(Consumer listener) {wireChangedListeners.remove(listener);} + public void removeWireLookChangedListener(Consumer listener) {wireLookChangedListeners.remove(listener);} - private void callWireChangedListeners() {wireChangedListeners.forEach(l -> l.accept(this));} + private void callWireLookChangedListeners() {wireLookChangedListeners.forEach(l -> l.accept(this));} // @formatter:on } \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/model/wires/MovablePin.java b/LogicUI/src/era/mi/gui/model/wires/MovablePin.java index 673b2578..949e7d76 100644 --- a/LogicUI/src/era/mi/gui/model/wires/MovablePin.java +++ b/LogicUI/src/era/mi/gui/model/wires/MovablePin.java @@ -4,9 +4,9 @@ import era.mi.gui.model.components.GUIComponent; public class MovablePin extends Pin { - public MovablePin(GUIComponent component, double relX, double relY) + public MovablePin(GUIComponent component, int logicWidth, double relX, double relY) { - super(component, relX, relY); + super(component, logicWidth, relX, relY); } @Override diff --git a/LogicUI/src/era/mi/gui/model/wires/Pin.java b/LogicUI/src/era/mi/gui/model/wires/Pin.java index 44ea3742..abafc071 100644 --- a/LogicUI/src/era/mi/gui/model/wires/Pin.java +++ b/LogicUI/src/era/mi/gui/model/wires/Pin.java @@ -11,15 +11,17 @@ import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle; public class Pin { public final GUIComponent component; + public final int logicWidth; protected double relX; protected double relY; private final List> pinMovedListeners; - public Pin(GUIComponent component, double relX, double relY) + public Pin(GUIComponent component, int logicWidth, double relX, double relY) { this.component = component; + this.logicWidth = logicWidth; this.relX = relX; this.relY = relY; diff --git a/LogicUI/src/era/mi/gui/model/wires/WireCrossPoint.java b/LogicUI/src/era/mi/gui/model/wires/WireCrossPoint.java index ebe30c30..73d98f53 100644 --- a/LogicUI/src/era/mi/gui/model/wires/WireCrossPoint.java +++ b/LogicUI/src/era/mi/gui/model/wires/WireCrossPoint.java @@ -11,12 +11,14 @@ import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle; public class WireCrossPoint extends GUIComponent { private ReadEnd end; + private final int logicWidth; - public WireCrossPoint(ViewModel model) + public WireCrossPoint(ViewModel model, int logicWidth) { super(model); + this.logicWidth = logicWidth; setSize(0, 0); - addPin(new Pin(this, 0, 0)); + addPin(new Pin(this, logicWidth, 0, 0)); } @Override @@ -28,6 +30,11 @@ public class WireCrossPoint extends GUIComponent public void setLogicModelBinding(ReadEnd end) { this.end = end; - end.addObserver((i, o) -> callComponentChangedListeners()); + end.addObserver((i, o) -> callComponentLookChangedListeners()); + } + + public int getLogicWidth() + { + return logicWidth; } } \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/modeladapter/LogicModelParameters.java b/LogicUI/src/era/mi/gui/modeladapter/LogicModelParameters.java new file mode 100644 index 00000000..435f7086 --- /dev/null +++ b/LogicUI/src/era/mi/gui/modeladapter/LogicModelParameters.java @@ -0,0 +1,7 @@ +package era.mi.gui.modeladapter; + +public class LogicModelParameters +{ + public int wireTravelTime; + public int gateProcessTime; +} \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/modeladapter/ViewLogicModelAdapter.java b/LogicUI/src/era/mi/gui/modeladapter/ViewLogicModelAdapter.java index f005a80b..c64ae971 100644 --- a/LogicUI/src/era/mi/gui/modeladapter/ViewLogicModelAdapter.java +++ b/LogicUI/src/era/mi/gui/modeladapter/ViewLogicModelAdapter.java @@ -1,6 +1,139 @@ package era.mi.gui.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.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import era.mi.gui.model.ViewModel; +import era.mi.gui.model.components.GUIAndGate; +import era.mi.gui.model.components.GUIComponent; +import era.mi.gui.model.components.GUINotGate; +import era.mi.gui.model.components.GUIOrGate; +import era.mi.gui.model.wires.GUIWire; +import era.mi.gui.model.wires.Pin; +import era.mi.gui.model.wires.WireCrossPoint; +import era.mi.gui.modeladapter.componentadapters.ComponentAdapter; +import era.mi.gui.modeladapter.componentadapters.SimpleGateAdapter; +import era.mi.logic.components.Component; +import era.mi.logic.components.gates.AndGate; +import era.mi.logic.components.gates.NotGate; +import era.mi.logic.components.gates.OrGate; +import era.mi.logic.timeline.Timeline; +import era.mi.logic.wires.Wire; +import era.mi.logic.wires.Wire.ReadEnd; + public class ViewLogicModelAdapter { + private final static Map, ComponentAdapter> componentAdapters; + static + { + Map, ComponentAdapter> componentAdaptersModifiable = new HashMap<>(); + componentAdaptersModifiable.put(GUIOrGate.class, new SimpleGateAdapter(OrGate::new)); + componentAdaptersModifiable.put(GUIAndGate.class, new SimpleGateAdapter(AndGate::new)); + componentAdaptersModifiable.put(GUINotGate.class, new SimpleGateAdapter((t, p, o, i) -> new NotGate(t, p, i[0], o))); + // TODO list all "primitive" adapters here + componentAdapters = Collections.unmodifiableMap(componentAdaptersModifiable); + } + + public static Timeline convert(ViewModel viewModel, LogicModelParameters params) + { + // 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); + Map logicWiresPerPinUnmodifiable = Collections.unmodifiableMap(logicWiresPerPin); + + Map oneToOneComponents = new HashMap<>(); + for (GUIComponent guiComp : viewModel.getComponents()) + { + // WireCrossPoints just vanish + if (!(guiComp instanceof WireCrossPoint)) + oneToOneComponents.put(guiComp, createAndLinkComponent(timeline, params, guiComp, logicWiresPerPinUnmodifiable, + componentAdapters.get(guiComp.getClass()))); + } + + // TODO handle complex components + + List logicComponents = new ArrayList<>(); + logicComponents.addAll(oneToOneComponents.values()); + + return timeline; + } + + private static Map convertWires(Set allPins, List wires, LogicModelParameters params, Timeline timeline) + { + Map> connectedPinGroups = getConnectedPinGroups(allPins, wires); + Map logicWiresPerPin = createLogicWires(params, timeline, connectedPinGroups); + setGUIWiresLogicModelBinding(wires, logicWiresPerPin); + return logicWiresPerPin; + } + + private static Map createLogicWires(LogicModelParameters params, Timeline timeline, Map> connectedPinGroups) + { + 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))); + return logicWiresPerPin; + } + + private static void setGUIWiresLogicModelBinding(List wires, Map logicWiresPerPin) + { + Map guiWireSharedReadEnd = logicWiresPerPin.values().stream().distinct() + .collect(Collectors.toMap(Function.identity(), Wire::createReadOnlyEnd)); + for (GUIWire guiWire : wires) + guiWire.setLogicModelBinding(guiWireSharedReadEnd.get(logicWiresPerPin.get(guiWire.getPin1()))); + } + + private static Map> getConnectedPinGroups(Set allPins, List wires) + { + Map> connectedPinsPerPin = new HashMap<>(); + + for (Pin p : allPins) + { + HashSet connectedPins = new HashSet<>(); + connectedPins.add(p); + connectedPinsPerPin.put(p, connectedPins); + } + + wires.forEach(wire -> + { + Pin pin1 = wire.getPin1(); + Pin pin2 = wire.getPin2(); + + Set pin1ConnectedPins = connectedPinsPerPin.get(pin1); + Set pin2ConnectedPins = connectedPinsPerPin.get(pin2); + + pin1ConnectedPins.addAll(pin2ConnectedPins); + pin1ConnectedPins.add(pin1); + pin1ConnectedPins.add(pin2); + + pin2ConnectedPins.forEach(pin -> connectedPinsPerPin.put(pin, pin1ConnectedPins)); + }); + return connectedPinsPerPin; + } + + @SuppressWarnings("unchecked") + private static Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, + GUIComponent guiComponent, Map logicWiresPerPin, ComponentAdapter adapter) + { + if (adapter == null) + throw new IllegalArgumentException("Unknown component class: " + guiComponent.getClass()); + return adapter.createAndLinkComponent(timeline, params, (G) guiComponent, logicWiresPerPin); + } + private ViewLogicModelAdapter() + { + throw new UnsupportedOperationException("No ViewLogicModelConverter instances"); + } } \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/modeladapter/componentadapters/ComponentAdapter.java b/LogicUI/src/era/mi/gui/modeladapter/componentadapters/ComponentAdapter.java new file mode 100644 index 00000000..8598fa83 --- /dev/null +++ b/LogicUI/src/era/mi/gui/modeladapter/componentadapters/ComponentAdapter.java @@ -0,0 +1,16 @@ +package era.mi.gui.modeladapter.componentadapters; + +import java.util.Map; + +import era.mi.gui.model.components.GUIComponent; +import era.mi.gui.model.wires.Pin; +import era.mi.gui.modeladapter.LogicModelParameters; +import era.mi.logic.components.Component; +import era.mi.logic.timeline.Timeline; +import era.mi.logic.wires.Wire; + +public interface ComponentAdapter +{ + public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, G guiComponent, + Map logicWiresPerPin); +} \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/modeladapter/componentadapters/SimpleGateAdapter.java b/LogicUI/src/era/mi/gui/modeladapter/componentadapters/SimpleGateAdapter.java new file mode 100644 index 00000000..e256ff03 --- /dev/null +++ b/LogicUI/src/era/mi/gui/modeladapter/componentadapters/SimpleGateAdapter.java @@ -0,0 +1,40 @@ +package era.mi.gui.modeladapter.componentadapters; + +import java.util.List; +import java.util.Map; + +import era.mi.gui.model.components.SimpleRectangularGUIGate; +import era.mi.gui.model.wires.Pin; +import era.mi.gui.modeladapter.LogicModelParameters; +import era.mi.logic.components.Component; +import era.mi.logic.timeline.Timeline; +import era.mi.logic.wires.Wire; +import era.mi.logic.wires.Wire.ReadEnd; +import era.mi.logic.wires.Wire.ReadWriteEnd; + +public class SimpleGateAdapter implements ComponentAdapter +{ + private final ComponentConstructor constructor; + + public SimpleGateAdapter(ComponentConstructor constructor) + { + this.constructor = constructor; + } + + @Override + public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, SimpleRectangularGUIGate guiComponent, + Map logicWiresPerPin) + { + ReadWriteEnd out = logicWiresPerPin.get(guiComponent.getOutputPin()).createReadWriteEnd(); + List inputPins = guiComponent.getInputPins(); + ReadEnd[] ins = new ReadEnd[inputPins.size()]; + for (int i = 0; i < inputPins.size(); i++) + ins[i] = logicWiresPerPin.get(inputPins.get(i)).createReadOnlyEnd(); + return constructor.newComponent(timeline, params.gateProcessTime, out, ins); + } + + public static interface ComponentConstructor + { + public Component newComponent(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd[] ins); + } +} \ No newline at end of file