@tscircuit/core 0.0.23 → 0.0.25
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 +273 -277
- package/dist/index.js +351 -67
- package/lib/Project.ts +1 -1
- package/lib/components/base-components/NormalComponent.ts +15 -0
- package/lib/components/base-components/PrimitiveComponent.ts +14 -11
- package/lib/components/base-components/Renderable.ts +2 -0
- package/lib/components/normal-components/Board.ts +3 -2
- package/lib/components/normal-components/Capacitor.ts +9 -2
- package/lib/components/normal-components/Resistor.ts +8 -2
- package/lib/components/primitive-components/Group.ts +6 -2
- package/lib/components/primitive-components/Net.ts +159 -0
- package/lib/components/primitive-components/Port.ts +17 -1
- package/lib/components/primitive-components/Trace.ts +153 -18
- package/lib/fiber/intrinsic-jsx.ts +1 -0
- package/lib/utils/getClosest.ts +29 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ import "react";
|
|
|
37
37
|
var orderedRenderPhases = [
|
|
38
38
|
"ReactSubtreesRender",
|
|
39
39
|
// probably going to be removed b/c subtrees should render instantly
|
|
40
|
+
"CreateNetsFromProps",
|
|
40
41
|
"CreateTracesFromProps",
|
|
41
42
|
"SourceRender",
|
|
42
43
|
"SourceParentAttachment",
|
|
@@ -54,6 +55,7 @@ var orderedRenderPhases = [
|
|
|
54
55
|
"PcbParentAttachment",
|
|
55
56
|
"PcbLayout",
|
|
56
57
|
"PcbTraceRender",
|
|
58
|
+
"PcbRouteNetIslands",
|
|
57
59
|
"CadModelRender",
|
|
58
60
|
"PcbAnalysis"
|
|
59
61
|
];
|
|
@@ -174,13 +176,13 @@ var PrimitiveComponent = class extends Renderable {
|
|
|
174
176
|
lowercaseComponentName = "";
|
|
175
177
|
externallyAddedAliases;
|
|
176
178
|
/**
|
|
177
|
-
* An
|
|
178
|
-
*
|
|
179
|
-
*
|
|
179
|
+
* An subcircuit is self-contained. All the selectors inside
|
|
180
|
+
* a subcircuit are relative to the subcircuit group. You can have multiple
|
|
181
|
+
* subcircuits and their selectors will not interact with each other (even if the
|
|
180
182
|
* components share the same names) unless you explicitly break out some ports
|
|
181
183
|
*/
|
|
182
|
-
get
|
|
183
|
-
return Boolean(this.props.
|
|
184
|
+
get isSubcircuit() {
|
|
185
|
+
return Boolean(this.props.subcircuit) || // Implied opaque group for top-level group
|
|
184
186
|
this.lowercaseComponentName === "group" && this?.parent?.props?.name === "$root";
|
|
185
187
|
}
|
|
186
188
|
source_group_id = null;
|
|
@@ -305,12 +307,12 @@ var PrimitiveComponent = class extends Renderable {
|
|
|
305
307
|
this.childrenPendingRemoval.push(component);
|
|
306
308
|
component.shouldBeRemoved = true;
|
|
307
309
|
}
|
|
308
|
-
|
|
310
|
+
getSubcircuitSelector() {
|
|
309
311
|
const name = this._parsedProps.name;
|
|
310
312
|
const endPart = name ? `${this.lowercaseComponentName}.${name}` : this.lowercaseComponentName;
|
|
311
313
|
if (!this.parent) return endPart;
|
|
312
|
-
if (this.parent.
|
|
313
|
-
return `${this.parent.
|
|
314
|
+
if (this.parent.isSubcircuit) return endPart;
|
|
315
|
+
return `${this.parent.getSubcircuitSelector()} > ${endPart}`;
|
|
314
316
|
}
|
|
315
317
|
getFullPathSelector() {
|
|
316
318
|
const name = this._parsedProps.name;
|
|
@@ -347,9 +349,9 @@ var PrimitiveComponent = class extends Renderable {
|
|
|
347
349
|
return true;
|
|
348
350
|
return false;
|
|
349
351
|
}
|
|
350
|
-
|
|
351
|
-
if (this.
|
|
352
|
-
const group = this.parent?.
|
|
352
|
+
getSubcircuit() {
|
|
353
|
+
if (this.isSubcircuit) return this;
|
|
354
|
+
const group = this.parent?.getSubcircuit();
|
|
353
355
|
if (!group)
|
|
354
356
|
throw new Error("Component is not inside an opaque group (no board?)");
|
|
355
357
|
return group;
|
|
@@ -409,6 +411,9 @@ var PrimitiveComponent = class extends Renderable {
|
|
|
409
411
|
if (parent?.props?.name && props?.name) {
|
|
410
412
|
return `<${cname}#${this._renderId}(.${parent?.props.name}>.${props?.name}) />`;
|
|
411
413
|
}
|
|
414
|
+
if (props?.from && props?.to) {
|
|
415
|
+
return `<${cname}#${this._renderId}(from:${props.from} to:${props?.to}) />`;
|
|
416
|
+
}
|
|
412
417
|
if (props?.name) {
|
|
413
418
|
return `<${cname}#${this._renderId} name=".${props?.name}" />`;
|
|
414
419
|
}
|
|
@@ -525,6 +530,18 @@ var Port = class extends PrimitiveComponent {
|
|
|
525
530
|
new Set(this.matchedComponents.flatMap((c) => c.getAvailablePcbLayers()))
|
|
526
531
|
);
|
|
527
532
|
}
|
|
533
|
+
/**
|
|
534
|
+
* Return traces that are explicitly connected to this port (not via a net)
|
|
535
|
+
*/
|
|
536
|
+
_getDirectlyConnectedTraces() {
|
|
537
|
+
const allSubcircuitTraces = this.getSubcircuit().selectAll(
|
|
538
|
+
"trace"
|
|
539
|
+
);
|
|
540
|
+
const connectedTraces = allSubcircuitTraces.filter(
|
|
541
|
+
(trace) => trace._isExplicitlyConnectedToPort(this)
|
|
542
|
+
);
|
|
543
|
+
return connectedTraces;
|
|
544
|
+
}
|
|
528
545
|
doInitialSourceRender() {
|
|
529
546
|
const { db } = this.project;
|
|
530
547
|
const { _parsedProps: props } = this;
|
|
@@ -983,6 +1000,149 @@ var createComponentsFromSoup = (soup) => {
|
|
|
983
1000
|
return components;
|
|
984
1001
|
};
|
|
985
1002
|
|
|
1003
|
+
// lib/components/primitive-components/Net.ts
|
|
1004
|
+
import { z as z3 } from "zod";
|
|
1005
|
+
|
|
1006
|
+
// lib/utils/pairs.ts
|
|
1007
|
+
function pairs(arr) {
|
|
1008
|
+
const result = [];
|
|
1009
|
+
for (let i = 0; i < arr.length - 1; i++) {
|
|
1010
|
+
result.push([arr[i], arr[i + 1]]);
|
|
1011
|
+
}
|
|
1012
|
+
return result;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// lib/components/primitive-components/Net.ts
|
|
1016
|
+
import { autoroute } from "@tscircuit/infgrid-ijump-astar";
|
|
1017
|
+
var netProps = z3.object({
|
|
1018
|
+
name: z3.string()
|
|
1019
|
+
});
|
|
1020
|
+
var Net = class extends PrimitiveComponent {
|
|
1021
|
+
source_net_id;
|
|
1022
|
+
getPortSelector() {
|
|
1023
|
+
return `net.${this.props.name}`;
|
|
1024
|
+
}
|
|
1025
|
+
doInitialSourceComponentRender() {
|
|
1026
|
+
const { db } = this.project;
|
|
1027
|
+
const { _parsedProps: props } = this;
|
|
1028
|
+
const net = db.source_net.insert({
|
|
1029
|
+
name: props.name,
|
|
1030
|
+
member_source_group_ids: []
|
|
1031
|
+
});
|
|
1032
|
+
this.source_net_id = net.source_net_id;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Get all ports connected to this net.
|
|
1036
|
+
*
|
|
1037
|
+
* TODO currently we're not checking for indirect connections (traces that are
|
|
1038
|
+
* connected to other traces that are in turn connected to the net)
|
|
1039
|
+
*/
|
|
1040
|
+
getAllConnectedPorts() {
|
|
1041
|
+
const allPorts = this.getSubcircuit().selectAll("port");
|
|
1042
|
+
const connectedPorts = [];
|
|
1043
|
+
for (const port of allPorts) {
|
|
1044
|
+
const traces = port._getDirectlyConnectedTraces();
|
|
1045
|
+
for (const trace of traces) {
|
|
1046
|
+
if (trace._isExplicitlyConnectedToNet(this)) {
|
|
1047
|
+
connectedPorts.push(port);
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return connectedPorts;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Get all traces that are directly connected to this net, i.e. they list
|
|
1056
|
+
* this net in their path, from, or to props
|
|
1057
|
+
*/
|
|
1058
|
+
_getAllDirectlyConnectedTraces() {
|
|
1059
|
+
const allTraces = this.getSubcircuit().selectAll("trace");
|
|
1060
|
+
const connectedTraces = [];
|
|
1061
|
+
for (const trace of allTraces) {
|
|
1062
|
+
if (trace._isExplicitlyConnectedToNet(this)) {
|
|
1063
|
+
connectedTraces.push(trace);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
return connectedTraces;
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Add PCB Traces to connect net islands together. A net island is a set of
|
|
1070
|
+
* ports that are connected to each other. If a there are multiple net islands
|
|
1071
|
+
* that means that the net is not fully connected and we need to add traces
|
|
1072
|
+
* such that the nets are fully connected
|
|
1073
|
+
*/
|
|
1074
|
+
doInitialPcbRouteNetIslands() {
|
|
1075
|
+
const { db } = this.project;
|
|
1076
|
+
const { _parsedProps: props } = this;
|
|
1077
|
+
const traces = this._getAllDirectlyConnectedTraces().filter(
|
|
1078
|
+
(trace) => (trace._portsRoutedOnPcb?.length ?? 0) > 0
|
|
1079
|
+
);
|
|
1080
|
+
const islands = [];
|
|
1081
|
+
for (const trace of traces) {
|
|
1082
|
+
const tracePorts = trace._portsRoutedOnPcb;
|
|
1083
|
+
const traceIsland = islands.find(
|
|
1084
|
+
(island) => tracePorts.some((port) => island.ports.includes(port))
|
|
1085
|
+
);
|
|
1086
|
+
if (!traceIsland) {
|
|
1087
|
+
islands.push({ ports: [...tracePorts], traces: [trace] });
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
traceIsland.traces.push(trace);
|
|
1091
|
+
traceIsland.ports.push(...tracePorts);
|
|
1092
|
+
}
|
|
1093
|
+
if (islands.length === 0) {
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
const islandPairs = pairs(islands);
|
|
1097
|
+
for (const [A, B] of islandPairs) {
|
|
1098
|
+
const Apositions = A.ports.map(
|
|
1099
|
+
(port) => port.getGlobalPcbPosition()
|
|
1100
|
+
);
|
|
1101
|
+
const Bpositions = B.ports.map(
|
|
1102
|
+
(port) => port.getGlobalPcbPosition()
|
|
1103
|
+
);
|
|
1104
|
+
let closestDist = Infinity;
|
|
1105
|
+
let closestPair = [-1, -1];
|
|
1106
|
+
for (let i = 0; i < Apositions.length; i++) {
|
|
1107
|
+
const Apos = Apositions[i];
|
|
1108
|
+
for (let j = 0; j < Bpositions.length; j++) {
|
|
1109
|
+
const Bpos = Bpositions[j];
|
|
1110
|
+
const dist = Math.sqrt(
|
|
1111
|
+
(Apos.x - Bpos.x) ** 2 + (Apos.y - Bpos.y) ** 2
|
|
1112
|
+
);
|
|
1113
|
+
if (dist < closestDist) {
|
|
1114
|
+
closestDist = dist;
|
|
1115
|
+
closestPair = [i, j];
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
const Aport = A.ports[closestPair[0]];
|
|
1120
|
+
const Bport = B.ports[closestPair[1]];
|
|
1121
|
+
const pcbElements = db.toArray().filter(
|
|
1122
|
+
(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"
|
|
1123
|
+
);
|
|
1124
|
+
const { solution } = autoroute(
|
|
1125
|
+
pcbElements.concat([
|
|
1126
|
+
{
|
|
1127
|
+
type: "source_trace",
|
|
1128
|
+
source_trace_id: "__net_trace_tmp",
|
|
1129
|
+
connected_source_port_ids: [
|
|
1130
|
+
Aport.source_port_id,
|
|
1131
|
+
Bport.source_port_id
|
|
1132
|
+
]
|
|
1133
|
+
}
|
|
1134
|
+
])
|
|
1135
|
+
);
|
|
1136
|
+
const trace = solution[0];
|
|
1137
|
+
if (!trace) {
|
|
1138
|
+
this.renderError("Failed to route net islands");
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
db.pcb_trace.insert(trace);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
|
|
986
1146
|
// lib/components/base-components/NormalComponent.ts
|
|
987
1147
|
var NormalComponent = class extends PrimitiveComponent {
|
|
988
1148
|
reactSubtrees = [];
|
|
@@ -1204,6 +1364,19 @@ var NormalComponent = class extends PrimitiveComponent {
|
|
|
1204
1364
|
}
|
|
1205
1365
|
return newPorts;
|
|
1206
1366
|
}
|
|
1367
|
+
_createNetsFromProps(propsWithConnections) {
|
|
1368
|
+
for (const prop of propsWithConnections) {
|
|
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
|
+
}
|
|
1379
|
+
}
|
|
1207
1380
|
/**
|
|
1208
1381
|
* Use data from our props to create ports for this component.
|
|
1209
1382
|
*
|
|
@@ -1239,10 +1412,24 @@ var NormalComponent = class extends PrimitiveComponent {
|
|
|
1239
1412
|
|
|
1240
1413
|
// lib/components/normal-components/Board.ts
|
|
1241
1414
|
import { boardProps } from "@tscircuit/props";
|
|
1242
|
-
import { identity as
|
|
1243
|
-
|
|
1415
|
+
import { identity as identity4 } from "transformation-matrix";
|
|
1416
|
+
|
|
1417
|
+
// lib/components/primitive-components/Group.ts
|
|
1418
|
+
import { groupProps } from "@tscircuit/props";
|
|
1419
|
+
import "transformation-matrix";
|
|
1420
|
+
import "zod";
|
|
1421
|
+
var Group = class extends NormalComponent {
|
|
1422
|
+
get config() {
|
|
1423
|
+
return {
|
|
1424
|
+
zodProps: groupProps
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1429
|
+
// lib/components/normal-components/Board.ts
|
|
1430
|
+
var Board = class extends Group {
|
|
1244
1431
|
pcb_board_id = null;
|
|
1245
|
-
get
|
|
1432
|
+
get isSubcircuit() {
|
|
1246
1433
|
return true;
|
|
1247
1434
|
}
|
|
1248
1435
|
get config() {
|
|
@@ -1270,7 +1457,7 @@ var Board = class extends NormalComponent {
|
|
|
1270
1457
|
this.pcb_board_id = null;
|
|
1271
1458
|
}
|
|
1272
1459
|
computePcbGlobalTransform() {
|
|
1273
|
-
return
|
|
1460
|
+
return identity4();
|
|
1274
1461
|
}
|
|
1275
1462
|
};
|
|
1276
1463
|
|
|
@@ -1282,7 +1469,7 @@ import "zod";
|
|
|
1282
1469
|
import { traceProps } from "@tscircuit/props";
|
|
1283
1470
|
import {
|
|
1284
1471
|
IJumpAutorouter,
|
|
1285
|
-
autoroute,
|
|
1472
|
+
autoroute as autoroute2,
|
|
1286
1473
|
getObstaclesFromSoup,
|
|
1287
1474
|
markObstaclesAsConnected
|
|
1288
1475
|
} from "@tscircuit/infgrid-ijump-astar";
|
|
@@ -1366,15 +1553,6 @@ var findPossibleTraceLayerCombinations = (hints, layer_path = []) => {
|
|
|
1366
1553
|
return candidates;
|
|
1367
1554
|
};
|
|
1368
1555
|
|
|
1369
|
-
// lib/utils/pairs.ts
|
|
1370
|
-
function pairs(arr) {
|
|
1371
|
-
const result = [];
|
|
1372
|
-
for (let i = 0; i < arr.length - 1; i++) {
|
|
1373
|
-
result.push([arr[i], arr[i + 1]]);
|
|
1374
|
-
}
|
|
1375
|
-
return result;
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
1556
|
// lib/utils/autorouting/mergeRoutes.ts
|
|
1379
1557
|
function pdist(a, b) {
|
|
1380
1558
|
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
@@ -1417,7 +1595,29 @@ var mergeRoutes = (routes) => {
|
|
|
1417
1595
|
return merged;
|
|
1418
1596
|
};
|
|
1419
1597
|
|
|
1598
|
+
// lib/utils/getClosest.ts
|
|
1599
|
+
var getDistance = (a, b) => {
|
|
1600
|
+
const aPos = "getGlobalPcbPosition" in a ? a.getGlobalPcbPosition() : a;
|
|
1601
|
+
const bPos = "getGlobalPcbPosition" in b ? b.getGlobalPcbPosition() : b;
|
|
1602
|
+
return Math.sqrt((aPos.x - bPos.x) ** 2 + (aPos.y - bPos.y) ** 2);
|
|
1603
|
+
};
|
|
1604
|
+
function getClosest(point, candidates) {
|
|
1605
|
+
if (candidates.length === 0)
|
|
1606
|
+
throw new Error("No candidates given to getClosest method");
|
|
1607
|
+
let closest = candidates[0];
|
|
1608
|
+
let closestDist = Infinity;
|
|
1609
|
+
for (const candidate of candidates) {
|
|
1610
|
+
const dist = getDistance(point, candidate);
|
|
1611
|
+
if (dist < closestDist) {
|
|
1612
|
+
closest = candidate;
|
|
1613
|
+
closestDist = dist;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
return closest;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1420
1619
|
// lib/components/primitive-components/Trace.ts
|
|
1620
|
+
import "zod";
|
|
1421
1621
|
var portToObjective = (port) => {
|
|
1422
1622
|
const portPosition = port.getGlobalPcbPosition();
|
|
1423
1623
|
return {
|
|
@@ -1429,12 +1629,17 @@ var Trace = class extends PrimitiveComponent {
|
|
|
1429
1629
|
source_trace_id = null;
|
|
1430
1630
|
pcb_trace_id = null;
|
|
1431
1631
|
schematic_trace_id = null;
|
|
1632
|
+
_portsRoutedOnPcb;
|
|
1633
|
+
constructor(props) {
|
|
1634
|
+
super(props);
|
|
1635
|
+
this._portsRoutedOnPcb = [];
|
|
1636
|
+
}
|
|
1432
1637
|
get config() {
|
|
1433
1638
|
return {
|
|
1434
1639
|
zodProps: traceProps
|
|
1435
1640
|
};
|
|
1436
1641
|
}
|
|
1437
|
-
|
|
1642
|
+
_getTracePortOrNetSelectorListFromProps() {
|
|
1438
1643
|
if ("from" in this.props && "to" in this.props) {
|
|
1439
1644
|
return [
|
|
1440
1645
|
typeof this.props.from === "string" ? this.props.from : this.props.from.getPortSelector(),
|
|
@@ -1448,19 +1653,29 @@ var Trace = class extends PrimitiveComponent {
|
|
|
1448
1653
|
}
|
|
1449
1654
|
return [];
|
|
1450
1655
|
}
|
|
1656
|
+
getTracePortPathSelectors() {
|
|
1657
|
+
return this._getTracePortOrNetSelectorListFromProps().filter(
|
|
1658
|
+
(selector) => !selector.includes("net.")
|
|
1659
|
+
);
|
|
1660
|
+
}
|
|
1661
|
+
getTracePathNetSelectors() {
|
|
1662
|
+
return this._getTracePortOrNetSelectorListFromProps().filter(
|
|
1663
|
+
(selector) => selector.includes("net.")
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1451
1666
|
_findConnectedPorts() {
|
|
1452
1667
|
const { db } = this.project;
|
|
1453
1668
|
const { _parsedProps: props, parent } = this;
|
|
1454
1669
|
if (!parent) throw new Error("Trace has no parent");
|
|
1455
1670
|
const portSelectors = this.getTracePortPathSelectors();
|
|
1456
|
-
const
|
|
1671
|
+
const portsWithSelectors = portSelectors.map((selector) => ({
|
|
1457
1672
|
selector,
|
|
1458
|
-
port: this.
|
|
1673
|
+
port: this.getSubcircuit().selectOne(selector, { type: "port" }) ?? null
|
|
1459
1674
|
}));
|
|
1460
|
-
for (const { selector, port } of
|
|
1675
|
+
for (const { selector, port } of portsWithSelectors) {
|
|
1461
1676
|
if (!port) {
|
|
1462
1677
|
const parentSelector = selector.replace(/\>.*$/, "");
|
|
1463
|
-
const targetComponent = this.
|
|
1678
|
+
const targetComponent = this.getSubcircuit().selectOne(parentSelector);
|
|
1464
1679
|
if (!targetComponent) {
|
|
1465
1680
|
this.renderError(`Could not find port for selector "${selector}"`);
|
|
1466
1681
|
} else {
|
|
@@ -1473,10 +1688,43 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1473
1688
|
}
|
|
1474
1689
|
}
|
|
1475
1690
|
}
|
|
1476
|
-
if (
|
|
1691
|
+
if (portsWithSelectors.some((p) => !p.port)) {
|
|
1477
1692
|
return { allPortsFound: false };
|
|
1478
1693
|
}
|
|
1479
|
-
return {
|
|
1694
|
+
return {
|
|
1695
|
+
allPortsFound: true,
|
|
1696
|
+
portsWithSelectors,
|
|
1697
|
+
ports: portsWithSelectors.map(({ port }) => port)
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
_findConnectedNets() {
|
|
1701
|
+
const nets = this.getTracePathNetSelectors().map((selector) => ({
|
|
1702
|
+
selector,
|
|
1703
|
+
net: this.getSubcircuit().selectOne(selector, { type: "net" })
|
|
1704
|
+
}));
|
|
1705
|
+
const undefinedNets = nets.filter((n) => !n.net);
|
|
1706
|
+
if (undefinedNets.length > 0) {
|
|
1707
|
+
this.renderError(
|
|
1708
|
+
`Could not find net for selector "${undefinedNets[0].selector}" inside ${this}`
|
|
1709
|
+
);
|
|
1710
|
+
}
|
|
1711
|
+
return nets;
|
|
1712
|
+
}
|
|
1713
|
+
/**
|
|
1714
|
+
* Determine if a trace is explicitly connected to a port (not via a net)
|
|
1715
|
+
*/
|
|
1716
|
+
_isExplicitlyConnectedToPort(port) {
|
|
1717
|
+
const { allPortsFound, portsWithSelectors: portsWithMetadata } = this._findConnectedPorts();
|
|
1718
|
+
if (!allPortsFound) return false;
|
|
1719
|
+
const ports = portsWithMetadata.map((p) => p.port);
|
|
1720
|
+
return ports.includes(port);
|
|
1721
|
+
}
|
|
1722
|
+
/**
|
|
1723
|
+
* Determine if a trace is explicitly connected to a net (not via a port)
|
|
1724
|
+
*/
|
|
1725
|
+
_isExplicitlyConnectedToNet(net) {
|
|
1726
|
+
const nets = this._findConnectedNets().map((n) => n.net);
|
|
1727
|
+
return nets.includes(net);
|
|
1480
1728
|
}
|
|
1481
1729
|
doInitialSourceTraceRender() {
|
|
1482
1730
|
const { db } = this.project;
|
|
@@ -1485,11 +1733,12 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1485
1733
|
this.renderError("Trace has no parent");
|
|
1486
1734
|
return;
|
|
1487
1735
|
}
|
|
1488
|
-
const { allPortsFound, ports } = this._findConnectedPorts();
|
|
1736
|
+
const { allPortsFound, portsWithSelectors: ports } = this._findConnectedPorts();
|
|
1489
1737
|
if (!allPortsFound) return;
|
|
1738
|
+
const nets = this._findConnectedNets();
|
|
1490
1739
|
const trace = db.source_trace.insert({
|
|
1491
1740
|
connected_source_port_ids: ports.map((p) => p.port.source_port_id),
|
|
1492
|
-
connected_source_net_ids:
|
|
1741
|
+
connected_source_net_ids: nets.map((n) => n.net.source_net_id)
|
|
1493
1742
|
});
|
|
1494
1743
|
this.source_trace_id = trace.source_trace_id;
|
|
1495
1744
|
}
|
|
@@ -1498,42 +1747,90 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1498
1747
|
const { _parsedProps: props, parent } = this;
|
|
1499
1748
|
if (!parent) throw new Error("Trace has no parent");
|
|
1500
1749
|
const { allPortsFound, ports } = this._findConnectedPorts();
|
|
1750
|
+
const portsConnectedOnPcbViaNet = [];
|
|
1501
1751
|
if (!allPortsFound) return;
|
|
1752
|
+
const nets = this._findConnectedNets();
|
|
1753
|
+
if (ports.length === 0 && nets.length === 2) {
|
|
1754
|
+
this.renderError(
|
|
1755
|
+
`Trace connects two nets, we haven't implemented a way to route this yet`
|
|
1756
|
+
);
|
|
1757
|
+
return;
|
|
1758
|
+
} else if (ports.length === 1 && nets.length === 1) {
|
|
1759
|
+
const port = ports[0];
|
|
1760
|
+
const portsInNet = nets[0].net.getAllConnectedPorts();
|
|
1761
|
+
const otherPortsInNet = portsInNet.filter((p) => p !== port);
|
|
1762
|
+
if (otherPortsInNet.length === 0) {
|
|
1763
|
+
console.log(
|
|
1764
|
+
"Nothing to connect this port to, the net is empty. TODO should emit a warning!"
|
|
1765
|
+
);
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
const closestPortInNet = getClosest(port, otherPortsInNet);
|
|
1769
|
+
portsConnectedOnPcbViaNet.push(closestPortInNet);
|
|
1770
|
+
ports.push(closestPortInNet);
|
|
1771
|
+
} else if (ports.length > 1 && nets.length >= 1) {
|
|
1772
|
+
this.renderError(
|
|
1773
|
+
`Trace has more than one port and one or more nets, we don't currently support this type of complex trace routing`
|
|
1774
|
+
);
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1502
1777
|
const pcbElements = db.toArray().filter(
|
|
1503
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"
|
|
1504
1779
|
);
|
|
1505
1780
|
const source_trace = db.source_trace.get(this.source_trace_id);
|
|
1506
1781
|
const hints = ports.flatMap(
|
|
1507
|
-
(
|
|
1782
|
+
(port) => port.matchedComponents.filter((c) => c.componentName === "TraceHint")
|
|
1508
1783
|
);
|
|
1509
1784
|
const pcbRouteHints = (this._parsedProps.pcbRouteHints ?? []).concat(
|
|
1510
1785
|
hints.flatMap((h) => h.getPcbRouteHints())
|
|
1511
1786
|
);
|
|
1512
1787
|
if (ports.length > 2) {
|
|
1513
1788
|
this.renderError(
|
|
1514
|
-
`Trace has more than two ports (${ports.map((p) => p.
|
|
1789
|
+
`Trace has more than two ports (${ports.map((p) => p.getString()).join(
|
|
1515
1790
|
", "
|
|
1516
1791
|
)}), routing between more than two ports for a single trace is not implemented`
|
|
1517
1792
|
);
|
|
1518
1793
|
return;
|
|
1519
1794
|
}
|
|
1795
|
+
const alreadyRoutedTraces = this.getSubcircuit().selectAll("trace").filter(
|
|
1796
|
+
(trace) => trace.renderPhaseStates.PcbTraceRender.initialized
|
|
1797
|
+
);
|
|
1798
|
+
const isAlreadyRouted = alreadyRoutedTraces.some(
|
|
1799
|
+
(trace) => trace._portsRoutedOnPcb.length === ports.length && trace._portsRoutedOnPcb.every(
|
|
1800
|
+
(portRoutedByOtherTrace) => ports.includes(portRoutedByOtherTrace)
|
|
1801
|
+
)
|
|
1802
|
+
);
|
|
1803
|
+
if (isAlreadyRouted) {
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1520
1806
|
if (pcbRouteHints.length === 0) {
|
|
1521
|
-
const { solution } =
|
|
1807
|
+
const { solution } = autoroute2(
|
|
1808
|
+
pcbElements.concat([
|
|
1809
|
+
{
|
|
1810
|
+
...source_trace,
|
|
1811
|
+
// manually override b/c some of the ports may be connected via nets
|
|
1812
|
+
// so they don't appear properly in the source_trace, we don't need
|
|
1813
|
+
// to do this if the algorithm correctly looks at connected_source_net_ids
|
|
1814
|
+
connected_source_port_ids: ports.map((p) => p.source_port_id)
|
|
1815
|
+
}
|
|
1816
|
+
])
|
|
1817
|
+
);
|
|
1522
1818
|
const inputPcbTrace = solution[0];
|
|
1523
1819
|
if (!inputPcbTrace) {
|
|
1524
1820
|
console.log(
|
|
1525
|
-
`Failed to find route
|
|
1821
|
+
`Failed to find route from ${ports[0]} to ${ports[1]} (TODO render error!)`
|
|
1526
1822
|
);
|
|
1527
1823
|
return;
|
|
1528
1824
|
}
|
|
1529
1825
|
const pcb_trace2 = db.pcb_trace.insert(inputPcbTrace);
|
|
1530
1826
|
this.pcb_trace_id = pcb_trace2.pcb_trace_id;
|
|
1827
|
+
this._portsRoutedOnPcb = ports;
|
|
1531
1828
|
return;
|
|
1532
1829
|
}
|
|
1533
1830
|
const orderedRouteObjectives = [
|
|
1534
|
-
portToObjective(ports[0]
|
|
1831
|
+
portToObjective(ports[0]),
|
|
1535
1832
|
...pcbRouteHints,
|
|
1536
|
-
portToObjective(ports[1]
|
|
1833
|
+
portToObjective(ports[1])
|
|
1537
1834
|
];
|
|
1538
1835
|
const candidateLayerCombinations = findPossibleTraceLayerCombinations(
|
|
1539
1836
|
orderedRouteObjectives
|
|
@@ -1600,7 +1897,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
|
|
|
1600
1897
|
const { db } = this.project;
|
|
1601
1898
|
const { _parsedProps: props, parent } = this;
|
|
1602
1899
|
if (!parent) throw new Error("Trace has no parent");
|
|
1603
|
-
const { allPortsFound, ports } = this._findConnectedPorts();
|
|
1900
|
+
const { allPortsFound, portsWithSelectors: ports } = this._findConnectedPorts();
|
|
1604
1901
|
if (!allPortsFound) return;
|
|
1605
1902
|
const obstacles = [];
|
|
1606
1903
|
const connection = {
|
|
@@ -1671,17 +1968,20 @@ var Resistor = class extends NormalComponent {
|
|
|
1671
1968
|
}
|
|
1672
1969
|
pin1 = this.portMap.pin1;
|
|
1673
1970
|
pin2 = this.portMap.pin2;
|
|
1971
|
+
doInitialCreateNetsFromProps() {
|
|
1972
|
+
this._createNetsFromProps([this.props.pullupFor, this.props.pullupTo]);
|
|
1973
|
+
}
|
|
1674
1974
|
doInitialCreateTracesFromProps() {
|
|
1675
1975
|
if (this.props.pullupFor && this.props.pullupTo) {
|
|
1676
1976
|
this.add(
|
|
1677
1977
|
new Trace({
|
|
1678
|
-
from: `${this.
|
|
1978
|
+
from: `${this.getSubcircuitSelector()} > port.1`,
|
|
1679
1979
|
to: this.props.pullupFor
|
|
1680
1980
|
})
|
|
1681
1981
|
);
|
|
1682
1982
|
this.add(
|
|
1683
1983
|
new Trace({
|
|
1684
|
-
from: `${this.
|
|
1984
|
+
from: `${this.getSubcircuitSelector()} > port.2`,
|
|
1685
1985
|
to: this.props.pullupTo
|
|
1686
1986
|
})
|
|
1687
1987
|
);
|
|
@@ -1743,17 +2043,23 @@ var Capacitor = class extends NormalComponent {
|
|
|
1743
2043
|
}
|
|
1744
2044
|
pin1 = this.portMap.pin1;
|
|
1745
2045
|
pin2 = this.portMap.pin2;
|
|
2046
|
+
doInitialCreateNetsFromProps() {
|
|
2047
|
+
this._createNetsFromProps([
|
|
2048
|
+
this.props.decouplingFor,
|
|
2049
|
+
this.props.decouplingTo
|
|
2050
|
+
]);
|
|
2051
|
+
}
|
|
1746
2052
|
doInitialCreateTracesFromProps() {
|
|
1747
2053
|
if (this.props.decouplingFor && this.props.decouplingTo) {
|
|
1748
2054
|
this.add(
|
|
1749
2055
|
new Trace({
|
|
1750
|
-
from: `${this.
|
|
2056
|
+
from: `${this.getSubcircuitSelector()} > port.1`,
|
|
1751
2057
|
to: this.props.decouplingFor
|
|
1752
2058
|
})
|
|
1753
2059
|
);
|
|
1754
2060
|
this.add(
|
|
1755
2061
|
new Trace({
|
|
1756
|
-
from: `${this.
|
|
2062
|
+
from: `${this.getSubcircuitSelector()} > port.2`,
|
|
1757
2063
|
to: this.props.decouplingTo
|
|
1758
2064
|
})
|
|
1759
2065
|
);
|
|
@@ -1774,17 +2080,6 @@ var Capacitor = class extends NormalComponent {
|
|
|
1774
2080
|
}
|
|
1775
2081
|
};
|
|
1776
2082
|
|
|
1777
|
-
// lib/components/primitive-components/Net.ts
|
|
1778
|
-
import { z as z5 } from "zod";
|
|
1779
|
-
var netProps = z5.object({
|
|
1780
|
-
name: z5.string()
|
|
1781
|
-
});
|
|
1782
|
-
var Net = class extends PrimitiveComponent {
|
|
1783
|
-
getPortSelector() {
|
|
1784
|
-
return `net.${this.props.name}`;
|
|
1785
|
-
}
|
|
1786
|
-
};
|
|
1787
|
-
|
|
1788
2083
|
// lib/components/primitive-components/TraceHint.ts
|
|
1789
2084
|
import "@tscircuit/props";
|
|
1790
2085
|
import { applyToPoint as applyToPoint4 } from "transformation-matrix";
|
|
@@ -1830,17 +2125,6 @@ var TraceHint = class extends PrimitiveComponent {
|
|
|
1830
2125
|
}
|
|
1831
2126
|
};
|
|
1832
2127
|
|
|
1833
|
-
// lib/components/primitive-components/Group.ts
|
|
1834
|
-
import { groupProps } from "@tscircuit/props";
|
|
1835
|
-
import "transformation-matrix";
|
|
1836
|
-
var Group = class extends PrimitiveComponent {
|
|
1837
|
-
get config() {
|
|
1838
|
-
return {
|
|
1839
|
-
zodProps: groupProps
|
|
1840
|
-
};
|
|
1841
|
-
}
|
|
1842
|
-
};
|
|
1843
|
-
|
|
1844
2128
|
// lib/components/normal-components/Chip.ts
|
|
1845
2129
|
import { chipProps } from "@tscircuit/props";
|
|
1846
2130
|
|
package/lib/Project.ts
CHANGED
|
@@ -117,7 +117,7 @@ export class Circuit {
|
|
|
117
117
|
|
|
118
118
|
selectOne(
|
|
119
119
|
selector: string,
|
|
120
|
-
opts
|
|
120
|
+
opts?: { type?: "component" | "port" },
|
|
121
121
|
): PrimitiveComponent | null {
|
|
122
122
|
return this.rootComponent?.selectOne(selector, opts) ?? null
|
|
123
123
|
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from "lib/fiber/create-instance-from-react-element"
|
|
17
17
|
import { getPortFromHints } from "lib/utils/getPortFromHints"
|
|
18
18
|
import { createComponentsFromSoup } from "lib/utils/createComponentsFromSoup"
|
|
19
|
+
import { Net } from "../primitive-components/Net"
|
|
19
20
|
|
|
20
21
|
export type PortMap<T extends string> = {
|
|
21
22
|
[K in T]: Port
|
|
@@ -307,6 +308,20 @@ export class NormalComponent<
|
|
|
307
308
|
return newPorts
|
|
308
309
|
}
|
|
309
310
|
|
|
311
|
+
_createNetsFromProps(propsWithConnections: (string | undefined | null)[]) {
|
|
312
|
+
for (const prop of propsWithConnections) {
|
|
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
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
310
325
|
/**
|
|
311
326
|
* Use data from our props to create ports for this component.
|
|
312
327
|
*
|