diff --git a/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java b/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java
index de1e43a6..f05d1c9e 100644
--- a/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java
+++ b/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java
@@ -25,6 +25,7 @@ import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
+import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
@@ -40,6 +41,7 @@ import org.diylc.core.IDIYComponent;
import org.diylc.core.Project;
import org.diylc.core.Template;
import org.diylc.core.Theme;
+import org.diylc.core.measures.Size;
import org.diylc.test.ITestProcessor;
/**
@@ -396,5 +398,12 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
/**
* Locks or unlocks a single component.
*/
- void lockComponent(IDIYComponent> c, boolean locked);
+ void lockComponent(IDIYComponent> c, boolean locked);
+
+ /**
+ * Checks for proximity between continuity areas and creates additional components around any problematic areas to visually identify them.
+ *
+ * @param threshold
+ */
+ List checkContinuityAreaProximity(Size threshold);
}
diff --git a/diylc/diylc-core/src/org/diylc/core/IView.java b/diylc/diylc-core/src/org/diylc/core/IView.java
index 8cff063e..75e2ca38 100644
--- a/diylc/diylc-core/src/org/diylc/core/IView.java
+++ b/diylc/diylc-core/src/org/diylc/core/IView.java
@@ -56,7 +56,7 @@ public interface IView {
int showConfirmDialog(String message, String title, int optionType, int messageType);
- boolean editProperties(List properties, Set defaultedProperties);
+ boolean editProperties(List properties, Set defaultedProperties, String title);
String showInputDialog(String message, String title);
diff --git a/diylc/diylc-core/src/org/diylc/presenter/AreaUtils.java b/diylc/diylc-core/src/org/diylc/presenter/AreaUtils.java
index 4a8674e7..ee30f1d3 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/AreaUtils.java
+++ b/diylc/diylc-core/src/org/diylc/presenter/AreaUtils.java
@@ -60,7 +60,7 @@ public class AreaUtils {
if (intersection != null && !intersection.isEmpty()) {
a1.add(a2);
consumed.set(j, true);
- } else { // maybe there's a connection between them
+ } else if (connections != null) { // maybe there's a connection between them
for (Connection p : connections) { // use getBounds to optimize the computation, don't get into complex math if not needed
if ((a1.getBounds().contains(p.getP1()) && a2.getBounds().contains(p.getP2()) && a1.contains(p.getP1()) && a2.contains(p.getP2())) ||
(a1.getBounds().contains(p.getP2()) && a2.getBounds().contains(p.getP1())) && a1.contains(p.getP2()) && a2.contains(p.getP1())) {
diff --git a/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java b/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java
index 4b78cf0c..b4e3c55a 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java
+++ b/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java
@@ -26,8 +26,11 @@ import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
@@ -55,6 +58,8 @@ import org.diylc.core.IDIYComponent;
import org.diylc.core.Project;
import org.diylc.core.Theme;
import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
/**
@@ -112,6 +117,8 @@ public class DrawingManager {
private IConfigurationManager> configManager;
+ private List proximityMarkers = null;
+
public DrawingManager(MessageDispatcher messageDispatcher, IConfigurationManager> configManager) {
super();
this.messageDispatcher = messageDispatcher;
@@ -470,6 +477,14 @@ public class DrawingManager {
g2d.draw(a);
}
}
+
+ if (this.proximityMarkers != null)
+ {
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
+ g2d.setColor(Color.red);
+ for (Shape s : this.proximityMarkers)
+ g2d.draw(s);
+ }
if (currentContinuityArea != null) {
Composite oldComposite = g2d.getComposite();
@@ -524,6 +539,7 @@ public class DrawingManager {
public void invalidateComponent(IDIYComponent> component) {
componentAreaMap.remove(component);
lastDrawnStateMap.remove(component);
+ proximityMarkers = null;
}
public ComponentArea getComponentArea(IDIYComponent> component) {
@@ -533,11 +549,13 @@ public class DrawingManager {
public void clearComponentAreaMap() {
componentAreaMap.clear();
lastDrawnStateMap.clear();
+ proximityMarkers = null;
}
public void clearContinuityArea() {
currentContinuityArea = null;
continuityAreaCache = null;
+ proximityMarkers = null;
}
public List> findComponentsAt(Point2D point, Project project) {
@@ -654,5 +672,41 @@ public class DrawingManager {
AreaUtils.crunchAreas(areas, connections);
return areas;
- }
+ }
+
+ public List getContinuityAreaProximity(Project project, float threshold) {
+ List continuityAreas = getContinuityAreas(project);
+ Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for approx good results
+ List expanded = continuityAreas.parallelStream().map((a) -> new Area(s.createStrokedShape(a))).collect(Collectors.toList());
+ List intersections = new ArrayList();
+ for (int i = 0; i < expanded.size() - 1; i++) {
+ for (int j = i + 1; j < expanded.size(); j++) {
+ Area first = new Area(expanded.get(i));
+ Area second = expanded.get(j);
+ first.intersect(second);
+ if (!first.isEmpty())
+ intersections.add(first);
+ }
+ }
+
+ AreaUtils.crunchAreas(intersections, null);
+ double minSize = new Size(1d, SizeUnit.cm).convertToPixels();
+
+ List shapes = new ArrayList();
+ for (Area a : intersections) {
+ Rectangle2D bounds2d = a.getBounds2D();
+ if (bounds2d.getWidth() < minSize)
+ bounds2d = new Rectangle2D.Double(bounds2d.getCenterX() - minSize / 2, bounds2d.getY(), minSize, bounds2d.getHeight());
+ if (bounds2d.getHeight() < minSize)
+ bounds2d = new Rectangle2D.Double(bounds2d.getX(), bounds2d.getCenterY() - minSize / 2, bounds2d.getWidth(), minSize);
+
+ shapes.add(new Area(new Ellipse2D.Double(bounds2d.getX(), bounds2d.getY(), bounds2d.getWidth(), bounds2d.getHeight())));
+ }
+
+ //AreaUtils.crunchAreas(shapes, null);
+
+ this.proximityMarkers = shapes;
+
+ return shapes;
+ }
}
diff --git a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java
index 9f9bc944..b7a879b9 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java
+++ b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java
@@ -863,7 +863,7 @@ public class Presenter implements IPlugInPort {
List properties = getMutualSelectionProperties();
if (properties != null && !properties.isEmpty()) {
Set defaultedProperties = new HashSet();
- boolean edited = view.editProperties(properties, defaultedProperties);
+ boolean edited = view.editProperties(properties, defaultedProperties, LangUtil.translate("Edit Selection"));
if (edited) {
try {
applyPropertiesToSelection(properties);
@@ -3412,4 +3412,9 @@ public class Presenter implements IPlugInPort {
}
return new Snapshot(currentProject.clone(), new HashSet>(selectedComponents), areas, new HashSet(drawingManager.getContinuityAreas(currentProject)));
}
+
+ @Override
+ public List checkContinuityAreaProximity(Size threshold) {
+ return drawingManager.getContinuityAreaProximity(this.currentProject, (float)threshold.convertToPixels());
+ }
}
diff --git a/diylc/diylc-core/src/org/diylc/presenter/update.xml b/diylc/diylc-core/src/org/diylc/presenter/update.xml
index 44bf0a97..47e0c829 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/update.xml
+++ b/diylc/diylc-core/src/org/diylc/presenter/update.xml
@@ -3354,6 +3354,10 @@
2020-12-06 00:00:00.000 CET
+
+ NEW_FEATURE
+ Added 'Check Trace Proximity' functionality under File -> Analyze menu that checks if there are any traces or pads that are too close to each other
+
NEW_FEATURE
'Tactile Micro-Switch' component added
diff --git a/diylc/diylc-library/lib/diylc-core.jar b/diylc/diylc-library/lib/diylc-core.jar
index 720a6978..5a07cd64 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 720a6978..5a07cd64 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/lib/swing-framework.jar b/diylc/diylc-swing/lib/swing-framework.jar
index 5c5c54e9..8688c27f 100644
Binary files a/diylc/diylc-swing/lib/swing-framework.jar and b/diylc/diylc-swing/lib/swing-framework.jar differ
diff --git a/diylc/diylc-swing/library/main.jar b/diylc/diylc-swing/library/main.jar
index 7ddfd9ff..a19b18e8 100644
Binary files a/diylc/diylc-swing/library/main.jar and b/diylc/diylc-swing/library/main.jar differ
diff --git a/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java b/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java
index 738df72b..e3e4ae02 100644
--- a/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java
+++ b/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java
@@ -23,6 +23,7 @@ import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
+import java.awt.geom.Area;
import java.awt.print.PrinterException;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -34,6 +35,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -63,17 +65,19 @@ import org.diylc.core.IView;
import org.diylc.core.Project;
import org.diylc.core.Template;
import org.diylc.core.Theme;
-import org.diylc.swing.images.IconLoader;
+import org.diylc.core.annotations.PositiveNonZeroMeasureValidator;
import org.diylc.core.measures.Nudge;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.editor.FlexibleLeadsEditor;
+import org.diylc.lang.LangUtil;
import org.diylc.netlist.Group;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.Summary;
import org.diylc.presenter.Presenter;
import org.diylc.swing.gui.DialogFactory;
import org.diylc.swing.gui.editor.PropertyEditorDialog;
+import org.diylc.swing.images.IconLoader;
import org.diylc.swing.plugins.config.ConfigPlugin;
import org.diylc.swing.plugins.edit.ComponentTransferable;
import org.diylc.swing.plugins.edit.FindDialog;
@@ -292,6 +296,10 @@ public class ActionFactory {
ISwingUI swingUI, INetlistAnalyzer summarizer) {
return new SummarizeNetlistAction(plugInPort, swingUI, summarizer);
}
+
+ public CheckProximityAction createCheckProximityAction(IPlugInPort plugInPort, ISwingUI swingUI) {
+ return new CheckProximityAction(plugInPort, swingUI);
+ }
// File menu actions.
@@ -419,7 +427,7 @@ public class ActionFactory {
@Override
public boolean editProperties(List properties,
- Set defaultedProperties) {
+ Set defaultedProperties, String title) {
return false;
}
}, InMemoryConfigurationManager.getInstance());
@@ -1931,4 +1939,76 @@ public class ActionFactory {
}, true);
}
}
+
+ public static class CheckProximityAction extends AbstractAction implements Cloneable {
+
+ private static final long serialVersionUID = 1L;
+
+ private IPlugInPort plugInPort;
+ private ISwingUI swingUI;
+ private Size threshold;
+
+ public CheckProximityAction(IPlugInPort plugInPort, ISwingUI swingUI) {
+ super();
+ this.plugInPort = plugInPort;
+ this.swingUI = swingUI;
+ putValue(AbstractAction.NAME, "Check Trace Proximity");
+ putValue(AbstractAction.SMALL_ICON, IconLoader.TraceProximity.getIcon());
+ this.threshold = new Size(0.5d, SizeUnit.mm);
+ }
+
+ public Size getThreshold() {
+ return threshold;
+ }
+
+ public void setThreshold(Size threshold) {
+ this.threshold = threshold;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ PropertyWrapper wrapper = new PropertyWrapper("Threshold", Size.class, "getThreshold", "setThreshold", false, new PositiveNonZeroMeasureValidator(), 0);
+ try {
+ wrapper.readFrom(CheckProximityAction.this);
+ } catch (Exception e1) {
+ LOG.error("Error reading proximity threshold", e1);
+ }
+
+ if (swingUI.editProperties(Arrays.asList(wrapper), new HashSet(), (String)CheckProximityAction.this.getValue(NAME))) {
+ try {
+ wrapper.writeTo(CheckProximityAction.this);
+ } catch (Exception e1) {
+ LOG.error("Error writing proximity threshold", e1);
+ }
+
+ swingUI.executeBackgroundTask(new ITask>() {
+
+ @Override
+ public List doInBackground() throws Exception {
+ return plugInPort.checkContinuityAreaProximity(threshold);
+ }
+
+ @Override
+ public void failed(Exception e) {
+ swingUI.showMessage(e.getMessage(), (String)CheckProximityAction.this.getValue(NAME), ISwingUI.INFORMATION_MESSAGE);
+ }
+
+ @Override
+ public void complete(List res) {
+ if (res == null || res.size() == 0)
+ swingUI.showMessage(LangUtil.translate("No proximity issues detected."),
+ (String)CheckProximityAction.this.getValue(NAME), ISwingUI.INFORMATION_MESSAGE);
+ else
+ swingUI.showMessage(String.format("%s potential proximity issue(s) detected and marked red.", res.size()),
+ (String)CheckProximityAction.this.getValue(NAME), ISwingUI.WARNING_MESSAGE);
+ }
+ }, true);
+ }
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return new CheckProximityAction(plugInPort, swingUI);
+ }
+ }
}
diff --git a/diylc/diylc-swing/src/org/diylc/swing/gui/DummyView.java b/diylc/diylc-swing/src/org/diylc/swing/gui/DummyView.java
index 845d8af2..0206d24b 100644
--- a/diylc/diylc-swing/src/org/diylc/swing/gui/DummyView.java
+++ b/diylc/diylc-swing/src/org/diylc/swing/gui/DummyView.java
@@ -44,7 +44,7 @@ public class DummyView implements IView {
}
@Override
- public boolean editProperties(List properties, Set defaultedProperties) {
+ public boolean editProperties(List properties, Set defaultedProperties, String title) {
return false;
}
diff --git a/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java b/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java
index 4556a48e..ed0117f3 100644
--- a/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java
+++ b/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java
@@ -298,9 +298,9 @@ public class MainFrame extends JFrame implements ISwingUI {
}
@Override
- public boolean editProperties(List properties, Set defaultedProperties) {
+ public boolean editProperties(List properties, Set defaultedProperties, String title) {
PropertyEditorDialog editor =
- DialogFactory.getInstance().createPropertyEditorDialog(properties, LangUtil.translate("Edit Selection"), true);
+ DialogFactory.getInstance().createPropertyEditorDialog(properties, title, true);
editor.setVisible(true);
defaultedProperties.addAll(editor.getDefaultedProperties());
return ButtonDialog.OK.equals(editor.getSelectedButtonCaption());
diff --git a/diylc/diylc-swing/src/org/diylc/swing/gui/TemplateDialog.java b/diylc/diylc-swing/src/org/diylc/swing/gui/TemplateDialog.java
index 70e5886e..6e3f1ce2 100644
--- a/diylc/diylc-swing/src/org/diylc/swing/gui/TemplateDialog.java
+++ b/diylc/diylc-swing/src/org/diylc/swing/gui/TemplateDialog.java
@@ -110,7 +110,7 @@ public class TemplateDialog extends JDialog {
}
@Override
- public boolean editProperties(List properties, Set defaultedProperties) {
+ public boolean editProperties(List properties, Set defaultedProperties, String title) {
return false;
}
}, InMemoryConfigurationManager.getInstance());
diff --git a/diylc/diylc-swing/src/org/diylc/swing/images/IconLoader.java b/diylc/diylc-swing/src/org/diylc/swing/images/IconLoader.java
index 8d320b3e..03072b31 100644
--- a/diylc/diylc-swing/src/org/diylc/swing/images/IconLoader.java
+++ b/diylc/diylc-swing/src/org/diylc/swing/images/IconLoader.java
@@ -67,7 +67,7 @@ public enum IconLoader {
JarBeanInto("jar_bean_into.png"), GraphEdgeDirected("graph_edge_directed.png"), LaserPointer("laserpointer.png"), TapeMeasure("tape_measure1.png"),
Elements("elements1.png"), GraphNodes("graph_nodes.png"), User("user1.png"), FlexibleLeads("flexible_leads.png"), Pads("pads.png"), Lock("lock.png"),
Unlock("lock_open.png"), Earth("earth.png"), Loadline("loadline.png"), LoadlineAdd("loadline_add.png"), SnapToGrid("snap_to_grid.png"),
- SnapToComponents("snap_to_components.png"), SnapToNone("snap_to_none.png");
+ SnapToComponents("snap_to_components.png"), SnapToNone("snap_to_none.png"), TraceProximity("trace_proximity.png");
protected String name;
diff --git a/diylc/diylc-swing/src/org/diylc/swing/images/trace_proximity.png b/diylc/diylc-swing/src/org/diylc/swing/images/trace_proximity.png
new file mode 100644
index 00000000..ec5be80e
Binary files /dev/null and b/diylc/diylc-swing/src/org/diylc/swing/images/trace_proximity.png differ
diff --git a/diylc/diylc-swing/src/org/diylc/swing/loadline/LoadlineEditorFrame.java b/diylc/diylc-swing/src/org/diylc/swing/loadline/LoadlineEditorFrame.java
index 43499abb..5b4ab7ab 100644
--- a/diylc/diylc-swing/src/org/diylc/swing/loadline/LoadlineEditorFrame.java
+++ b/diylc/diylc-swing/src/org/diylc/swing/loadline/LoadlineEditorFrame.java
@@ -54,16 +54,15 @@ import org.diylc.components.misc.LoadlineEntity;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IView;
import org.diylc.core.Project;
-import org.diylc.swing.images.IconLoader;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.core.measures.Voltage;
import org.diylc.core.measures.VoltageUnit;
-import org.diylc.lang.LangUtil;
import org.diylc.presenter.Presenter;
import org.diylc.swing.gui.DialogFactory;
import org.diylc.swing.gui.TranslatedPanel;
import org.diylc.swing.gui.editor.PropertyEditorDialog;
+import org.diylc.swing.images.IconLoader;
import org.diylc.swing.plugins.canvas.CanvasPanel;
import org.diylc.swing.plugins.file.FileFilterEnum;
import org.diylc.swingframework.ButtonDialog;
@@ -520,9 +519,9 @@ public class LoadlineEditorFrame extends JFrame implements IView {
@Override
public boolean editProperties(List properties,
- Set defaultedProperties) {
+ Set defaultedProperties, String title) {
PropertyEditorDialog editor = DialogFactory.getInstance().createPropertyEditorDialog(LoadlineEditorFrame.this, properties,
- LangUtil.translate("Edit Selection"), true);
+ title, true);
editor.setVisible(true);
defaultedProperties.addAll(editor.getDefaultedProperties());
return ButtonDialog.OK.equals(editor.getSelectedButtonCaption());
diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/file/FileMenuPlugin.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/file/FileMenuPlugin.java
index f6a3c857..b0d4af1e 100644
--- a/diylc/diylc-swing/src/org/diylc/swing/plugins/file/FileMenuPlugin.java
+++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/file/FileMenuPlugin.java
@@ -91,6 +91,7 @@ public class FileMenuPlugin implements IPlugIn, IDynamicSubmenuHandler {
swingUI.injectSubmenu(ANALYZE_TITLE, IconLoader.Scientist.getIcon(), FILE_TITLE);
swingUI.injectMenuAction(actionFactory.createBomAction(plugInPort), ANALYZE_TITLE);
swingUI.injectMenuAction(actionFactory.createGenerateNetlistAction(plugInPort, swingUI), ANALYZE_TITLE);
+ swingUI.injectMenuAction(actionFactory.createCheckProximityAction(plugInPort, swingUI), ANALYZE_TITLE);
List summarizers = plugInPort.getNetlistAnalyzers();
if (summarizers != null) {
diff --git a/diylc/diylc-swing/test/org/diylc/MockView.java b/diylc/diylc-swing/test/org/diylc/MockView.java
index 756e9700..7f070b38 100644
--- a/diylc/diylc-swing/test/org/diylc/MockView.java
+++ b/diylc/diylc-swing/test/org/diylc/MockView.java
@@ -23,7 +23,7 @@ public class MockView implements IView {
@Override
public boolean editProperties(List properties,
- Set defaultedProperties) {
+ Set defaultedProperties, String title) {
// TODO Auto-generated method stub
return false;
}