@tscircuit/core 0.0.25 → 0.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +22 -4
- package/dist/index.js +95 -52
- package/lib/components/base-components/NormalComponent.ts +2 -11
- package/lib/components/base-components/PrimitiveComponent.ts +34 -0
- package/lib/components/primitive-components/Trace.ts +81 -50
- package/lib/utils/autorouting/findPossibleTraceLayerCombinations.ts +1 -1
- package/lib/utils/autorouting/mergeRoutes.ts +1 -0
- package/lib/utils/components/createNetsFromProps.ts +15 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -133,6 +133,14 @@ declare abstract class PrimitiveComponent<ZodProps extends ZodType = any> extend
|
|
|
133
133
|
*/
|
|
134
134
|
computeSchematicGlobalTransform(): Matrix;
|
|
135
135
|
getSchematicSymbol(variant?: "horz" | "vert" | null): SchSymbol | null;
|
|
136
|
+
/**
|
|
137
|
+
* Subcircuit groups have a prop called "layout" that can include manual
|
|
138
|
+
* placements for pcb components. These are typically added from an IDE
|
|
139
|
+
*/
|
|
140
|
+
_getManualPlacementForComponent(component: PrimitiveComponent): {
|
|
141
|
+
x: number;
|
|
142
|
+
y: number;
|
|
143
|
+
} | null;
|
|
136
144
|
getGlobalPcbPosition(): {
|
|
137
145
|
x: number;
|
|
138
146
|
y: number;
|
|
@@ -401,10 +409,19 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
401
409
|
ports?: undefined;
|
|
402
410
|
portsWithSelectors?: undefined;
|
|
403
411
|
};
|
|
404
|
-
_findConnectedNets():
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
412
|
+
_findConnectedNets(): {
|
|
413
|
+
nets: Net[];
|
|
414
|
+
netsWithSelectors: Array<{
|
|
415
|
+
selector: string;
|
|
416
|
+
net: Net;
|
|
417
|
+
}>;
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
* Get all the traces that are connected in any degree to this trace, this is
|
|
421
|
+
* used during autorouting to routes to pass through traces connected to the
|
|
422
|
+
* same net.
|
|
423
|
+
*/
|
|
424
|
+
_getAllTracesConnectedToSameNet(): Trace[];
|
|
408
425
|
/**
|
|
409
426
|
* Determine if a trace is explicitly connected to a port (not via a net)
|
|
410
427
|
*/
|
|
@@ -413,6 +430,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
413
430
|
* Determine if a trace is explicitly connected to a net (not via a port)
|
|
414
431
|
*/
|
|
415
432
|
_isExplicitlyConnectedToNet(net: Net): boolean;
|
|
433
|
+
doInitialCreateNetsFromProps(): void;
|
|
416
434
|
doInitialSourceTraceRender(): void;
|
|
417
435
|
doInitialPcbTraceRender(): void;
|
|
418
436
|
doInitialSchematicTraceRender(): void;
|
package/dist/index.js
CHANGED
|
@@ -274,7 +274,32 @@ var PrimitiveComponent = class extends Renderable {
|
|
|
274
274
|
if (!config.schematicSymbolName) return null;
|
|
275
275
|
return symbols[`${config.schematicSymbolName}_${variant}`] ?? null;
|
|
276
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Subcircuit groups have a prop called "layout" that can include manual
|
|
279
|
+
* placements for pcb components. These are typically added from an IDE
|
|
280
|
+
*/
|
|
281
|
+
_getManualPlacementForComponent(component) {
|
|
282
|
+
if (!this.isSubcircuit) return null;
|
|
283
|
+
const layout = this.props.layout;
|
|
284
|
+
if (!layout) return null;
|
|
285
|
+
const placementConfig = layout.manual_pcb_placement_config;
|
|
286
|
+
if (!placementConfig) return null;
|
|
287
|
+
for (const position of placementConfig.positions) {
|
|
288
|
+
if (isMatchingSelector(component, position.selector)) {
|
|
289
|
+
const center = applyToPoint(
|
|
290
|
+
this.computePcbGlobalTransform(),
|
|
291
|
+
position.center
|
|
292
|
+
);
|
|
293
|
+
return center;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
277
298
|
getGlobalPcbPosition() {
|
|
299
|
+
const manualPlacement = this.getSubcircuit()._getManualPlacementForComponent(this);
|
|
300
|
+
if (manualPlacement) {
|
|
301
|
+
return manualPlacement;
|
|
302
|
+
}
|
|
278
303
|
return applyToPoint(this.computePcbGlobalTransform(), { x: 0, y: 0 });
|
|
279
304
|
}
|
|
280
305
|
getGlobalSchematicPosition() {
|
|
@@ -1143,6 +1168,17 @@ var Net = class extends PrimitiveComponent {
|
|
|
1143
1168
|
}
|
|
1144
1169
|
};
|
|
1145
1170
|
|
|
1171
|
+
// lib/utils/components/createNetsFromProps.ts
|
|
1172
|
+
var createNetsFromProps = (component, props) => {
|
|
1173
|
+
for (const prop of props) {
|
|
1174
|
+
if (typeof prop === "string" && prop.startsWith("net.")) {
|
|
1175
|
+
if (!component.getSubcircuit().selectOne(prop)) {
|
|
1176
|
+
component.getSubcircuit().add(new Net({ name: prop.split("net.")[1] }));
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1146
1182
|
// lib/components/base-components/NormalComponent.ts
|
|
1147
1183
|
var NormalComponent = class extends PrimitiveComponent {
|
|
1148
1184
|
reactSubtrees = [];
|
|
@@ -1365,17 +1401,7 @@ var NormalComponent = class extends PrimitiveComponent {
|
|
|
1365
1401
|
return newPorts;
|
|
1366
1402
|
}
|
|
1367
1403
|
_createNetsFromProps(propsWithConnections) {
|
|
1368
|
-
|
|
1369
|
-
if (typeof prop === "string" && prop.startsWith("net.")) {
|
|
1370
|
-
if (!this.getSubcircuit().selectOne(prop)) {
|
|
1371
|
-
this.getSubcircuit().add(
|
|
1372
|
-
new Net({
|
|
1373
|
-
name: prop.split(".")[1]
|
|
1374
|
-
})
|
|
1375
|
-
);
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1404
|
+
createNetsFromProps(this, propsWithConnections);
|
|
1379
1405
|
}
|
|
1380
1406
|
/**
|
|
1381
1407
|
* Use data from our props to create ports for this component.
|
|
@@ -1469,7 +1495,6 @@ import "zod";
|
|
|
1469
1495
|
import { traceProps } from "@tscircuit/props";
|
|
1470
1496
|
import {
|
|
1471
1497
|
IJumpAutorouter,
|
|
1472
|
-
autoroute as autoroute2,
|
|
1473
1498
|
getObstaclesFromSoup,
|
|
1474
1499
|
markObstaclesAsConnected
|
|
1475
1500
|
} from "@tscircuit/infgrid-ijump-astar";
|
|
@@ -1558,6 +1583,7 @@ function pdist(a, b) {
|
|
|
1558
1583
|
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
1559
1584
|
}
|
|
1560
1585
|
var mergeRoutes = (routes) => {
|
|
1586
|
+
if (routes.length === 1) return routes[0];
|
|
1561
1587
|
if (routes.some((r) => r.length === 0)) {
|
|
1562
1588
|
throw new Error("Cannot merge routes with zero length");
|
|
1563
1589
|
}
|
|
@@ -1698,17 +1724,35 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1698
1724
|
};
|
|
1699
1725
|
}
|
|
1700
1726
|
_findConnectedNets() {
|
|
1701
|
-
const
|
|
1702
|
-
selector
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1727
|
+
const netsWithSelectors = this.getTracePathNetSelectors().map(
|
|
1728
|
+
(selector) => ({
|
|
1729
|
+
selector,
|
|
1730
|
+
net: this.getSubcircuit().selectOne(selector, { type: "net" })
|
|
1731
|
+
})
|
|
1732
|
+
);
|
|
1733
|
+
const undefinedNets = netsWithSelectors.filter((n) => !n.net);
|
|
1706
1734
|
if (undefinedNets.length > 0) {
|
|
1707
1735
|
this.renderError(
|
|
1708
1736
|
`Could not find net for selector "${undefinedNets[0].selector}" inside ${this}`
|
|
1709
1737
|
);
|
|
1710
1738
|
}
|
|
1711
|
-
return nets;
|
|
1739
|
+
return { netsWithSelectors, nets: netsWithSelectors.map((n) => n.net) };
|
|
1740
|
+
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Get all the traces that are connected in any degree to this trace, this is
|
|
1743
|
+
* used during autorouting to routes to pass through traces connected to the
|
|
1744
|
+
* same net.
|
|
1745
|
+
*/
|
|
1746
|
+
_getAllTracesConnectedToSameNet() {
|
|
1747
|
+
const traces = this.getSubcircuit().selectAll("trace");
|
|
1748
|
+
const myNets = this._findConnectedNets().nets;
|
|
1749
|
+
const myPorts = this._findConnectedPorts().ports ?? [];
|
|
1750
|
+
return traces.filter((t) => {
|
|
1751
|
+
if (t === this) return false;
|
|
1752
|
+
const tNets = t._findConnectedNets().nets;
|
|
1753
|
+
const tPorts = t._findConnectedPorts().ports ?? [];
|
|
1754
|
+
return tNets.some((n) => myNets.includes(n)) || tPorts.some((p) => myPorts.includes(p));
|
|
1755
|
+
});
|
|
1712
1756
|
}
|
|
1713
1757
|
/**
|
|
1714
1758
|
* Determine if a trace is explicitly connected to a port (not via a net)
|
|
@@ -1723,9 +1767,12 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1723
1767
|
* Determine if a trace is explicitly connected to a net (not via a port)
|
|
1724
1768
|
*/
|
|
1725
1769
|
_isExplicitlyConnectedToNet(net) {
|
|
1726
|
-
const nets = this._findConnectedNets().
|
|
1770
|
+
const nets = this._findConnectedNets().nets;
|
|
1727
1771
|
return nets.includes(net);
|
|
1728
1772
|
}
|
|
1773
|
+
doInitialCreateNetsFromProps() {
|
|
1774
|
+
createNetsFromProps(this, this.getTracePathNetSelectors());
|
|
1775
|
+
}
|
|
1729
1776
|
doInitialSourceTraceRender() {
|
|
1730
1777
|
const { db } = this.project;
|
|
1731
1778
|
const { _parsedProps: props, parent } = this;
|
|
@@ -1735,10 +1782,10 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1735
1782
|
}
|
|
1736
1783
|
const { allPortsFound, portsWithSelectors: ports } = this._findConnectedPorts();
|
|
1737
1784
|
if (!allPortsFound) return;
|
|
1738
|
-
const nets = this._findConnectedNets();
|
|
1785
|
+
const nets = this._findConnectedNets().nets;
|
|
1739
1786
|
const trace = db.source_trace.insert({
|
|
1740
1787
|
connected_source_port_ids: ports.map((p) => p.port.source_port_id),
|
|
1741
|
-
connected_source_net_ids: nets.map((n) => n.
|
|
1788
|
+
connected_source_net_ids: nets.map((n) => n.source_net_id)
|
|
1742
1789
|
});
|
|
1743
1790
|
this.source_trace_id = trace.source_trace_id;
|
|
1744
1791
|
}
|
|
@@ -1749,7 +1796,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1749
1796
|
const { allPortsFound, ports } = this._findConnectedPorts();
|
|
1750
1797
|
const portsConnectedOnPcbViaNet = [];
|
|
1751
1798
|
if (!allPortsFound) return;
|
|
1752
|
-
const nets = this._findConnectedNets();
|
|
1799
|
+
const nets = this._findConnectedNets().netsWithSelectors;
|
|
1753
1800
|
if (ports.length === 0 && nets.length === 2) {
|
|
1754
1801
|
this.renderError(
|
|
1755
1802
|
`Trace connects two nets, we haven't implemented a way to route this yet`
|
|
@@ -1775,7 +1822,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1775
1822
|
return;
|
|
1776
1823
|
}
|
|
1777
1824
|
const pcbElements = db.toArray().filter(
|
|
1778
|
-
(elm) => elm.type === "pcb_smtpad" || elm.type === "pcb_trace" || elm.type === "pcb_plated_hole" || elm.type === "pcb_hole" || elm.type === "source_port" || elm.type === "pcb_port"
|
|
1825
|
+
(elm) => elm.type === "pcb_smtpad" || elm.type === "pcb_trace" || elm.type === "pcb_plated_hole" || elm.type === "pcb_hole" || elm.type === "source_port" || elm.type === "pcb_port" || elm.type === "source_trace"
|
|
1779
1826
|
);
|
|
1780
1827
|
const source_trace = db.source_trace.get(this.source_trace_id);
|
|
1781
1828
|
const hints = ports.flatMap(
|
|
@@ -1803,35 +1850,19 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1803
1850
|
if (isAlreadyRouted) {
|
|
1804
1851
|
return;
|
|
1805
1852
|
}
|
|
1853
|
+
let orderedRouteObjectives = [];
|
|
1806
1854
|
if (pcbRouteHints.length === 0) {
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
);
|
|
1818
|
-
const inputPcbTrace = solution[0];
|
|
1819
|
-
if (!inputPcbTrace) {
|
|
1820
|
-
console.log(
|
|
1821
|
-
`Failed to find route from ${ports[0]} to ${ports[1]} (TODO render error!)`
|
|
1822
|
-
);
|
|
1823
|
-
return;
|
|
1824
|
-
}
|
|
1825
|
-
const pcb_trace2 = db.pcb_trace.insert(inputPcbTrace);
|
|
1826
|
-
this.pcb_trace_id = pcb_trace2.pcb_trace_id;
|
|
1827
|
-
this._portsRoutedOnPcb = ports;
|
|
1828
|
-
return;
|
|
1855
|
+
orderedRouteObjectives = [
|
|
1856
|
+
portToObjective(ports[0]),
|
|
1857
|
+
portToObjective(ports[1])
|
|
1858
|
+
];
|
|
1859
|
+
} else {
|
|
1860
|
+
orderedRouteObjectives = [
|
|
1861
|
+
portToObjective(ports[0]),
|
|
1862
|
+
...pcbRouteHints,
|
|
1863
|
+
portToObjective(ports[1])
|
|
1864
|
+
];
|
|
1829
1865
|
}
|
|
1830
|
-
const orderedRouteObjectives = [
|
|
1831
|
-
portToObjective(ports[0]),
|
|
1832
|
-
...pcbRouteHints,
|
|
1833
|
-
portToObjective(ports[1])
|
|
1834
|
-
];
|
|
1835
1866
|
const candidateLayerCombinations = findPossibleTraceLayerCombinations(
|
|
1836
1867
|
orderedRouteObjectives
|
|
1837
1868
|
);
|
|
@@ -1846,6 +1877,16 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1846
1877
|
orderedRouteObjectives,
|
|
1847
1878
|
this.source_trace_id
|
|
1848
1879
|
);
|
|
1880
|
+
const allConnectedTraceIds = this._getAllTracesConnectedToSameNet().map(
|
|
1881
|
+
(t) => t.source_trace_id
|
|
1882
|
+
);
|
|
1883
|
+
for (const obstacle of obstacles) {
|
|
1884
|
+
if (obstacle.connectedTo.some(
|
|
1885
|
+
(connection) => allConnectedTraceIds.includes(connection)
|
|
1886
|
+
)) {
|
|
1887
|
+
obstacle.connectedTo.push(this.source_trace_id);
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1849
1890
|
const candidateLayerSelections = candidateLayerCombinations[0].layer_path;
|
|
1850
1891
|
const orderedRoutePoints = orderedRouteObjectives.map((t, idx) => {
|
|
1851
1892
|
if (t.via) {
|
|
@@ -1860,6 +1901,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1860
1901
|
for (const [a, b] of pairs(orderedRoutePoints)) {
|
|
1861
1902
|
const BOUNDS_MARGIN = 2;
|
|
1862
1903
|
const ijump = new IJumpAutorouter({
|
|
1904
|
+
OBSTACLE_MARGIN: 0.3,
|
|
1863
1905
|
input: {
|
|
1864
1906
|
obstacles,
|
|
1865
1907
|
connections: [
|
|
@@ -1879,8 +1921,8 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1879
1921
|
});
|
|
1880
1922
|
const traces = ijump.solveAndMapToTraces();
|
|
1881
1923
|
if (traces.length === 0) {
|
|
1882
|
-
|
|
1883
|
-
`Could not find a route between ${a
|
|
1924
|
+
console.log(
|
|
1925
|
+
`Could not find a route between ${a} and ${b} for ${this} (TODO render error)`
|
|
1884
1926
|
);
|
|
1885
1927
|
return;
|
|
1886
1928
|
}
|
|
@@ -1891,6 +1933,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1891
1933
|
route: mergeRoutes(routes),
|
|
1892
1934
|
source_trace_id: this.source_trace_id
|
|
1893
1935
|
});
|
|
1936
|
+
this._portsRoutedOnPcb = ports;
|
|
1894
1937
|
this.pcb_trace_id = pcb_trace.pcb_trace_id;
|
|
1895
1938
|
}
|
|
1896
1939
|
doInitialSchematicTraceRender() {
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { getPortFromHints } from "lib/utils/getPortFromHints"
|
|
18
18
|
import { createComponentsFromSoup } from "lib/utils/createComponentsFromSoup"
|
|
19
19
|
import { Net } from "../primitive-components/Net"
|
|
20
|
+
import { createNetsFromProps } from "lib/utils/components/createNetsFromProps"
|
|
20
21
|
|
|
21
22
|
export type PortMap<T extends string> = {
|
|
22
23
|
[K in T]: Port
|
|
@@ -309,17 +310,7 @@ export class NormalComponent<
|
|
|
309
310
|
}
|
|
310
311
|
|
|
311
312
|
_createNetsFromProps(propsWithConnections: (string | undefined | null)[]) {
|
|
312
|
-
|
|
313
|
-
if (typeof prop === "string" && prop.startsWith("net.")) {
|
|
314
|
-
if (!this.getSubcircuit().selectOne(prop)) {
|
|
315
|
-
this.getSubcircuit().add(
|
|
316
|
-
new Net({
|
|
317
|
-
name: prop.split(".")[1],
|
|
318
|
-
}),
|
|
319
|
-
)
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
313
|
+
createNetsFromProps(this, propsWithConnections)
|
|
323
314
|
}
|
|
324
315
|
|
|
325
316
|
/**
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type Matrix,
|
|
16
16
|
} from "transformation-matrix"
|
|
17
17
|
import { isMatchingSelector } from "lib/utils/selector-matching"
|
|
18
|
+
import type { LayoutBuilder } from "@tscircuit/layout"
|
|
18
19
|
|
|
19
20
|
export interface BaseComponentConfig {
|
|
20
21
|
schematicSymbolName?: BaseSymbolName | null
|
|
@@ -168,7 +169,40 @@ export abstract class PrimitiveComponent<
|
|
|
168
169
|
)
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Subcircuit groups have a prop called "layout" that can include manual
|
|
174
|
+
* placements for pcb components. These are typically added from an IDE
|
|
175
|
+
*/
|
|
176
|
+
_getManualPlacementForComponent(
|
|
177
|
+
component: PrimitiveComponent,
|
|
178
|
+
): { x: number; y: number } | null {
|
|
179
|
+
if (!this.isSubcircuit) return null
|
|
180
|
+
|
|
181
|
+
const layout: LayoutBuilder = this.props.layout
|
|
182
|
+
if (!layout) return null
|
|
183
|
+
|
|
184
|
+
const placementConfig = layout.manual_pcb_placement_config
|
|
185
|
+
if (!placementConfig) return null
|
|
186
|
+
|
|
187
|
+
for (const position of placementConfig.positions) {
|
|
188
|
+
if (isMatchingSelector(component, position.selector)) {
|
|
189
|
+
const center = applyToPoint(
|
|
190
|
+
this.computePcbGlobalTransform(),
|
|
191
|
+
position.center,
|
|
192
|
+
)
|
|
193
|
+
return center
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return null
|
|
198
|
+
}
|
|
199
|
+
|
|
171
200
|
getGlobalPcbPosition(): { x: number; y: number } {
|
|
201
|
+
const manualPlacement =
|
|
202
|
+
this.getSubcircuit()._getManualPlacementForComponent(this)
|
|
203
|
+
if (manualPlacement) {
|
|
204
|
+
return manualPlacement
|
|
205
|
+
}
|
|
172
206
|
return applyToPoint(this.computePcbGlobalTransform(), { x: 0, y: 0 })
|
|
173
207
|
}
|
|
174
208
|
|
|
@@ -22,12 +22,16 @@ import type {
|
|
|
22
22
|
import { computeObstacleBounds } from "lib/utils/autorouting/computeObstacleBounds"
|
|
23
23
|
import { projectPointInDirection } from "lib/utils/projectPointInDirection"
|
|
24
24
|
import type { TraceHint } from "./TraceHint"
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
findPossibleTraceLayerCombinations,
|
|
27
|
+
type CandidateTraceLayerCombination,
|
|
28
|
+
} from "lib/utils/autorouting/findPossibleTraceLayerCombinations"
|
|
26
29
|
import { pairs } from "lib/utils/pairs"
|
|
27
30
|
import { mergeRoutes } from "lib/utils/autorouting/mergeRoutes"
|
|
28
31
|
import type { Net } from "./Net"
|
|
29
32
|
import { getClosest } from "lib/utils/getClosest"
|
|
30
33
|
import { z } from "zod"
|
|
34
|
+
import { createNetsFromProps } from "lib/utils/components/createNetsFromProps"
|
|
31
35
|
|
|
32
36
|
type PcbRouteObjective =
|
|
33
37
|
| RouteHintPoint
|
|
@@ -144,20 +148,47 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
144
148
|
}
|
|
145
149
|
}
|
|
146
150
|
|
|
147
|
-
_findConnectedNets():
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
_findConnectedNets(): {
|
|
152
|
+
nets: Net[]
|
|
153
|
+
netsWithSelectors: Array<{ selector: string; net: Net }>
|
|
154
|
+
} {
|
|
155
|
+
const netsWithSelectors = this.getTracePathNetSelectors().map(
|
|
156
|
+
(selector) => ({
|
|
157
|
+
selector,
|
|
158
|
+
net: this.getSubcircuit().selectOne(selector, { type: "net" }) as Net,
|
|
159
|
+
}),
|
|
160
|
+
)
|
|
152
161
|
|
|
153
|
-
const undefinedNets =
|
|
162
|
+
const undefinedNets = netsWithSelectors.filter((n) => !n.net)
|
|
154
163
|
if (undefinedNets.length > 0) {
|
|
155
164
|
this.renderError(
|
|
156
165
|
`Could not find net for selector "${undefinedNets[0].selector}" inside ${this}`,
|
|
157
166
|
)
|
|
158
167
|
}
|
|
159
168
|
|
|
160
|
-
return nets
|
|
169
|
+
return { netsWithSelectors, nets: netsWithSelectors.map((n) => n.net) }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get all the traces that are connected in any degree to this trace, this is
|
|
174
|
+
* used during autorouting to routes to pass through traces connected to the
|
|
175
|
+
* same net.
|
|
176
|
+
*/
|
|
177
|
+
_getAllTracesConnectedToSameNet(): Trace[] {
|
|
178
|
+
const traces = this.getSubcircuit().selectAll("trace") as Trace[]
|
|
179
|
+
|
|
180
|
+
const myNets = this._findConnectedNets().nets
|
|
181
|
+
const myPorts = this._findConnectedPorts().ports ?? []
|
|
182
|
+
|
|
183
|
+
return traces.filter((t) => {
|
|
184
|
+
if (t === this) return false
|
|
185
|
+
const tNets = t._findConnectedNets().nets
|
|
186
|
+
const tPorts = t._findConnectedPorts().ports ?? []
|
|
187
|
+
return (
|
|
188
|
+
tNets.some((n) => myNets.includes(n)) ||
|
|
189
|
+
tPorts.some((p) => myPorts.includes(p))
|
|
190
|
+
)
|
|
191
|
+
})
|
|
161
192
|
}
|
|
162
193
|
|
|
163
194
|
/**
|
|
@@ -175,10 +206,14 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
175
206
|
* Determine if a trace is explicitly connected to a net (not via a port)
|
|
176
207
|
*/
|
|
177
208
|
_isExplicitlyConnectedToNet(net: Net) {
|
|
178
|
-
const nets = this._findConnectedNets().
|
|
209
|
+
const nets = this._findConnectedNets().nets
|
|
179
210
|
return nets.includes(net)
|
|
180
211
|
}
|
|
181
212
|
|
|
213
|
+
doInitialCreateNetsFromProps(): void {
|
|
214
|
+
createNetsFromProps(this, this.getTracePathNetSelectors())
|
|
215
|
+
}
|
|
216
|
+
|
|
182
217
|
doInitialSourceTraceRender(): void {
|
|
183
218
|
const { db } = this.project!
|
|
184
219
|
const { _parsedProps: props, parent } = this
|
|
@@ -192,11 +227,11 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
192
227
|
this._findConnectedPorts()
|
|
193
228
|
if (!allPortsFound) return
|
|
194
229
|
|
|
195
|
-
const nets = this._findConnectedNets()
|
|
230
|
+
const nets = this._findConnectedNets().nets
|
|
196
231
|
|
|
197
232
|
const trace = db.source_trace.insert({
|
|
198
233
|
connected_source_port_ids: ports.map((p) => p.port.source_port_id!),
|
|
199
|
-
connected_source_net_ids: nets.map((n) => n.
|
|
234
|
+
connected_source_net_ids: nets.map((n) => n.source_net_id!),
|
|
200
235
|
})
|
|
201
236
|
|
|
202
237
|
this.source_trace_id = trace.source_trace_id
|
|
@@ -213,7 +248,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
213
248
|
|
|
214
249
|
if (!allPortsFound) return
|
|
215
250
|
|
|
216
|
-
const nets = this._findConnectedNets()
|
|
251
|
+
const nets = this._findConnectedNets().netsWithSelectors
|
|
217
252
|
|
|
218
253
|
if (ports.length === 0 && nets.length === 2) {
|
|
219
254
|
// Find the two optimal points to connect the two nets
|
|
@@ -254,7 +289,8 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
254
289
|
elm.type === "pcb_plated_hole" ||
|
|
255
290
|
elm.type === "pcb_hole" ||
|
|
256
291
|
elm.type === "source_port" ||
|
|
257
|
-
elm.type === "pcb_port"
|
|
292
|
+
elm.type === "pcb_port" ||
|
|
293
|
+
elm.type === "source_trace",
|
|
258
294
|
)
|
|
259
295
|
|
|
260
296
|
const source_trace = db.source_trace.get(this.source_trace_id!)!
|
|
@@ -296,44 +332,24 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
296
332
|
return
|
|
297
333
|
}
|
|
298
334
|
|
|
299
|
-
|
|
300
|
-
const { solution } = autoroute(
|
|
301
|
-
pcbElements.concat([
|
|
302
|
-
{
|
|
303
|
-
...source_trace,
|
|
304
|
-
// manually override b/c some of the ports may be connected via nets
|
|
305
|
-
// so they don't appear properly in the source_trace, we don't need
|
|
306
|
-
// to do this if the algorithm correctly looks at connected_source_net_ids
|
|
307
|
-
connected_source_port_ids: ports.map((p) => p.source_port_id!),
|
|
308
|
-
} as SourceTrace,
|
|
309
|
-
]),
|
|
310
|
-
)
|
|
311
|
-
// TODO for some reason, the solution gets duplicated inside ijump-astar
|
|
312
|
-
const inputPcbTrace = solution[0]
|
|
313
|
-
|
|
314
|
-
if (!inputPcbTrace) {
|
|
315
|
-
// TODO render error indicating we could not find a route
|
|
316
|
-
console.log(
|
|
317
|
-
`Failed to find route from ${ports[0]} to ${ports[1]} (TODO render error!)`,
|
|
318
|
-
)
|
|
319
|
-
return
|
|
320
|
-
}
|
|
321
|
-
const pcb_trace = db.pcb_trace.insert(inputPcbTrace as any)
|
|
335
|
+
let orderedRouteObjectives: PcbRouteObjective[] = []
|
|
322
336
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
337
|
+
if (pcbRouteHints.length === 0) {
|
|
338
|
+
orderedRouteObjectives = [
|
|
339
|
+
portToObjective(ports[0]),
|
|
340
|
+
portToObjective(ports[1]),
|
|
341
|
+
]
|
|
342
|
+
} else {
|
|
343
|
+
// When we have hints, we have to order the hints then route between each
|
|
344
|
+
// terminal of the trace and the hints
|
|
345
|
+
// TODO order based on proximity to ports
|
|
346
|
+
orderedRouteObjectives = [
|
|
347
|
+
portToObjective(ports[0]),
|
|
348
|
+
...pcbRouteHints,
|
|
349
|
+
portToObjective(ports[1]),
|
|
350
|
+
]
|
|
326
351
|
}
|
|
327
352
|
|
|
328
|
-
// When we have hints, we have to order the hints then route between each
|
|
329
|
-
// terminal of the trace and the hints
|
|
330
|
-
// TODO order based on proximity to ports
|
|
331
|
-
const orderedRouteObjectives: PcbRouteObjective[] = [
|
|
332
|
-
portToObjective(ports[0]),
|
|
333
|
-
...pcbRouteHints,
|
|
334
|
-
portToObjective(ports[1]),
|
|
335
|
-
]
|
|
336
|
-
|
|
337
353
|
// Hints can indicate where there should be a via, but the layer is allowed
|
|
338
354
|
// to be unspecified, therefore we need to find possible layer combinations
|
|
339
355
|
// to go to each hint and still route to the start and end points
|
|
@@ -356,6 +372,19 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
356
372
|
this.source_trace_id!,
|
|
357
373
|
)
|
|
358
374
|
|
|
375
|
+
const allConnectedTraceIds = this._getAllTracesConnectedToSameNet().map(
|
|
376
|
+
(t) => t.source_trace_id,
|
|
377
|
+
)
|
|
378
|
+
for (const obstacle of obstacles) {
|
|
379
|
+
if (
|
|
380
|
+
obstacle.connectedTo.some((connection) =>
|
|
381
|
+
allConnectedTraceIds.includes(connection),
|
|
382
|
+
)
|
|
383
|
+
) {
|
|
384
|
+
obstacle.connectedTo.push(this.source_trace_id!)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
359
388
|
// TODO explore all candidate layer combinations if one fails
|
|
360
389
|
const candidateLayerSelections = candidateLayerCombinations[0].layer_path
|
|
361
390
|
|
|
@@ -377,6 +406,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
377
406
|
for (const [a, b] of pairs(orderedRoutePoints)) {
|
|
378
407
|
const BOUNDS_MARGIN = 2 //mm
|
|
379
408
|
const ijump = new IJumpAutorouter({
|
|
409
|
+
OBSTACLE_MARGIN: 0.3,
|
|
380
410
|
input: {
|
|
381
411
|
obstacles,
|
|
382
412
|
connections: [
|
|
@@ -396,8 +426,8 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
396
426
|
})
|
|
397
427
|
const traces = ijump.solveAndMapToTraces()
|
|
398
428
|
if (traces.length === 0) {
|
|
399
|
-
|
|
400
|
-
`Could not find a route between ${a
|
|
429
|
+
console.log(
|
|
430
|
+
`Could not find a route between ${a} and ${b} for ${this} (TODO render error)`,
|
|
401
431
|
)
|
|
402
432
|
return
|
|
403
433
|
}
|
|
@@ -410,6 +440,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
410
440
|
route: mergeRoutes(routes),
|
|
411
441
|
source_trace_id: this.source_trace_id!,
|
|
412
442
|
})
|
|
443
|
+
this._portsRoutedOnPcb = ports
|
|
413
444
|
this.pcb_trace_id = pcb_trace.pcb_trace_id
|
|
414
445
|
}
|
|
415
446
|
|
|
@@ -13,7 +13,7 @@ const LAYER_SELECTION_PREFERENCE = ["top", "bottom", "inner1", "inner2"]
|
|
|
13
13
|
* top -> bottom -> bottom -> top
|
|
14
14
|
* bottom -> top -> top -> bottom
|
|
15
15
|
*/
|
|
16
|
-
interface CandidateTraceLayerCombination {
|
|
16
|
+
export interface CandidateTraceLayerCombination {
|
|
17
17
|
layer_path: string[]
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -11,6 +11,7 @@ function pdist(a: any, b: any) {
|
|
|
11
11
|
* reverse the next route and append it to the previous route.
|
|
12
12
|
*/
|
|
13
13
|
export const mergeRoutes = (routes: PCBTrace["route"][]) => {
|
|
14
|
+
if (routes.length === 1) return routes[0]
|
|
14
15
|
// routes = routes.filter((route) => route.length > 0)
|
|
15
16
|
if (routes.some((r) => r.length === 0)) {
|
|
16
17
|
throw new Error("Cannot merge routes with zero length")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PrimitiveComponent } from "lib/components/base-components/PrimitiveComponent"
|
|
2
|
+
import { Net } from "lib/components/primitive-components/Net"
|
|
3
|
+
|
|
4
|
+
export const createNetsFromProps = (
|
|
5
|
+
component: PrimitiveComponent,
|
|
6
|
+
props: (string | undefined | null)[],
|
|
7
|
+
) => {
|
|
8
|
+
for (const prop of props) {
|
|
9
|
+
if (typeof prop === "string" && prop.startsWith("net.")) {
|
|
10
|
+
if (!component.getSubcircuit().selectOne(prop)) {
|
|
11
|
+
component.getSubcircuit().add(new Net({ name: prop.split("net.")[1] }))
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@tscircuit/core",
|
|
3
3
|
"module": "index.ts",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.27",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"files": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@biomejs/biome": "^1.8.3",
|
|
19
|
-
"@tscircuit/layout": "^0.0.
|
|
19
|
+
"@tscircuit/layout": "^0.0.28",
|
|
20
20
|
"@tscircuit/log-soup": "^1.0.2",
|
|
21
21
|
"@types/bun": "latest",
|
|
22
22
|
"@types/react": "^18.3.3",
|