From fb49022cdded4f77139b8d921dd0e08d59d9e074 Mon Sep 17 00:00:00 2001 From: Amy Wilder Date: Tue, 10 Feb 2026 19:37:49 -0500 Subject: [PATCH 1/3] Copy final commit from collision-perf branch --- src/jls/edit/SimpleEditor.java | 204 +++++++++++++++++++++------------ src/jls/elem/Element.java | 33 ++++++ 2 files changed, 162 insertions(+), 75 deletions(-) diff --git a/src/jls/edit/SimpleEditor.java b/src/jls/edit/SimpleEditor.java index d588375..fbaa1e5 100755 --- a/src/jls/edit/SimpleEditor.java +++ b/src/jls/edit/SimpleEditor.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.net.URL; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -2838,18 +2839,73 @@ private boolean overlap() { }*/ + // Set is O(n log(n)) to traverse over and doesn't benefit from cache locality. + // we're doing a lot of iterating over the same collection here, + // so it makes sense to use a temporary array for cache locality and O(n) traversal. + Element[] selectedArr = selected.toArray(Element[]::new); + ArrayList elementsArr = new ArrayList(circuit.getElements()); + // elementsArr.removeAll(selected); + { + int i = 0; + while (i < elementsArr.size()) { + if (selected.contains(elementsArr.get(i))) { + // "swap" remove - slightly cheaper because we don't care about order + elementsArr.set(i, elementsArr.get(elementsArr.size() - 1)); + elementsArr.remove(elementsArr.size() - 1); // pop + // don't increment on erase because [i] now refers to a new element + } + else { + i++; + } + } + } + + // bounding box just adds a redundant step if selection is 1 + if (selected.size() > 1) { + int xmin = Integer.MAX_VALUE; + int xmax = Integer.MIN_VALUE; + int ymin = Integer.MAX_VALUE; + int ymax = Integer.MIN_VALUE; + + // build up a bounding box + for (Element sel : selectedArr) { + xmin = Math.min(xmin, sel.getX()); + xmax = Math.max(xmax, sel.getX() + sel.getWidth()); + ymin = Math.min(ymin, sel.getY()); + ymax = Math.max(ymax, sel.getY() + sel.getHeight()); + } + + Rectangle bounds = new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin); + + // elementsArr.removeIf(el -> !el.isOverlapping(bounds)); + { + int i = 0; + while (i < elementsArr.size()) { + if (!elementsArr.get(i).isOverlapping(bounds)) { + // "swap" remove - slightly cheaper because we don't care about order + elementsArr.set(i, elementsArr.get(elementsArr.size() - 1)); + elementsArr.remove(elementsArr.size() - 1); // pop + // don't increment on erase because [i] now refers to a new element + } + else { + i++; + } + } + } + } + elementsArr.trimToSize(); + // check every element in the selected set - for (Element sel : selected) { + for (Element sel : selectedArr) { - // check against every element in the circuit - for (Element el : circuit.getElements()) { + boolean anyIntersection = false; - // ignore elements in the selected set - if (selected.contains(el)) - continue; + // check against every (unselected, bounds-overlapping) element in the circuit + for (Element el : elementsArr) { // check simple overlap of areas if (sel.intersects(el)) { + anyIntersection = true; // no overlap if possible connection, boolean ok = false; @@ -2927,42 +2983,42 @@ else if (el instanceof Wire) { // selected is not a wire end else { - // put to wire end - for (Put put : sel.getAllPuts()) { - - // if not a wire end, ignore - if (!(el instanceof WireEnd)) - continue; + // if element is a wire end, ... + if (el instanceof WireEnd) { WireEnd end = (WireEnd)el; - // if don't line up, ignore - if (put.getX() != end.getX() || put.getY() != end.getY()) { - continue; - } + // put to wire end + for (Put put : sel.getAllPuts()) { - // if already attached to this wire end, ignore - if (end == put.getWireEnd()) { - ok = true; - continue; - } + // if don't line up, ignore + if (put.getX() != end.getX() || put.getY() != end.getY()) { + continue; + } - // if attached through a single wire, ignore - WireEnd putEnd = put.getWireEnd(); - if (putEnd != null && - putEnd.getOnlyWire().getOtherEnd(putEnd) == end) { - ok = true; - continue; - } + // if already attached to this wire end, ignore + if (end == put.getWireEnd()) { + ok = true; + continue; + } - // if cannot connect, return - if (!canConnect(end,put)) { - untouchAll(); - return true; - } + // if attached through a single wire, ignore + WireEnd putEnd = put.getWireEnd(); + if (putEnd != null && + putEnd.getOnlyWire().getOtherEnd(putEnd) == end) { + ok = true; + continue; + } - end.setTouching(true); - put.setTouching(true); - ok = true; + // if cannot connect, return + if (!canConnect(end,put)) { + untouchAll(); + return true; + } + + end.setTouching(true); + put.setTouching(true); + ok = true; + } } } if (!ok) { @@ -2971,52 +3027,50 @@ else if (el instanceof Wire) { return true; } } + } - // no intersection, but wires may be overlapping wire ends - // or puts might line up - else { + // no intersection, but wires may be overlapping wire ends + // or puts might line up + if (!anyIntersection) { - // see if wires connected to a wire end dragged onto wire ends - if (sel instanceof WireEnd) { - WireEnd end = (WireEnd)sel; - for (Wire wire : end.getWires()) { - for (Element elm : circuit.getElements()) { - if (sel == elm) - continue; - if (!(elm instanceof WireEnd)) { - continue; - } - WireEnd otherEnd = (WireEnd)elm; - if (wire.touches(otherEnd)) { - overlapMessage = "overlap"; - untouchAll(); - return true; - } + // see if wires connected to a wire end dragged onto wire ends + if (sel instanceof WireEnd) { + WireEnd end = (WireEnd)sel; + for (Wire wire : end.getWires()) { + for (Element el : elementsArr) { + if (!(el instanceof WireEnd)) { + continue; + } + WireEnd otherEnd = (WireEnd)el; + if (wire.touches(otherEnd)) { + overlapMessage = "overlap"; + untouchAll(); + return true; } } } + } - // see if wires connected to puts dragged onto wire ends - for (Put p : sel.getAllPuts()) { - if (p.isAttached()) { - Wire wire = p.getWireEnd().getOnlyWire(); - for (Element elm : circuit.getElements()) { - if (sel == elm) - continue; - if (!(elm instanceof WireEnd)) { - continue; - } - WireEnd otherEnd = (WireEnd)elm; - if (wire.touches(otherEnd)) { - overlapMessage = "overlap"; - untouchAll(); - return true; - } + // see if wires connected to puts dragged onto wire ends + for (Put p : sel.getAllPuts()) { + if (p.isAttached()) { + Wire wire = p.getWireEnd().getOnlyWire(); + for (Element el : elementsArr) { + if (!(el instanceof WireEnd)) { + continue; + } + WireEnd otherEnd = (WireEnd)el; + if (wire.touches(otherEnd)) { + overlapMessage = "overlap"; + untouchAll(); + return true; } } } + } - // check all put combinations + // check all put combinations + for (Element el : elementsArr) { for (Put p1 : sel.getAllPuts()) { for (Put p2 : el.getAllPuts()) { @@ -3028,7 +3082,7 @@ else if (el instanceof Wire) { // ignore overlaps on already connected puts WireEnd end1 = p1.getWireEnd(); WireEnd end2 = p2.getWireEnd(); - if (end1 != null && end2 != null && + if (end1 != null && end2 != null && end1.getOnlyWire().getOtherEnd(end1) == end2) { continue; } @@ -3047,8 +3101,8 @@ else if (el instanceof Wire) { } } } - repaint(); - return false; + repaint(); + return false; } // end of overlap method /** diff --git a/src/jls/elem/Element.java b/src/jls/elem/Element.java index 8de848f..b19a3be 100755 --- a/src/jls/elem/Element.java +++ b/src/jls/elem/Element.java @@ -114,6 +114,26 @@ public int getY() { return y; } // end of getY method + + /** + * Get width of this element. + * + * @return the width. + */ + public int getWidth() { + + return width; + } // end of getWidth method + + /** + * Get height of this element. + * + * @return the height. + */ + public int getHeight() { + + return height; + } // end of getHeight method /** * Get the trace position of this element. @@ -328,6 +348,19 @@ public boolean isInside(Rectangle rect) { return rect.contains(me); } // end of isInside method + /** + * See if this element is intersecting a given rectangle. + * + * @param rect The given rectangle. + * + * @return true if the element is intersecting, false if not. + */ + public boolean isOverlapping(Rectangle rect) { + + Rectangle me = getRect(); + return rect.intersects(me); + } // end of isOverlapping method + /** * Set/reset highlight. * From 00601ab3717416c7442d4e222b086743ea1a49c9 Mon Sep 17 00:00:00 2001 From: Amy Wilder Date: Tue, 10 Feb 2026 19:51:58 -0500 Subject: [PATCH 2/3] Make Eclipse stop being mad --- src/jls/edit/SimpleEditor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jls/edit/SimpleEditor.java b/src/jls/edit/SimpleEditor.java index fbaa1e5..3347ce2 100755 --- a/src/jls/edit/SimpleEditor.java +++ b/src/jls/edit/SimpleEditor.java @@ -2842,8 +2842,8 @@ private boolean overlap() { // Set is O(n log(n)) to traverse over and doesn't benefit from cache locality. // we're doing a lot of iterating over the same collection here, // so it makes sense to use a temporary array for cache locality and O(n) traversal. - Element[] selectedArr = selected.toArray(Element[]::new); - ArrayList elementsArr = new ArrayList(circuit.getElements()); + Element[] selectedArr = selected.toArray(new Element[selected.size()]); + ArrayList elementsArr = new ArrayList(circuit.getElements()); // elementsArr.removeAll(selected); { int i = 0; From 806068bc4dd57440bfa4f6310d89c5ad4c3bae93 Mon Sep 17 00:00:00 2001 From: Amy Wilder <74995093+HenryWilder@users.noreply.github.com> Date: Wed, 11 Feb 2026 13:13:05 -0500 Subject: [PATCH 3/3] Fix lazy untouch bug Touching and then moving would not update the "touching" status of elements that stopped being touched, until overlap() fails. --- src/jls/edit/SimpleEditor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jls/edit/SimpleEditor.java b/src/jls/edit/SimpleEditor.java index 3347ce2..447ee8b 100755 --- a/src/jls/edit/SimpleEditor.java +++ b/src/jls/edit/SimpleEditor.java @@ -2839,6 +2839,8 @@ private boolean overlap() { }*/ + untouchAll(); // may have moved and stopped touching + // Set is O(n log(n)) to traverse over and doesn't benefit from cache locality. // we're doing a lot of iterating over the same collection here, // so it makes sense to use a temporary array for cache locality and O(n) traversal.