Circuit analysis algorithm that supports netlist extraction, 'Highlight connected Areas' and 'Guitar Wiring Analyzer' features improved to take PCB trace layer and component z-order into account for better accuracy

This commit is contained in:
Bane Stojkovic
2022-11-23 22:50:01 +01:00
parent ec5e1ae233
commit d3d98b3058
28 changed files with 447 additions and 160 deletions
@@ -23,9 +23,13 @@ package org.diylc.common;
public enum PCBLayer { public enum PCBLayer {
_1, _2, _3, _4, _5; _1, _2, _3, _4, _5, _6, _7, _8;
public String toString() { public String toString() {
return name().substring(1); return name().substring(1);
}; };
public int getId() {
return Integer.parseInt(toString());
}
} }
@@ -0,0 +1,53 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.core;
/**
* Component with all extra info for it to be rendered on the screen.
*
* @author Branislav Stojkovic
*/
public class ComponentForRender {
private IDIYComponent<?> component;
private ComponentState state;
private int zOrder;
public ComponentForRender(IDIYComponent<?> component, ComponentState state, int zOrder) {
super();
this.component = component;
this.state = state;
this.zOrder = zOrder;
}
public IDIYComponent<?> getComponent() {
return component;
}
public ComponentState getState() {
return state;
}
public int getZOrder() {
return zOrder;
}
}
@@ -0,0 +1,6 @@
package org.diylc.core;
public interface ILayer {
int getLayerId();
}
@@ -23,6 +23,8 @@ import java.awt.geom.Point2D;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import org.diylc.presenter.ContinuityArea;
import org.diylc.utils.RTree; import org.diylc.utils.RTree;
/*** /***
@@ -34,9 +36,9 @@ import org.diylc.utils.RTree;
public class ContinuityGraph { public class ContinuityGraph {
private RTree<ContinuityNode> groupTree; private RTree<ContinuityNode> groupTree;
private Map<Integer, List<Area>> areaGroupMap; private Map<Integer, List<ContinuityArea>> areaGroupMap;
public ContinuityGraph(RTree<ContinuityNode> groupTree, Map<Integer, List<Area>> areaGroupMap) { public ContinuityGraph(RTree<ContinuityNode> groupTree, Map<Integer, List<ContinuityArea>> areaGroupMap) {
super(); super();
this.groupTree = groupTree; this.groupTree = groupTree;
this.areaGroupMap = areaGroupMap; this.areaGroupMap = areaGroupMap;
@@ -44,22 +46,26 @@ public class ContinuityGraph {
public List<Area> findAreasFor(Point2D point) { public List<Area> findAreasFor(Point2D point) {
Optional<ContinuityNode> first = groupTree.search(point, NetlistBuilder.eps).stream() Optional<ContinuityNode> first = groupTree.search(point, NetlistBuilder.eps).stream()
.filter(node -> node.getArea().contains(point)).findFirst(); .filter(node -> node.getArea().getArea().contains(point)).findFirst();
return first.map(node -> areaGroupMap.get(node.getGroupId())).orElse(null); return first.map(node ->
areaGroupMap.get(node.getGroupId())
.stream()
.map(x -> x.getArea())
.collect(Collectors.toList())).orElse(null);
} }
public static class ContinuityNode { public static class ContinuityNode {
private Area area; private ContinuityArea area;
private Integer groupId; private Integer groupId;
public ContinuityNode(Area area, Integer groupId) { public ContinuityNode(ContinuityArea area, Integer groupId) {
super(); super();
this.area = area; this.area = area;
this.groupId = groupId; this.groupId = groupId;
} }
public Area getArea() { public ContinuityArea getArea() {
return area; return area;
} }
@@ -52,7 +52,11 @@ public class Group implements Comparable<Group> {
} }
public Group connect(IDIYComponent<?> component, int index) { public Group connect(IDIYComponent<?> component, int index) {
getNodes().add(new Node(component, index)); return connect(component, index, 0);
}
public Group connect(IDIYComponent<?> component, int index, int zOrder) {
getNodes().add(new Node(component, index, zOrder));
return this; return this;
} }
@@ -80,7 +80,7 @@ public abstract class NetlistAnalyzer {
for (int i = 0; i < c.getControlPointCount(); i++) { for (int i = 0; i < c.getControlPointCount(); i++) {
if (i != n.getPointIndex() && c.getInternalLinkName(i, n.getPointIndex()) != null) { if (i != n.getPointIndex() && c.getInternalLinkName(i, n.getPointIndex()) != null) {
TreeLeaf l = new TreeLeaf(c, i, n.getPointIndex()); TreeLeaf l = new TreeLeaf(c, i, n.getPointIndex());
Node newNodeA = new Node(c, i); Node newNodeA = new Node(c, i, n.getZIndex());
if (!visited.contains(newNodeA)) { if (!visited.contains(newNodeA)) {
// visited.add(n); // visited.add(n);
Tree newCurrentPath = null; Tree newCurrentPath = null;
@@ -22,6 +22,7 @@ import java.awt.geom.Point2D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -39,6 +40,7 @@ import org.diylc.lang.LangUtil;
import org.diylc.netlist.ContinuityGraph.ContinuityNode; import org.diylc.netlist.ContinuityGraph.ContinuityNode;
import org.diylc.presenter.ComponentProcessor; import org.diylc.presenter.ComponentProcessor;
import org.diylc.presenter.Connection; import org.diylc.presenter.Connection;
import org.diylc.presenter.ContinuityArea;
import org.diylc.utils.RTree; import org.diylc.utils.RTree;
import com.google.common.graph.GraphBuilder; import com.google.common.graph.GraphBuilder;
import com.google.common.graph.ImmutableGraph; import com.google.common.graph.ImmutableGraph;
@@ -55,8 +57,8 @@ public class NetlistBuilder {
.translate("Maximum number of switching combinations exceeded. Allowed: %s, actual: %s"); .translate("Maximum number of switching combinations exceeded. Allowed: %s, actual: %s");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static List<Netlist> extractNetlists(boolean includeSwitches, Project project, List<Area> continuityAreas) public static List<Netlist> extractNetlists(boolean includeSwitches, Project project,
throws NetlistException { List<ContinuityArea> continuityAreas) throws NetlistException {
Map<Netlist, Netlist> result = new HashMap<Netlist, Netlist>(); Map<Netlist, Netlist> result = new HashMap<Netlist, Netlist>();
List<Node> nodes = new ArrayList<Node>(); List<Node> nodes = new ArrayList<Node>();
@@ -64,27 +66,30 @@ public class NetlistBuilder {
Set<String> commonNodes = new HashSet<String>(); Set<String> commonNodes = new HashSet<String>();
for (IDIYComponent<?> c : project.getComponents()) { for (int j = 0; j < project.getComponents().size(); j++) {
IDIYComponent<?> c = project.getComponents().get(j);
ComponentType type = ComponentProcessor.getInstance() ComponentType type = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) c.getClass()); .extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) c.getClass());
// extract nodes // extract nodes
if (!(c instanceof IContinuity) && !(c instanceof ICommonNode)) { if (!(c instanceof IContinuity) && !(c instanceof ICommonNode)) {
// regular components, create a node per control point
for (int i = 0; i < c.getControlPointCount(); i++) { for (int i = 0; i < c.getControlPointCount(); i++) {
String nodeName = c.getControlPointNodeName(i); String nodeName = c.getControlPointNodeName(i);
if (nodeName != null if (c.isControlPointSticky(i) && nodeName != null
&& (!includeSwitches || !ISwitch.class.isAssignableFrom(type.getInstanceClass()))) { && (!includeSwitches || !ISwitch.class.isAssignableFrom(type.getInstanceClass()))) {
nodes.add(new Node(c, i)); nodes.add(new Node(c, i, j));
} }
} }
} }
// create only one node per common label to avoid duplications in the netlist, as they will get eventually connected anyways // create only one node per common label to avoid duplications in the netlist, as they will
// get eventually connected anyways
if (c instanceof ICommonNode) { if (c instanceof ICommonNode) {
String label = ((ICommonNode) c).getCommonNodeLabel(); String label = ((ICommonNode) c).getCommonNodeLabel();
if (!commonNodes.contains(label)) { if (!commonNodes.contains(label)) {
commonNodes.add(label); commonNodes.add(label);
nodes.add(new Node(c, 0)); nodes.add(new Node(c, 0, j));
} }
} }
@@ -142,8 +147,8 @@ public class NetlistBuilder {
} }
List<Connection> connections = getConnections(project, switchPositions); List<Connection> connections = getConnections(project, switchPositions);
Netlist netlist = NetlistBuilder.buildNetlist(project.getComponents(), nodes, Netlist netlist =
continuityAreas, connections); NetlistBuilder.buildNetlist(project.getComponents(), nodes, continuityAreas, connections);
// merge graphs that are effectively the same // merge graphs that are effectively the same
if (result.containsKey(netlist)) { if (result.containsKey(netlist)) {
@@ -176,7 +181,7 @@ public class NetlistBuilder {
} }
public static Netlist buildNetlist(List<IDIYComponent<?>> components, Collection<Node> nodes, public static Netlist buildNetlist(List<IDIYComponent<?>> components, Collection<Node> nodes,
Collection<Area> continuityAreas, Collection<Connection> connections) { Collection<ContinuityArea> continuityAreas, Collection<Connection> connections) {
ImmutableGraph<ProjectGraphNode> graph = buildGraph(nodes, continuityAreas, connections); ImmutableGraph<ProjectGraphNode> graph = buildGraph(nodes, continuityAreas, connections);
Traverser<ProjectGraphNode> traverser = Traverser.forGraph(graph); Traverser<ProjectGraphNode> traverser = Traverser.forGraph(graph);
@@ -211,7 +216,7 @@ public class NetlistBuilder {
return netlist; return netlist;
} }
public static ContinuityGraph buildContinuityGraph(Collection<Area> continuityAreas, public static ContinuityGraph buildContinuityGraph(Collection<ContinuityArea> continuityAreas,
Collection<Connection> connections) { Collection<Connection> connections) {
ImmutableGraph<ProjectGraphNode> graph = ImmutableGraph<ProjectGraphNode> graph =
buildGraph(new ArrayList<Node>(), continuityAreas, connections); buildGraph(new ArrayList<Node>(), continuityAreas, connections);
@@ -224,7 +229,7 @@ public class NetlistBuilder {
int groupId = 1; int groupId = 1;
RTree<ContinuityNode> groupTree = new RTree<ContinuityGraph.ContinuityNode>(); RTree<ContinuityNode> groupTree = new RTree<ContinuityGraph.ContinuityNode>();
Map<Integer, List<Area>> areaGroupMap = new HashMap<Integer, List<Area>>(); Map<Integer, List<ContinuityArea>> areaGroupMap = new HashMap<Integer, List<ContinuityArea>>();
while (true) { while (true) {
@@ -237,12 +242,13 @@ public class NetlistBuilder {
final int finalGroupId = groupId; final int finalGroupId = groupId;
List<Area> areaList = new ArrayList<Area>(); List<ContinuityArea> areaList = new ArrayList<ContinuityArea>();
traverser.breadthFirst(nodeArr[i]).forEach(node -> { traverser.breadthFirst(nodeArr[i]).forEach(node -> {
visited.add(node); visited.add(node);
if (node.getArea() != null) { if (node.getArea() != null) {
areaList.add(node.getArea()); areaList.add(node.getArea());
groupTree.insert(node.getArea(), new ContinuityNode(node.getArea(), finalGroupId)); groupTree.insert(node.getArea().getArea(),
new ContinuityNode(node.getArea(), finalGroupId));
} }
}); });
@@ -257,7 +263,7 @@ public class NetlistBuilder {
/**** /****
* Build an {@link ImmutableGraph} containing the following nodes: project nodes, both ends of * Build an {@link ImmutableGraph} containing the following nodes: project nodes, both ends of
* each connection and one node per continuity areas. All vertices are added based on object * each connection and one node per continuity area. All vertices are added based on object
* proximity or intersection. * proximity or intersection.
* *
* @param nodes * @param nodes
@@ -266,45 +272,52 @@ public class NetlistBuilder {
* @return * @return
*/ */
private static ImmutableGraph<ProjectGraphNode> buildGraph(Collection<Node> nodes, private static ImmutableGraph<ProjectGraphNode> buildGraph(Collection<Node> nodes,
Collection<Area> continuityAreas, Collection<Connection> connections) { Collection<ContinuityArea> continuityAreas, Collection<Connection> connections) {
Map<Point2D, List<ProjectGraphNode>> nodeMap = new HashMap<Point2D, List<ProjectGraphNode>>();
RTree<ProjectGraphNode> nodeTree = new RTree<ProjectGraphNode>(); Map<Point2D, List<ProjectGraphNode>> nodeMapByPoint =
new HashMap<Point2D, List<ProjectGraphNode>>();
// RTree<ProjectGraphNode> nodeTree = new RTree<ProjectGraphNode>();
RTree<ProjectGraphNode> areaTree = new RTree<ProjectGraphNode>(); RTree<ProjectGraphNode> areaTree = new RTree<ProjectGraphNode>();
Builder<ProjectGraphNode> builder = GraphBuilder.undirected().<ProjectGraphNode>immutable(); Builder<ProjectGraphNode> builder = GraphBuilder.undirected().<ProjectGraphNode>immutable();
List<ProjectGraphNode> controlPointNodeList = new ArrayList<ProjectGraphNode>();
for (Node node : nodes) { for (Node node : nodes) {
Point2D p = snapToEps(node.getPoint2D(), eps); Point2D p = snapToEps(node.getPoint2D(), eps);
ProjectGraphNode graphNode = new ProjectGraphNode(p, node); ProjectGraphNode graphNode = new ProjectGraphNode(p, node);
builder.addNode(graphNode); builder.addNode(graphNode);
nodeTree.insert(new float[] {(float) (p.getX() - eps), (float) (p.getY() - eps)}, // nodeTree.insert(new float[] {(float) (p.getX() - eps), (float) (p.getY() - eps)},
new float[] {eps * 2, eps * 2}, graphNode); // new float[] {eps * 2, eps * 2}, graphNode);
nodeMap.computeIfAbsent(p, (point) -> new ArrayList<ProjectGraphNode>()) nodeMapByPoint.computeIfAbsent(p, (point) -> new ArrayList<ProjectGraphNode>())
.add(new ProjectGraphNode(p, node)); .add(new ProjectGraphNode(p, node));
controlPointNodeList.add(graphNode);
} }
for (Connection conn : connections) { for (Connection conn : connections) {
Point2D p1 = snapToEps(conn.getP1(), eps); Point2D p1 = snapToEps(conn.getP1(), eps);
Point2D p2 = snapToEps(conn.getP2(), eps); Point2D p2 = snapToEps(conn.getP2(), eps);
ProjectGraphNode graphNode1 = new ProjectGraphNode(p1); ProjectGraphNode graphNode1 = new ProjectGraphNode(p1, conn.getZIndex());
ProjectGraphNode graphNode2 = new ProjectGraphNode(p2); ProjectGraphNode graphNode2 = new ProjectGraphNode(p2, conn.getZIndex());
nodeTree.insert(new float[] {(float) p1.getX() - eps, (float) p1.getY() - eps}, // nodeTree.insert(new float[] {(float) p1.getX() - eps, (float) p1.getY() - eps},
new float[] {eps * 2, eps * 2}, graphNode1); // new float[] {eps * 2, eps * 2}, graphNode1);
nodeTree.insert(new float[] {(float) p2.getX() - eps, (float) p2.getY() - eps}, // nodeTree.insert(new float[] {(float) p2.getX() - eps, (float) p2.getY() - eps},
new float[] {eps * 2, eps * 2}, graphNode2); // new float[] {eps * 2, eps * 2}, graphNode2);
builder.addNode(graphNode1); builder.addNode(graphNode1);
builder.addNode(graphNode2); builder.addNode(graphNode2);
builder.putEdge(graphNode1, graphNode2); builder.putEdge(graphNode1, graphNode2);
nodeMap.computeIfAbsent(p1, (point) -> new ArrayList<ProjectGraphNode>()).add(graphNode1); nodeMapByPoint.computeIfAbsent(p1, (point) -> new ArrayList<ProjectGraphNode>())
nodeMap.computeIfAbsent(p2, (point) -> new ArrayList<ProjectGraphNode>()).add(graphNode2); .add(graphNode1);
nodeMapByPoint.computeIfAbsent(p2, (point) -> new ArrayList<ProjectGraphNode>())
.add(graphNode2);
} }
for (Map.Entry<Point2D, List<ProjectGraphNode>> entry : nodeMap.entrySet()) { for (Map.Entry<Point2D, List<ProjectGraphNode>> entry : nodeMapByPoint.entrySet()) {
ProjectGraphNode[] nodeArr = entry.getValue().toArray(new ProjectGraphNode[0]); ProjectGraphNode[] nodeArr = entry.getValue().toArray(new ProjectGraphNode[0]);
for (int i = 0; i < nodeArr.length - 1; i++) { for (int i = 0; i < nodeArr.length - 1; i++) {
for (int j = i + 1; j < nodeArr.length; j++) { for (int j = i + 1; j < nodeArr.length; j++) {
@@ -313,30 +326,36 @@ public class NetlistBuilder {
} }
} }
for (Area area : continuityAreas) { for (ContinuityArea area : continuityAreas) {
ProjectGraphNode areaNode = new ProjectGraphNode(area); ProjectGraphNode areaNode = new ProjectGraphNode(area);
builder.addNode(areaNode); builder.addNode(areaNode);
ProjectGraphNode[] nodeArr = nodeTree.search(area).stream() // List<ProjectGraphNode> candidateNodes = nodeTree.search(area.getArea()).stream()
.filter(graphNode -> area.contains(graphNode.getPoint()) // .filter(graphNode -> area.getArea().contains(graphNode.getPoint())
|| area.intersects(graphNode.getPoint().getX() - eps / 2, // || area.getArea().intersects(graphNode.getPoint().getX() - eps / 2,
graphNode.getPoint().getY() - eps / 2, eps, eps)) // graphNode.getPoint().getY() - eps / 2, eps, eps))
.collect(Collectors.toList()).toArray(new ProjectGraphNode[0]); // .collect(Collectors.toList());
//
// List<ProjectGraphNode> nodesAbove = candidateNodes.stream()
// .filter(x -> x.getNode().getZIndex() > area.getZOrder())
// .sorted(Comparator.comparing(x -> x.getNode().getZIndex(), Comparator.reverseOrder()))
// .collect(Collectors.toList());
//
// for (ProjectGraphNode node : nodeArr) {
// builder.putEdge(node, areaNode);
// }
for (ProjectGraphNode node : nodeArr) { // for (int i = 0; i < nodeArr.length - 1; i++) {
builder.putEdge(node, areaNode); // for (int j = i + 1; j < nodeArr.length; j++) {
} // builder.putEdge(nodeArr[i], nodeArr[j]);
// }
// }
for (int i = 0; i < nodeArr.length - 1; i++) { areaTree.search(area.getArea()).stream().filter(graphNode -> {
for (int j = i + 1; j < nodeArr.length; j++) { if (graphNode.getArea() != null && graphNode.getArea() != area
builder.putEdge(nodeArr[i], nodeArr[j]); && graphNode.getArea().getLayerId() == area.getLayerId()) {
} Area a = new Area(graphNode.getArea().getArea());
} a.intersect(area.getArea());
areaTree.search(area).stream().filter(graphNode -> {
if (graphNode.getArea() != null && graphNode.getArea() != area) {
Area a = new Area(graphNode.getArea());
a.intersect(area);
return !a.isEmpty(); return !a.isEmpty();
} }
return false; return false;
@@ -344,36 +363,68 @@ public class NetlistBuilder {
builder.putEdge(graphNode, areaNode); builder.putEdge(graphNode, areaNode);
}); });
areaTree.insert(area, areaNode); areaTree.insert(area.getArea(), areaNode);
}
for (Map.Entry<Point2D, List<ProjectGraphNode>> entry : nodeMapByPoint.entrySet()) {
final Point2D point = entry.getKey();
List<ProjectGraphNode> candidateAreaNodes = areaTree.search(point, eps).stream()
.filter(graphNode -> graphNode.getArea().getArea().contains(point)
|| graphNode.getArea().getArea().intersects(point.getX() - eps / 2,
point.getY() - eps / 2, eps, eps))
.collect(Collectors.toList());
if (candidateAreaNodes.isEmpty())
continue;
for (ProjectGraphNode pointNode : entry.getValue()) {
List<ProjectGraphNode> areaNodesBelowPoint = candidateAreaNodes.stream()
.filter(x -> x.getArea().getZIndex() < pointNode.getZIndex())
.sorted(Comparator.comparing(x -> x.getArea().getZIndex(), Comparator.reverseOrder()))
.collect(Collectors.toList());
if (areaNodesBelowPoint.isEmpty()) {
builder.putEdge(pointNode, candidateAreaNodes.get(0));
} else {
builder.putEdge(pointNode, areaNodesBelowPoint.get(0));
}
}
} }
return builder.build(); return builder.build();
} }
@SuppressWarnings({"unchecked", "unlikely-arg-type"}) @SuppressWarnings({"unchecked", "unlikely-arg-type"})
private static List<Connection> getConnections(Project project, Map<ISwitch, Integer> switchPositions) { private static List<Connection> getConnections(Project project,
Map<ISwitch, Integer> switchPositions) {
Set<Connection> connections = new TreeSet<Connection>(); Set<Connection> connections = new TreeSet<Connection>();
Map<String, List<IDIYComponent<?>>> commonNodeMap = new HashMap<String, List<IDIYComponent<?>>>(); Map<String, List<IDIYComponent<?>>> commonNodeMap =
new HashMap<String, List<IDIYComponent<?>>>();
for (IDIYComponent<?> c : project.getComponents()) { for (int z = 0; z < project.getComponents().size(); z++) {
ComponentType type = IDIYComponent<?> c = project.getComponents().get(z);
ComponentProcessor.getInstance().extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) c.getClass()); ComponentType type = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) c.getClass());
// handle direct connections // handle direct connections
if (c instanceof IContinuity) { if (c instanceof IContinuity) {
for (int i = 0; i < c.getControlPointCount() - 1; i++) for (int i = 0; i < c.getControlPointCount() - 1; i++)
for (int j = i + 1; j < c.getControlPointCount(); j++) for (int j = i + 1; j < c.getControlPointCount(); j++)
if (((IContinuity) c).arePointsConnected(i, j)) if (((IContinuity) c).arePointsConnected(i, j))
connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j))); connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j), z));
} }
// handle switches // handle switches
if (ISwitch.class.isAssignableFrom(type.getInstanceClass()) && switchPositions.containsKey(c)) { if (ISwitch.class.isAssignableFrom(type.getInstanceClass())
&& switchPositions.containsKey(c)) {
int position = switchPositions.get(c); int position = switchPositions.get(c);
ISwitch s = (ISwitch) c; ISwitch s = (ISwitch) c;
for (int i = 0; i < c.getControlPointCount() - 1; i++) for (int i = 0; i < c.getControlPointCount() - 1; i++)
for (int j = i + 1; j < c.getControlPointCount(); j++) for (int j = i + 1; j < c.getControlPointCount(); j++)
if (s.arePointsConnected(i, j, position)) if (s.arePointsConnected(i, j, position))
connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j))); connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j), z));
} }
// handle common nodes // handle common nodes
if (ICommonNode.class.isAssignableFrom(type.getInstanceClass())) { if (ICommonNode.class.isAssignableFrom(type.getInstanceClass())) {
@@ -387,7 +438,8 @@ public class NetlistBuilder {
for (List<IDIYComponent<?>> commonPoints : commonNodeMap.values()) { for (List<IDIYComponent<?>> commonPoints : commonNodeMap.values()) {
IDIYComponent<?>[] commonPointArr = commonPoints.toArray(new IDIYComponent<?>[0]); IDIYComponent<?>[] commonPointArr = commonPoints.toArray(new IDIYComponent<?>[0]);
for (int i = 0; i < commonPointArr.length - 1; i++) { for (int i = 0; i < commonPointArr.length - 1; i++) {
connections.add(new Connection(commonPointArr[i].getControlPoint(0), commonPointArr[i + 1].getControlPoint(0))); connections.add(new Connection(commonPointArr[i].getControlPoint(0),
commonPointArr[i + 1].getControlPoint(0), 0));
} }
} }
@@ -399,19 +451,22 @@ public class NetlistBuilder {
Math.round(point.getY() / eps) * eps); Math.round(point.getY() / eps) * eps);
} }
private static class ProjectGraphNode { static class ProjectGraphNode {
private Point2D point; private Point2D point;
private Node node; private Node node;
private Area area; private ContinuityArea area;
private int zIndex;
public ProjectGraphNode(Area area) { public ProjectGraphNode(ContinuityArea area) {
super(); super();
this.area = area; this.area = area;
this.zIndex = area.getZIndex();
} }
public ProjectGraphNode(Point2D point) { public ProjectGraphNode(Point2D point, int zIndex) {
super(); super();
this.point = point; this.point = point;
this.zIndex = zIndex;
this.node = null; this.node = null;
} }
@@ -419,17 +474,22 @@ public class NetlistBuilder {
super(); super();
this.point = point; this.point = point;
this.node = node; this.node = node;
this.zIndex = node.getZIndex();
} }
public Point2D getPoint() { public Point2D getPoint() {
return point; return point;
} }
public int getZIndex() {
return zIndex;
}
public Node getNode() { public Node getNode() {
return node; return node;
} }
public Area getArea() { public ContinuityArea getArea() {
return area; return area;
} }
} }
@@ -34,11 +34,13 @@ public class Node implements Comparable<Node> {
private IDIYComponent<?> component; private IDIYComponent<?> component;
private int pointIndex; private int pointIndex;
private int zIndex;
public Node(IDIYComponent<?> component, int pointIndex) { public Node(IDIYComponent<?> component, int pointIndex, int zIndex) {
super(); super();
this.component = component; this.component = component;
this.pointIndex = pointIndex; this.pointIndex = pointIndex;
this.zIndex = zIndex;
} }
public IDIYComponent<?> getComponent() { public IDIYComponent<?> getComponent() {
@@ -49,6 +51,10 @@ public class Node implements Comparable<Node> {
return pointIndex; return pointIndex;
} }
public int getZIndex() {
return zIndex;
}
public String getDisplayName() { public String getDisplayName() {
String[] section = component.getSectionNames(pointIndex); String[] section = component.getSectionNames(pointIndex);
return (section == null || section.length > 1 ? "" : section[0] + ".") + component.getControlPointNodeName(pointIndex); return (section == null || section.length > 1 ? "" : section[0] + ".") + component.getControlPointNodeName(pointIndex);
@@ -29,6 +29,7 @@ public class ComponentArea implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private int zOrder;
private Area outlineArea; private Area outlineArea;
private Collection<Area> continuityPositiveAreas; private Collection<Area> continuityPositiveAreas;
private Collection<Area> continuityNegativeAreas; private Collection<Area> continuityNegativeAreas;
@@ -36,13 +37,18 @@ public class ComponentArea implements Serializable {
public ComponentArea() { public ComponentArea() {
} }
public ComponentArea(Area outlineArea, Collection<Area> continuityPositiveAreas, Collection<Area> continuityNegativeAreas) { public ComponentArea(int zOrder, Area outlineArea, Collection<Area> continuityPositiveAreas, Collection<Area> continuityNegativeAreas) {
super(); super();
this.zOrder = zOrder;
this.outlineArea = outlineArea; this.outlineArea = outlineArea;
this.continuityPositiveAreas = continuityPositiveAreas; this.continuityPositiveAreas = continuityPositiveAreas;
this.continuityNegativeAreas = continuityNegativeAreas; this.continuityNegativeAreas = continuityNegativeAreas;
} }
public int getZOrder() {
return zOrder;
}
public Area getOutlineArea() { public Area getOutlineArea() {
return outlineArea; return outlineArea;
} }
@@ -28,10 +28,12 @@ public class Connection implements Comparable<Connection> {
private Point2D p1; private Point2D p1;
private Point2D p2; private Point2D p2;
private int zIndex;
public Connection(Point2D p1, Point2D p2) { public Connection(Point2D p1, Point2D p2, int zIndex) {
this.p1 = p1.getX() < p2.getX() || p1.getX() == p2.getX() && p1.getY() < p2.getY() ? p1 : p2; this.p1 = p1.getX() < p2.getX() || p1.getX() == p2.getX() && p1.getY() < p2.getY() ? p1 : p2;
this.p2 = p1.getX() < p2.getX() || p1.getX() == p2.getX() && p1.getY() < p2.getY() ? p2 : p1; this.p2 = p1.getX() < p2.getX() || p1.getX() == p2.getX() && p1.getY() < p2.getY() ? p2 : p1;
this.zIndex = zIndex;
} }
public Point2D getP1() { public Point2D getP1() {
@@ -42,12 +44,17 @@ public class Connection implements Comparable<Connection> {
return p2; return p2;
} }
public int getZIndex() {
return zIndex;
}
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + ((p1 == null) ? 0 : p1.hashCode()); result = prime * result + ((p1 == null) ? 0 : p1.hashCode());
result = prime * result + ((p2 == null) ? 0 : p2.hashCode()); result = prime * result + ((p2 == null) ? 0 : p2.hashCode());
result = prime * result + zIndex;
return result; return result;
} }
@@ -70,6 +77,8 @@ public class Connection implements Comparable<Connection> {
return false; return false;
} else if (!p2.equals(other.p2)) } else if (!p2.equals(other.p2))
return false; return false;
if (zIndex != other.zIndex)
return false;
return true; return true;
} }
@@ -0,0 +1,52 @@
/*
*
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with DIYLC. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.presenter;
import java.awt.geom.Area;
import java.io.Serializable;
public class ContinuityArea implements Serializable {
private static final long serialVersionUID = 1L;
private int zIndex;
private int layerId;
private Area area;
public ContinuityArea() {}
public ContinuityArea(int zIndex, int layerId, Area area) {
super();
this.zIndex = zIndex;
this.layerId = layerId;
this.area = area;
}
public int getZIndex() {
return zIndex;
}
public Area getArea() {
return area;
}
public int getLayerId() {
return layerId;
}
}
@@ -29,6 +29,7 @@ import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.diylc.common.ComponentType; import org.diylc.common.ComponentType;
import org.diylc.core.ComponentForRender;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.Project; import org.diylc.core.Project;
@@ -63,14 +64,14 @@ public class DrawingCache {
* @param zoom * @param zoom
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void bulkPrepare(Map<IDIYComponent<?>, ComponentState> components, G2DWrapper g2d, boolean outlineMode, Project project, double zoom) { public void bulkPrepare(Collection<ComponentForRender> components, G2DWrapper g2d, boolean outlineMode, Project project, double zoom) {
// run all renders in parallel to utilize all CPU cores // run all renders in parallel to utilize all CPU cores
components.entrySet().stream().parallel().forEach(x -> { components.stream().parallel().forEach(x -> {
ComponentType type = ComponentProcessor.getInstance() ComponentType type = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) x.getKey().getClass()); .extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) x.getComponent().getClass());
if (type != null && type.getEnableCache()) { if (type != null && type.getEnableCache()) {
renderAndCache(x.getKey(), g2d, x.getValue(), outlineMode, project, zoom); renderAndCache(x.getComponent(), g2d, x.getState(), outlineMode, project, zoom, x.getZOrder());
} }
}); });
} }
@@ -88,7 +89,7 @@ public class DrawingCache {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void draw(IDIYComponent<?> component, G2DWrapper g2d, ComponentState componentState, public void draw(IDIYComponent<?> component, G2DWrapper g2d, ComponentState componentState,
boolean outlineMode, Project project, double zoom, Rectangle2D visibleRect) { boolean outlineMode, Project project, double zoom, int zOrder, Rectangle2D visibleRect) {
ComponentType type = ComponentProcessor.getInstance() ComponentType type = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass()); .extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
@@ -96,7 +97,7 @@ public class DrawingCache {
// if we need to apply caching // if we need to apply caching
Point2D firstPoint = component.getControlPoint(0); Point2D firstPoint = component.getControlPoint(0);
CacheValue value = renderAndCache(component, g2d, componentState, outlineMode, project, zoom); CacheValue value = renderAndCache(component, g2d, componentState, outlineMode, project, zoom, zOrder);
if (value == null) { if (value == null) {
return; return;
@@ -175,7 +176,7 @@ public class DrawingCache {
} }
private CacheValue renderAndCache(IDIYComponent<?> component, G2DWrapper g2d, ComponentState componentState, private CacheValue renderAndCache(IDIYComponent<?> component, G2DWrapper g2d, ComponentState componentState,
boolean outlineMode, Project project, double zoom) { boolean outlineMode, Project project, double zoom, int zOrder) {
// if we need to apply caching // if we need to apply caching
Point2D firstPoint = component.getControlPoint(0); Point2D firstPoint = component.getControlPoint(0);
@@ -225,7 +226,7 @@ public class DrawingCache {
G2DWrapper wrapper = new G2DWrapper(cg2d, zoom); G2DWrapper wrapper = new G2DWrapper(cg2d, zoom);
// initialize wrapper // initialize wrapper
wrapper.startedDrawingComponent(); wrapper.startedDrawingComponent(zOrder);
long componentStart = System.nanoTime(); long componentStart = System.nanoTime();
@@ -39,6 +39,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -52,9 +53,11 @@ import org.diylc.common.GridType;
import org.diylc.common.IComponentFilter; import org.diylc.common.IComponentFilter;
import org.diylc.common.IPlugInPort; import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache; import org.diylc.common.ObjectCache;
import org.diylc.core.ComponentForRender;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IContinuity; import org.diylc.core.IContinuity;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.ILayer;
import org.diylc.core.Project; import org.diylc.core.Project;
import org.diylc.core.Theme; import org.diylc.core.Theme;
import org.diylc.core.VisibilityPolicy; import org.diylc.core.VisibilityPolicy;
@@ -136,7 +139,7 @@ public class DrawingManager {
configManager.writeValue(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME); configManager.writeValue(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
} }
componentAreaMap = new HashMap<IDIYComponent<?>, ComponentArea>(); componentAreaMap = new LinkedHashMap<IDIYComponent<?>, ComponentArea>();
lastDrawnStateMap = new HashMap<IDIYComponent<?>, ComponentState>(); lastDrawnStateMap = new HashMap<IDIYComponent<?>, ComponentState>();
String debugComponentAreasStr = System.getProperty(DEBUG_COMPONENT_AREAS); String debugComponentAreasStr = System.getProperty(DEBUG_COMPONENT_AREAS);
debugComponentAreas = debugComponentAreas =
@@ -252,9 +255,10 @@ public class DrawingManager {
// g2dWrapper.resetTx(); // g2dWrapper.resetTx();
// put components and their states in a map // put components and their states in a map
Map<IDIYComponent<?>, ComponentState> componentStateMap = Map<IDIYComponent<?>, ComponentState> componentStateMap = new HashMap<IDIYComponent<?>, ComponentState>();
new HashMap<IDIYComponent<?>, ComponentState>(); List<ComponentForRender> componentForRenderList = new ArrayList<ComponentForRender>();
for (IDIYComponent<?> component : project.getComponents()) { for (int i = 0; i < project.getComponents().size(); i++) {
IDIYComponent<?> component = project.getComponents().get(i);
ComponentState state = ComponentState.NORMAL; ComponentState state = ComponentState.NORMAL;
if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) { if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) {
if (dragInProgress) { if (dragInProgress) {
@@ -264,17 +268,21 @@ public class DrawingManager {
} }
} }
componentStateMap.put(component, state); componentStateMap.put(component, state);
componentForRenderList.add(new ComponentForRender(component, state, i));
} }
boolean outlineMode = drawOptions.contains(DrawOption.OUTLINE_MODE); boolean outlineMode = drawOptions.contains(DrawOption.OUTLINE_MODE);
// give the cache a chance to prepare all the components in multiple threads // give the cache a chance to prepare all the components in multiple threads
if (drawOptions.contains(DrawOption.ENABLE_CACHING)) { if (drawOptions.contains(DrawOption.ENABLE_CACHING)) {
DrawingCache.Instance.bulkPrepare(componentStateMap, g2dWrapper, outlineMode, project, zoom); DrawingCache.Instance.bulkPrepare(componentForRenderList, g2dWrapper, outlineMode, project, zoom);
} }
// componentAreaMap.clear(); // componentAreaMap.clear();
for (IDIYComponent<?> component : project.getComponents()) {
for (int i = 0; i < project.getComponents().size(); i++) {
IDIYComponent<?> component = project.getComponents().get(i);
// Do not draw the component if it's filtered out. // Do not draw the component if it's filtered out.
if (filter != null && !filter.testComponent(component)) { if (filter != null && !filter.testComponent(component)) {
continue; continue;
@@ -285,7 +293,7 @@ public class DrawingManager {
boolean trackArea = lastDrawnStateMap.get(component) != state; boolean trackArea = lastDrawnStateMap.get(component) != state;
synchronized (g2d) { synchronized (g2d) {
g2dWrapper.startedDrawingComponent(); g2dWrapper.startedDrawingComponent(i);
if (!trackArea) { if (!trackArea) {
g2dWrapper.stopTracking(); g2dWrapper.stopTracking();
} }
@@ -300,7 +308,7 @@ public class DrawingManager {
if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache
DrawingCache.Instance.draw(component, g2dWrapper, state, DrawingCache.Instance.draw(component, g2dWrapper, state,
drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, visibleRect); drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, i, visibleRect);
else // go straight to the wrapper else // go straight to the wrapper
component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
@@ -386,7 +394,7 @@ public class DrawingManager {
// Draw component slot in a separate composite. // Draw component slot in a separate composite.
if (componentSlot != null) { if (componentSlot != null) {
g2dWrapper.startedDrawingComponent(); g2dWrapper.startedDrawingComponent(project.getComponents().size());
g2dWrapper.setComposite(slotComposite); g2dWrapper.setComposite(slotComposite);
for (IDIYComponent<?> component : componentSlot) { for (IDIYComponent<?> component : componentSlot) {
try { try {
@@ -684,77 +692,100 @@ public class DrawingManager {
messageDispatcher.dispatchMessage(EventType.REPAINT); messageDispatcher.dispatchMessage(EventType.REPAINT);
} }
public void findContinuityAreaAtPoint(Project project, Point2D p) { public void findContinuityAreaAtPoint(Point2D p) {
if (continuityGraphCache == null) if (continuityGraphCache == null)
continuityGraphCache = getContinuityGraph(project); continuityGraphCache = getContinuityGraph();
currentContinuityAreas = continuityGraphCache.findAreasFor(p); currentContinuityAreas = continuityGraphCache.findAreasFor(p);
} }
public List<Area> getContinuityAreas(Project project) { public List<ContinuityArea> getContinuityAreas() {
// Find all individual continuity areas for all components // Find all individual continuity areas for all components
List<Area> preliminaryAreas = new ArrayList<Area>(); List<ContinuityArea> preliminaryAreas = new ArrayList<ContinuityArea>();
List<Boolean> checkBreakout = new ArrayList<Boolean>(); List<Boolean> checkBreakout = new ArrayList<Boolean>();
for (IDIYComponent<?> c : project.getComponents()) { for (Map.Entry<IDIYComponent<?>, ComponentArea> entry : this.componentAreaMap.entrySet()) {
ComponentArea a = getComponentArea(c); ComponentArea a = entry.getValue();
if (a == null || a.getOutlineArea() == null) if (a == null || a.getOutlineArea() == null)
continue; continue;
IDIYComponent<?> component = entry.getKey();
int layerId;
if (component instanceof ILayer) {
layerId = ((ILayer)component).getLayerId();
} else {
layerId = component.hashCode();
}
Collection<Area> positiveAreas = a.getContinuityPositiveAreas(); Collection<Area> positiveAreas = a.getContinuityPositiveAreas();
if (positiveAreas != null) if (positiveAreas != null) {
for (Area a1 : positiveAreas) { for (Area a1 : positiveAreas) {
preliminaryAreas.add(a1); preliminaryAreas.add(new ContinuityArea(a.getZOrder(), layerId, a1));
checkBreakout.add(false); checkBreakout.add(false);
} }
}
Collection<Area> negativeAreas = a.getContinuityNegativeAreas(); Collection<Area> negativeAreas = a.getContinuityNegativeAreas();
if (negativeAreas != null) { if (negativeAreas != null) {
for (Area na : negativeAreas) for (Area na : negativeAreas) {
for (int i = 0; i < preliminaryAreas.size(); i++) { for (int i = 0; i < preliminaryAreas.size(); i++) {
Area a1 = preliminaryAreas.get(i); ContinuityArea a1 = preliminaryAreas.get(i);
if (a1.intersects(na.getBounds2D())) { if (a1.getLayerId() == layerId && a1.getArea().intersects(na.getBounds2D())) {
a1.subtract(na); a1.getArea().subtract(na);
checkBreakout.set(i, true); checkBreakout.set(i, true);
} }
} }
} }
} }
}
// Check if we need to break some areas out in case they are interrupted // Check if we need to break some areas out in case they are interrupted
List<Area> areas = new ArrayList<Area>(); List<ContinuityArea> areas = new ArrayList<ContinuityArea>();
for (int i = 0; i < preliminaryAreas.size(); i++) { for (int i = 0; i < preliminaryAreas.size(); i++) {
Area a = preliminaryAreas.get(i); ContinuityArea a = preliminaryAreas.get(i);
if (checkBreakout.get(i)) if (checkBreakout.get(i)) {
areas.addAll(AreaUtils.tryAreaBreakout(a)); List<Area> breakoutAreas = AreaUtils.tryAreaBreakout(a.getArea());
else if (breakoutAreas.size() == 1) {
areas.add(a);
} else {
areas.addAll(breakoutAreas.stream()
.map(area -> new ContinuityArea(a.getZIndex(), a.getLayerId(), area))
.collect(Collectors.toList()));
}
} else
areas.add(a); areas.add(a);
} }
return areas; return areas;
} }
public ContinuityGraph getContinuityGraph(Project project) { public ContinuityGraph getContinuityGraph() {
Set<Connection> connections = new HashSet<Connection>(); Set<Connection> connections = new HashSet<Connection>();
for (IDIYComponent<?> c : project.getComponents()) { int z = 0;
for (IDIYComponent<?> c : this.componentAreaMap.keySet()) {
if (c instanceof IContinuity) { if (c instanceof IContinuity) {
for (int i = 0; i < c.getControlPointCount() - 1; i++) for (int i = 0; i < c.getControlPointCount() - 1; i++)
for (int j = i + 1; j < c.getControlPointCount(); j++) for (int j = i + 1; j < c.getControlPointCount(); j++)
if (((IContinuity) c).arePointsConnected(i, j)) if (((IContinuity) c).arePointsConnected(i, j))
connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j))); connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j), z));
} }
z++;
} }
List<Area> areas = getContinuityAreas(project); List<ContinuityArea> areas = getContinuityAreas();
return NetlistBuilder.buildContinuityGraph(areas, connections); return NetlistBuilder.buildContinuityGraph(areas, connections);
} }
public List<Area> getContinuityAreaProximity(Project project, float threshold) { public List<Area> getContinuityAreaProximity(float threshold) {
List<Area> continuityAreas = getContinuityAreas(project); List<ContinuityArea> continuityAreas = getContinuityAreas();
Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for
// approx good results // approx good results
List<Area> expanded = continuityAreas.parallelStream() List<Area> expanded = continuityAreas.parallelStream()
.map((a) -> new Area(s.createStrokedShape(a))).collect(Collectors.toList()); .map((a) -> new Area(s.createStrokedShape(a.getArea()))).collect(Collectors.toList());
List<Area> intersections = new ArrayList<Area>(); List<Area> intersections = new ArrayList<Area>();
for (int i = 0; i < expanded.size() - 1; i++) { for (int i = 0; i < expanded.size() - 1; i++) {
for (int j = i + 1; j < expanded.size(); j++) { for (int j = i + 1; j < expanded.size(); j++) {
@@ -104,6 +104,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
private Shape lastShape; private Shape lastShape;
private double zoom; private double zoom;
private int zOrder;
/** /**
* Creates a wrapper around specified {@link Graphics2D} object. * Creates a wrapper around specified {@link Graphics2D} object.
@@ -127,22 +128,25 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
/** /**
* Clears out the current area and caches canvas settings. * Clears out the current area and caches canvas settings.
*
* @param zOrder
*/ */
public void startedDrawingComponent() { public void startedDrawingComponent(int zOrder) {
drawingComponent = true; this.zOrder = zOrder;
currentArea = new Area(); this.drawingComponent = true;
continuityPositiveAreas = new HashMap<String, Area>(); this.currentArea = new Area();
continuityNegativeAreas = new HashMap<String, Area>(); this.continuityPositiveAreas = new HashMap<String, Area>();
this.continuityNegativeAreas = new HashMap<String, Area>();
// trackingStacks.clear(); // trackingStacks.clear();
continuityMarker = null; this.continuityMarker = null;
originalStroke = canvasGraphics.getStroke(); this.originalStroke = canvasGraphics.getStroke();
originalColor = canvasGraphics.getColor(); this.originalColor = canvasGraphics.getColor();
originalTx = canvasGraphics.getTransform(); this.originalTx = canvasGraphics.getTransform();
originalComposite = canvasGraphics.getComposite(); this.originalComposite = canvasGraphics.getComposite();
originalFont = canvasGraphics.getFont(); this.originalFont = canvasGraphics.getFont();
currentTx = new AffineTransform(); this.currentTx = new AffineTransform();
initialTx = canvasGraphics.getTransform(); this.initialTx = canvasGraphics.getTransform();
lastShape = null; this.lastShape = null;
startTracking(); startTracking();
} }
@@ -159,7 +163,8 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
canvasGraphics.setComposite(originalComposite); canvasGraphics.setComposite(originalComposite);
canvasGraphics.setFont(originalFont); canvasGraphics.setFont(originalFont);
// Map<String, Integer> sorted = sortByComparator(trackingStacks, false); // Map<String, Integer> sorted = sortByComparator(trackingStacks, false);
return new ComponentArea(currentArea, new ArrayList<Area>(continuityPositiveAreas.values()), new ArrayList<Area>(continuityNegativeAreas.values())); return new ComponentArea(zOrder,currentArea, new ArrayList<Area>(continuityPositiveAreas.values()),
new ArrayList<Area>(continuityNegativeAreas.values()));
} }
// start - used for caching // start - used for caching
@@ -680,7 +680,7 @@ public class Presenter implements IPlugInPort {
projectFileManager.notifyFileChange(); projectFileManager.notifyFileChange();
} }
} else if (configManager.readBoolean(HIGHLIGHT_CONTINUITY_AREA, false) || altDown) { } else if (configManager.readBoolean(HIGHLIGHT_CONTINUITY_AREA, false) || altDown) {
drawingManager.findContinuityAreaAtPoint(currentProject, scaledPoint); drawingManager.findContinuityAreaAtPoint(scaledPoint);
messageDispatcher.dispatchMessage(EventType.REPAINT); messageDispatcher.dispatchMessage(EventType.REPAINT);
} else { } else {
List<IDIYComponent<?>> newSelection = new ArrayList<IDIYComponent<?>>(selectedComponents); List<IDIYComponent<?>> newSelection = new ArrayList<IDIYComponent<?>>(selectedComponents);
@@ -2809,7 +2809,7 @@ public class Presenter implements IPlugInPort {
@Override @Override
public List<Netlist> extractNetlists(boolean includeSwitches) throws NetlistException { public List<Netlist> extractNetlists(boolean includeSwitches) throws NetlistException {
List<Area> continuityAreas = drawingManager.getContinuityAreas(currentProject); List<ContinuityArea> continuityAreas = drawingManager.getContinuityAreas();
return NetlistBuilder.extractNetlists(includeSwitches, currentProject, continuityAreas); return NetlistBuilder.extractNetlists(includeSwitches, currentProject, continuityAreas);
} }
@@ -2874,11 +2874,12 @@ public class Presenter implements IPlugInPort {
ComponentArea componentArea = drawingManager.getComponentArea(c); ComponentArea componentArea = drawingManager.getComponentArea(c);
areas.put(c, componentArea); areas.put(c, componentArea);
} }
return new Snapshot(currentProject.clone(), new HashSet<IDIYComponent<?>>(selectedComponents), areas, new HashSet<Area>(drawingManager.getContinuityAreas(currentProject))); return new Snapshot(currentProject.clone(), new HashSet<IDIYComponent<?>>(selectedComponents), areas,
new HashSet<ContinuityArea>(drawingManager.getContinuityAreas()));
} }
@Override @Override
public List<Area> checkContinuityAreaProximity(Size threshold) { public List<Area> checkContinuityAreaProximity(Size threshold) {
return drawingManager.getContinuityAreaProximity(this.currentProject, (float)threshold.convertToPixels()); return drawingManager.getContinuityAreaProximity((float)threshold.convertToPixels());
} }
} }
@@ -4158,6 +4158,10 @@
<changeType>BUG_FIX</changeType> <changeType>BUG_FIX</changeType>
<description>Fixed an issue that would cause capacitors to fail when creating, editing, moving or deleting</description> <description>Fixed an issue that would cause capacitors to fail when creating, editing, moving or deleting</description>
</org.diylc.appframework.update.Change> </org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Circuit analysis algorithm that supports netlist extraction, 'Highlight connected Areas' and 'Guitar Wiring Analyzer' features improved to take PCB trace layer and component z-order into account for better accuracy</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change> <org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType> <changeType>NEW_FEATURE</changeType>
<description>Added standard models in the drop down menu of the component toolbar</description> <description>Added standard models in the drop down menu of the component toolbar</description>
@@ -1,12 +1,12 @@
package org.diylc.test; package org.diylc.test;
import java.awt.geom.Area;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.Project; import org.diylc.core.Project;
import org.diylc.presenter.ComponentArea; import org.diylc.presenter.ComponentArea;
import org.diylc.presenter.ContinuityArea;
public class Snapshot implements Serializable { public class Snapshot implements Serializable {
@@ -15,13 +15,13 @@ public class Snapshot implements Serializable {
private Project project; private Project project;
private Set<IDIYComponent<?>> selection; private Set<IDIYComponent<?>> selection;
private Map<IDIYComponent<?>, ComponentArea> areas; private Map<IDIYComponent<?>, ComponentArea> areas;
private Set<Area> continuityAreas; private Set<ContinuityArea> continuityAreas;
public Snapshot() { public Snapshot() {
} }
public Snapshot(Project project, Set<IDIYComponent<?>> selection, public Snapshot(Project project, Set<IDIYComponent<?>> selection,
Map<IDIYComponent<?>, ComponentArea> areas, Set<Area> continuityAreas) { Map<IDIYComponent<?>, ComponentArea> areas, Set<ContinuityArea> continuityAreas) {
super(); super();
this.project = project; this.project = project;
this.selection = selection; this.selection = selection;
@@ -41,7 +41,7 @@ public class Snapshot implements Serializable {
return areas; return areas;
} }
public Set<Area> getContinuityAreas() { public Set<ContinuityArea> getContinuityAreas() {
return continuityAreas; return continuityAreas;
} }
Binary file not shown.
@@ -30,6 +30,7 @@ import org.diylc.components.transform.AbstractBoardTransformer;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver; import org.diylc.core.IDrawingObserver;
import org.diylc.core.ILayer;
import org.diylc.core.Project; import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy; import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.ComponentDescriptor;
@@ -44,7 +45,7 @@ import org.diylc.utils.Constants;
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board with solder pads", zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board with solder pads",
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG,
keywordTag = "Perf Board", transformer = AbstractBoardTransformer.class, enableCache = true) keywordTag = "Perf Board", transformer = AbstractBoardTransformer.class, enableCache = true)
public class PerfBoard extends AbstractBoard { public class PerfBoard extends AbstractBoard implements ILayer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -121,6 +122,11 @@ public class PerfBoard extends AbstractBoard {
return null; return null;
} }
@Override
public int getLayerId() {
return 1;
}
@Override @Override
public void drawIcon(Graphics2D g2d, int width, int height) { public void drawIcon(Graphics2D g2d, int width, int height) {
int factor = 32 / width; int factor = 32 / width;
@@ -31,6 +31,7 @@ import org.diylc.components.transform.VeroBoardTransformer;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver; import org.diylc.core.IDrawingObserver;
import org.diylc.core.ILayer;
import org.diylc.core.Project; import org.diylc.core.Project;
import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty; import org.diylc.core.annotations.EditableProperty;
@@ -41,7 +42,7 @@ import org.diylc.utils.Constants;
instanceNamePrefix = "Board", instanceNamePrefix = "Board",
description = "Perforated FR4 board with copper strips connecting 3 holes in a row (aka TriPad Board)", description = "Perforated FR4 board with copper strips connecting 3 holes in a row (aka TriPad Board)",
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, transformer = VeroBoardTransformer.class, enableCache = true) keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, transformer = VeroBoardTransformer.class, enableCache = true)
public class TriPadBoard extends AbstractVeroBoard { public class TriPadBoard extends AbstractVeroBoard implements ILayer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -203,4 +204,9 @@ public class TriPadBoard extends AbstractVeroBoard {
public String getControlPointNodeName(int index) { public String getControlPointNodeName(int index) {
return null; return null;
} }
@Override
public int getLayerId() {
return 1;
}
} }
@@ -31,6 +31,7 @@ import org.diylc.components.transform.VeroBoardTransformer;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver; import org.diylc.core.IDrawingObserver;
import org.diylc.core.ILayer;
import org.diylc.core.Project; import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy; import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.ComponentDescriptor;
@@ -42,7 +43,7 @@ import org.diylc.utils.Constants;
description = "Perforated FR4 board with copper strips connecting all holes in a row", description = "Perforated FR4 board with copper strips connecting all holes in a row",
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
transformer = VeroBoardTransformer.class, enableCache = true) transformer = VeroBoardTransformer.class, enableCache = true)
public class VeroBoard extends AbstractVeroBoard { public class VeroBoard extends AbstractVeroBoard implements ILayer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -110,7 +111,10 @@ public class VeroBoard extends AbstractVeroBoard {
} }
} }
@Override
public int getLayerId() {
return 1;
}
@Override @Override
public String getControlPointNodeName(int index) { public String getControlPointNodeName(int index) {
@@ -27,6 +27,7 @@ import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.CreationMethod; import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.ILayer;
import org.diylc.core.VisibilityPolicy; import org.diylc.core.VisibilityPolicy;
import org.diylc.core.annotations.BomPolicy; import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.ComponentDescriptor;
@@ -40,7 +41,7 @@ import org.diylc.core.measures.SizeUnit;
description = "Straight copper trace", zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW, description = "Straight copper trace", zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW,
autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB", autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB",
transformer = SimpleComponentTransformer.class) transformer = SimpleComponentTransformer.class)
public class CopperTrace extends AbstractLeadedComponent<Void> { public class CopperTrace extends AbstractLeadedComponent<Void> implements ILayer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -100,6 +101,11 @@ public class CopperTrace extends AbstractLeadedComponent<Void> {
this.layer = layer; this.layer = layer;
} }
@Override
public int getLayerId() {
return getLayer().getId();
}
@Override @Override
protected float getLeadThickness() { protected float getLeadThickness() {
return (float) getThickness().convertToPixels(); return (float) getThickness().convertToPixels();
@@ -28,6 +28,7 @@ import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver; import org.diylc.core.IDrawingObserver;
import org.diylc.core.ILayer;
import org.diylc.core.annotations.BomPolicy; import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty; import org.diylc.core.annotations.EditableProperty;
@@ -40,7 +41,7 @@ import org.diylc.core.measures.SizeUnit;
zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false, zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB", transformer = SimpleComponentTransformer.class, keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB", transformer = SimpleComponentTransformer.class,
enableCache = true) enableCache = true)
public class CurvedTrace extends AbstractCurvedComponent<Void> { public class CurvedTrace extends AbstractCurvedComponent<Void> implements ILayer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -89,6 +90,11 @@ public class CurvedTrace extends AbstractCurvedComponent<Void> {
this.layer = layer; this.layer = layer;
} }
@Override
public int getLayerId() {
return getLayer().getId();
}
@Override @Override
public Void getValue() { public Void getValue() {
return null; return null;
@@ -32,6 +32,7 @@ import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver; import org.diylc.core.IDrawingObserver;
import org.diylc.core.ILayer;
import org.diylc.core.Project; import org.diylc.core.Project;
import org.diylc.core.VisibilityPolicy; import org.diylc.core.VisibilityPolicy;
import org.diylc.core.annotations.BomPolicy; import org.diylc.core.annotations.BomPolicy;
@@ -43,7 +44,7 @@ import org.diylc.core.measures.SizeUnit;
@ComponentDescriptor(name = "Ground Fill", author = "Branislav Stojkovic", category = "Connectivity", @ComponentDescriptor(name = "Ground Fill", author = "Branislav Stojkovic", category = "Connectivity",
instanceNamePrefix = "GF", description = "Polygonal ground fill area", zOrder = IDIYComponent.TRACE, instanceNamePrefix = "GF", description = "Polygonal ground fill area", zOrder = IDIYComponent.TRACE,
bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false, transformer = SimpleComponentTransformer.class) bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false, transformer = SimpleComponentTransformer.class)
public class GroundFill extends AbstractComponent<Void> { public class GroundFill extends AbstractComponent<Void> implements ILayer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -149,6 +150,10 @@ public class GroundFill extends AbstractComponent<Void> {
this.layer = layer; this.layer = layer;
} }
@Override
public int getLayerId() {
return getLayer().getId();
}
@Deprecated @Deprecated
@Override @Override
@@ -28,6 +28,7 @@ import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver; import org.diylc.core.IDrawingObserver;
import org.diylc.core.ILayer;
import org.diylc.core.Project; import org.diylc.core.Project;
import org.diylc.core.VisibilityPolicy; import org.diylc.core.VisibilityPolicy;
import org.diylc.core.annotations.BomPolicy; import org.diylc.core.annotations.BomPolicy;
@@ -44,7 +45,7 @@ import org.diylc.utils.Constants;
description = "Copper solder pad, round or square", instanceNamePrefix = "Pad", description = "Copper solder pad, round or square", instanceNamePrefix = "Pad",
zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false, zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB", transformer = SimpleComponentTransformer.class, enableCache = true) keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB", transformer = SimpleComponentTransformer.class, enableCache = true)
public class SolderPad extends AbstractComponent<Void> { public class SolderPad extends AbstractComponent<Void> implements ILayer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -129,6 +130,11 @@ public class SolderPad extends AbstractComponent<Void> {
this.layer = layer; this.layer = layer;
} }
@Override
public int getLayerId() {
return getLayer().getId();
}
@Override @Override
public String getName() { public String getName() {
return super.getName(); return super.getName();
@@ -93,7 +93,7 @@ public class SpiceAnalyzer extends NetlistAnalyzer implements INetlistAnalyzer {
if (c instanceof ISpiceMapper) if (c instanceof ISpiceMapper)
pointIndex = ((ISpiceMapper)c).mapToSpiceNode(pointIndex); pointIndex = ((ISpiceMapper)c).mapToSpiceNode(pointIndex);
int nodeIndex = find(new Node(c, pointIndex), groups); int nodeIndex = find(new Node(c, pointIndex, 0), groups);
if (nodeIndex < 0) if (nodeIndex < 0)
nodeIndex = unconnectedIndex++; nodeIndex = unconnectedIndex++;
Binary file not shown.
Binary file not shown.