diff --git a/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java b/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java index 6726c576..b5ef3e3f 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java +++ b/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java @@ -275,5 +275,22 @@ public class ComponentProcessor { } componentTransformerMap.put(clazz.getName(), transformer); return transformer; + } + + public static boolean hasStickyPoint(IDIYComponent c) { + for (int i = 0; i < c.getControlPointCount(); i++) + if (c.isControlPointSticky(0)) + return true; + return false; + } + + private static int touchSensitivity = 5; + + public static boolean componentPointsTouch(IDIYComponent c1, IDIYComponent c2) { + for (int i = 0; i < c1.getControlPointCount(); i++) + for (int j = 0; j < c2.getControlPointCount(); j++) + if (c1.isControlPointSticky(i) && c2.isControlPointSticky(j) && c1.getControlPoint(i).distance(c2.getControlPoint(j)) < touchSensitivity) + return true; + return false; } } diff --git a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java index c68b4c52..37161cb1 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java +++ b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java @@ -1939,63 +1939,141 @@ public class Presenter implements IPlugInPort { } messageDispatcher.dispatchMessage(EventType.SELECTION_CHANGED, selectedComponents, controlPointMap.keySet()); } + + private static boolean useNetlistForExpandSelection = false; @SuppressWarnings("unchecked") @Override public void expandSelection(ExpansionMode expansionMode) { - LOG.info(String.format("expandSelection(%s)", expansionMode)); + LOG.info(String.format("expandSelection(%s)", expansionMode)); List> newSelection = new ArrayList>(this.selectedComponents); - List netlists = extractNetlists(false); - - if (netlists == null) - return; - - List>> allGroups = NetlistAnalyzer.extractComponentGroups(netlists); - // Find control points of all selected components and all types - Set selectedNamePrefixes = new HashSet(); - if (expansionMode == ExpansionMode.SAME_TYPE) { - for (IDIYComponent component : getSelectedComponents()) { - selectedNamePrefixes.add(ComponentProcessor.getInstance() - .extractComponentTypeFrom((Class>) component.getClass()).getNamePrefix()); + // experimental mode using netlist to expand selection. Works fine but doesn't include any connectivity components + // like traces and wires, so it's not ideal + if (useNetlistForExpandSelection) { + List netlists = extractNetlists(false); + + if (netlists == null) + return; + + List>> allGroups = NetlistAnalyzer.extractComponentGroups(netlists); + // Find control points of all selected components and all types + Set selectedNamePrefixes = new HashSet(); + if (expansionMode == ExpansionMode.SAME_TYPE) { + for (IDIYComponent component : getSelectedComponents()) { + selectedNamePrefixes.add(ComponentProcessor.getInstance() + .extractComponentTypeFrom((Class>) component.getClass()).getNamePrefix()); + } } - } - // Now try to find components that intersect with at least one component - // in the pool. - for (IDIYComponent component : getCurrentProject().getComponents()) { - // no need to consider it, it's already in the selection - if (newSelection.contains(component)) - continue; - // construct a list of component groups that contain the current component - List>> componentGroups = new ArrayList>>(); - for (Set> e : allGroups) - if (e.contains(component)) - componentGroups.add(e); - if (componentGroups.isEmpty()) - continue; - // Skip already selected components or ones that cannot be stuck to - // other components. - boolean matches = false; - outer: for (IDIYComponent selectedComponent : this.selectedComponents) { - // try to find the selectedComponent in one of the groups - for (Set> s : componentGroups) - if (s.contains(selectedComponent)) { - matches = true; - break outer; - } - } - - if (matches) { - switch (expansionMode) { - case ALL: - case IMMEDIATE: - newSelection.add(component); - break; - case SAME_TYPE: - if (selectedNamePrefixes.contains(ComponentProcessor.getInstance() - .extractComponentTypeFrom((Class>) component.getClass()).getNamePrefix())) { - newSelection.add(component); + // Now try to find components that intersect with at least one component + // in the pool. + for (IDIYComponent component : getCurrentProject().getComponents()) { + // no need to consider it, it's already in the selection + if (newSelection.contains(component)) + continue; + // construct a list of component groups that contain the current component + List>> componentGroups = new ArrayList>>(); + for (Set> e : allGroups) + if (e.contains(component)) + componentGroups.add(e); + if (componentGroups.isEmpty()) + continue; + // Skip already selected components or ones that cannot be stuck to + // other components. + boolean matches = false; + outer: for (IDIYComponent selectedComponent : this.selectedComponents) { + // try to find the selectedComponent in one of the groups + for (Set> s : componentGroups) + if (s.contains(selectedComponent)) { + matches = true; + break outer; } + } + + if (matches) { + switch (expansionMode) { + case ALL: + case IMMEDIATE: + newSelection.add(component); + break; + case SAME_TYPE: + if (selectedNamePrefixes.contains(ComponentProcessor.getInstance() + .extractComponentTypeFrom((Class>) component.getClass()).getNamePrefix())) { + newSelection.add(component); + } + break; + } + } + } + } else { + Set selectedNamePrefixes = new HashSet(); + if (expansionMode == ExpansionMode.SAME_TYPE) { + // find prefixes of all selected components to bundle similar component types together + for (IDIYComponent component : getSelectedComponents()) { + selectedNamePrefixes.add(ComponentProcessor.getInstance() + .extractComponentTypeFrom((Class>) component.getClass()).getNamePrefix()); + } + } + // Now try to find components that intersect with at least one component + // in the pool. + for (IDIYComponent component : getCurrentProject().getComponents()) { + // Skip already selected components or ones that cannot be stuck to + // other components. + ComponentArea area = drawingManager.getComponentArea(component); + if (newSelection.contains(component) || !(ComponentProcessor.hasStickyPoint(component) || (area != null + && area.getContinuityPositiveAreas() == null))) + continue; + boolean matches = false; + for (IDIYComponent selectedComponent : this.selectedComponents) { + ComponentArea selectedArea = drawingManager.getComponentArea(selectedComponent); + if (ComponentProcessor.componentPointsTouch(component, selectedComponent)) { + matches = true; break; + } + if (selectedArea == null || selectedArea.getContinuityPositiveAreas() == null) + continue; + + // do a rough check, if the outlines don't intersect there's no chance of a match + // so don't waste time doing the precise check + if (area.getOutlineArea() != null && selectedArea.getOutlineArea() != null && + !area.getOutlineArea().getBounds().intersects(selectedArea.getOutlineArea().getBounds())) + continue; + + // create a unified continuity area for both components + Area area1 = new Area(); + for (Area a : area.getContinuityPositiveAreas()) + area1.add(a); + if (area.getContinuityNegativeAreas() != null) + for(Area a : area.getContinuityNegativeAreas()) + area1.subtract(a); + + Area area2 = new Area(); + for (Area a : selectedArea.getContinuityPositiveAreas()) + area2.add(a); + if (selectedArea.getContinuityNegativeAreas() != null) + for(Area a : selectedArea.getContinuityNegativeAreas()) + area2.subtract(a); + + // now check the intersection, if there's something we have a match + area1.intersect(area2); + if (!area1.isEmpty()) { + matches = true; + break; + } + } + + if (matches) { + switch (expansionMode) { + case ALL: + case IMMEDIATE: + newSelection.add(component); + break; + case SAME_TYPE: + if (selectedNamePrefixes.contains(ComponentProcessor.getInstance() + .extractComponentTypeFrom((Class>) component.getClass()).getNamePrefix())) { + newSelection.add(component); + } + break; + } } } } diff --git a/diylc/diylc-core/src/org/diylc/presenter/update.xml b/diylc/diylc-core/src/org/diylc/presenter/update.xml index 7bd6d63e..f376a88c 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/update.xml +++ b/diylc/diylc-core/src/org/diylc/presenter/update.xml @@ -2781,6 +2781,10 @@ NEW_FEATURE Added "P-0+o (Proto) Board" component by Kristian BlÄsol + + + IMPROVEMENT + Better "Expand Selection" logic. Works well with conductive components like traces and wires and regular components like resistors and capacitors BUG_FIX diff --git a/diylc/diylc-library/lib/diylc-core.jar b/diylc/diylc-library/lib/diylc-core.jar index a9fc6359..a142bdea 100644 Binary files a/diylc/diylc-library/lib/diylc-core.jar and b/diylc/diylc-library/lib/diylc-core.jar differ diff --git a/diylc/diylc-swing/lib/diylc-core.jar b/diylc/diylc-swing/lib/diylc-core.jar index a9fc6359..a142bdea 100644 Binary files a/diylc/diylc-swing/lib/diylc-core.jar and b/diylc/diylc-swing/lib/diylc-core.jar differ diff --git a/diylc/diylc-swing/library/main.jar b/diylc/diylc-swing/library/main.jar index 81a548d4..99b2a12a 100644 Binary files a/diylc/diylc-swing/library/main.jar and b/diylc/diylc-swing/library/main.jar differ