mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-10 17:22:09 +10:00
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
This commit is contained in:
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -397,4 +399,11 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
* Locks or unlocks a single component.
|
||||
*/
|
||||
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<Area> checkContinuityAreaProximity(Size threshold);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public interface IView {
|
||||
|
||||
int showConfirmDialog(String message, String title, int optionType, int messageType);
|
||||
|
||||
boolean editProperties(List<PropertyWrapper> properties, Set<PropertyWrapper> defaultedProperties);
|
||||
boolean editProperties(List<PropertyWrapper> properties, Set<PropertyWrapper> defaultedProperties, String title);
|
||||
|
||||
String showInputDialog(String message, String title);
|
||||
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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<Area> proximityMarkers = null;
|
||||
|
||||
public DrawingManager(MessageDispatcher<EventType> messageDispatcher, IConfigurationManager<?> configManager) {
|
||||
super();
|
||||
this.messageDispatcher = messageDispatcher;
|
||||
@@ -471,6 +478,14 @@ public class DrawingManager {
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
|
||||
@@ -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<IDIYComponent<?>> findComponentsAt(Point2D point, Project project) {
|
||||
@@ -655,4 +673,40 @@ public class DrawingManager {
|
||||
|
||||
return areas;
|
||||
}
|
||||
|
||||
public List<Area> getContinuityAreaProximity(Project project, float threshold) {
|
||||
List<Area> continuityAreas = getContinuityAreas(project);
|
||||
Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for approx good results
|
||||
List<Area> expanded = continuityAreas.parallelStream().map((a) -> new Area(s.createStrokedShape(a))).collect(Collectors.toList());
|
||||
List<Area> intersections = new ArrayList<Area>();
|
||||
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<Area> shapes = new ArrayList<Area>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -863,7 +863,7 @@ public class Presenter implements IPlugInPort {
|
||||
List<PropertyWrapper> properties = getMutualSelectionProperties();
|
||||
if (properties != null && !properties.isEmpty()) {
|
||||
Set<PropertyWrapper> defaultedProperties = new HashSet<PropertyWrapper>();
|
||||
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<IDIYComponent<?>>(selectedComponents), areas, new HashSet<Area>(drawingManager.getContinuityAreas(currentProject)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Area> checkContinuityAreaProximity(Size threshold) {
|
||||
return drawingManager.getContinuityAreaProximity(this.currentProject, (float)threshold.convertToPixels());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3354,6 +3354,10 @@
|
||||
<releaseDate>2020-12-06 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>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</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>'Tactile Micro-Switch' component added</description>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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;
|
||||
@@ -293,6 +297,10 @@ public class ActionFactory {
|
||||
return new SummarizeNetlistAction(plugInPort, swingUI, summarizer);
|
||||
}
|
||||
|
||||
public CheckProximityAction createCheckProximityAction(IPlugInPort plugInPort, ISwingUI swingUI) {
|
||||
return new CheckProximityAction(plugInPort, swingUI);
|
||||
}
|
||||
|
||||
// File menu actions.
|
||||
|
||||
public static class NewAction extends AbstractAction {
|
||||
@@ -419,7 +427,7 @@ public class ActionFactory {
|
||||
|
||||
@Override
|
||||
public boolean editProperties(List<PropertyWrapper> properties,
|
||||
Set<PropertyWrapper> defaultedProperties) {
|
||||
Set<PropertyWrapper> 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<PropertyWrapper>(), (String)CheckProximityAction.this.getValue(NAME))) {
|
||||
try {
|
||||
wrapper.writeTo(CheckProximityAction.this);
|
||||
} catch (Exception e1) {
|
||||
LOG.error("Error writing proximity threshold", e1);
|
||||
}
|
||||
|
||||
swingUI.executeBackgroundTask(new ITask<List<Area>>() {
|
||||
|
||||
@Override
|
||||
public List<Area> 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<Area> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class DummyView implements IView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean editProperties(List<PropertyWrapper> properties, Set<PropertyWrapper> defaultedProperties) {
|
||||
public boolean editProperties(List<PropertyWrapper> properties, Set<PropertyWrapper> defaultedProperties, String title) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -298,9 +298,9 @@ public class MainFrame extends JFrame implements ISwingUI {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean editProperties(List<PropertyWrapper> properties, Set<PropertyWrapper> defaultedProperties) {
|
||||
public boolean editProperties(List<PropertyWrapper> properties, Set<PropertyWrapper> 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());
|
||||
|
||||
@@ -110,7 +110,7 @@ public class TemplateDialog extends JDialog {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean editProperties(List<PropertyWrapper> properties, Set<PropertyWrapper> defaultedProperties) {
|
||||
public boolean editProperties(List<PropertyWrapper> properties, Set<PropertyWrapper> defaultedProperties, String title) {
|
||||
return false;
|
||||
}
|
||||
}, InMemoryConfigurationManager.getInstance());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
@@ -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<PropertyWrapper> properties,
|
||||
Set<PropertyWrapper> defaultedProperties) {
|
||||
Set<PropertyWrapper> 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());
|
||||
|
||||
@@ -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<INetlistAnalyzer> summarizers = plugInPort.getNetlistAnalyzers();
|
||||
if (summarizers != null) {
|
||||
|
||||
@@ -23,7 +23,7 @@ public class MockView implements IView {
|
||||
|
||||
@Override
|
||||
public boolean editProperties(List<PropertyWrapper> properties,
|
||||
Set<PropertyWrapper> defaultedProperties) {
|
||||
Set<PropertyWrapper> defaultedProperties, String title) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user