netlist fixes

This commit is contained in:
Bane Stojkovic
2025-04-25 22:50:07 +02:00
parent 5c09ebf7f8
commit 355c9f2b49
9 changed files with 358 additions and 187 deletions
@@ -92,7 +92,7 @@ public abstract class AbstractNetlistAnalyzer {
for (int i = 0; i < c.getControlPointCount(); i++) {
if (i != n.getPointIndex() && c.getInternalLinkName(i, n.getPointIndex()) != null) {
TreeLeaf l = new TreeLeaf(c, i, n.getPointIndex());
Node newNodeA = new Node(c, i, n.getZIndex());
Node newNodeA = new Node(c, i);
if (!visited.contains(newNodeA)) {
// visited.add(n);
Tree newCurrentPath = null;
@@ -53,11 +53,7 @@ public class Group implements Comparable<Group> {
}
public Group connect(IDIYComponent<?> component, int index) {
return connect(component, index, 0);
}
public Group connect(IDIYComponent<?> component, int index, int zOrder) {
getNodes().add(new Node(component, index, zOrder));
getNodes().add(new Node(component, index));
return this;
}
@@ -92,7 +92,7 @@ public abstract class NetlistAnalyzer {
continue;
if (c.getInternalLinkName(targetPoint, sourcePoint) != null) {
Node newNodeA = new Node(c, targetPoint, n.getZIndex());
Node newNodeA = new Node(c, targetPoint);
if (pathVisited.contains(newNodeA))
continue;
@@ -75,7 +75,7 @@ public class NetlistBuilder {
String nodeName = c.getControlPointNodeName(i);
if (c.isControlPointSticky(i) && nodeName != null
&& (!includeSwitches || !ISwitch.class.isAssignableFrom(type.getInstanceClass()))) {
nodes.add(new Node(c, i, j));
nodes.add(new Node(c, i));
}
}
}
@@ -86,7 +86,7 @@ public class NetlistBuilder {
String label = ((ICommonNode) c).getCommonNodeLabel();
if (!commonNodes.contains(label)) {
commonNodes.add(label);
nodes.add(new Node(c, 0, j));
nodes.add(new Node(c, 0));
}
}
@@ -22,6 +22,7 @@
package org.diylc.netlist;
import java.awt.geom.Point2D;
import java.util.Objects;
import org.diylc.core.ICommonNode;
import org.diylc.core.IDIYComponent;
@@ -35,13 +36,11 @@ public class Node implements Comparable<Node> {
private IDIYComponent<?> component;
private int pointIndex;
private int zIndex;
public Node(IDIYComponent<?> component, int pointIndex, int zIndex) {
public Node(IDIYComponent<?> component, int pointIndex) {
super();
this.component = component;
this.pointIndex = pointIndex;
this.zIndex = zIndex;
}
public IDIYComponent<?> getComponent() {
@@ -52,10 +51,6 @@ public class Node implements Comparable<Node> {
return pointIndex;
}
public int getZIndex() {
return zIndex;
}
public String getDisplayName() {
String[] section = component.getSectionNames(pointIndex);
return (section == null || section.length > 1 ? "" : section[0] + ".") + component.getControlPointNodeName(pointIndex);
@@ -66,31 +61,16 @@ public class Node implements Comparable<Node> {
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((component == null) ? 0 : component.hashCode());
result = prime * result + pointIndex;
return result;
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Node)) return false;
Node other = (Node) obj;
return Objects.equals(component, other.component) && pointIndex == other.pointIndex;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Node other = (Node) obj;
if (component == null) {
if (other.component != null)
return false;
} else if (!component.equals(other.component))
return false;
if (pointIndex != other.pointIndex)
return false;
return true;
public int hashCode() {
return Objects.hash(component, pointIndex);
}
@Override
@@ -59,7 +59,7 @@ public class SpiceSumarizer {
if (c instanceof ISpiceMapper)
pointIndex = ((ISpiceMapper)c).mapToSpiceNode(pointIndex);
int nodeIndex = find(new Node(c, pointIndex, 0), groups);
int nodeIndex = find(new Node(c, pointIndex), groups);
if (nodeIndex < 0)
nodeIndex = unconnectedIndex++;
@@ -14,6 +14,10 @@ import org.diylc.common.PropertyWrapper;
import org.diylc.core.IDIYComponent;
import org.diylc.core.ISwitch;
import org.diylc.core.Project;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.NetlistBuilder;
import org.diylc.netlist.NetlistException;
import org.diylc.netlist.Node;
import org.diylc.plugins.chatbot.model.*;
import org.diylc.presenter.ComponentProcessor;
import org.diylc.presenter.ContinuityArea;
@@ -27,16 +31,31 @@ public class AiProjectBuilder {
List<AiComponent> components = project.getComponents().stream().map(AiProjectBuilder::mapComponent).toList();
List<Set<NetlistBuilder.TerminalRef>> sets =
NetlistBuilder.buildNets(project.getComponents(), continuityAreas);
// List<Set<Node>> sets =
List<Netlist> netlists = null;
try {
netlists = NetlistBuilder.extractNetlists(false, project, continuityAreas);
} catch (NetlistException e) {
throw new RuntimeException(e);
}
Map<String, Set<String>> nets;
if (netlists == null || netlists.isEmpty()) {
nets = new HashMap<>();
} else {
AtomicInteger counter = new AtomicInteger();
nets = netlists.get(0).getGroups().stream().map(s -> s.getNodes().stream()
.map(AiProjectBuilder::mapTerminal)
.collect(Collectors.toSet()))
.collect(Collectors.toMap(x -> "N" + String.format("%03d", counter.incrementAndGet()),
Function.identity()));
}
AtomicInteger counter = new AtomicInteger();
Map<String, Set<String>> nets =
sets.stream().map(s -> s.stream()
.map(AiProjectBuilder::mapTerminal)
.collect(Collectors.toSet()))
.collect(Collectors.toMap(x -> "N" + String.format("%03d", counter.incrementAndGet()),
Function.identity()));
// Map<String, Set<String>> nets =
// sets.stream().map(s -> s.stream()
// .map(AiProjectBuilder::mapTerminal)
// .collect(Collectors.toSet()))
// .collect(Collectors.toMap(x -> "N" + String.format("%03d", counter.incrementAndGet()),
// Function.identity()));
List<AiSwitch> switches =
project.getComponents().stream()
@@ -161,7 +180,7 @@ public class AiProjectBuilder {
fromPos, toPos, pos, terminals.isEmpty() ? null : terminals);
}
static String mapTerminal(NetlistBuilder.TerminalRef terminalRef) {
return terminalRef.component.getName() + "." + terminalRef.terminalIndex;
static String mapTerminal(Node terminalRef) {
return terminalRef.getComponent().getName() + "." + terminalRef.getPointIndex();
}
}
@@ -1,137 +1,125 @@
package org.diylc.plugins.chatbot.service;
import org.diylc.core.IContinuity;
import org.diylc.core.IDIYComponent;
import org.diylc.core.ISwitch;
import org.diylc.presenter.ContinuityArea;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.util.*;
public class NetlistBuilder {
public static class TerminalRef {
public final IDIYComponent<?> component;
public final int terminalIndex;
public TerminalRef(IDIYComponent<?> component, int terminalIndex) {
this.component = component;
this.terminalIndex = terminalIndex;
}
public Point2D getPosition() {
return component.getControlPoint(terminalIndex);
}
@Override
public String toString() {
return component.getName() + "." + terminalIndex;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof TerminalRef)) return false;
TerminalRef other = (TerminalRef) obj;
return Objects.equals(component, other.component) && terminalIndex == other.terminalIndex;
}
@Override
public int hashCode() {
return Objects.hash(component, terminalIndex);
}
}
public static List<Set<TerminalRef>> buildNets(List<IDIYComponent<?>> components, List<ContinuityArea> continuityAreas) {
Map<TerminalRef, Integer> terminalIndex = new HashMap<>();
List<TerminalRef> allTerminals = new ArrayList<>();
int idx = 0;
for (IDIYComponent<?> c : components) {
for (int i = 0; i < c.getControlPointCount(); i++) {
if (c.isControlPointSticky(i)) {
TerminalRef ref = new TerminalRef(c, i);
terminalIndex.put(ref, idx++);
allTerminals.add(ref);
}
}
}
UnionFind uf = new UnionFind(allTerminals.size());
// Step 1: connect conductive components
for (IDIYComponent<?> c : components) {
if (c instanceof IContinuity continuity && !(c instanceof ISwitch)) {
int count = c.getControlPointCount();
for (int i = 0; i < count; i++) {
for (int j = i + 1; j < count; j++) {
if (continuity.arePointsConnected(i, j)) {
TerminalRef a = new TerminalRef(c, i);
TerminalRef b = new TerminalRef(c, j);
uf.union(terminalIndex.get(a), terminalIndex.get(b));
}
}
}
}
}
// Step 2: connect terminals inside continuity areas
for (ContinuityArea continuity : continuityAreas) {
Area area = continuity.getSimplifiedArea();
List<TerminalRef> inside = new ArrayList<>();
for (TerminalRef t : allTerminals) {
Point2D p = t.getPosition();
if (area.contains(p)) {
inside.add(t);
}
}
for (int i = 0; i < inside.size(); i++) {
for (int j = i + 1; j < inside.size(); j++) {
uf.union(terminalIndex.get(inside.get(i)), terminalIndex.get(inside.get(j)));
}
}
}
Map<Integer, Set<TerminalRef>> netMap = new HashMap<>();
for (TerminalRef t : allTerminals) {
int root = uf.find(terminalIndex.get(t));
netMap.computeIfAbsent(root, k -> new HashSet<>()).add(t);
}
// Filter out nets with only one terminal and remove exact duplicates
List<Set<TerminalRef>> nets = new ArrayList<>();
Set<Set<TerminalRef>> seen = new HashSet<>();
for (Set<TerminalRef> net : netMap.values()) {
if (net.size() > 1 && seen.add(net)) {
nets.add(net);
}
}
return nets;
}
// Union-Find with path compression
static class UnionFind {
int[] parent;
UnionFind(int n) {
parent = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
}
int find(int x) {
if (parent[x] != x)
parent[x] = find(parent[x]);
return parent[x];
}
void union(int x, int y) {
parent[find(x)] = find(y);
}
}
}
//package org.diylc.plugins.chatbot.service;
//
//import org.diylc.core.IContinuity;
//import org.diylc.core.IDIYComponent;
//import org.diylc.core.ISwitch;
//import org.diylc.netlist.Node;
//import org.diylc.presenter.ContinuityArea;
//
//import java.awt.geom.Area;
//import java.awt.geom.Point2D;
//import java.util.*;
//
//public class NetlistBuilder {
// private static final double eps = 1e-6;
//
// public static List<Set<Node>> buildNets(List<IDIYComponent<?>> components, List<ContinuityArea> continuityAreas) {
// Map<Node, Integer> terminalIndex = new HashMap<>();
// List<Node> allTerminals = new ArrayList<>();
// int idx = 0;
//
// for (IDIYComponent<?> c : components) {
// for (int i = 0; i < c.getControlPointCount(); i++) {
// if (c.isControlPointSticky(i)) {
// Node ref = new Node(c, i);
// terminalIndex.put(ref, idx++);
// allTerminals.add(ref);
// }
// }
// }
//
// UnionFind uf = new UnionFind(allTerminals.size());
//
// // Step 1: connect conductive components
// for (IDIYComponent<?> c : components) {
// if (c instanceof IContinuity continuity && !(c instanceof ISwitch)) {
// int count = c.getControlPointCount();
// for (int i = 0; i < count; i++) {
// for (int j = i + 1; j < count; j++) {
// if (continuity.arePointsConnected(i, j)) {
// Node a = new Node(c, i);
// Node b = new Node(c, j);
// uf.union(terminalIndex.get(a), terminalIndex.get(b));
// }
// }
// }
// }
// }
//
// // Step 2: connect terminals inside continuity areas
// for (ContinuityArea continuity : continuityAreas) {
// Area area = continuity.getSimplifiedArea();
// List<Node> inside = new ArrayList<>();
//
// for (Node t : allTerminals) {
// Point2D p = t.getPoint2D();
// if (area.contains(p) || area.intersects(p.getX() - eps/2, p.getY() - eps/2, eps, eps)) {
// inside.add(t);
// }
// }
//
// for (int i = 0; i < inside.size(); i++) {
// for (int j = i + 1; j < inside.size(); j++) {
// uf.union(terminalIndex.get(inside.get(i)), terminalIndex.get(inside.get(j)));
// }
// }
// }
//
// Map<Integer, Set<Node>> netMap = new HashMap<>();
// for (Node t : allTerminals) {
// int root = uf.find(terminalIndex.get(t));
// netMap.computeIfAbsent(root, k -> new HashSet<>()).add(t);
// }
//
// // Filter out nets with only one terminal and remove exact duplicates
// List<Set<Node>> nets = new ArrayList<>();
// Set<Set<Node>> seen = new HashSet<>();
//
// for (Set<Node> net : netMap.values()) {
// if (net.size() > 1 && seen.add(net)) {
// nets.add(net);
// }
// }
// return nets;
//
// }
//
// // Union-Find with path compression and union by size
// static class UnionFind {
// private final int[] parent;
// private final int[] size;
//
// UnionFind(int n) {
// parent = new int[n];
// size = new int[n];
// for (int i = 0; i < n; i++) {
// parent[i] = i;
// size[i] = 1;
// }
// }
//
// int find(int x) {
// if (parent[x] != x) {
// parent[x] = find(parent[x]); // Path compression
// }
// return parent[x];
// }
//
// void union(int x, int y) {
// int rootX = find(x);
// int rootY = find(y);
//
// if (rootX == rootY) return;
//
// // Union by size: attach smaller tree to larger tree
// if (size[rootX] < size[rootY]) {
// parent[rootX] = rootY;
// size[rootY] += size[rootX];
// } else {
// parent[rootY] = rootX;
// size[rootX] += size[rootY];
// }
// }
// }
//}
//
@@ -0,0 +1,188 @@
//package org.diylc.plugins.chatbot.service;
//
//import org.diylc.core.IContinuity;
//import org.diylc.core.IDIYComponent;
//import org.diylc.core.ComponentState;
//import org.diylc.core.IDrawingObserver;
//import org.diylc.core.Project;
//import org.diylc.core.VisibilityPolicy;
//import org.diylc.netlist.Node;
//import org.diylc.presenter.ContinuityArea;
//import org.junit.Test;
//import org.diylc.components.AbstractComponent;
//
//import java.awt.Graphics2D;
//import java.awt.geom.Area;
//import java.awt.geom.Point2D;
//import java.util.*;
//
//import static org.junit.Assert.*;
//
//public class NetlistBuilderTest {
//
// // Mock component that implements IContinuity
// private static class MockConductiveComponent extends AbstractComponent<Void> implements IContinuity {
// private final Point2D[] points;
// private final boolean[][] connections;
//
// public MockConductiveComponent(Point2D[] points, boolean[][] connections) {
// this.points = points;
// this.connections = connections;
// }
//
// @Override
// public int getControlPointCount() {
// return points.length;
// }
//
// @Override
// public Point2D getControlPoint(int index) {
// return points[index];
// }
//
// @Override
// public boolean isControlPointSticky(int index) {
// return true;
// }
//
// @Override
// public boolean arePointsConnected(int index1, int index2) {
// return connections[index1][index2];
// }
//
// @Override
// public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project, IDrawingObserver drawingObserver) {
// // Not needed for testing
// }
//
// @Override
// public void drawIcon(Graphics2D g2d, int width, int height) {
// // Not needed for testing
// }
//
// @Override
// public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
// return VisibilityPolicy.ALWAYS;
// }
//
// @Override
// public void setControlPoint(Point2D point, int index) {
// // Not needed for testing
// }
//
// @Override
// public Void getValue() {
// return null;
// }
//
// @Override
// public void setValue(Void value) {
// // Not needed for testing
// }
// }
//
// // Mock component that doesn't implement IContinuity
// private static class MockNonConductiveComponent extends AbstractComponent<Void> {
// private final Point2D[] points;
//
// public MockNonConductiveComponent(Point2D[] points) {
// this.points = points;
// }
//
// @Override
// public int getControlPointCount() {
// return points.length;
// }
//
// @Override
// public Point2D getControlPoint(int index) {
// return points[index];
// }
//
// @Override
// public boolean isControlPointSticky(int index) {
// return true;
// }
//
// @Override
// public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project, IDrawingObserver drawingObserver) {
// // Not needed for testing
// }
//
// @Override
// public void drawIcon(Graphics2D g2d, int width, int height) {
// // Not needed for testing
// }
//
// @Override
// public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
// return VisibilityPolicy.ALWAYS;
// }
//
// @Override
// public void setControlPoint(Point2D point, int index) {
// // Not needed for testing
// }
//
// @Override
// public Void getValue() {
// return null;
// }
//
// @Override
// public void setValue(Void value) {
// // Not needed for testing
// }
// }
//
// @Test
// public void testBuildNets() {
// // Create test components
// Point2D[] points1 = {
// new Point2D.Double(0, 0),
// new Point2D.Double(10, 0),
// new Point2D.Double(20, 0)
// };
// boolean[][] connections1 = {
// {false, true, false},
// {true, false, true},
// {false, true, false}
// };
// MockConductiveComponent comp1 = new MockConductiveComponent(points1, connections1);
//
// Point2D[] points2 = {
// new Point2D.Double(30, 0),
// new Point2D.Double(40, 0)
// };
// MockNonConductiveComponent comp2 = new MockNonConductiveComponent(points2);
//
// // Create a continuity area that connects comp1 and comp2
// Area area = new Area();
// area.add(new Area(new java.awt.geom.Rectangle2D.Double(15, -5, 20, 10)));
// ContinuityArea continuityArea = new ContinuityArea(0, area);
//
// // Build nets
// List<IDIYComponent<?>> components = Arrays.asList(comp1, comp2);
// List<ContinuityArea> continuityAreas = Collections.singletonList(continuityArea);
// List<Set<Node>> nets = NetlistBuilder.buildNets(components, continuityAreas);
//
// // Verify results
// assertEquals("Should have exactly one net", 1, nets.size());
// Set<Node> net = nets.get(0);
// assertEquals("Net should contain all terminals", 5, net.size());
//
// // Verify that all terminals are connected
// Set<Integer> terminalIndices = new HashSet<>();
// for (Node node : net) {
// terminalIndices.add(node.getPointIndex());
// System.out.println("Found terminal: " + node.getPointIndex() + " at " + node.getPoint2D());
// }
// System.out.println("All terminals: " + terminalIndices);
// assertEquals("Should contain all terminal indices", 5, terminalIndices.size());
// assertTrue("Should contain terminal 0", terminalIndices.contains(0));
// assertTrue("Should contain terminal 1", terminalIndices.contains(1));
// assertTrue("Should contain terminal 2", terminalIndices.contains(2));
// assertTrue("Should contain terminal 3", terminalIndices.contains(3));
// assertTrue("Should contain terminal 4", terminalIndices.contains(4));
// }
//}