From e66a4e0369a36fe2c86ad8c2f94a3da4c871e4f7 Mon Sep 17 00:00:00 2001 From: hneemann Date: Thu, 17 Mar 2016 11:54:44 +0100 Subject: [PATCH] lines consistency checker is running --- .../gui/components/CircuitComponent.java | 1 - .../digital/gui/draw/graphics/Vector.java | 19 ++ .../digital/gui/draw/parts/Circuit.java | 32 +++- .../neemann/digital/gui/draw/parts/Wire.java | 46 +++++ .../draw/parts/WireConsistencyChecker.java | 85 +++++++++ .../digital/gui/draw/parts/WireMerger.java | 173 ++++++++++++++++++ .../gui/draw/parts/WireMergerTest.java | 75 ++++++++ 7 files changed, 426 insertions(+), 5 deletions(-) create mode 100644 src/main/java/de/neemann/digital/gui/draw/parts/WireConsistencyChecker.java create mode 100644 src/main/java/de/neemann/digital/gui/draw/parts/WireMerger.java create mode 100644 src/test/java/de/neemann/digital/gui/draw/parts/WireMergerTest.java 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 ec1f501c9..5c20297b6 100644 --- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java +++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java @@ -23,7 +23,6 @@ public class CircuitComponent extends JComponent { private static final String delAction = "myDelAction"; private final Circuit circuit; - ; private Mouse listener; public CircuitComponent(Circuit circuit) { diff --git a/src/main/java/de/neemann/digital/gui/draw/graphics/Vector.java b/src/main/java/de/neemann/digital/gui/draw/graphics/Vector.java index 73628ef59..5572351be 100644 --- a/src/main/java/de/neemann/digital/gui/draw/graphics/Vector.java +++ b/src/main/java/de/neemann/digital/gui/draw/graphics/Vector.java @@ -78,4 +78,23 @@ public class Vector implements Moveable { public boolean inside(Vector min, Vector max) { return min.x <= x && x <= max.x && min.y <= y && y <= max.y; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Vector vector = (Vector) o; + + if (x != vector.x) return false; + return y == vector.y; + + } + + @Override + public int hashCode() { + int result = x; + result = 31 * result + y; + return result; + } } diff --git a/src/main/java/de/neemann/digital/gui/draw/parts/Circuit.java b/src/main/java/de/neemann/digital/gui/draw/parts/Circuit.java index 09e9c5039..13fadc230 100644 --- a/src/main/java/de/neemann/digital/gui/draw/parts/Circuit.java +++ b/src/main/java/de/neemann/digital/gui/draw/parts/Circuit.java @@ -1,6 +1,7 @@ package de.neemann.digital.gui.draw.parts; import de.neemann.digital.gui.draw.graphics.Graphic; +import de.neemann.digital.gui.draw.graphics.Style; import de.neemann.digital.gui.draw.graphics.Vector; import de.neemann.digital.gui.draw.shapes.Drawable; @@ -11,19 +12,23 @@ import java.util.Iterator; * @author hneemann */ public class Circuit implements Drawable { - + private static final Vector RAD = new Vector(2, 2); private final ArrayList visualParts; - private final ArrayList wires; + private transient ArrayList dots; + private ArrayList wires; public Circuit() { visualParts = new ArrayList<>(); wires = new ArrayList<>(); + dots = new ArrayList<>(); } @Override public void drawTo(Graphic graphic) { for (Wire w : wires) w.drawTo(graphic); + for (Vector d : dots) + graphic.drawCircle(d.sub(RAD), d.add(RAD), Style.WIRE); for (VisualPart p : visualParts) p.drawTo(graphic); } @@ -32,8 +37,27 @@ public class Circuit implements Drawable { visualParts.add(visualPart); } - public void add(Wire wire) { - wires.add(wire); + public void add(Wire newWire) { + if (newWire.p1.equals(newWire.p2)) + return; + + int len = wires.size(); + for (int i = 0; i < len; i++) { + Wire present = wires.get(i); + if (present.contains(newWire.p1)) { + wires.set(i, new Wire(present.p1, newWire.p1)); + wires.add(new Wire(present.p2, newWire.p1)); + } else if (present.contains(newWire.p2)) { + wires.set(i, new Wire(present.p1, newWire.p2)); + wires.add(new Wire(present.p2, newWire.p2)); + } + ; + } + + wires.add(newWire); + WireConsistencyChecker checker = new WireConsistencyChecker(wires); + wires = checker.check(); + dots = WireConsistencyChecker.createDots(wires); } public ArrayList getParts() { diff --git a/src/main/java/de/neemann/digital/gui/draw/parts/Wire.java b/src/main/java/de/neemann/digital/gui/draw/parts/Wire.java index 280dc6227..f681ab7b3 100644 --- a/src/main/java/de/neemann/digital/gui/draw/parts/Wire.java +++ b/src/main/java/de/neemann/digital/gui/draw/parts/Wire.java @@ -32,4 +32,50 @@ public class Wire implements Drawable, Moveable { public void setP2(Vector p2) { this.p2 = p2; } + + public boolean contains(Vector v) { + if (p1.x == p2.x && p1.x == v.x) + return (p1.y < v.y && v.y < p2.y) || (p2.y < v.y && v.y < p1.y); + else if (p1.y == p2.y && p1.y == v.y) + return (p1.x < v.x && v.x < p2.x) || (p2.x < v.x && v.x < p1.x); + else + return false; + } + + public Orientation getOrientation() { + if (p1.x == p2.x) + return Orientation.vertical; + if (p1.y == p2.y) + return Orientation.horzontal; + return Orientation.diagonal; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Wire wire = (Wire) o; + + if (!p1.equals(wire.p1)) return false; + return p2.equals(wire.p2); + + } + + @Override + public int hashCode() { + int result = p1.hashCode(); + result = 31 * result + p2.hashCode(); + return result; + } + + @Override + public String toString() { + return "Wire{" + + "p1=" + p1 + + ", p2=" + p2 + + '}'; + } + + enum Orientation {horzontal, vertical, diagonal} } diff --git a/src/main/java/de/neemann/digital/gui/draw/parts/WireConsistencyChecker.java b/src/main/java/de/neemann/digital/gui/draw/parts/WireConsistencyChecker.java new file mode 100644 index 000000000..a62ba792d --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/draw/parts/WireConsistencyChecker.java @@ -0,0 +1,85 @@ +package de.neemann.digital.gui.draw.parts; + +import de.neemann.digital.gui.draw.graphics.Vector; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * @author hneemann + */ +public class WireConsistencyChecker { + private ArrayList wires; + + public WireConsistencyChecker(ArrayList wires) { + this.wires = wires; + } + + public static ArrayList createDots(ArrayList wires) { + HashMap map = new HashMap<>(); + for (Wire w : wires) { + inc(map, w.p1); + inc(map, w.p2); + } + + ArrayList dots = new ArrayList<>(); + for (Map.Entry e : map.entrySet()) + if (e.getValue().counter > 2) + dots.add(e.getKey()); + return dots; + } + + private static void inc(HashMap map, Vector vector) { + Counter c = map.get(vector); + if (c == null) { + c = new Counter(); + map.put(vector, c); + } + c.inc(); + } + + public ArrayList check() { + wires = merge(wires); + return wires; + } + + private ArrayList merge(ArrayList wires) { + + ArrayList dots = createDots(wires); + + ArrayList newWires = new ArrayList<>(); + WireMerger hori = new WireMerger(Wire.Orientation.horzontal); + WireMerger vert = new WireMerger(Wire.Orientation.vertical); + + for (Wire w : wires) { + if (!w.p1.equals(w.p2)) + switch (w.getOrientation()) { + case horzontal: + hori.add(w); + break; + case vertical: + vert.add(w); + break; + default: + newWires.add(w); + } + } + + hori.protectDots(dots); + vert.protectDots(dots); + + hori.addTo(newWires); + vert.addTo(newWires); + + return newWires; + } + + private static class Counter { + private int counter; + + public void inc() { + counter++; + } + } +} diff --git a/src/main/java/de/neemann/digital/gui/draw/parts/WireMerger.java b/src/main/java/de/neemann/digital/gui/draw/parts/WireMerger.java new file mode 100644 index 000000000..8037c04da --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/draw/parts/WireMerger.java @@ -0,0 +1,173 @@ +package de.neemann.digital.gui.draw.parts; + +import de.neemann.digital.gui.draw.graphics.Vector; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * @author hneemann + */ +public class WireMerger { + + private HashMap wireContainers; + private OrientationHandler handler; + + public WireMerger(Wire.Orientation orientation) { + wireContainers = new HashMap<>(); + switch (orientation) { + case horzontal: + handler = new OrientationHandlerHorizontal(); + break; + case vertical: + handler = new OrientationHandlerVertical(); + break; + default: + throw new RuntimeException("wrong line orientation"); + } + } + + public void add(Wire w) { + SimpleWire sw = new SimpleWire(handler.getWireClass(w.p1), handler.getS(w.p1), handler.getS(w.p2)); + WireContainer wc = wireContainers.get(sw.wireClass); + if (wc == null) { + wc = new WireContainer(sw.wireClass); + wireContainers.put(sw.wireClass, wc); + } + wc.add(sw); + } + + public void addTo(ArrayList wires) { + for (WireContainer wc : wireContainers.values()) { + wc.addTo(wires); + } + } + + public void protectDots(ArrayList dots) { + for (Vector v : dots) { + WireContainer wc = wireContainers.get(handler.getWireClass(v)); + if (wc != null) // is possible because diagonals are not included + wc.protect(handler.getS(v)); + } + } + + interface OrientationHandler { + Wire toWire(SimpleWire wire); + + int getS(Vector v); + + int getWireClass(Vector v); + } + + public static class SimpleWire { + protected int wireClass; + protected int s1; + protected int s2; + + public SimpleWire(int wireClass, int s1, int s2) { + this.wireClass = wireClass; + if (s2 < s1) { + this.s1 = s2; + this.s2 = s1; + } else { + this.s1 = s1; + this.s2 = s2; + } + } + + public boolean tryMerge(SimpleWire other) { + if (s2 < other.s1 || other.s2 < s1) + return false; + else { + s1 = Math.min(s1, other.s1); + s2 = Math.max(s2, other.s2); + return true; + } + } + + public boolean containsAsInner(int s) { + return s1 < s && s2 > s; + } + } + + public static class OrientationHandlerHorizontal implements OrientationHandler { + + @Override + public Wire toWire(SimpleWire wire) { + return new Wire(new Vector(wire.s1, wire.wireClass), new Vector(wire.s2, wire.wireClass)); + } + + @Override + public int getWireClass(Vector v) { + return v.y; + } + + @Override + public int getS(Vector v) { + return v.x; + } + } + + public static class OrientationHandlerVertical implements OrientationHandler { + + @Override + public Wire toWire(SimpleWire wire) { + return new Wire(new Vector(wire.wireClass, wire.s1), new Vector(wire.wireClass, wire.s2)); + } + + @Override + public int getS(Vector v) { + return v.y; + } + + @Override + public int getWireClass(Vector v) { + return v.x; + } + + } + + private class WireContainer { + public int wireClass; + public ArrayList wires; + + public WireContainer(int wireClass) { + this.wireClass = wireClass; + wires = new ArrayList<>(); + } + + public void add(SimpleWire newSimpleWire) { + wires.add(newSimpleWire); + simplify(newSimpleWire); + } + + private void simplify(SimpleWire changedWire) { + for (SimpleWire wire : wires) { + if (!wire.equals(changedWire)) { + if (wire.tryMerge(changedWire)) { + wires.remove(changedWire); + simplify(wire); + return; + } + } + } + } + + public void addTo(ArrayList list) { + for (SimpleWire sw : wires) + list.add(handler.toWire(sw)); + } + + public void protect(int s) { + int len = wires.size(); + for (int i = 0; i < len; i++) { + SimpleWire sw = wires.get(i); + if (sw.containsAsInner(s)) { + int s2 = sw.s2; + sw.s2 = s; + wires.add(new SimpleWire(wireClass, s, s2)); + } + } + } + } +} diff --git a/src/test/java/de/neemann/digital/gui/draw/parts/WireMergerTest.java b/src/test/java/de/neemann/digital/gui/draw/parts/WireMergerTest.java new file mode 100644 index 000000000..7de0397f9 --- /dev/null +++ b/src/test/java/de/neemann/digital/gui/draw/parts/WireMergerTest.java @@ -0,0 +1,75 @@ +package de.neemann.digital.gui.draw.parts; + +import de.neemann.digital.gui.draw.graphics.Vector; +import junit.framework.TestCase; + +import java.util.ArrayList; + +/** + * @author hneemann + */ +public class WireMergerTest extends TestCase { + + public void testHorizontal() { + WireMerger.OrientationHandler handler = new WireMerger.OrientationHandlerHorizontal(); + + assertEquals(1, handler.getS(new Vector(1, 3))); + assertEquals(3, handler.getWireClass(new Vector(1, 3))); + + Wire wire = handler.toWire(new WireMerger.SimpleWire(1, 3, 7)); + assertEquals(new Vector(3, 1), wire.p1); + assertEquals(new Vector(7, 1), wire.p2); + } + + public void testVertical() { + WireMerger.OrientationHandler handler = new WireMerger.OrientationHandlerVertical(); + + assertEquals(1, handler.getS(new Vector(3, 1))); + assertEquals(3, handler.getWireClass(new Vector(3, 1))); + + + Wire wire = handler.toWire(new WireMerger.SimpleWire(1, 3, 7)); + assertEquals(new Vector(1, 3), wire.p1); + assertEquals(new Vector(1, 7), wire.p2); + } + + public void testMerge1() { + WireMerger wm = new WireMerger(Wire.Orientation.horzontal); + wm.add(new Wire(new Vector(1, 3), new Vector(5, 3))); + wm.add(new Wire(new Vector(5, 3), new Vector(8, 3))); + wm.add(new Wire(new Vector(1, 4), new Vector(5, 4))); + wm.add(new Wire(new Vector(5, 5), new Vector(8, 5))); + + wm.add(new Wire(new Vector(1, 6), new Vector(6, 6))); + wm.add(new Wire(new Vector(4, 6), new Vector(8, 6))); + + wm.add(new Wire(new Vector(1, 7), new Vector(4, 7))); + wm.add(new Wire(new Vector(5, 7), new Vector(8, 7))); + + + ArrayList newWires = new ArrayList<>(); + wm.addTo(newWires); + + assertEquals(6, newWires.size()); + assertTrue(newWires.contains(new Wire(new Vector(1, 3), new Vector(8, 3)))); + assertTrue(newWires.contains(new Wire(new Vector(1, 4), new Vector(5, 4)))); + assertTrue(newWires.contains(new Wire(new Vector(5, 5), new Vector(8, 5)))); + + assertTrue(newWires.contains(new Wire(new Vector(1, 6), new Vector(8, 6)))); + + assertTrue(newWires.contains(new Wire(new Vector(1, 7), new Vector(4, 7)))); + assertTrue(newWires.contains(new Wire(new Vector(5, 7), new Vector(8, 7)))); + } + + public void testMerge2() { + WireMerger wm = new WireMerger(Wire.Orientation.horzontal); + wm.add(new Wire(new Vector(1, 3), new Vector(3, 3))); + wm.add(new Wire(new Vector(6, 3), new Vector(8, 3))); + wm.add(new Wire(new Vector(2, 3), new Vector(7, 3))); + + ArrayList newWires = new ArrayList<>(); + wm.addTo(newWires); + assertEquals(1, newWires.size()); + assertEquals(new Wire(new Vector(1, 3), new Vector(8, 3)), newWires.get(0)); + } +} \ No newline at end of file