mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-07 12:24:58 +10:00
Refactor netlist calculation
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
<classpathentry kind="lib" path="lib/java-http-proxy.jar"/>
|
||||
<classpathentry kind="lib" path="lib/AppleJavaExtensions.jar"/>
|
||||
<classpathentry kind="lib" path="lib/xstream-1.4.19.jar"/>
|
||||
<classpathentry kind="lib" path="lib/guava-31.1-jre.jar"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
Binary file not shown.
@@ -33,6 +33,7 @@ import java.util.Arrays;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.measures.Size;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link IDIYComponent} that contains component name and toString.
|
||||
@@ -98,6 +99,17 @@ public abstract class AbstractComponent<T> implements IDIYComponent<T> {
|
||||
*/
|
||||
protected int getClosestOdd(double x) {
|
||||
return ((int) x / 2) * 2 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest odd number, i.e. x when x is odd, or x + 1 when x is even.
|
||||
*
|
||||
* @param x
|
||||
* @return
|
||||
*/
|
||||
protected int getClosestOdd(Size s) {
|
||||
double x = s.convertToPixels();
|
||||
return ((int) x / 2) * 2 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
*
|
||||
* 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.netlist;
|
||||
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.diylc.utils.RTree;
|
||||
|
||||
/***
|
||||
* A graph containing continuity areas sorted into groups that are connected together.
|
||||
*
|
||||
* @author bancika
|
||||
*
|
||||
*/
|
||||
public class ContinuityGraph {
|
||||
|
||||
private RTree<ContinuityNode> groupTree;
|
||||
private Map<Integer, List<Area>> areaGroupMap;
|
||||
|
||||
public ContinuityGraph(RTree<ContinuityNode> groupTree, Map<Integer, List<Area>> areaGroupMap) {
|
||||
super();
|
||||
this.groupTree = groupTree;
|
||||
this.areaGroupMap = areaGroupMap;
|
||||
}
|
||||
|
||||
public List<Area> findAreasFor(Point2D point) {
|
||||
Optional<ContinuityNode> first = groupTree.search(point, NetlistBuilder.eps).stream()
|
||||
.filter(node -> node.getArea().contains(point)).findFirst();
|
||||
|
||||
return first.map(node -> areaGroupMap.get(node.getGroupId())).orElse(null);
|
||||
}
|
||||
|
||||
public static class ContinuityNode {
|
||||
private Area area;
|
||||
private Integer groupId;
|
||||
|
||||
public ContinuityNode(Area area, Integer groupId) {
|
||||
super();
|
||||
this.area = area;
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public Area getArea() {
|
||||
return area;
|
||||
}
|
||||
|
||||
public Integer getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
package org.diylc.netlist;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -40,6 +41,10 @@ public class Group implements Comparable<Group> {
|
||||
public Group() {
|
||||
|
||||
}
|
||||
|
||||
public Group(Collection<Node> nodes) {
|
||||
this.nodes = new HashSet<Node>(nodes);
|
||||
}
|
||||
|
||||
public Group(Node node1, Node node2) {
|
||||
nodes.add(node1);
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
*
|
||||
* 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.netlist;
|
||||
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.netlist.ContinuityGraph.ContinuityNode;
|
||||
import org.diylc.presenter.Connection;
|
||||
import org.diylc.utils.RTree;
|
||||
import com.google.common.graph.GraphBuilder;
|
||||
import com.google.common.graph.ImmutableGraph;
|
||||
import com.google.common.graph.ImmutableGraph.Builder;
|
||||
import com.google.common.graph.Traverser;
|
||||
|
||||
public class NetlistBuilder {
|
||||
|
||||
public static final float eps = 4; // consider any nodes closer than this as connected
|
||||
|
||||
public static Netlist buildNetlist(List<IDIYComponent<?>> components, Collection<Node> nodes,
|
||||
Collection<Area> continuityAreas, Collection<Connection> connections) {
|
||||
ImmutableGraph<ProjectGraphNode> graph = buildGraph(nodes, continuityAreas, connections);
|
||||
|
||||
Traverser<ProjectGraphNode> traverser = Traverser.forGraph(graph);
|
||||
Set<ProjectGraphNode> visited = new HashSet<ProjectGraphNode>();
|
||||
|
||||
ProjectGraphNode[] nodeArr = graph.nodes().toArray(new ProjectGraphNode[0]);
|
||||
|
||||
Netlist netlist = new Netlist(components);
|
||||
|
||||
while (true) {
|
||||
|
||||
int i = 0;
|
||||
while (i < nodeArr.length && visited.contains(nodeArr[i]))
|
||||
i++;
|
||||
|
||||
if (i == graph.nodes().size())
|
||||
break;
|
||||
|
||||
// build a group of connected nodes
|
||||
Group g = new Group();
|
||||
traverser.breadthFirst(nodeArr[i]).forEach(node -> {
|
||||
visited.add(node);
|
||||
if (node.getNode() != null) {
|
||||
g.getNodes().add(node.getNode());
|
||||
}
|
||||
});
|
||||
if (g.getNodes().size() > 1) {
|
||||
netlist.add(g);
|
||||
}
|
||||
}
|
||||
|
||||
return netlist;
|
||||
}
|
||||
|
||||
public static ContinuityGraph buildContinuityGraph(Collection<Area> continuityAreas, Collection<Connection> connections) {
|
||||
ImmutableGraph<ProjectGraphNode> graph = buildGraph(new ArrayList<Node>(), continuityAreas, connections);
|
||||
|
||||
Traverser<ProjectGraphNode> traverser = Traverser.forGraph(graph);
|
||||
Set<ProjectGraphNode> visited = new HashSet<ProjectGraphNode>();
|
||||
|
||||
ProjectGraphNode[] nodeArr = graph.nodes().toArray(new ProjectGraphNode[0]);
|
||||
|
||||
int groupId = 1;
|
||||
|
||||
RTree<ContinuityNode> groupTree = new RTree<ContinuityGraph.ContinuityNode>();
|
||||
Map<Integer, List<Area>> areaGroupMap = new HashMap<Integer, List<Area>>();
|
||||
|
||||
while (true) {
|
||||
|
||||
int i = 0;
|
||||
while (i < nodeArr.length && visited.contains(nodeArr[i]))
|
||||
i++;
|
||||
|
||||
if (i == graph.nodes().size())
|
||||
break;
|
||||
|
||||
final int finalGroupId = groupId;
|
||||
|
||||
List<Area> areaList = new ArrayList<Area>();
|
||||
traverser.breadthFirst(nodeArr[i]).forEach(node -> {
|
||||
visited.add(node);
|
||||
if (node.getArea() != null) {
|
||||
areaList.add(node.getArea());
|
||||
groupTree.insert(node.getArea(), new ContinuityNode(node.getArea(), finalGroupId));
|
||||
}
|
||||
});
|
||||
|
||||
if (!areaList.isEmpty()) {
|
||||
areaGroupMap.put(groupId, areaList);
|
||||
groupId++;
|
||||
}
|
||||
}
|
||||
|
||||
return new ContinuityGraph(groupTree, areaGroupMap);
|
||||
}
|
||||
|
||||
/****
|
||||
* 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
|
||||
* proximity or intersection.
|
||||
*
|
||||
* @param nodes
|
||||
* @param continuityAreas
|
||||
* @param connections
|
||||
* @return
|
||||
*/
|
||||
private static ImmutableGraph<ProjectGraphNode> buildGraph(Collection<Node> nodes,
|
||||
Collection<Area> continuityAreas, Collection<Connection> connections) {
|
||||
Map<Point2D, List<ProjectGraphNode>> nodeMap = new HashMap<Point2D, List<ProjectGraphNode>>();
|
||||
|
||||
RTree<ProjectGraphNode> nodeTree = new RTree<ProjectGraphNode>();
|
||||
RTree<ProjectGraphNode> areaTree = new RTree<ProjectGraphNode>();
|
||||
|
||||
Builder<ProjectGraphNode> builder = GraphBuilder.undirected().<ProjectGraphNode>immutable();
|
||||
|
||||
for (Node node : nodes) {
|
||||
Point2D p = snapToEps(node.getPoint2D(), eps);
|
||||
ProjectGraphNode graphNode = new ProjectGraphNode(p, node);
|
||||
builder.addNode(graphNode);
|
||||
nodeTree.insert(new float[] {(float) (p.getX() - eps), (float) (p.getY() - eps)},
|
||||
new float[] {eps * 2, eps * 2}, graphNode);
|
||||
nodeMap.computeIfAbsent(p, (point) -> new ArrayList<ProjectGraphNode>())
|
||||
.add(new ProjectGraphNode(p, node));
|
||||
}
|
||||
|
||||
for (Connection conn : connections) {
|
||||
Point2D p1 = snapToEps(conn.getP1(), eps);
|
||||
Point2D p2 = snapToEps(conn.getP2(), eps);
|
||||
|
||||
ProjectGraphNode graphNode1 = new ProjectGraphNode(p1);
|
||||
ProjectGraphNode graphNode2 = new ProjectGraphNode(p2);
|
||||
|
||||
nodeTree.insert(new float[] {(float) p1.getX() - eps, (float) p1.getY() - eps},
|
||||
new float[] {eps * 2, eps * 2}, graphNode1);
|
||||
nodeTree.insert(new float[] {(float) p2.getX() - eps, (float) p2.getY() - eps},
|
||||
new float[] {eps * 2, eps * 2}, graphNode2);
|
||||
|
||||
builder.addNode(graphNode1);
|
||||
builder.addNode(graphNode2);
|
||||
builder.putEdge(graphNode1, graphNode2);
|
||||
|
||||
nodeMap.computeIfAbsent(p1, (point) -> new ArrayList<ProjectGraphNode>()).add(graphNode1);
|
||||
nodeMap.computeIfAbsent(p2, (point) -> new ArrayList<ProjectGraphNode>()).add(graphNode2);
|
||||
}
|
||||
|
||||
for (Map.Entry<Point2D, List<ProjectGraphNode>> entry : nodeMap.entrySet()) {
|
||||
ProjectGraphNode[] nodeArr = entry.getValue().toArray(new ProjectGraphNode[0]);
|
||||
for (int i = 0; i < nodeArr.length - 1; i++) {
|
||||
for (int j = i + 1; j < nodeArr.length; j++) {
|
||||
builder.putEdge(nodeArr[i], nodeArr[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Area area : continuityAreas) {
|
||||
ProjectGraphNode areaNode = new ProjectGraphNode(area);
|
||||
builder.addNode(areaNode);
|
||||
|
||||
ProjectGraphNode[] nodeArr = nodeTree.search(area).stream()
|
||||
.filter(graphNode -> area.contains(graphNode.getPoint())
|
||||
|| area.intersects(graphNode.getPoint().getX() - eps / 2,
|
||||
graphNode.getPoint().getY() - eps / 2, eps, eps))
|
||||
.collect(Collectors.toList()).toArray(new ProjectGraphNode[0]);
|
||||
|
||||
for (ProjectGraphNode node : nodeArr) {
|
||||
builder.putEdge(node, areaNode);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nodeArr.length - 1; i++) {
|
||||
for (int j = i + 1; j < nodeArr.length; j++) {
|
||||
builder.putEdge(nodeArr[i], nodeArr[j]);
|
||||
}
|
||||
}
|
||||
|
||||
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 false;
|
||||
}).forEach(graphNode -> {
|
||||
builder.putEdge(graphNode, areaNode);
|
||||
});
|
||||
|
||||
areaTree.insert(area, areaNode);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static Point2D snapToEps(Point2D point, double eps) {
|
||||
return new Point2D.Double(Math.round(point.getX() / eps) * eps,
|
||||
Math.round(point.getY() / eps) * eps);
|
||||
}
|
||||
|
||||
private static class ProjectGraphNode {
|
||||
private Point2D point;
|
||||
private Node node;
|
||||
private Area area;
|
||||
|
||||
public ProjectGraphNode(Area area) {
|
||||
super();
|
||||
this.area = area;
|
||||
}
|
||||
|
||||
public ProjectGraphNode(Point2D point) {
|
||||
super();
|
||||
this.point = point;
|
||||
this.node = null;
|
||||
}
|
||||
|
||||
public ProjectGraphNode(Point2D point, Node node) {
|
||||
super();
|
||||
this.point = point;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public Point2D getPoint() {
|
||||
return point;
|
||||
}
|
||||
|
||||
public Node getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public Area getArea() {
|
||||
return area;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
package org.diylc.netlist;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
/**
|
||||
@@ -51,6 +52,10 @@ public class Node implements Comparable<Node> {
|
||||
String[] section = component.getSectionNames(pointIndex);
|
||||
return (section == null || section.length > 1 ? "" : section[0] + ".") + component.getControlPointNodeName(pointIndex);
|
||||
}
|
||||
|
||||
public Point2D getPoint2D() {
|
||||
return component.getControlPoint(pointIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
@@ -1,87 +1,65 @@
|
||||
/*
|
||||
*
|
||||
* 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;
|
||||
|
||||
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.Point2D;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.measures.Size;
|
||||
|
||||
public class CalcUtils {
|
||||
|
||||
/**
|
||||
* Rounds the number to the closest grid line.
|
||||
*
|
||||
* @param x
|
||||
* @return
|
||||
*/
|
||||
public static double roundToGrid(double x, Size gridSpacing) {
|
||||
double grid = gridSpacing.convertToPixels();
|
||||
return (Math.round(1f * x / grid) * grid);
|
||||
}
|
||||
|
||||
public static void snapPointToGrid(Point2D point, Size gridSpacing) {
|
||||
double x = roundToGrid(point.getX(), gridSpacing);
|
||||
double y = roundToGrid(point.getY(), gridSpacing);
|
||||
point.setLocation(x, y);
|
||||
import org.diylc.core.measures.Size;
|
||||
|
||||
public class CalcUtils {
|
||||
|
||||
/**
|
||||
* Rounds the number to the closest grid line.
|
||||
*
|
||||
* @param x
|
||||
* @return
|
||||
*/
|
||||
public static double roundToGrid(double x, Size gridSpacing) {
|
||||
double grid = gridSpacing.convertToPixels();
|
||||
return (Math.round(1f * x / grid) * grid);
|
||||
}
|
||||
|
||||
|
||||
public static void snapPointToGrid(Point2D point, Size gridSpacing) {
|
||||
double x = roundToGrid(point.getX(), gridSpacing);
|
||||
double y = roundToGrid(point.getY(), gridSpacing);
|
||||
point.setLocation(x, y);
|
||||
}
|
||||
|
||||
public static Size findClosestMultiplierOf(Size factor, Size target, int step) {
|
||||
Size ret = new Size(0d, factor.getUnit());
|
||||
while (ret.convertToPixels() < target.convertToPixels())
|
||||
ret = new Size(ret.getValue() + factor.getValue() * step, factor.getUnit());
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void snapPointToObjects(Point2D point, Size gridSpacing, IDIYComponent<?> component, List<IDIYComponent<?>> components) {
|
||||
|
||||
public static void snapPointToObjects(Point2D point, Size gridSpacing, IDIYComponent<?> component,
|
||||
List<IDIYComponent<?>> components) {
|
||||
for (IDIYComponent<?> c : components) {
|
||||
if (c == component)
|
||||
continue;
|
||||
for (int i = 0; i < c.getControlPointCount(); i++)
|
||||
if (c.isControlPointSticky(i) && point.distance(c.getControlPoint(i)) < gridSpacing.convertToPixels() / 2) {
|
||||
if (c.isControlPointSticky(i)
|
||||
&& point.distance(c.getControlPoint(i)) < gridSpacing.convertToPixels() / 2) {
|
||||
point.setLocation(c.getControlPoint(i));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void expandConnections(Set<Connection> connections) {
|
||||
Set<Connection> toAdd = new TreeSet<Connection>();
|
||||
for (Connection c1 : connections)
|
||||
for (Connection c2 : connections) {
|
||||
if (c1 == c2)
|
||||
continue;
|
||||
if (c1.getP1().distance(c2.getP1()) < DrawingManager.CONTROL_POINT_SIZE)
|
||||
toAdd.add(new Connection(c1.getP2(), c2.getP2()));
|
||||
if (c1.getP1().distance(c2.getP2()) < DrawingManager.CONTROL_POINT_SIZE)
|
||||
toAdd.add(new Connection(c1.getP2(), c2.getP1()));
|
||||
if (c1.getP2().distance(c2.getP1()) < DrawingManager.CONTROL_POINT_SIZE)
|
||||
toAdd.add(new Connection(c1.getP1(), c2.getP2()));
|
||||
if (c1.getP2().distance(c2.getP2()) < DrawingManager.CONTROL_POINT_SIZE)
|
||||
toAdd.add(new Connection(c1.getP1(), c2.getP1()));
|
||||
}
|
||||
if (connections.addAll(toAdd))
|
||||
expandConnections(connections);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ 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.netlist.ContinuityGraph;
|
||||
import org.diylc.netlist.NetlistBuilder;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
/**
|
||||
@@ -94,8 +96,8 @@ public class DrawingManager {
|
||||
// determine which components are invalidated when they are not in the map.
|
||||
private Map<IDIYComponent<?>, ComponentState> lastDrawnStateMap;
|
||||
|
||||
private Area currentContinuityArea;
|
||||
private List<Area> continuityAreaCache = null;
|
||||
private List<Area> currentContinuityAreas;
|
||||
private ContinuityGraph continuityGraphCache = null;
|
||||
|
||||
private Composite slotComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
|
||||
private Composite lockedComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
|
||||
@@ -464,11 +466,12 @@ public class DrawingManager {
|
||||
g2d.draw(s);
|
||||
}
|
||||
|
||||
if (currentContinuityArea != null) {
|
||||
if (currentContinuityAreas != null) {
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
|
||||
g2d.setColor(Color.green);
|
||||
g2d.fill(currentContinuityArea);
|
||||
for (Area a : currentContinuityAreas)
|
||||
g2d.fill(a);
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
@@ -625,8 +628,8 @@ public class DrawingManager {
|
||||
}
|
||||
|
||||
public void clearContinuityArea() {
|
||||
currentContinuityArea = null;
|
||||
continuityAreaCache = null;
|
||||
currentContinuityAreas = null;
|
||||
continuityGraphCache = null;
|
||||
proximityMarkers = null;
|
||||
}
|
||||
|
||||
@@ -682,34 +685,20 @@ public class DrawingManager {
|
||||
}
|
||||
|
||||
public void findContinuityAreaAtPoint(Project project, Point2D p) {
|
||||
if (continuityAreaCache == null)
|
||||
continuityAreaCache = getContinuityAreas(project);
|
||||
if (continuityGraphCache == null)
|
||||
continuityGraphCache = getContinuityGraph(project);
|
||||
|
||||
for (Area a : continuityAreaCache) {
|
||||
if (a.contains(p)) {
|
||||
currentContinuityArea = a;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
currentContinuityArea = null;
|
||||
currentContinuityAreas = continuityGraphCache.findAreasFor(p);
|
||||
}
|
||||
|
||||
|
||||
public List<Area> getContinuityAreas(Project project) {
|
||||
// Find all individual continuity areas for all components
|
||||
List<Area> preliminaryAreas = new ArrayList<Area>();
|
||||
List<Boolean> checkBreakout = new ArrayList<Boolean>();
|
||||
Set<Connection> connections = new HashSet<Connection>();
|
||||
|
||||
for (IDIYComponent<?> c : project.getComponents()) {
|
||||
ComponentArea a = getComponentArea(c);
|
||||
|
||||
if (c instanceof IContinuity) {
|
||||
for (int i = 0; i < c.getControlPointCount() - 1; i++)
|
||||
for (int j = i + 1; j < c.getControlPointCount(); j++)
|
||||
if (((IContinuity) c).arePointsConnected(i, j))
|
||||
connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j)));
|
||||
}
|
||||
|
||||
if (a == null || a.getOutlineArea() == null)
|
||||
continue;
|
||||
Collection<Area> positiveAreas = a.getContinuityPositiveAreas();
|
||||
@@ -741,13 +730,25 @@ public class DrawingManager {
|
||||
areas.add(a);
|
||||
}
|
||||
|
||||
CalcUtils.expandConnections(connections);
|
||||
|
||||
AreaUtils.crunchAreas(areas, connections);
|
||||
|
||||
return areas;
|
||||
}
|
||||
|
||||
public ContinuityGraph getContinuityGraph(Project project) {
|
||||
Set<Connection> connections = new HashSet<Connection>();
|
||||
for (IDIYComponent<?> c : project.getComponents()) {
|
||||
if (c instanceof IContinuity) {
|
||||
for (int i = 0; i < c.getControlPointCount() - 1; i++)
|
||||
for (int j = i + 1; j < c.getControlPointCount(); j++)
|
||||
if (((IContinuity) c).arePointsConnected(i, j))
|
||||
connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j)));
|
||||
}
|
||||
}
|
||||
|
||||
List<Area> areas = getContinuityAreas(project);
|
||||
|
||||
return NetlistBuilder.buildContinuityGraph(areas, connections);
|
||||
}
|
||||
|
||||
public List<Area> getContinuityAreaProximity(Project project, float threshold) {
|
||||
List<Area> continuityAreas = getContinuityAreas(project);
|
||||
Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for
|
||||
|
||||
@@ -83,10 +83,10 @@ import org.diylc.core.annotations.IAutoCreator;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.lang.LangUtil;
|
||||
import org.diylc.netlist.Group;
|
||||
import org.diylc.netlist.INetlistParser;
|
||||
import org.diylc.netlist.Netlist;
|
||||
import org.diylc.netlist.NetlistAnalyzer;
|
||||
import org.diylc.netlist.NetlistBuilder;
|
||||
import org.diylc.netlist.Node;
|
||||
import org.diylc.netlist.Position;
|
||||
import org.diylc.netlist.SwitchSetup;
|
||||
@@ -3191,7 +3191,7 @@ public class Presenter implements IPlugInPort {
|
||||
positions[i] = 0;
|
||||
|
||||
// grab continuity areas
|
||||
List<Area> continuity = drawingManager.getContinuityAreas(currentProject);
|
||||
List<Area> continuityAreas = drawingManager.getContinuityAreas(currentProject);
|
||||
|
||||
int i = switches.size() - 1;
|
||||
while (i >= 0) {
|
||||
@@ -3203,14 +3203,15 @@ public class Presenter implements IPlugInPort {
|
||||
posList.add(new Position(switches.get(j), positions[j]));
|
||||
}
|
||||
List<Connection> connections = getConnections(switchPositions);
|
||||
Netlist graph = constructNetlist(nodes, connections, continuity, currentProject.getComponents());
|
||||
|
||||
Netlist netlist = NetlistBuilder.buildNetlist(currentProject.getComponents(), nodes, continuityAreas, connections);
|
||||
|
||||
// merge graphs that are effectively the same
|
||||
if (result.containsKey(graph)) {
|
||||
result.get(graph).getSwitchSetup().add(new SwitchSetup(posList));
|
||||
if (result.containsKey(netlist)) {
|
||||
result.get(netlist).getSwitchSetup().add(new SwitchSetup(posList));
|
||||
} else {
|
||||
graph.getSwitchSetup().add(new SwitchSetup(posList));
|
||||
result.put(graph, graph);
|
||||
netlist.getSwitchSetup().add(new SwitchSetup(posList));
|
||||
result.put(netlist, netlist);
|
||||
}
|
||||
|
||||
// find the next combination if possible
|
||||
@@ -3235,112 +3236,6 @@ public class Presenter implements IPlugInPort {
|
||||
return netlists;
|
||||
}
|
||||
|
||||
private Netlist constructNetlist(List<Node> nodes, List<Connection> connections, List<Area> continuityAreas, List<IDIYComponent<?>> components) {
|
||||
Netlist netlist = new Netlist(components);
|
||||
|
||||
// debugging code
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// sb.append("Nodes:").append("\n");
|
||||
// for (Node n : nodes) {
|
||||
// sb.append(n.toString()).append("\n");
|
||||
// }
|
||||
// sb.append("Connections:").append("\n");
|
||||
// for (Line2D n : connections) {
|
||||
// sb.append(n.getP1()).append(":").append(n.getP2()).append("\n");
|
||||
// }
|
||||
// LOG.debug(sb.toString());
|
||||
|
||||
double t = DrawingManager.CONTROL_POINT_SIZE;
|
||||
for (int i = 0; i < nodes.size() - 1; i++)
|
||||
for (int j = i + 1; j < nodes.size(); j++) {
|
||||
Node node1 = nodes.get(i);
|
||||
Node node2 = nodes.get(j);
|
||||
Point2D point1 = node1.getComponent().getControlPoint(node1.getPointIndex());
|
||||
Point2D point2 = node2.getComponent().getControlPoint(node2.getPointIndex());
|
||||
|
||||
String commonPoint1 = node1.getComponent().getCommonPointName(node1.getPointIndex());
|
||||
String commonPoint2 = node2.getComponent().getCommonPointName(node2.getPointIndex());
|
||||
|
||||
// try both directions
|
||||
if (point1.distance(point2) < t
|
||||
|| checkGraphConnection(point1, point2, connections, continuityAreas, new boolean[connections.size()])
|
||||
|| checkGraphConnection(point2, point1, connections, continuityAreas, new boolean[connections.size()])
|
||||
|| (commonPoint1 != null && commonPoint1.equalsIgnoreCase(commonPoint2))) {
|
||||
boolean added = false;
|
||||
// add to an existing vertex if possible
|
||||
for (Group g : netlist.getGroups())
|
||||
if (g.getNodes().contains(node1)) {
|
||||
g.getNodes().add(node2);
|
||||
added = true;
|
||||
} else if (g.getNodes().contains(node2)) {
|
||||
g.getNodes().add(node1);
|
||||
added = true;
|
||||
}
|
||||
if (!added)
|
||||
netlist.getGroups().add(new Group(node1, node2));
|
||||
}
|
||||
}
|
||||
|
||||
// merge overlapping groups if needed
|
||||
boolean reduce = true;
|
||||
while (reduce) {
|
||||
reduce = false;
|
||||
List<Group> groups = netlist.getSortedGroups();
|
||||
Iterator<Group> i = groups.iterator();
|
||||
while (i.hasNext()) {
|
||||
Group g1 = i.next();
|
||||
for (Group g2 : groups) {
|
||||
if (g1 != g2 && !Collections.disjoint(g1.getNodes(), g2.getNodes())) {
|
||||
i.remove();
|
||||
g2.getNodes().addAll(g1.getNodes());
|
||||
reduce = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reduce) {
|
||||
netlist.getGroups().clear();
|
||||
netlist.getGroups().addAll(groups);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(netlist.getSwitchSetup());
|
||||
|
||||
return netlist;
|
||||
}
|
||||
|
||||
private boolean checkGraphConnection(Point2D point1, Point2D point2, List<Connection> connections,
|
||||
List<Area> continuityAreas, boolean[] visited) {
|
||||
double t = DrawingManager.CONTROL_POINT_SIZE;
|
||||
|
||||
if (point1.distance(point2) < t)
|
||||
return true;
|
||||
|
||||
for (Area a : continuityAreas) {
|
||||
if (a.contains(point1) && a.contains(point2))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < connections.size(); i++) {
|
||||
if (visited[i])
|
||||
continue;
|
||||
|
||||
Connection c = connections.get(i);
|
||||
if (point1.distance(c.getP1()) < t) {
|
||||
visited[i] = true;
|
||||
if (checkGraphConnection(c.getP2(), point2, connections, continuityAreas, visited))
|
||||
return true;
|
||||
}
|
||||
if (point1.distance(c.getP2()) < t) {
|
||||
visited[i] = true;
|
||||
if (checkGraphConnection(c.getP1(), point2, connections, continuityAreas, visited))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "unlikely-arg-type"})
|
||||
private List<Connection> getConnections(Map<ISwitch, Integer> switchPositions) {
|
||||
Set<Connection> connections = new TreeSet<Connection>();
|
||||
@@ -3366,8 +3261,6 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
}
|
||||
|
||||
CalcUtils.expandConnections(connections);
|
||||
|
||||
return new ArrayList<Connection>(connections);
|
||||
}
|
||||
|
||||
|
||||
@@ -4004,7 +4004,11 @@
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Rotary Switch (Sealed)' component</description>
|
||||
<description>Added 'Rotary Switch (Sealed)' component in several switching configurations</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Rotary Switch (Open)' component in several switching configurations</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
@@ -4018,10 +4022,18 @@
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Ability to rotate 'Lever Switch' and 'Schaller Megaswitch' by any angle</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>DIYLC Netlists can be generated including or excluding switching configurations</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Ability to rotate all guitar/bass pickups through the 'Transform Selection' feature or keyboard shortcut</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Improved the speed and accuracy of netlist calculation</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
|
||||
@@ -46,5 +46,36 @@ public class Pair<A, B> {
|
||||
|
||||
public void setSecond(B second) {
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((first == null) ? 0 : first.hashCode());
|
||||
result = prime * result + ((second == null) ? 0 : second.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Pair other = (Pair) obj;
|
||||
if (first == null) {
|
||||
if (other.first != null)
|
||||
return false;
|
||||
} else if (!first.equals(other.first))
|
||||
return false;
|
||||
if (second == null) {
|
||||
if (other.second != null)
|
||||
return false;
|
||||
} else if (!second.equals(other.second))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,678 @@
|
||||
package org.diylc.utils;
|
||||
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Implementation of an arbitrary-dimension RTree. Based on R-Trees: A Dynamic
|
||||
* Index Structure for Spatial Searching (Antonn Guttmann, 1984)
|
||||
* <p>
|
||||
* This class is not thread-safe.
|
||||
* <p>
|
||||
* Copyright 2010 Russ Weeks rweeks@newbrightidea.com Licensed under the GNU
|
||||
* LGPL License details here: http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @param <T> the type of entry to store in this RTree.
|
||||
*/
|
||||
public class RTree<T> {
|
||||
public enum SeedPicker {LINEAR, QUADRATIC}
|
||||
|
||||
private final int maxEntries;
|
||||
private final int minEntries;
|
||||
private final int numDims;
|
||||
|
||||
private final float[] pointDims;
|
||||
|
||||
private final SeedPicker seedPicker;
|
||||
|
||||
private Node root;
|
||||
|
||||
private volatile int size;
|
||||
|
||||
/**
|
||||
* Creates a new RTree.
|
||||
*
|
||||
* @param maxEntries maximum number of entries per node
|
||||
* @param minEntries minimum number of entries per node (except for the root node)
|
||||
* @param numDims the number of dimensions of the RTree.
|
||||
*/
|
||||
public RTree(int maxEntries, int minEntries, int numDims, SeedPicker seedPicker) {
|
||||
assert (minEntries <= (maxEntries / 2));
|
||||
this.numDims = numDims;
|
||||
this.maxEntries = maxEntries;
|
||||
this.minEntries = minEntries;
|
||||
this.seedPicker = seedPicker;
|
||||
pointDims = new float[numDims];
|
||||
root = buildRoot(true);
|
||||
}
|
||||
|
||||
public RTree(int maxEntries, int minEntries, int numDims) {
|
||||
this(maxEntries, minEntries, numDims, SeedPicker.LINEAR);
|
||||
}
|
||||
|
||||
private Node buildRoot(boolean asLeaf) {
|
||||
float[] initCoords = new float[numDims];
|
||||
float[] initDimensions = new float[numDims];
|
||||
for (int i = 0; i < this.numDims; i++) {
|
||||
initCoords[i] = (float) Math.sqrt(Float.MAX_VALUE);
|
||||
initDimensions[i] = -2.0f * (float) Math.sqrt(Float.MAX_VALUE);
|
||||
}
|
||||
return new Node(initCoords, initDimensions, asLeaf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new RTree using default parameters: maximum 50 entries per node
|
||||
* minimum 2 entries per node 2 dimensions
|
||||
*/
|
||||
public RTree() {
|
||||
this(50, 2, 2, SeedPicker.LINEAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum number of entries per node
|
||||
*/
|
||||
public int getMaxEntries() {
|
||||
return maxEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the minimum number of entries per node for all nodes except the
|
||||
* root.
|
||||
*/
|
||||
public int getMinEntries() {
|
||||
return minEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of dimensions of the tree
|
||||
*/
|
||||
public int getNumDims() {
|
||||
return numDims;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of items in this tree.
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the RTree for objects overlapping with the given rectangle.
|
||||
*
|
||||
* @param coords the corner of the rectangle that is the lower bound of every
|
||||
* dimension (eg. the top-left corner)
|
||||
* @param dimensions the dimensions of the rectangle.
|
||||
* @return a list of objects whose rectangles overlap with the given
|
||||
* rectangle.
|
||||
*/
|
||||
public List<T> search(float[] coords, float[] dimensions) {
|
||||
assert (coords.length == numDims);
|
||||
assert (dimensions.length == numDims);
|
||||
LinkedList<T> results = new LinkedList<T>();
|
||||
search(coords, dimensions, root, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
public List<T> search(Area area) {
|
||||
Rectangle2D bounds2D = area.getBounds2D();
|
||||
return search(new float[] { (float) bounds2D.getMinX(), (float) bounds2D.getMinY() } , new float[] { (float) bounds2D.getWidth(), (float) bounds2D.getHeight() });
|
||||
}
|
||||
|
||||
public List<T> search(Point2D point, double t) {
|
||||
return search(new float[] { (float) (point.getX() - t / 2), (float) (point.getY() - t / 2) },
|
||||
new float[] { (float) t, (float) t });
|
||||
}
|
||||
|
||||
private void search(float[] coords, float[] dimensions, Node n,
|
||||
LinkedList<T> results) {
|
||||
if (n.leaf) {
|
||||
for (Node e : n.children) {
|
||||
if (isOverlap(coords, dimensions, e.coords, e.dimensions)) {
|
||||
results.add(((Entry) e).entry);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Node c : n.children) {
|
||||
if (isOverlap(coords, dimensions, c.coords, c.dimensions)) {
|
||||
search(coords, dimensions, c, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the entry associated with the given rectangle from the RTree
|
||||
*
|
||||
* @param coords the corner of the rectangle that is the lower bound in every
|
||||
* dimension
|
||||
* @param dimensions the dimensions of the rectangle
|
||||
* @param entry the entry to delete
|
||||
* @return true iff the entry was deleted from the RTree.
|
||||
*/
|
||||
public boolean delete(float[] coords, float[] dimensions, T entry) {
|
||||
assert (coords.length == numDims);
|
||||
assert (dimensions.length == numDims);
|
||||
Node l = findLeaf(root, coords, dimensions, entry);
|
||||
if (l == null) {
|
||||
System.out.println("WTF?");
|
||||
findLeaf(root, coords, dimensions, entry);
|
||||
}
|
||||
assert (l != null) : "Could not find leaf for entry to delete";
|
||||
assert (l.leaf) : "Entry is not found at leaf?!?";
|
||||
ListIterator<Node> li = l.children.listIterator();
|
||||
T removed = null;
|
||||
while (li.hasNext()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Entry e = (Entry) li.next();
|
||||
if (e.entry.equals(entry)) {
|
||||
removed = e.entry;
|
||||
li.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (removed != null) {
|
||||
condenseTree(l);
|
||||
size--;
|
||||
}
|
||||
if (size == 0) {
|
||||
root = buildRoot(true);
|
||||
}
|
||||
return (removed != null);
|
||||
}
|
||||
|
||||
public boolean delete(float[] coords, T entry) {
|
||||
return delete(coords, pointDims, entry);
|
||||
}
|
||||
|
||||
private Node findLeaf(Node n, float[] coords, float[] dimensions, T entry) {
|
||||
if (n.leaf) {
|
||||
for (Node c : n.children) {
|
||||
if (((Entry) c).entry.equals(entry)) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
for (Node c : n.children) {
|
||||
if (isOverlap(c.coords, c.dimensions, coords, dimensions)) {
|
||||
Node result = findLeaf(c, coords, dimensions, entry);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void condenseTree(Node n) {
|
||||
Set<Node> q = new HashSet<Node>();
|
||||
while (n != root) {
|
||||
if (n.leaf && (n.children.size() < minEntries)) {
|
||||
q.addAll(n.children);
|
||||
n.parent.children.remove(n);
|
||||
} else if (!n.leaf && (n.children.size() < minEntries)) {
|
||||
// probably a more efficient way to do this...
|
||||
LinkedList<Node> toVisit = new LinkedList<Node>(n.children);
|
||||
while (!toVisit.isEmpty()) {
|
||||
Node c = toVisit.pop();
|
||||
if (c.leaf) {
|
||||
q.addAll(c.children);
|
||||
} else {
|
||||
toVisit.addAll(c.children);
|
||||
}
|
||||
}
|
||||
n.parent.children.remove(n);
|
||||
} else {
|
||||
tighten(n);
|
||||
}
|
||||
n = n.parent;
|
||||
}
|
||||
if (root.children.size() == 0) {
|
||||
root = buildRoot(true);
|
||||
} else if ((root.children.size() == 1) && (!root.leaf)) {
|
||||
root = root.children.get(0);
|
||||
root.parent = null;
|
||||
} else {
|
||||
tighten(root);
|
||||
}
|
||||
for (Node ne : q) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Entry e = (Entry) ne;
|
||||
insert(e.coords, e.dimensions, e.entry);
|
||||
}
|
||||
size -= q.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the RTree
|
||||
*/
|
||||
public void clear() {
|
||||
root = buildRoot(true);
|
||||
// let the GC take care of the rest.
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given entry into the RTree, associated with the given
|
||||
* rectangle.
|
||||
*
|
||||
* @param coords the corner of the rectangle that is the lower bound in every
|
||||
* dimension
|
||||
* @param dimensions the dimensions of the rectangle
|
||||
* @param entry the entry to insert
|
||||
*/
|
||||
public void insert(float[] coords, float[] dimensions, T entry) {
|
||||
assert (coords.length == numDims);
|
||||
assert (dimensions.length == numDims);
|
||||
Entry e = new Entry(coords, dimensions, entry);
|
||||
Node l = chooseLeaf(root, e);
|
||||
l.children.add(e);
|
||||
size++;
|
||||
e.parent = l;
|
||||
if (l.children.size() > maxEntries) {
|
||||
Node[] splits = splitNode(l);
|
||||
adjustTree(splits[0], splits[1]);
|
||||
} else {
|
||||
adjustTree(l, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void insert(Area a, T entry) {
|
||||
Rectangle2D bounds2d = a.getBounds2D();
|
||||
insert(new float[] {(float) bounds2d.getMinX(), (float) bounds2d.getMinY()},
|
||||
new float[] {(float) bounds2d.getWidth(), (float) bounds2d.getHeight()}, entry);
|
||||
}
|
||||
|
||||
public void insert(Point2D p, double t, T entry) {
|
||||
insert(new float[] {(float) (p.getX() - t / 2), (float) (p.getY() - t /2) },
|
||||
new float[] {(float) t, (float) t }, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for inserting a point
|
||||
*
|
||||
* @param coords
|
||||
* @param entry
|
||||
*/
|
||||
public void insert(float[] coords, T entry) {
|
||||
insert(coords, pointDims, entry);
|
||||
}
|
||||
|
||||
public void visit(NodeVisitor<T> nv) {
|
||||
float[] coordBuf = new float[numDims];
|
||||
float[] dimBuf = new float[numDims];
|
||||
Queue<Node> toVisit = new LinkedList<>();
|
||||
Map<Node,Integer> nodeDepths = new HashMap<>();
|
||||
nodeDepths.put(root, 0);
|
||||
toVisit.add(root);
|
||||
while (!toVisit.isEmpty()) {
|
||||
Node currentNode = toVisit.remove();
|
||||
if (currentNode.parent != null) {
|
||||
nodeDepths.put(currentNode, nodeDepths.get(currentNode.parent) + 1);
|
||||
}
|
||||
System.arraycopy(currentNode.coords, 0, coordBuf, 0, numDims);
|
||||
System.arraycopy(currentNode.dimensions, 0, dimBuf, 0, numDims);
|
||||
if (currentNode instanceof RTree.Entry) {
|
||||
nv.visit(nodeDepths.get(currentNode), coordBuf, dimBuf, ((Entry)currentNode).entry);
|
||||
} else {
|
||||
nv.visit(nodeDepths.get(currentNode), coordBuf, dimBuf, null);
|
||||
}
|
||||
toVisit.addAll(currentNode.children);
|
||||
}
|
||||
}
|
||||
|
||||
public interface NodeVisitor<V> {
|
||||
public void visit(int depth, float[] coords, float[] dimensions, V value);
|
||||
}
|
||||
|
||||
private void adjustTree(Node n, Node nn) {
|
||||
if (n == root) {
|
||||
if (nn != null) {
|
||||
// build new root and add children.
|
||||
root = buildRoot(false);
|
||||
root.children.add(n);
|
||||
n.parent = root;
|
||||
root.children.add(nn);
|
||||
nn.parent = root;
|
||||
}
|
||||
tighten(root);
|
||||
return;
|
||||
}
|
||||
tighten(n);
|
||||
if (nn != null) {
|
||||
tighten(nn);
|
||||
if (n.parent.children.size() > maxEntries) {
|
||||
Node[] splits = splitNode(n.parent);
|
||||
adjustTree(splits[0], splits[1]);
|
||||
}
|
||||
}
|
||||
if (n.parent != null) {
|
||||
adjustTree(n.parent, null);
|
||||
}
|
||||
}
|
||||
|
||||
private Node[] splitNode(Node n) {
|
||||
// TODO: this class probably calls "tighten" a little too often.
|
||||
// For instance the call at the end of the "while (!cc.isEmpty())" loop
|
||||
// could be modified and inlined because it's only adjusting for the addition
|
||||
// of a single node. Left as-is for now for readability.
|
||||
@SuppressWarnings("unchecked")
|
||||
Node[] nn = new RTree.Node[]
|
||||
{n, new Node(n.coords, n.dimensions, n.leaf)};
|
||||
nn[1].parent = n.parent;
|
||||
if (nn[1].parent != null) {
|
||||
nn[1].parent.children.add(nn[1]);
|
||||
}
|
||||
LinkedList<Node> cc = new LinkedList<Node>(n.children);
|
||||
n.children.clear();
|
||||
Node[] ss = seedPicker == SeedPicker.LINEAR ? lPickSeeds(cc) : qPickSeeds(cc);
|
||||
nn[0].children.add(ss[0]);
|
||||
nn[1].children.add(ss[1]);
|
||||
tighten(nn);
|
||||
while (!cc.isEmpty()) {
|
||||
if ((nn[0].children.size() >= minEntries)
|
||||
&& (nn[1].children.size() + cc.size() == minEntries)) {
|
||||
nn[1].children.addAll(cc);
|
||||
cc.clear();
|
||||
tighten(nn); // Not sure this is required.
|
||||
return nn;
|
||||
} else if ((nn[1].children.size() >= minEntries)
|
||||
&& (nn[0].children.size() + cc.size() == minEntries)) {
|
||||
nn[0].children.addAll(cc);
|
||||
cc.clear();
|
||||
tighten(nn); // Not sure this is required.
|
||||
return nn;
|
||||
}
|
||||
Node c = seedPicker == SeedPicker.LINEAR ? lPickNext(cc) : qPickNext(cc, nn);
|
||||
Node preferred;
|
||||
float e0 = getRequiredExpansion(nn[0].coords, nn[0].dimensions, c);
|
||||
float e1 = getRequiredExpansion(nn[1].coords, nn[1].dimensions, c);
|
||||
if (e0 < e1) {
|
||||
preferred = nn[0];
|
||||
} else if (e0 > e1) {
|
||||
preferred = nn[1];
|
||||
} else {
|
||||
float a0 = getArea(nn[0].dimensions);
|
||||
float a1 = getArea(nn[1].dimensions);
|
||||
if (a0 < a1) {
|
||||
preferred = nn[0];
|
||||
} else if (e0 > a1) {
|
||||
preferred = nn[1];
|
||||
} else {
|
||||
if (nn[0].children.size() < nn[1].children.size()) {
|
||||
preferred = nn[0];
|
||||
} else if (nn[0].children.size() > nn[1].children.size()) {
|
||||
preferred = nn[1];
|
||||
} else {
|
||||
preferred = nn[(int) Math.round(Math.random())];
|
||||
}
|
||||
}
|
||||
}
|
||||
preferred.children.add(c);
|
||||
tighten(preferred);
|
||||
}
|
||||
return nn;
|
||||
}
|
||||
|
||||
// Implementation of Quadratic PickSeeds
|
||||
private RTree<T>.Node[] qPickSeeds(LinkedList<Node> nn) {
|
||||
@SuppressWarnings("unchecked")
|
||||
RTree<T>.Node[] bestPair = new RTree.Node[2];
|
||||
float maxWaste = -1.0f * Float.MAX_VALUE;
|
||||
for (Node n1 : nn) {
|
||||
for (Node n2 : nn) {
|
||||
if (n1 == n2) continue;
|
||||
float n1a = getArea(n1.dimensions);
|
||||
float n2a = getArea(n2.dimensions);
|
||||
float ja = 1.0f;
|
||||
for (int i = 0; i < numDims; i++) {
|
||||
float jc0 = Math.min(n1.coords[i], n2.coords[i]);
|
||||
float jc1 = Math.max(n1.coords[i] + n1.dimensions[i], n2.coords[i] + n2.dimensions[i]);
|
||||
ja *= (jc1 - jc0);
|
||||
}
|
||||
float waste = ja - n1a - n2a;
|
||||
if (waste > maxWaste) {
|
||||
maxWaste = waste;
|
||||
bestPair[0] = n1;
|
||||
bestPair[1] = n2;
|
||||
}
|
||||
}
|
||||
}
|
||||
nn.remove(bestPair[0]);
|
||||
nn.remove(bestPair[1]);
|
||||
return bestPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of QuadraticPickNext
|
||||
*
|
||||
* @param cc the children to be divided between the new nodes, one item will be removed from this list.
|
||||
* @param nn the candidate nodes for the children to be added to.
|
||||
*/
|
||||
private Node qPickNext(LinkedList<Node> cc, Node[] nn) {
|
||||
float maxDiff = -1.0f * Float.MAX_VALUE;
|
||||
Node nextC = null;
|
||||
for (Node c : cc) {
|
||||
float n0Exp = getRequiredExpansion(nn[0].coords, nn[0].dimensions, c);
|
||||
float n1Exp = getRequiredExpansion(nn[1].coords, nn[1].dimensions, c);
|
||||
float diff = Math.abs(n1Exp - n0Exp);
|
||||
if (diff > maxDiff) {
|
||||
maxDiff = diff;
|
||||
nextC = c;
|
||||
}
|
||||
}
|
||||
assert (nextC != null) : "No node selected from qPickNext";
|
||||
cc.remove(nextC);
|
||||
return nextC;
|
||||
}
|
||||
|
||||
// Implementation of LinearPickSeeds
|
||||
private RTree<T>.Node[] lPickSeeds(LinkedList<Node> nn) {
|
||||
@SuppressWarnings("unchecked")
|
||||
RTree<T>.Node[] bestPair = new RTree.Node[2];
|
||||
boolean foundBestPair = false;
|
||||
float bestSep = 0.0f;
|
||||
for (int i = 0; i < numDims; i++) {
|
||||
float dimLb = Float.MAX_VALUE, dimMinUb = Float.MAX_VALUE;
|
||||
float dimUb = -1.0f * Float.MAX_VALUE, dimMaxLb = -1.0f * Float.MAX_VALUE;
|
||||
Node nMaxLb = null, nMinUb = null;
|
||||
for (Node n : nn) {
|
||||
if (n.coords[i] < dimLb) {
|
||||
dimLb = n.coords[i];
|
||||
}
|
||||
if (n.dimensions[i] + n.coords[i] > dimUb) {
|
||||
dimUb = n.dimensions[i] + n.coords[i];
|
||||
}
|
||||
if (n.coords[i] > dimMaxLb) {
|
||||
dimMaxLb = n.coords[i];
|
||||
nMaxLb = n;
|
||||
}
|
||||
if (n.dimensions[i] + n.coords[i] < dimMinUb) {
|
||||
dimMinUb = n.dimensions[i] + n.coords[i];
|
||||
nMinUb = n;
|
||||
}
|
||||
}
|
||||
float sep = (nMaxLb == nMinUb) ? -1.0f :
|
||||
Math.abs((dimMinUb - dimMaxLb) / (dimUb - dimLb));
|
||||
if (sep >= bestSep) {
|
||||
bestPair[0] = nMaxLb;
|
||||
bestPair[1] = nMinUb;
|
||||
bestSep = sep;
|
||||
foundBestPair = true;
|
||||
}
|
||||
}
|
||||
// In the degenerate case where all points are the same, the above
|
||||
// algorithm does not find a best pair. Just pick the first 2
|
||||
// children.
|
||||
if (!foundBestPair) {
|
||||
bestPair = new RTree.Node[]{nn.get(0), nn.get(1)};
|
||||
}
|
||||
nn.remove(bestPair[0]);
|
||||
nn.remove(bestPair[1]);
|
||||
return bestPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of LinearPickNext
|
||||
*
|
||||
* @param cc the children to be divided between the new nodes, one item will be removed from this list.
|
||||
*/
|
||||
private Node lPickNext(LinkedList<Node> cc) {
|
||||
return cc.pop();
|
||||
}
|
||||
|
||||
private void tighten(Node... nodes) {
|
||||
assert (nodes.length >= 1) : "Pass some nodes to tighten!";
|
||||
for (Node n : nodes) {
|
||||
assert (n.children.size() > 0) : "tighten() called on empty node!";
|
||||
float[] minCoords = new float[numDims];
|
||||
float[] maxCoords = new float[numDims];
|
||||
for (int i = 0; i < numDims; i++) {
|
||||
minCoords[i] = Float.MAX_VALUE;
|
||||
maxCoords[i] = -1.0f * Float.MAX_VALUE;
|
||||
|
||||
for (Node c : n.children) {
|
||||
// we may have bulk-added a bunch of children to a node (eg. in
|
||||
// splitNode)
|
||||
// so here we just enforce the child->parent relationship.
|
||||
c.parent = n;
|
||||
if (c.coords[i] < minCoords[i]) {
|
||||
minCoords[i] = c.coords[i];
|
||||
}
|
||||
if ((c.coords[i] + c.dimensions[i]) > maxCoords[i]) {
|
||||
maxCoords[i] = (c.coords[i] + c.dimensions[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < numDims; i++) {
|
||||
// Convert max coords to dimensions
|
||||
maxCoords[i] -= minCoords[i];
|
||||
}
|
||||
System.arraycopy(minCoords, 0, n.coords, 0, numDims);
|
||||
System.arraycopy(maxCoords, 0, n.dimensions, 0, numDims);
|
||||
}
|
||||
}
|
||||
|
||||
private RTree<T>.Node chooseLeaf(RTree<T>.Node n, RTree<T>.Entry e) {
|
||||
if (n.leaf) {
|
||||
return n;
|
||||
}
|
||||
float minInc = Float.MAX_VALUE;
|
||||
Node next = null;
|
||||
for (RTree<T>.Node c : n.children) {
|
||||
float inc = getRequiredExpansion(c.coords, c.dimensions, e);
|
||||
if (inc < minInc) {
|
||||
minInc = inc;
|
||||
next = c;
|
||||
} else if (inc == minInc) {
|
||||
float curArea = 1.0f;
|
||||
float thisArea = 1.0f;
|
||||
for (int i = 0; i < c.dimensions.length; i++) {
|
||||
curArea *= next.dimensions[i];
|
||||
thisArea *= c.dimensions[i];
|
||||
}
|
||||
if (thisArea < curArea) {
|
||||
next = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
return chooseLeaf(next, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the increase in area necessary for the given rectangle to cover the
|
||||
* given entry.
|
||||
*/
|
||||
private float getRequiredExpansion(float[] coords, float[] dimensions, Node e) {
|
||||
float area = getArea(dimensions);
|
||||
float[] deltas = new float[dimensions.length];
|
||||
for (int i = 0; i < deltas.length; i++) {
|
||||
if (coords[i] + dimensions[i] < e.coords[i] + e.dimensions[i]) {
|
||||
deltas[i] = e.coords[i] + e.dimensions[i] - coords[i] - dimensions[i];
|
||||
} else if (coords[i] + dimensions[i] > e.coords[i] + e.dimensions[i]) {
|
||||
deltas[i] = coords[i] - e.coords[i];
|
||||
}
|
||||
}
|
||||
float expanded = 1.0f;
|
||||
for (int i = 0; i < dimensions.length; i++) {
|
||||
expanded *= dimensions[i] + deltas[i];
|
||||
}
|
||||
return (expanded - area);
|
||||
}
|
||||
|
||||
private float getArea(float[] dimensions) {
|
||||
float area = 1.0f;
|
||||
for (int i = 0; i < dimensions.length; i++) {
|
||||
area *= dimensions[i];
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
private boolean isOverlap(float[] scoords, float[] sdimensions,
|
||||
float[] coords, float[] dimensions) {
|
||||
final float FUDGE_FACTOR = 1.001f;
|
||||
for (int i = 0; i < scoords.length; i++) {
|
||||
boolean overlapInThisDimension = false;
|
||||
if (scoords[i] == coords[i]) {
|
||||
overlapInThisDimension = true;
|
||||
} else if (scoords[i] < coords[i]) {
|
||||
if (scoords[i] + FUDGE_FACTOR * sdimensions[i] >= coords[i]) {
|
||||
overlapInThisDimension = true;
|
||||
}
|
||||
} else if (scoords[i] > coords[i]) {
|
||||
if (coords[i] + FUDGE_FACTOR * dimensions[i] >= scoords[i]) {
|
||||
overlapInThisDimension = true;
|
||||
}
|
||||
}
|
||||
if (!overlapInThisDimension) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private class Node {
|
||||
final float[] coords;
|
||||
final float[] dimensions;
|
||||
final LinkedList<Node> children;
|
||||
final boolean leaf;
|
||||
|
||||
Node parent;
|
||||
|
||||
private Node(float[] coords, float[] dimensions, boolean leaf) {
|
||||
this.coords = new float[coords.length];
|
||||
this.dimensions = new float[dimensions.length];
|
||||
System.arraycopy(coords, 0, this.coords, 0, coords.length);
|
||||
System.arraycopy(dimensions, 0, this.dimensions, 0, dimensions.length);
|
||||
this.leaf = leaf;
|
||||
children = new LinkedList<Node>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Entry extends Node {
|
||||
final T entry;
|
||||
|
||||
public Entry(float[] coords, float[] dimensions, T entry) {
|
||||
// an entry isn't actually a leaf (its parent is a leaf)
|
||||
// but all the algorithms should stop at the first leaf they encounter,
|
||||
// so this little hack shouldn't be a problem.
|
||||
super(coords, dimensions, true);
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Entry: " + entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public class Eyelet extends AbstractComponent<String> {
|
||||
}
|
||||
int diameter = getClosestOdd((int) size.convertToPixels());
|
||||
int holeDiameter = getClosestOdd((int) holeSize.convertToPixels());
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(1f));
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
g2d.setColor(color);
|
||||
drawingObserver.startTrackingContinuityArea(true);
|
||||
g2d.fillOval((int)(point.getX() - diameter / 2), (int)(point.getY() - diameter / 2), diameter, diameter);
|
||||
|
||||
@@ -123,7 +123,7 @@ public class ActionFactory {
|
||||
public ImportAction createImportAction(IPlugInPort plugInPort, ISwingUI swingUI) {
|
||||
return new ImportAction(plugInPort, swingUI);
|
||||
}
|
||||
|
||||
|
||||
public ImportNetlistAction createImportNetlistAction(IPlugInPort plugInPort, ISwingUI swingUI) {
|
||||
return new ImportNetlistAction(plugInPort, swingUI);
|
||||
}
|
||||
@@ -275,7 +275,7 @@ public class ActionFactory {
|
||||
String defaultValue) {
|
||||
return new ToggleAction(title, configKey, groupName, defaultValue, null);
|
||||
}
|
||||
|
||||
|
||||
public ToggleAction createToggleAction(String title, String configKey, String groupName,
|
||||
String defaultValue, Icon icon) {
|
||||
return new ToggleAction(title, configKey, groupName, defaultValue, icon);
|
||||
@@ -293,16 +293,16 @@ public class ActionFactory {
|
||||
return new RenumberAction(plugInPort, xAxisFirst);
|
||||
}
|
||||
|
||||
public GenerateNetlistAction createGenerateNetlistAction(IPlugInPort plugInPort,
|
||||
ISwingUI swingUI) {
|
||||
return new GenerateNetlistAction(plugInPort, swingUI);
|
||||
public GenerateNetlistAction createGenerateNetlistAction(IPlugInPort plugInPort, ISwingUI swingUI,
|
||||
boolean includeSwitches) {
|
||||
return new GenerateNetlistAction(plugInPort, swingUI, includeSwitches);
|
||||
}
|
||||
|
||||
public SummarizeNetlistAction createSummarizeNetlistAction(IPlugInPort plugInPort,
|
||||
ISwingUI swingUI, INetlistAnalyzer summarizer) {
|
||||
return new SummarizeNetlistAction(plugInPort, swingUI, summarizer);
|
||||
}
|
||||
|
||||
|
||||
public CheckProximityAction createCheckProximityAction(IPlugInPort plugInPort, ISwingUI swingUI) {
|
||||
return new CheckProximityAction(plugInPort, swingUI);
|
||||
}
|
||||
@@ -420,7 +420,7 @@ public class ActionFactory {
|
||||
public void showMessage(String message, String title, int messageType) {
|
||||
JOptionPane.showMessageDialog(null, message, title, messageType);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String showInputDialog(String message, String title) {
|
||||
return JOptionPane.showInputDialog(null, message, title, JOptionPane.QUESTION_MESSAGE);
|
||||
@@ -459,8 +459,9 @@ public class ActionFactory {
|
||||
presenter.loadProjectFromFile(file.getAbsolutePath());
|
||||
// Grab all components and paste them into the main
|
||||
// presenter
|
||||
plugInPort.pasteComponents(ComponentTransferableFactory.getInstance()
|
||||
.build(presenter.getCurrentProject().getComponents(), presenter.getCurrentProject().getGroups()), false, false);
|
||||
plugInPort.pasteComponents(ComponentTransferableFactory.getInstance().build(
|
||||
presenter.getCurrentProject().getComponents(),
|
||||
presenter.getCurrentProject().getGroups()), false, false);
|
||||
// Cleanup components in the temp presenter, don't need
|
||||
// them anymore
|
||||
presenter.selectAll(0);
|
||||
@@ -480,7 +481,7 @@ public class ActionFactory {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ImportNetlistAction extends AbstractAction {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -495,13 +496,14 @@ public class ActionFactory {
|
||||
public ImportNetlistAction(IPlugInPort plugInPort, ISwingUI swingUI) {
|
||||
super();
|
||||
this.plugInPort = plugInPort;
|
||||
this.swingUI = swingUI;
|
||||
this.swingUI = swingUI;
|
||||
putValue(AbstractAction.NAME, "Import Netlist");
|
||||
putValue(AbstractAction.SMALL_ICON, IconLoader.ImportNetlist.getIcon());
|
||||
|
||||
this.parserDefinitions = plugInPort.getNetlistParserDefinitions();
|
||||
this.extensions = parserDefinitions.stream().map(x -> x.getFileExt()).collect(Collectors.toList());
|
||||
|
||||
|
||||
this.parserDefinitions = plugInPort.getNetlistParserDefinitions();
|
||||
this.extensions =
|
||||
parserDefinitions.stream().map(x -> x.getFileExt()).collect(Collectors.toList());
|
||||
|
||||
this.filter = new FileFilter() {
|
||||
|
||||
@Override
|
||||
@@ -530,12 +532,12 @@ public class ActionFactory {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
LOG.info("ImportNetlistAction triggered");
|
||||
|
||||
final File file = DialogFactory.getInstance().showOpenDialog(this.filter,
|
||||
null, this.extensions.get(0), null);
|
||||
|
||||
final File file = DialogFactory.getInstance().showOpenDialog(this.filter, null,
|
||||
this.extensions.get(0), null);
|
||||
// TODO: identity by extension
|
||||
final INetlistParser parser = parserDefinitions.get(0);
|
||||
|
||||
|
||||
if (file != null) {
|
||||
swingUI.executeBackgroundTask(new ITask<List<ParsedNetlistEntry>>() {
|
||||
|
||||
@@ -543,7 +545,8 @@ public class ActionFactory {
|
||||
public List<ParsedNetlistEntry> doInBackground() throws Exception {
|
||||
LOG.debug("Importing netlist from " + file.getAbsolutePath());
|
||||
List<String> outputWarnings = new ArrayList<String>();
|
||||
List<ParsedNetlistEntry> entries = parser.parseFile(file.getAbsolutePath(), outputWarnings);
|
||||
List<ParsedNetlistEntry> entries =
|
||||
parser.parseFile(file.getAbsolutePath(), outputWarnings);
|
||||
if (!outputWarnings.isEmpty())
|
||||
LOG.warn("Parsing produced warnings:\n" + String.join("\n", outputWarnings));
|
||||
return entries;
|
||||
@@ -552,17 +555,22 @@ public class ActionFactory {
|
||||
@Override
|
||||
public void complete(List<ParsedNetlistEntry> entries) {
|
||||
try {
|
||||
NetlistImportDialog dialog = DialogFactory.getInstance().createNetlistImportDialog(plugInPort,entries);
|
||||
NetlistImportDialog dialog =
|
||||
DialogFactory.getInstance().createNetlistImportDialog(plugInPort, entries);
|
||||
dialog.setVisible(true);
|
||||
if (ButtonDialog.OK.equals(dialog.getSelectedButtonCaption())) {
|
||||
List<String> outputWarnings = new ArrayList<String>();
|
||||
Map<String, Class<?>> results = dialog.getResults();
|
||||
List<ParsedNetlistComponent> parsedComponents = entries.stream().map(entry ->
|
||||
new ParsedNetlistComponent(results.get(entry.getRawType()), entry.getValues())).collect(Collectors.toList());
|
||||
List<IDIYComponent<?>> components = parser.generateComponents(parsedComponents, outputWarnings);
|
||||
List<ParsedNetlistComponent> parsedComponents = entries.stream()
|
||||
.map(entry -> new ParsedNetlistComponent(results.get(entry.getRawType()),
|
||||
entry.getValues()))
|
||||
.collect(Collectors.toList());
|
||||
List<IDIYComponent<?>> components =
|
||||
parser.generateComponents(parsedComponents, outputWarnings);
|
||||
if (!outputWarnings.isEmpty())
|
||||
LOG.warn("Component creation produced warnings:\n" + String.join("\n", outputWarnings));
|
||||
plugInPort.pasteComponents(new ComponentTransferable(components), false, false);
|
||||
LOG.warn("Component creation produced warnings:\n"
|
||||
+ String.join("\n", outputWarnings));
|
||||
plugInPort.pasteComponents(new ComponentTransferable(components), false, false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
swingUI.showMessage("Could not import netlist file: " + e.getMessage(), "Error",
|
||||
@@ -962,7 +970,7 @@ public class ActionFactory {
|
||||
LOG.debug("Exporting variants to " + file.getAbsolutePath());
|
||||
|
||||
try {
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
|
||||
ProjectFileManager.xStreamSerializer.toXML(variantPkg, out);
|
||||
out.close();
|
||||
LOG.info("Exported variants succesfully");
|
||||
@@ -1117,7 +1125,7 @@ public class ActionFactory {
|
||||
LOG.debug("Exporting variants to " + file.getAbsolutePath());
|
||||
|
||||
try {
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
|
||||
ProjectFileManager.xStreamSerializer.toXML(variantPkg, out);
|
||||
out.close();
|
||||
LOG.info("Exported building blocks succesfully");
|
||||
@@ -1237,8 +1245,8 @@ public class ActionFactory {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
LOG.info("Cut triggered");
|
||||
clipboard.setContents(
|
||||
ComponentTransferableFactory.getInstance().build(plugInPort.getSelectedComponents(), plugInPort.getCurrentProject().getGroups()),
|
||||
clipboard.setContents(ComponentTransferableFactory.getInstance()
|
||||
.build(plugInPort.getSelectedComponents(), plugInPort.getCurrentProject().getGroups()),
|
||||
clipboardOwner);
|
||||
plugInPort.deleteSelectedComponents();
|
||||
}
|
||||
@@ -1297,8 +1305,8 @@ public class ActionFactory {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
LOG.info("Copy triggered");
|
||||
clipboard.setContents(
|
||||
ComponentTransferableFactory.getInstance().build(plugInPort.getSelectedComponents(), plugInPort.getCurrentProject().getGroups()),
|
||||
clipboard.setContents(ComponentTransferableFactory.getInstance()
|
||||
.build(plugInPort.getSelectedComponents(), plugInPort.getCurrentProject().getGroups()),
|
||||
clipboardOwner);
|
||||
}
|
||||
}
|
||||
@@ -1505,9 +1513,9 @@ public class ActionFactory {
|
||||
this.plugInPort = plugInPort;
|
||||
putValue(AbstractAction.NAME, "Delete Selection");
|
||||
if (Utils.isMac()) {
|
||||
putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0));
|
||||
putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0));
|
||||
} else {
|
||||
putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
|
||||
putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
|
||||
}
|
||||
putValue(AbstractAction.SMALL_ICON, IconLoader.Delete.getIcon());
|
||||
}
|
||||
@@ -1779,9 +1787,9 @@ public class ActionFactory {
|
||||
putValue(IView.CHECK_BOX_MENU_ITEM, true);
|
||||
putValue(AbstractAction.SELECTED_KEY,
|
||||
ConfigurationManager.getInstance().readBoolean(configKey, defaultValue));
|
||||
|
||||
|
||||
LOG.info("Initializing " + configKey + " to " + getValue(AbstractAction.SELECTED_KEY));
|
||||
|
||||
|
||||
ConfigurationManager.getInstance().addConfigListener(configKey, new IConfigListener() {
|
||||
|
||||
@Override
|
||||
@@ -1815,7 +1823,8 @@ public class ActionFactory {
|
||||
|
||||
private String configKey;
|
||||
|
||||
public ToggleAction(String title, String configKey, String groupName, String defaultValue, Icon icon) {
|
||||
public ToggleAction(String title, String configKey, String groupName, String defaultValue,
|
||||
Icon icon) {
|
||||
super();
|
||||
this.configKey = configKey;
|
||||
putValue(AbstractAction.NAME, title);
|
||||
@@ -1836,7 +1845,8 @@ public class ActionFactory {
|
||||
|
||||
@Override
|
||||
public void valueChanged(String key, Object value) {
|
||||
putValue(AbstractAction.SELECTED_KEY, getValue(AbstractAction.NAME).toString().equalsIgnoreCase(value.toString()));
|
||||
putValue(AbstractAction.SELECTED_KEY,
|
||||
getValue(AbstractAction.NAME).toString().equalsIgnoreCase(value.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1915,12 +1925,19 @@ public class ActionFactory {
|
||||
|
||||
private IPlugInPort plugInPort;
|
||||
private ISwingUI swingUI;
|
||||
private boolean includeSwitches;
|
||||
|
||||
public GenerateNetlistAction(IPlugInPort plugInPort, ISwingUI swingUI) {
|
||||
public GenerateNetlistAction(IPlugInPort plugInPort, ISwingUI swingUI,
|
||||
boolean includeSwitches) {
|
||||
super();
|
||||
this.plugInPort = plugInPort;
|
||||
this.swingUI = swingUI;
|
||||
putValue(AbstractAction.NAME, "Generate DIYLC Netlist");
|
||||
this.includeSwitches = includeSwitches;
|
||||
if (includeSwitches) {
|
||||
putValue(AbstractAction.NAME, "Generate DIYLC Netlist (incl. Switches)");
|
||||
} else {
|
||||
putValue(AbstractAction.NAME, "Generate DIYLC Netlist (excl. Switches)");
|
||||
}
|
||||
putValue(AbstractAction.SMALL_ICON, IconLoader.Web.getIcon());
|
||||
}
|
||||
|
||||
@@ -1930,7 +1947,8 @@ public class ActionFactory {
|
||||
|
||||
@Override
|
||||
public List<Netlist> doInBackground() throws Exception {
|
||||
return plugInPort.extractNetlists(true);
|
||||
LOG.info("Generating netlist, includeSwitches = " + includeSwitches + "...");
|
||||
return plugInPort.extractNetlists(includeSwitches);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1941,6 +1959,7 @@ public class ActionFactory {
|
||||
|
||||
@Override
|
||||
public void complete(List<Netlist> res) {
|
||||
LOG.info("Finished fenerating netlist");
|
||||
if (res == null) {
|
||||
swingUI.showMessage("The generated netlist is empty, nothing to show.", "DIYLC Netlist",
|
||||
ISwingUI.INFORMATION_MESSAGE);
|
||||
@@ -1950,8 +1969,12 @@ public class ActionFactory {
|
||||
|
||||
for (Netlist netlist : res) {
|
||||
sb.append("<p style=\"font-family: " + new JLabel().getFont().getName()
|
||||
+ "; font-size: 9px\"><b>Switch configuration: ").append(netlist.getSwitchSetup())
|
||||
.append("</b><br><br>Connected node groups:<br>");
|
||||
+ "; font-size: 9px\">");
|
||||
if (includeSwitches) {
|
||||
sb.append("<b>Switch configuration: ").append(netlist.getSwitchSetup())
|
||||
.append("</b><br><br>");
|
||||
}
|
||||
sb.append("Connected node groups:<br><br>");
|
||||
for (Group v : netlist.getSortedGroups()) {
|
||||
sb.append(" ").append(v.getSortedNodes()).append("<br>");
|
||||
}
|
||||
@@ -2035,7 +2058,7 @@ public class ActionFactory {
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class CheckProximityAction extends AbstractAction implements Cloneable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -2047,65 +2070,75 @@ public class ActionFactory {
|
||||
public CheckProximityAction(IPlugInPort plugInPort, ISwingUI swingUI) {
|
||||
super();
|
||||
this.plugInPort = plugInPort;
|
||||
this.swingUI = swingUI;
|
||||
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);
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
PropertyWrapper wrapper = new PropertyWrapper("Threshold", Size.class, "getThreshold",
|
||||
"setThreshold", false, new PositiveNonZeroMeasureValidator(), 0);
|
||||
try {
|
||||
wrapper.readFrom(CheckProximityAction.this);
|
||||
wrapper.readFrom(CheckProximityAction.this);
|
||||
} catch (Exception e1) {
|
||||
LOG.error("Error reading proximity threshold", e1);
|
||||
}
|
||||
|
||||
|
||||
List<PropertyWrapper> properties = Arrays.asList(wrapper);
|
||||
PropertyEditorDialog editor =
|
||||
DialogFactory.getInstance().createPropertyEditorDialog(properties, (String)CheckProximityAction.this.getValue(NAME), true);
|
||||
PropertyEditorDialog editor = DialogFactory.getInstance().createPropertyEditorDialog(
|
||||
properties, (String) CheckProximityAction.this.getValue(NAME), true);
|
||||
editor.setVisible(true);
|
||||
if (ButtonDialog.OK.equals(editor.getSelectedButtonCaption())) {
|
||||
if (ButtonDialog.OK.equals(editor.getSelectedButtonCaption())) {
|
||||
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(), LangUtil.translate((String)CheckProximityAction.this.getValue(NAME)), ISwingUI.INFORMATION_MESSAGE);
|
||||
swingUI.showMessage(e.getMessage(),
|
||||
LangUtil.translate((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."),
|
||||
LangUtil.translate((String)CheckProximityAction.this.getValue(NAME)), ISwingUI.INFORMATION_MESSAGE);
|
||||
LangUtil.translate((String) CheckProximityAction.this.getValue(NAME)),
|
||||
ISwingUI.INFORMATION_MESSAGE);
|
||||
else
|
||||
swingUI.showMessage(String.format(LangUtil.translate("%s potential proximity issue(s) detected and marked red."), res.size()),
|
||||
LangUtil.translate((String)CheckProximityAction.this.getValue(NAME)), ISwingUI.WARNING_MESSAGE);
|
||||
swingUI
|
||||
.showMessage(
|
||||
String.format(
|
||||
LangUtil.translate(
|
||||
"%s potential proximity issue(s) detected and marked red."),
|
||||
res.size()),
|
||||
LangUtil.translate((String) CheckProximityAction.this.getValue(NAME)),
|
||||
ISwingUI.WARNING_MESSAGE);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return new CheckProximityAction(plugInPort, swingUI);
|
||||
|
||||
@@ -96,7 +96,9 @@ public class FileMenuPlugin implements IPlugIn, IDynamicSubmenuHandler {
|
||||
swingUI.injectMenuAction(actionFactory.createCheckProximityAction(plugInPort, swingUI),
|
||||
ANALYZE_TITLE);
|
||||
swingUI.injectMenuAction(actionFactory.createBomAction(plugInPort), ANALYZE_TITLE);
|
||||
swingUI.injectMenuAction(actionFactory.createGenerateNetlistAction(plugInPort, swingUI),
|
||||
swingUI.injectMenuAction(actionFactory.createGenerateNetlistAction(plugInPort, swingUI, false),
|
||||
ANALYZE_TITLE);
|
||||
swingUI.injectMenuAction(actionFactory.createGenerateNetlistAction(plugInPort, swingUI, true),
|
||||
ANALYZE_TITLE);
|
||||
|
||||
List<INetlistAnalyzer> summarizers = plugInPort.getNetlistAnalyzers();
|
||||
|
||||
@@ -227,8 +227,7 @@ public class RegressionTestRunner {
|
||||
}
|
||||
|
||||
List<Netlist> netlists = presenter.extractNetlists(true);
|
||||
File netlistOutputDir = new File(
|
||||
file.getParentFile().getParentFile().getAbsolutePath() + File.separator + "netlist");
|
||||
File netlistOutputDir = new File(outputDir.getAbsolutePath() + File.separator + "netlist");
|
||||
File netlistOutputFile = new File(netlistOutputDir.getAbsolutePath() + File.separator
|
||||
+ file.getName().replace(".diy", ".txt"));
|
||||
File netlistInputFile = new File(inputDir.getAbsolutePath() + File.separator + "netlist"
|
||||
|
||||
Reference in New Issue
Block a user