From f3f6c7d0012df210ca819a72e82cfbd24ef41c3b Mon Sep 17 00:00:00 2001 From: hneemann Date: Thu, 9 May 2019 20:30:39 +0200 Subject: [PATCH] Adds a simple auto wire function; closes #274 --- .../neemann/digital/draw/elements/Pins.java | 14 ++++ .../digital/draw/elements/VisualElement.java | 36 +++++++-- .../de/neemann/digital/draw/shapes/Shape.java | 2 +- .../digital/draw/shapes/ShapeFactory.java | 2 +- .../gui/components/CircuitComponent.java | 43 ++++++++++- .../digital/integration/TestInGUI.java | 12 +++ src/test/resources/dig/autoWire.dig | 75 +++++++++++++++++++ 7 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 src/test/resources/dig/autoWire.dig diff --git a/src/main/java/de/neemann/digital/draw/elements/Pins.java b/src/main/java/de/neemann/digital/draw/elements/Pins.java index e20e8bb02..facaa662b 100644 --- a/src/main/java/de/neemann/digital/draw/elements/Pins.java +++ b/src/main/java/de/neemann/digital/draw/elements/Pins.java @@ -13,6 +13,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import static de.neemann.digital.draw.shapes.GenericShape.SIZE; + /** * A list of pins */ @@ -90,4 +92,16 @@ public class Pins implements Iterable { public Pin get(int index) { return allPins.get(index); } + + /** + * @return false if there are two pins next to each other in horizontal direction + */ + boolean autoWireCompatible() { + for (Pin p1 : allPins) + for (Pin p2 : allPins) { + if (p1.getPos().add(SIZE, 0).equals(p2.getPos())) + return false; + } + return true; + } } diff --git a/src/main/java/de/neemann/digital/draw/elements/VisualElement.java b/src/main/java/de/neemann/digital/draw/elements/VisualElement.java index c28cae651..1fe46005c 100644 --- a/src/main/java/de/neemann/digital/draw/elements/VisualElement.java +++ b/src/main/java/de/neemann/digital/draw/elements/VisualElement.java @@ -11,6 +11,7 @@ import de.neemann.digital.core.element.*; import de.neemann.digital.draw.graphics.*; import de.neemann.digital.draw.shapes.*; import de.neemann.digital.draw.shapes.Shape; +import de.neemann.digital.draw.shapes.custom.CustomShape; import de.neemann.digital.gui.components.CircuitComponent; import de.neemann.gui.Screen; @@ -34,6 +35,7 @@ public class VisualElement implements Drawable, Movable, AttributeListener { private transient Element element; // shape is set to null and recreated if needed if attributes are changed private transient Shape shape; + private transient Boolean autoWire; // shapes are recreated if attributes are changed, therefore a factory is necessary and not only a simple shape! private transient ShapeFactory shapeFactory; private transient Transform transform; @@ -102,7 +104,12 @@ public class VisualElement implements Drawable, Movable, AttributeListener { @Override public void attributeChanged() { + resetShape(); + } + + private void resetShape() { shape = null; + autoWire = null; resetGeometry(); } @@ -182,6 +189,19 @@ public class VisualElement implements Drawable, Movable, AttributeListener { return shape; } + /** + * @return true if this shape supports auto wire + */ + public boolean isAutoWireCompatible() { + if (autoWire == null) { + Shape shape = getShape(); + autoWire = !(shape instanceof DILShape || shape instanceof CustomShape || shape instanceof LayoutShape); + if (autoWire) + autoWire = shape.getPins().autoWireCompatible(); + } + return autoWire; + } + @Override public void drawTo(Graphic graphic, Style highLight) { drawShape(graphic, highLight); @@ -196,7 +216,7 @@ public class VisualElement implements Drawable, Movable, AttributeListener { } private void drawShape(Graphic graphic, Style highLight) { - Graphic gr = new GraphicTransform(graphic, createTransform()); + Graphic gr = new GraphicTransform(graphic, getTransform()); Shape shape = getShape(); shape.drawTo(gr, highLight); for (Pin p : shape.getPins()) @@ -204,7 +224,7 @@ public class VisualElement implements Drawable, Movable, AttributeListener { p.getDirection() == Pin.Direction.input ? Style.WIRE : Style.WIRE_OUT); } - private Transform createTransform() { + private Transform getTransform() { if (transform == null) { int rotate = getRotate(); if (rotate == 0) @@ -293,12 +313,12 @@ public class VisualElement implements Drawable, Movable, AttributeListener { */ public Pins getPins() { Shape shape = getShape(); - Transform tr = createTransform(); + Transform tr = getTransform(); Pins pins = shape.getPins(); - Pins transformed = new Pins(); + Pins transformedPins = new Pins(); for (Pin p : pins) - transformed.add(new Pin(tr.transform(p.getPos()), p).setVisualElement(this)); - return transformed; + transformedPins.add(new Pin(tr.transform(p.getPos()), p).setVisualElement(this)); + return transformedPins; } /** @@ -311,7 +331,7 @@ public class VisualElement implements Drawable, Movable, AttributeListener { this.ioState = ioState; if (ioState == null) { interactor = null; - shape = null; + resetShape(); } else interactor = getShape().applyStateMonitor(ioState, guiObserver); } @@ -379,7 +399,7 @@ public class VisualElement implements Drawable, Movable, AttributeListener { */ public boolean elementDragged(CircuitComponent cc, Point pos, Vector posInComponent, SyncAccess modelSync) { if (interactor != null) - return interactor.dragged(cc, posInComponent, transform, ioState, element, modelSync); + return interactor.dragged(cc, posInComponent, getTransform(), ioState, element, modelSync); else return false; } diff --git a/src/main/java/de/neemann/digital/draw/shapes/Shape.java b/src/main/java/de/neemann/digital/draw/shapes/Shape.java index d71832b7c..67835f0f9 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/Shape.java +++ b/src/main/java/de/neemann/digital/draw/shapes/Shape.java @@ -21,7 +21,7 @@ public interface Shape extends Drawable, ObservableValueReader { * Puts the pins name and the pins x-y-position together! * This information is used to calculate the models connections from the wiring in the circuit. * Don't create your own {@link de.neemann.digital.core.element.PinInfo} instance! Try to use - * the instances passed to the constructor of the shape via the {@link ShapeFactory}s {@link ShapeFactory#Creator} interface. + * the instances passed to the constructor of the shape via the {@link ShapeFactory}s {@link ShapeFactory.Creator} interface. * * @return the pins */ diff --git a/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java b/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java index c6a7eeb8e..d763b102c 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java +++ b/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java @@ -250,7 +250,7 @@ public final class ShapeFactory { } /** - * creates a new shape + * Creates a new shape */ public interface Creator { /** diff --git a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java index 11f452c7a..35bb9a84f 100644 --- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java +++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java @@ -15,8 +15,8 @@ import de.neemann.digital.core.io.InValue; import de.neemann.digital.core.io.Out; import de.neemann.digital.core.switching.Switch; import de.neemann.digital.draw.elements.*; -import de.neemann.digital.draw.graphics.*; import de.neemann.digital.draw.graphics.Vector; +import de.neemann.digital.draw.graphics.*; import de.neemann.digital.draw.library.ElementLibrary; import de.neemann.digital.draw.library.ElementNotFoundException; import de.neemann.digital.draw.library.LibraryListener; @@ -42,8 +42,8 @@ import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.util.*; import java.util.List; +import java.util.*; import static de.neemann.digital.draw.shapes.GenericShape.SIZE; import static de.neemann.digital.draw.shapes.GenericShape.SIZE2; @@ -1457,8 +1457,10 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe @Override void clicked(MouseEvent e) { - if (mouse.isPrimaryClick(e) && !isLocked()) + if (mouse.isPrimaryClick(e) && !isLocked()) { modify(new ModifyInsertElement(element)); + insertWires(element); + } mouseNormal.activate(); focusWasLost = false; } @@ -1476,6 +1478,37 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe } + private void insertWires(VisualElement element) { + if (element.isAutoWireCompatible()) { + Modifications.Builder wires = new Modifications.Builder(Lang.get("lib_wires")); + for (Pin p : element.getPins()) + insertWirePin(p, element.getRotate(), wires); + modify(wires.build()); + } + } + + private void insertWirePin(Pin p, int rotate, Modifications.Builder wires) { + TransformRotate tr = new TransformRotate(new Vector(0, 0), rotate); + Vector pos = new Vector(-SIZE, 0); + if (p.getDirection() != PinDescription.Direction.input) + pos = new Vector(SIZE, 0); + + pos = tr.transform(pos); + pos = pos.add(p.getPos()); + boolean found = false; + List el = circuit.getElementListAt(pos, false); + for (VisualElement ve : el) { + final Pin pinAt = circuit.getPinAt(pos, ve); + if (pinAt != null && pinAt.getPos().equals(pos)) { + found = true; + break; + } + } + if (found) + wires.add(new ModifyInsertWire(new Wire(pos, p.getPos()))); + } + + private final class MouseControllerMoveElement extends MouseController { private VisualElement visualElement; private Vector delta; @@ -1505,8 +1538,10 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe if (!isLocked()) { visualElement.setPos(raster(visualElement.getPos())); if (!visualElement.getPos().equals(initialPos) - || visualElement.getRotate() != initialRot) + || visualElement.getRotate() != initialRot) { addModificationAlreadyMade(new ModifyMoveAndRotElement(visualElement, initialPos)); + insertWires(visualElement); + } normalEnd = true; } mouseNormal.activate(); diff --git a/src/test/java/de/neemann/digital/integration/TestInGUI.java b/src/test/java/de/neemann/digital/integration/TestInGUI.java index a70dbed23..6670eee4a 100644 --- a/src/test/java/de/neemann/digital/integration/TestInGUI.java +++ b/src/test/java/de/neemann/digital/integration/TestInGUI.java @@ -338,6 +338,18 @@ public class TestInGUI extends TestCase { .execute(); } + public void testAutoWire() { + new GuiTester() + .add(new DrawCircuit("dig/autoWire.dig")) + .press("F8") + .delay(500) + .add(new GuiTester.CheckTextInWindow<>(ValueTableDialog.class, Lang.get("msg_test_N_Passed", ""))) + .add(new GuiTester.CheckTableRows<>(ValueTableDialog.class, 4)) + .add(new GuiTester.CloseTopMost()) + .add(new GuiTester.CloseTopMost()) + .execute(); + } + public void testDraw() { new GuiTester() .add(new DrawCircuit("../../main/dig/sequential/JK-MS.dig")) diff --git a/src/test/resources/dig/autoWire.dig b/src/test/resources/dig/autoWire.dig new file mode 100644 index 000000000..d7b14965e --- /dev/null +++ b/src/test/resources/dig/autoWire.dig @@ -0,0 +1,75 @@ + + + 1 + + + + In + + + Label + A + + + + + + In + + + Label + B + + + + + + Not + + + + + Not + + + + + And + + + + + Not + + + + + Out + + + Label + Y + + + + + + Testcase + + + Testdata + + A B Y +0 0 0 +0 1 1 +1 0 1 +1 1 1 + + + + + + + + + \ No newline at end of file