@tscircuit/core 0.0.550 → 0.0.552

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +3 -3
  2. package/dist/index.js +1200 -1211
  3. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -54,7 +54,7 @@ __export(components_exports, {
54
54
  Subcircuit: () => Subcircuit,
55
55
  Switch: () => Switch,
56
56
  TestPoint: () => TestPoint,
57
- Trace: () => Trace2,
57
+ Trace: () => Trace3,
58
58
  TraceHint: () => TraceHint,
59
59
  Transistor: () => Transistor,
60
60
  Via: () => Via
@@ -3602,10 +3602,10 @@ var getNumericSchPinStyle = (pinStyles, pinLabels) => {
3602
3602
  };
3603
3603
 
3604
3604
  // lib/components/primitive-components/Trace/Trace.ts
3605
- import { MultilayerIjump } from "@tscircuit/infgrid-ijump-astar";
3605
+ import "@tscircuit/infgrid-ijump-astar";
3606
3606
  import { traceProps } from "@tscircuit/props";
3607
3607
  import "circuit-json";
3608
- import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map";
3608
+ import "circuit-json-to-connectivity-map";
3609
3609
 
3610
3610
  // lib/utils/autorouting/DirectLineRouter.ts
3611
3611
  var DirectLineRouter = class {
@@ -3890,198 +3890,7 @@ function tryNow(fn) {
3890
3890
  // lib/components/primitive-components/Trace/Trace.ts
3891
3891
  import "zod";
3892
3892
 
3893
- // lib/components/primitive-components/Trace/compute-trace-length.ts
3894
- function getTraceLength(route) {
3895
- let totalLength = 0;
3896
- for (let i = 0; i < route.length; i++) {
3897
- const point = route[i];
3898
- if (point.route_type === "wire") {
3899
- const nextPoint = route[i + 1];
3900
- if (nextPoint) {
3901
- const dx = nextPoint.x - point.x;
3902
- const dy = nextPoint.y - point.y;
3903
- totalLength += Math.sqrt(dx * dx + dy * dy);
3904
- }
3905
- } else if (point.route_type === "via") {
3906
- totalLength += 1.6;
3907
- }
3908
- }
3909
- return totalLength;
3910
- }
3911
-
3912
- // lib/components/primitive-components/Trace/create-schematic-trace-crossing-segments.ts
3913
- import { distance as distance2, doesLineIntersectLine } from "@tscircuit/math-utils";
3914
-
3915
- // lib/components/primitive-components/Trace/get-other-schematic-traces.ts
3916
- var getOtherSchematicTraces = ({
3917
- db,
3918
- source_trace_id,
3919
- sameNetOnly,
3920
- differentNetOnly
3921
- }) => {
3922
- if (!sameNetOnly && !differentNetOnly) {
3923
- differentNetOnly = true;
3924
- }
3925
- const mySourceTrace = db.source_trace.get(source_trace_id);
3926
- const traces = [];
3927
- for (const otherSchematicTrace of db.schematic_trace.list()) {
3928
- if (otherSchematicTrace.source_trace_id === source_trace_id) continue;
3929
- const otherSourceTrace = db.source_trace.get(
3930
- otherSchematicTrace.source_trace_id
3931
- );
3932
- const isSameNet = otherSourceTrace?.subcircuit_connectivity_map_key === mySourceTrace.subcircuit_connectivity_map_key;
3933
- if (differentNetOnly && isSameNet) {
3934
- continue;
3935
- }
3936
- if (sameNetOnly && !isSameNet) {
3937
- continue;
3938
- }
3939
- traces.push(otherSchematicTrace);
3940
- }
3941
- return traces;
3942
- };
3943
-
3944
- // lib/components/primitive-components/Trace/create-schematic-trace-crossing-segments.ts
3945
- import { getUnitVectorFromPointAToB } from "@tscircuit/math-utils";
3946
- var createSchematicTraceCrossingSegments = ({
3947
- edges: inputEdges,
3948
- otherEdges
3949
- }) => {
3950
- const edges = [...inputEdges];
3951
- for (let i = 0; i < edges.length; i++) {
3952
- if (i > 2e3) {
3953
- throw new Error(
3954
- "Over 2000 iterations spent inside createSchematicTraceCrossingSegments, you have triggered an infinite loop, please report this!"
3955
- );
3956
- }
3957
- const edge = edges[i];
3958
- const edgeOrientation = Math.abs(edge.from.x - edge.to.x) < 0.01 ? "vertical" : edge.from.y === edge.to.y ? "horizontal" : "not-orthogonal";
3959
- if (edgeOrientation === "not-orthogonal") {
3960
- continue;
3961
- }
3962
- const otherEdgesIntersections = [];
3963
- for (const otherEdge of otherEdges) {
3964
- const otherOrientation = otherEdge.from.x === otherEdge.to.x ? "vertical" : otherEdge.from.y === otherEdge.to.y ? "horizontal" : "not-orthogonal";
3965
- if (otherOrientation === "not-orthogonal") continue;
3966
- if (edgeOrientation === otherOrientation)
3967
- continue;
3968
- const hasIntersection = doesLineIntersectLine(
3969
- [edge.from, edge.to],
3970
- [otherEdge.from, otherEdge.to],
3971
- { lineThickness: 0.01 }
3972
- );
3973
- if (hasIntersection) {
3974
- const intersectX = edgeOrientation === "vertical" ? edge.from.x : otherEdge.from.x;
3975
- const intersectY = edgeOrientation === "vertical" ? otherEdge.from.y : edge.from.y;
3976
- const crossingPoint2 = { x: intersectX, y: intersectY };
3977
- otherEdgesIntersections.push({
3978
- otherEdge,
3979
- crossingPoint: crossingPoint2,
3980
- distanceFromEdgeFrom: distance2(edge.from, crossingPoint2)
3981
- });
3982
- }
3983
- }
3984
- if (otherEdgesIntersections.length === 0) continue;
3985
- let closestIntersection = otherEdgesIntersections[0];
3986
- for (const intersection of otherEdgesIntersections) {
3987
- if (intersection.distanceFromEdgeFrom < closestIntersection.distanceFromEdgeFrom) {
3988
- closestIntersection = intersection;
3989
- }
3990
- }
3991
- const crossingPoint = closestIntersection.crossingPoint;
3992
- const crossingSegmentLength = 0.075;
3993
- if (crossingPoint.x === edge.from.x && crossingPoint.y === edge.from.y) {
3994
- continue;
3995
- }
3996
- const crossingUnitVec = getUnitVectorFromPointAToB(edge.from, crossingPoint);
3997
- const beforeCrossing = {
3998
- x: crossingPoint.x - crossingUnitVec.x * crossingSegmentLength / 2,
3999
- y: crossingPoint.y - crossingUnitVec.y * crossingSegmentLength / 2
4000
- };
4001
- const afterCrossing = {
4002
- x: crossingPoint.x + crossingUnitVec.x * crossingSegmentLength / 2,
4003
- y: crossingPoint.y + crossingUnitVec.y * crossingSegmentLength / 2
4004
- };
4005
- const overshot = distance2(afterCrossing, edge.to) < crossingSegmentLength;
4006
- const newEdges = [
4007
- { from: edge.from, to: beforeCrossing },
4008
- { from: beforeCrossing, to: afterCrossing, is_crossing: true },
4009
- { from: afterCrossing, to: edge.to }
4010
- ];
4011
- edges.splice(i, 1, ...newEdges);
4012
- i += newEdges.length - 2;
4013
- if (overshot) {
4014
- i++;
4015
- }
4016
- }
4017
- return edges;
4018
- };
4019
-
4020
- // lib/components/primitive-components/Trace/create-schematic-trace-junctions.ts
4021
- var getIntersectionPoint = (edge1, edge2) => {
4022
- if (edge1.from.x === edge1.to.x && edge2.from.x === edge2.to.x) {
4023
- return null;
4024
- }
4025
- if (edge1.from.x === edge1.to.x) {
4026
- const x2 = edge1.from.x;
4027
- const m22 = (edge2.to.y - edge2.from.y) / (edge2.to.x - edge2.from.x);
4028
- const b22 = edge2.from.y - m22 * edge2.from.x;
4029
- const y2 = m22 * x2 + b22;
4030
- if (x2 >= Math.min(edge2.from.x, edge2.to.x) && x2 <= Math.max(edge2.from.x, edge2.to.x) && y2 >= Math.min(edge2.from.y, edge2.to.y) && y2 <= Math.max(edge2.from.y, edge2.to.y)) {
4031
- return { x: x2, y: y2 };
4032
- }
4033
- return null;
4034
- }
4035
- if (edge2.from.x === edge2.to.x) {
4036
- const x2 = edge2.from.x;
4037
- const m12 = (edge1.to.y - edge1.from.y) / (edge1.to.x - edge1.from.x);
4038
- const b12 = edge1.from.y - m12 * edge1.from.x;
4039
- const y2 = m12 * x2 + b12;
4040
- if (x2 >= Math.min(edge1.from.x, edge1.to.x) && x2 <= Math.max(edge1.from.x, edge1.to.x) && y2 >= Math.min(edge1.from.y, edge1.to.y) && y2 <= Math.max(edge1.from.y, edge1.to.y)) {
4041
- return { x: x2, y: y2 };
4042
- }
4043
- return null;
4044
- }
4045
- const m1 = (edge1.to.y - edge1.from.y) / (edge1.to.x - edge1.from.x);
4046
- const b1 = edge1.from.y - m1 * edge1.from.x;
4047
- const m2 = (edge2.to.y - edge2.from.y) / (edge2.to.x - edge2.from.x);
4048
- const b2 = edge2.from.y - m2 * edge2.from.x;
4049
- if (m1 === m2) {
4050
- return null;
4051
- }
4052
- const x = (b2 - b1) / (m1 - m2);
4053
- const y = m1 * x + b1;
4054
- const isWithinEdge1 = x >= Math.min(edge1.from.x, edge1.to.x) && x <= Math.max(edge1.from.x, edge1.to.x) && y >= Math.min(edge1.from.y, edge1.to.y) && y <= Math.max(edge1.from.y, edge1.to.y);
4055
- const isWithinEdge2 = x >= Math.min(edge2.from.x, edge2.to.x) && x <= Math.max(edge2.from.x, edge2.to.x) && y >= Math.min(edge2.from.y, edge2.to.y) && y <= Math.max(edge2.from.y, edge2.to.y);
4056
- if (isWithinEdge1 && isWithinEdge2) {
4057
- return { x, y };
4058
- }
4059
- return null;
4060
- };
4061
- var createSchematicTraceJunctions = ({
4062
- edges: myEdges,
4063
- db,
4064
- source_trace_id
4065
- }) => {
4066
- const otherEdges = getOtherSchematicTraces({
4067
- db,
4068
- source_trace_id,
4069
- sameNetOnly: true
4070
- }).flatMap((t) => t.edges);
4071
- const junctions = /* @__PURE__ */ new Set();
4072
- for (const myEdge of myEdges) {
4073
- for (const otherEdge of otherEdges) {
4074
- const intersection = getIntersectionPoint(myEdge, otherEdge);
4075
- if (intersection) {
4076
- const pointKey = `${intersection.x},${intersection.y}`;
4077
- return [{ x: intersection.x, y: intersection.y }];
4078
- }
4079
- }
4080
- }
4081
- return [];
4082
- };
4083
-
4084
- // lib/components/primitive-components/Trace/get-max-length-from-connected-capacitors.ts
3893
+ // lib/components/primitive-components/Trace/trace-utils/get-max-length-from-connected-capacitors.ts
4085
3894
  var getMaxLengthFromConnectedCapacitors = (ports, { db }) => {
4086
3895
  const capacitorMaxLengths = ports.map((port) => {
4087
3896
  const sourcePort = db.source_port.get(port.source_port_id);
@@ -4098,219 +3907,20 @@ var getMaxLengthFromConnectedCapacitors = (ports, { db }) => {
4098
3907
  return Math.min(...capacitorMaxLengths);
4099
3908
  };
4100
3909
 
4101
- // lib/components/primitive-components/Trace/get-obstacles-for-trace.ts
4102
- import { getUnitVectorFromDirection } from "@tscircuit/math-utils";
4103
-
4104
- // lib/utils/autorouting/getBoundsForSchematic.ts
4105
- function getBoundsForSchematic(db) {
4106
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
4107
- for (const elm of db) {
4108
- let cx, cy, w, h;
4109
- if (elm.type === "schematic_component") {
4110
- cx = elm.center?.x;
4111
- cy = elm.center?.y;
4112
- w = elm.size?.width;
4113
- h = elm.size?.height;
4114
- } else if (elm.type === "schematic_box") {
4115
- cx = elm.x;
4116
- cy = elm.y;
4117
- w = elm.width;
4118
- h = elm.height;
4119
- } else if (elm.type === "schematic_port") {
4120
- cx = elm.center?.x;
4121
- cy = elm.center?.y;
4122
- w = 0.2;
4123
- h = 0.2;
4124
- } else if (elm.type === "schematic_text") {
4125
- cx = elm.position?.x;
4126
- cy = elm.position?.y;
4127
- w = (elm.text?.length ?? 0) * 0.1;
4128
- h = 0.2;
4129
- }
4130
- if (typeof cx === "number" && typeof cy === "number" && typeof w === "number" && typeof h === "number") {
4131
- minX = Math.min(minX, cx - w / 2);
4132
- maxX = Math.max(maxX, cx + w / 2);
4133
- minY = Math.min(minY, cy - h / 2);
4134
- maxY = Math.max(maxY, cy + h / 2);
4135
- }
3910
+ // lib/components/primitive-components/Trace/trace-utils/get-trace-display-name.ts
3911
+ function getTraceDisplayName({
3912
+ ports,
3913
+ nets
3914
+ }) {
3915
+ if (ports.length >= 2) {
3916
+ return `${ports[0]?.selector} to ${ports[1]?.selector}`;
4136
3917
  }
4137
- return { minX, maxX, minY, maxY };
3918
+ if (ports.length === 1 && nets.length === 1) {
3919
+ return `${ports[0]?.selector} to net.${nets[0]._parsedProps.name}`;
3920
+ }
3921
+ return void 0;
4138
3922
  }
4139
3923
 
4140
- // lib/utils/autorouting/getObstaclesFromBounds.ts
4141
- function getObstaclesFromBounds(bounds, opts = {}) {
4142
- const { minX, maxX, minY, maxY } = bounds;
4143
- const PADDING = opts.padding ?? 1;
4144
- if (!isFinite(minX) || !isFinite(maxX) || !isFinite(minY) || !isFinite(maxY))
4145
- return [];
4146
- const left = minX - PADDING;
4147
- const right = maxX + PADDING;
4148
- const top = maxY + PADDING;
4149
- const bottom = minY - PADDING;
4150
- const thickness = 0.01;
4151
- return [
4152
- // Top border (horizontal)
4153
- {
4154
- type: "rect",
4155
- layers: ["top"],
4156
- center: { x: (left + right) / 2, y: top },
4157
- width: right - left,
4158
- height: thickness,
4159
- connectedTo: []
4160
- },
4161
- // Bottom border (horizontal)
4162
- {
4163
- type: "rect",
4164
- layers: ["top"],
4165
- center: { x: (left + right) / 2, y: bottom },
4166
- width: right - left,
4167
- height: thickness,
4168
- connectedTo: []
4169
- },
4170
- // Left border (vertical)
4171
- {
4172
- type: "rect",
4173
- layers: ["top"],
4174
- center: { x: left, y: (top + bottom) / 2 },
4175
- width: thickness,
4176
- height: top - bottom,
4177
- connectedTo: []
4178
- },
4179
- // Right border (vertical)
4180
- {
4181
- type: "rect",
4182
- layers: ["top"],
4183
- center: { x: right, y: (top + bottom) / 2 },
4184
- width: thickness,
4185
- height: top - bottom,
4186
- connectedTo: []
4187
- }
4188
- ];
4189
- }
4190
-
4191
- // lib/components/primitive-components/Trace/get-obstacles-for-trace.ts
4192
- var getSchematicObstaclesForTrace = (trace) => {
4193
- const db = trace.root.db;
4194
- const connectedPorts = trace._findConnectedPorts().ports ?? [];
4195
- const connectedPortIds = new Set(
4196
- connectedPorts.map((p) => p.schematic_port_id)
4197
- );
4198
- const obstacles = [];
4199
- for (const elm of db.toArray()) {
4200
- if (elm.type === "schematic_component") {
4201
- const isSymbol = Boolean(elm.symbol_name);
4202
- const dominateAxis = elm.size.width > elm.size.height ? "horz" : "vert";
4203
- obstacles.push({
4204
- type: "rect",
4205
- layers: ["top"],
4206
- center: elm.center,
4207
- width: elm.size.width + (isSymbol && dominateAxis === "horz" ? -0.5 : 0),
4208
- height: elm.size.height + (isSymbol && dominateAxis === "vert" ? -0.5 : 0),
4209
- connectedTo: []
4210
- });
4211
- }
4212
- if (elm.type === "schematic_port") {
4213
- if (connectedPortIds.has(elm.schematic_port_id)) {
4214
- continue;
4215
- }
4216
- const dirVec = elm.facing_direction ? getUnitVectorFromDirection(elm.facing_direction) : {
4217
- x: 0,
4218
- y: 0
4219
- };
4220
- obstacles.push({
4221
- type: "rect",
4222
- layers: ["top"],
4223
- center: {
4224
- x: elm.center.x - dirVec.x * 0.1,
4225
- y: elm.center.y - dirVec.y * 0.1
4226
- },
4227
- width: 0.1 + Math.abs(dirVec.x) * 0.3,
4228
- height: 0.1 + Math.abs(dirVec.y) * 0.3,
4229
- connectedTo: []
4230
- });
4231
- }
4232
- if (elm.type === "schematic_text") {
4233
- obstacles.push({
4234
- type: "rect",
4235
- layers: ["top"],
4236
- center: elm.position,
4237
- width: (elm.text?.length ?? 0) * 0.1,
4238
- height: 0.2,
4239
- connectedTo: []
4240
- });
4241
- }
4242
- if (elm.type === "schematic_box") {
4243
- obstacles.push({
4244
- type: "rect",
4245
- layers: ["top"],
4246
- center: { x: elm.x, y: elm.y },
4247
- width: elm.width,
4248
- height: elm.height,
4249
- connectedTo: []
4250
- });
4251
- }
4252
- }
4253
- const bounds = getBoundsForSchematic(db.toArray());
4254
- obstacles.push(...getObstaclesFromBounds(bounds, { padding: 1 }));
4255
- return obstacles;
4256
- };
4257
-
4258
- // lib/components/primitive-components/Trace/get-trace-display-name.ts
4259
- function getTraceDisplayName({
4260
- ports,
4261
- nets
4262
- }) {
4263
- if (ports.length >= 2) {
4264
- return `${ports[0]?.selector} to ${ports[1]?.selector}`;
4265
- }
4266
- if (ports.length === 1 && nets.length === 1) {
4267
- return `${ports[0]?.selector} to net.${nets[0]._parsedProps.name}`;
4268
- }
4269
- return void 0;
4270
- }
4271
-
4272
- // lib/components/primitive-components/Trace/push-edges-of-schematic-trace-to-prevent-overlap.ts
4273
- import { doesLineIntersectLine as doesLineIntersectLine2 } from "@tscircuit/math-utils";
4274
- var pushEdgesOfSchematicTraceToPreventOverlap = ({
4275
- edges,
4276
- db,
4277
- source_trace_id
4278
- }) => {
4279
- const mySourceTrace = db.source_trace.get(source_trace_id);
4280
- const otherEdges = getOtherSchematicTraces({
4281
- db,
4282
- source_trace_id,
4283
- differentNetOnly: true
4284
- }).flatMap((t) => t.edges);
4285
- const edgeOrientation = (edge) => {
4286
- const { from, to } = edge;
4287
- return from.x === to.x ? "vertical" : "horizontal";
4288
- };
4289
- for (const mySegment of edges) {
4290
- const mySegmentOrientation = edgeOrientation(mySegment);
4291
- const findOverlappingParallelSegment = () => otherEdges.find(
4292
- (otherEdge) => edgeOrientation(otherEdge) === mySegmentOrientation && doesLineIntersectLine2(
4293
- [mySegment.from, mySegment.to],
4294
- [otherEdge.from, otherEdge.to],
4295
- {
4296
- lineThickness: 0.05
4297
- }
4298
- )
4299
- );
4300
- let overlappingParallelSegmentFromOtherTrace = findOverlappingParallelSegment();
4301
- while (overlappingParallelSegmentFromOtherTrace) {
4302
- if (mySegmentOrientation === "horizontal") {
4303
- mySegment.from.y += 0.1;
4304
- mySegment.to.y += 0.1;
4305
- } else {
4306
- mySegment.from.x += 0.1;
4307
- mySegment.to.x += 0.1;
4308
- }
4309
- overlappingParallelSegmentFromOtherTrace = findOverlappingParallelSegment();
4310
- }
4311
- }
4312
- };
4313
-
4314
3924
  // lib/utils/is-route-outside-board.ts
4315
3925
  var isRouteOutsideBoard = (mergedRoute, { db }) => {
4316
3926
  const pcbBoard = db.pcb_board.list()[0];
@@ -4760,122 +4370,1116 @@ var computeSchematicNetLabelCenter = ({
4760
4370
  return center;
4761
4371
  };
4762
4372
 
4763
- // lib/components/primitive-components/Trace/Trace.ts
4764
- var portToObjective = (port) => {
4765
- const portPosition = port._getGlobalPcbPositionAfterLayout();
4766
- return {
4767
- ...portPosition,
4768
- layers: port.getAvailablePcbLayers()
4769
- };
4770
- };
4771
- var SHOULD_USE_SINGLE_LAYER_ROUTING = false;
4772
- var Trace2 = class extends PrimitiveComponent2 {
4773
- source_trace_id = null;
4774
- pcb_trace_id = null;
4775
- schematic_trace_id = null;
4776
- _portsRoutedOnPcb;
4777
- subcircuit_connectivity_map_key = null;
4778
- _traceConnectionHash = null;
4779
- constructor(props) {
4780
- super(props);
4781
- this._portsRoutedOnPcb = [];
4782
- }
4783
- get config() {
4784
- return {
4785
- zodProps: traceProps,
4786
- componentName: "Trace"
4787
- };
4373
+ // lib/components/primitive-components/Trace/Trace_doInitialSchematicTraceRender.ts
4374
+ import { MultilayerIjump } from "@tscircuit/infgrid-ijump-astar";
4375
+ import "circuit-json";
4376
+
4377
+ // lib/components/primitive-components/Trace/trace-utils/create-schematic-trace-crossing-segments.ts
4378
+ import { distance as distance2, doesLineIntersectLine } from "@tscircuit/math-utils";
4379
+
4380
+ // lib/components/primitive-components/Trace/trace-utils/get-other-schematic-traces.ts
4381
+ var getOtherSchematicTraces = ({
4382
+ db,
4383
+ source_trace_id,
4384
+ sameNetOnly,
4385
+ differentNetOnly
4386
+ }) => {
4387
+ if (!sameNetOnly && !differentNetOnly) {
4388
+ differentNetOnly = true;
4788
4389
  }
4789
- _getTracePortOrNetSelectorListFromProps() {
4790
- if ("from" in this.props && "to" in this.props) {
4791
- return [
4792
- typeof this.props.from === "string" ? this.props.from : this.props.from.getPortSelector(),
4793
- typeof this.props.to === "string" ? this.props.to : this.props.to.getPortSelector()
4794
- ];
4390
+ const mySourceTrace = db.source_trace.get(source_trace_id);
4391
+ const traces = [];
4392
+ for (const otherSchematicTrace of db.schematic_trace.list()) {
4393
+ if (otherSchematicTrace.source_trace_id === source_trace_id) continue;
4394
+ const otherSourceTrace = db.source_trace.get(
4395
+ otherSchematicTrace.source_trace_id
4396
+ );
4397
+ const isSameNet = otherSourceTrace?.subcircuit_connectivity_map_key === mySourceTrace.subcircuit_connectivity_map_key;
4398
+ if (differentNetOnly && isSameNet) {
4399
+ continue;
4795
4400
  }
4796
- if ("path" in this.props) {
4797
- return this.props.path.map(
4798
- (p) => typeof p === "string" ? p : p.getPortSelector()
4799
- );
4401
+ if (sameNetOnly && !isSameNet) {
4402
+ continue;
4800
4403
  }
4801
- return [];
4802
- }
4803
- getTracePortPathSelectors() {
4804
- return this._getTracePortOrNetSelectorListFromProps().filter(
4805
- (selector) => !selector.includes("net.")
4806
- );
4807
- }
4808
- getTracePathNetSelectors() {
4809
- return this._getTracePortOrNetSelectorListFromProps().filter(
4810
- (selector) => selector.includes("net.")
4811
- );
4404
+ traces.push(otherSchematicTrace);
4812
4405
  }
4813
- _findConnectedPorts() {
4814
- const { db } = this.root;
4815
- const { _parsedProps: props, parent } = this;
4816
- if (!parent) throw new Error("Trace has no parent");
4817
- const portSelectors = this.getTracePortPathSelectors();
4818
- const portsWithSelectors = portSelectors.map((selector) => ({
4819
- selector,
4820
- port: this.getSubcircuit().selectOne(selector, { type: "port" }) ?? null
4821
- }));
4822
- for (const { selector, port } of portsWithSelectors) {
4823
- if (!port) {
4824
- let parentSelector;
4825
- let portToken;
4826
- const dotIndex = selector.lastIndexOf(".");
4827
- if (dotIndex !== -1 && dotIndex > selector.lastIndexOf(" ")) {
4828
- parentSelector = selector.slice(0, dotIndex);
4829
- portToken = selector.slice(dotIndex + 1);
4830
- } else {
4831
- const match = selector.match(/^(.*[ >])?([^ >]+)$/);
4832
- parentSelector = match?.[1]?.trim() ?? "";
4833
- portToken = match?.[2] ?? selector;
4834
- }
4835
- let targetComponent = parentSelector ? this.getSubcircuit().selectOne(parentSelector) : null;
4836
- if (!targetComponent && parentSelector && !/[.#\[]/.test(parentSelector)) {
4837
- targetComponent = this.getSubcircuit().selectOne(`.${parentSelector}`);
4838
- }
4839
- if (!targetComponent) {
4840
- if (parentSelector) {
4841
- this.renderError(
4842
- `Could not find port for selector "${selector}". Component "${parentSelector}" not found`
4843
- );
4844
- } else {
4845
- this.renderError(`Could not find port for selector "${selector}"`);
4846
- }
4847
- } else {
4848
- const ports = targetComponent.children.filter(
4849
- (c) => c.componentName === "Port"
4850
- );
4851
- const portLabel = portToken.includes(".") ? portToken.split(".").pop() ?? "" : portToken;
4852
- const portNames = ports.map((c) => c.getNameAndAliases()).flat();
4853
- const hasCustomLabels = portNames.some(
4854
- (n) => !/^(pin\d+|\d+)$/.test(n)
4855
- );
4856
- const labelList = Array.from(new Set(portNames)).join(", ");
4857
- let detail;
4858
- if (ports.length === 0) {
4859
- detail = "It has no ports";
4860
- } else if (!hasCustomLabels) {
4861
- detail = `It has ${ports.length} pins and no pinLabels (consider adding pinLabels)`;
4862
- } else {
4863
- detail = `It has [${labelList}]`;
4864
- }
4865
- this.renderError(
4866
- `Could not find port for selector "${selector}". Component "${targetComponent.props.name ?? parentSelector}" found, but does not have pin "${portLabel}". ${detail}`
4867
- );
4868
- }
4869
- }
4406
+ return traces;
4407
+ };
4408
+
4409
+ // lib/components/primitive-components/Trace/trace-utils/create-schematic-trace-crossing-segments.ts
4410
+ import { getUnitVectorFromPointAToB } from "@tscircuit/math-utils";
4411
+ var createSchematicTraceCrossingSegments = ({
4412
+ edges: inputEdges,
4413
+ otherEdges
4414
+ }) => {
4415
+ const edges = [...inputEdges];
4416
+ for (let i = 0; i < edges.length; i++) {
4417
+ if (i > 2e3) {
4418
+ throw new Error(
4419
+ "Over 2000 iterations spent inside createSchematicTraceCrossingSegments, you have triggered an infinite loop, please report this!"
4420
+ );
4870
4421
  }
4871
- if (portsWithSelectors.some((p) => !p.port)) {
4872
- return { allPortsFound: false };
4422
+ const edge = edges[i];
4423
+ const edgeOrientation = Math.abs(edge.from.x - edge.to.x) < 0.01 ? "vertical" : edge.from.y === edge.to.y ? "horizontal" : "not-orthogonal";
4424
+ if (edgeOrientation === "not-orthogonal") {
4425
+ continue;
4426
+ }
4427
+ const otherEdgesIntersections = [];
4428
+ for (const otherEdge of otherEdges) {
4429
+ const otherOrientation = otherEdge.from.x === otherEdge.to.x ? "vertical" : otherEdge.from.y === otherEdge.to.y ? "horizontal" : "not-orthogonal";
4430
+ if (otherOrientation === "not-orthogonal") continue;
4431
+ if (edgeOrientation === otherOrientation)
4432
+ continue;
4433
+ const hasIntersection = doesLineIntersectLine(
4434
+ [edge.from, edge.to],
4435
+ [otherEdge.from, otherEdge.to],
4436
+ { lineThickness: 0.01 }
4437
+ );
4438
+ if (hasIntersection) {
4439
+ const intersectX = edgeOrientation === "vertical" ? edge.from.x : otherEdge.from.x;
4440
+ const intersectY = edgeOrientation === "vertical" ? otherEdge.from.y : edge.from.y;
4441
+ const crossingPoint2 = { x: intersectX, y: intersectY };
4442
+ otherEdgesIntersections.push({
4443
+ otherEdge,
4444
+ crossingPoint: crossingPoint2,
4445
+ distanceFromEdgeFrom: distance2(edge.from, crossingPoint2)
4446
+ });
4447
+ }
4448
+ }
4449
+ if (otherEdgesIntersections.length === 0) continue;
4450
+ let closestIntersection = otherEdgesIntersections[0];
4451
+ for (const intersection of otherEdgesIntersections) {
4452
+ if (intersection.distanceFromEdgeFrom < closestIntersection.distanceFromEdgeFrom) {
4453
+ closestIntersection = intersection;
4454
+ }
4455
+ }
4456
+ const crossingPoint = closestIntersection.crossingPoint;
4457
+ const crossingSegmentLength = 0.075;
4458
+ if (crossingPoint.x === edge.from.x && crossingPoint.y === edge.from.y) {
4459
+ continue;
4460
+ }
4461
+ const crossingUnitVec = getUnitVectorFromPointAToB(edge.from, crossingPoint);
4462
+ const beforeCrossing = {
4463
+ x: crossingPoint.x - crossingUnitVec.x * crossingSegmentLength / 2,
4464
+ y: crossingPoint.y - crossingUnitVec.y * crossingSegmentLength / 2
4465
+ };
4466
+ const afterCrossing = {
4467
+ x: crossingPoint.x + crossingUnitVec.x * crossingSegmentLength / 2,
4468
+ y: crossingPoint.y + crossingUnitVec.y * crossingSegmentLength / 2
4469
+ };
4470
+ const overshot = distance2(afterCrossing, edge.to) < crossingSegmentLength;
4471
+ const newEdges = [
4472
+ { from: edge.from, to: beforeCrossing },
4473
+ { from: beforeCrossing, to: afterCrossing, is_crossing: true },
4474
+ { from: afterCrossing, to: edge.to }
4475
+ ];
4476
+ edges.splice(i, 1, ...newEdges);
4477
+ i += newEdges.length - 2;
4478
+ if (overshot) {
4479
+ i++;
4480
+ }
4481
+ }
4482
+ return edges;
4483
+ };
4484
+
4485
+ // lib/components/primitive-components/Trace/trace-utils/create-schematic-trace-junctions.ts
4486
+ var getIntersectionPoint = (edge1, edge2) => {
4487
+ if (edge1.from.x === edge1.to.x && edge2.from.x === edge2.to.x) {
4488
+ return null;
4489
+ }
4490
+ if (edge1.from.x === edge1.to.x) {
4491
+ const x2 = edge1.from.x;
4492
+ const m22 = (edge2.to.y - edge2.from.y) / (edge2.to.x - edge2.from.x);
4493
+ const b22 = edge2.from.y - m22 * edge2.from.x;
4494
+ const y2 = m22 * x2 + b22;
4495
+ if (x2 >= Math.min(edge2.from.x, edge2.to.x) && x2 <= Math.max(edge2.from.x, edge2.to.x) && y2 >= Math.min(edge2.from.y, edge2.to.y) && y2 <= Math.max(edge2.from.y, edge2.to.y)) {
4496
+ return { x: x2, y: y2 };
4497
+ }
4498
+ return null;
4499
+ }
4500
+ if (edge2.from.x === edge2.to.x) {
4501
+ const x2 = edge2.from.x;
4502
+ const m12 = (edge1.to.y - edge1.from.y) / (edge1.to.x - edge1.from.x);
4503
+ const b12 = edge1.from.y - m12 * edge1.from.x;
4504
+ const y2 = m12 * x2 + b12;
4505
+ if (x2 >= Math.min(edge1.from.x, edge1.to.x) && x2 <= Math.max(edge1.from.x, edge1.to.x) && y2 >= Math.min(edge1.from.y, edge1.to.y) && y2 <= Math.max(edge1.from.y, edge1.to.y)) {
4506
+ return { x: x2, y: y2 };
4507
+ }
4508
+ return null;
4509
+ }
4510
+ const m1 = (edge1.to.y - edge1.from.y) / (edge1.to.x - edge1.from.x);
4511
+ const b1 = edge1.from.y - m1 * edge1.from.x;
4512
+ const m2 = (edge2.to.y - edge2.from.y) / (edge2.to.x - edge2.from.x);
4513
+ const b2 = edge2.from.y - m2 * edge2.from.x;
4514
+ if (m1 === m2) {
4515
+ return null;
4516
+ }
4517
+ const x = (b2 - b1) / (m1 - m2);
4518
+ const y = m1 * x + b1;
4519
+ const isWithinEdge1 = x >= Math.min(edge1.from.x, edge1.to.x) && x <= Math.max(edge1.from.x, edge1.to.x) && y >= Math.min(edge1.from.y, edge1.to.y) && y <= Math.max(edge1.from.y, edge1.to.y);
4520
+ const isWithinEdge2 = x >= Math.min(edge2.from.x, edge2.to.x) && x <= Math.max(edge2.from.x, edge2.to.x) && y >= Math.min(edge2.from.y, edge2.to.y) && y <= Math.max(edge2.from.y, edge2.to.y);
4521
+ if (isWithinEdge1 && isWithinEdge2) {
4522
+ return { x, y };
4523
+ }
4524
+ return null;
4525
+ };
4526
+ var createSchematicTraceJunctions = ({
4527
+ edges: myEdges,
4528
+ db,
4529
+ source_trace_id
4530
+ }) => {
4531
+ const otherEdges = getOtherSchematicTraces({
4532
+ db,
4533
+ source_trace_id,
4534
+ sameNetOnly: true
4535
+ }).flatMap((t) => t.edges);
4536
+ const junctions = /* @__PURE__ */ new Set();
4537
+ for (const myEdge of myEdges) {
4538
+ for (const otherEdge of otherEdges) {
4539
+ const intersection = getIntersectionPoint(myEdge, otherEdge);
4540
+ if (intersection) {
4541
+ const pointKey = `${intersection.x},${intersection.y}`;
4542
+ return [{ x: intersection.x, y: intersection.y }];
4543
+ }
4544
+ }
4545
+ }
4546
+ return [];
4547
+ };
4548
+
4549
+ // lib/components/primitive-components/Trace/trace-utils/get-obstacles-for-trace.ts
4550
+ import { getUnitVectorFromDirection } from "@tscircuit/math-utils";
4551
+
4552
+ // lib/utils/autorouting/getBoundsForSchematic.ts
4553
+ function getBoundsForSchematic(db) {
4554
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
4555
+ for (const elm of db) {
4556
+ let cx, cy, w, h;
4557
+ if (elm.type === "schematic_component") {
4558
+ cx = elm.center?.x;
4559
+ cy = elm.center?.y;
4560
+ w = elm.size?.width;
4561
+ h = elm.size?.height;
4562
+ } else if (elm.type === "schematic_box") {
4563
+ cx = elm.x;
4564
+ cy = elm.y;
4565
+ w = elm.width;
4566
+ h = elm.height;
4567
+ } else if (elm.type === "schematic_port") {
4568
+ cx = elm.center?.x;
4569
+ cy = elm.center?.y;
4570
+ w = 0.2;
4571
+ h = 0.2;
4572
+ } else if (elm.type === "schematic_text") {
4573
+ cx = elm.position?.x;
4574
+ cy = elm.position?.y;
4575
+ w = (elm.text?.length ?? 0) * 0.1;
4576
+ h = 0.2;
4577
+ }
4578
+ if (typeof cx === "number" && typeof cy === "number" && typeof w === "number" && typeof h === "number") {
4579
+ minX = Math.min(minX, cx - w / 2);
4580
+ maxX = Math.max(maxX, cx + w / 2);
4581
+ minY = Math.min(minY, cy - h / 2);
4582
+ maxY = Math.max(maxY, cy + h / 2);
4583
+ }
4584
+ }
4585
+ return { minX, maxX, minY, maxY };
4586
+ }
4587
+
4588
+ // lib/utils/autorouting/getObstaclesFromBounds.ts
4589
+ function getObstaclesFromBounds(bounds, opts = {}) {
4590
+ const { minX, maxX, minY, maxY } = bounds;
4591
+ const PADDING = opts.padding ?? 1;
4592
+ if (!isFinite(minX) || !isFinite(maxX) || !isFinite(minY) || !isFinite(maxY))
4593
+ return [];
4594
+ const left = minX - PADDING;
4595
+ const right = maxX + PADDING;
4596
+ const top = maxY + PADDING;
4597
+ const bottom = minY - PADDING;
4598
+ const thickness = 0.01;
4599
+ return [
4600
+ // Top border (horizontal)
4601
+ {
4602
+ type: "rect",
4603
+ layers: ["top"],
4604
+ center: { x: (left + right) / 2, y: top },
4605
+ width: right - left,
4606
+ height: thickness,
4607
+ connectedTo: []
4608
+ },
4609
+ // Bottom border (horizontal)
4610
+ {
4611
+ type: "rect",
4612
+ layers: ["top"],
4613
+ center: { x: (left + right) / 2, y: bottom },
4614
+ width: right - left,
4615
+ height: thickness,
4616
+ connectedTo: []
4617
+ },
4618
+ // Left border (vertical)
4619
+ {
4620
+ type: "rect",
4621
+ layers: ["top"],
4622
+ center: { x: left, y: (top + bottom) / 2 },
4623
+ width: thickness,
4624
+ height: top - bottom,
4625
+ connectedTo: []
4626
+ },
4627
+ // Right border (vertical)
4628
+ {
4629
+ type: "rect",
4630
+ layers: ["top"],
4631
+ center: { x: right, y: (top + bottom) / 2 },
4632
+ width: thickness,
4633
+ height: top - bottom,
4634
+ connectedTo: []
4635
+ }
4636
+ ];
4637
+ }
4638
+
4639
+ // lib/components/primitive-components/Trace/trace-utils/get-obstacles-for-trace.ts
4640
+ var getSchematicObstaclesForTrace = (trace) => {
4641
+ const db = trace.root.db;
4642
+ const connectedPorts = trace._findConnectedPorts().ports ?? [];
4643
+ const connectedPortIds = new Set(
4644
+ connectedPorts.map((p) => p.schematic_port_id)
4645
+ );
4646
+ const obstacles = [];
4647
+ for (const elm of db.toArray()) {
4648
+ if (elm.type === "schematic_component") {
4649
+ const isSymbol = Boolean(elm.symbol_name);
4650
+ const dominateAxis = elm.size.width > elm.size.height ? "horz" : "vert";
4651
+ obstacles.push({
4652
+ type: "rect",
4653
+ layers: ["top"],
4654
+ center: elm.center,
4655
+ width: elm.size.width + (isSymbol && dominateAxis === "horz" ? -0.5 : 0),
4656
+ height: elm.size.height + (isSymbol && dominateAxis === "vert" ? -0.5 : 0),
4657
+ connectedTo: []
4658
+ });
4659
+ }
4660
+ if (elm.type === "schematic_port") {
4661
+ if (connectedPortIds.has(elm.schematic_port_id)) {
4662
+ continue;
4663
+ }
4664
+ const dirVec = elm.facing_direction ? getUnitVectorFromDirection(elm.facing_direction) : {
4665
+ x: 0,
4666
+ y: 0
4667
+ };
4668
+ obstacles.push({
4669
+ type: "rect",
4670
+ layers: ["top"],
4671
+ center: {
4672
+ x: elm.center.x - dirVec.x * 0.1,
4673
+ y: elm.center.y - dirVec.y * 0.1
4674
+ },
4675
+ width: 0.1 + Math.abs(dirVec.x) * 0.3,
4676
+ height: 0.1 + Math.abs(dirVec.y) * 0.3,
4677
+ connectedTo: []
4678
+ });
4679
+ }
4680
+ if (elm.type === "schematic_text") {
4681
+ obstacles.push({
4682
+ type: "rect",
4683
+ layers: ["top"],
4684
+ center: elm.position,
4685
+ width: (elm.text?.length ?? 0) * 0.1,
4686
+ height: 0.2,
4687
+ connectedTo: []
4688
+ });
4689
+ }
4690
+ if (elm.type === "schematic_box") {
4691
+ obstacles.push({
4692
+ type: "rect",
4693
+ layers: ["top"],
4694
+ center: { x: elm.x, y: elm.y },
4695
+ width: elm.width,
4696
+ height: elm.height,
4697
+ connectedTo: []
4698
+ });
4699
+ }
4700
+ }
4701
+ const bounds = getBoundsForSchematic(db.toArray());
4702
+ obstacles.push(...getObstaclesFromBounds(bounds, { padding: 1 }));
4703
+ return obstacles;
4704
+ };
4705
+
4706
+ // lib/components/primitive-components/Trace/trace-utils/push-edges-of-schematic-trace-to-prevent-overlap.ts
4707
+ import { doesLineIntersectLine as doesLineIntersectLine2 } from "@tscircuit/math-utils";
4708
+ var pushEdgesOfSchematicTraceToPreventOverlap = ({
4709
+ edges,
4710
+ db,
4711
+ source_trace_id
4712
+ }) => {
4713
+ const mySourceTrace = db.source_trace.get(source_trace_id);
4714
+ const otherEdges = getOtherSchematicTraces({
4715
+ db,
4716
+ source_trace_id,
4717
+ differentNetOnly: true
4718
+ }).flatMap((t) => t.edges);
4719
+ const edgeOrientation = (edge) => {
4720
+ const { from, to } = edge;
4721
+ return from.x === to.x ? "vertical" : "horizontal";
4722
+ };
4723
+ for (const mySegment of edges) {
4724
+ const mySegmentOrientation = edgeOrientation(mySegment);
4725
+ const findOverlappingParallelSegment = () => otherEdges.find(
4726
+ (otherEdge) => edgeOrientation(otherEdge) === mySegmentOrientation && doesLineIntersectLine2(
4727
+ [mySegment.from, mySegment.to],
4728
+ [otherEdge.from, otherEdge.to],
4729
+ {
4730
+ lineThickness: 0.05
4731
+ }
4732
+ )
4733
+ );
4734
+ let overlappingParallelSegmentFromOtherTrace = findOverlappingParallelSegment();
4735
+ while (overlappingParallelSegmentFromOtherTrace) {
4736
+ if (mySegmentOrientation === "horizontal") {
4737
+ mySegment.from.y += 0.1;
4738
+ mySegment.to.y += 0.1;
4739
+ } else {
4740
+ mySegment.from.x += 0.1;
4741
+ mySegment.to.x += 0.1;
4742
+ }
4743
+ overlappingParallelSegmentFromOtherTrace = findOverlappingParallelSegment();
4744
+ }
4745
+ }
4746
+ };
4747
+
4748
+ // lib/components/primitive-components/Trace/Trace_doInitialSchematicTraceRender.ts
4749
+ var Trace_doInitialSchematicTraceRender = (trace) => {
4750
+ if (trace.root?.schematicDisabled) return;
4751
+ const { db } = trace.root;
4752
+ const { _parsedProps: props, parent } = trace;
4753
+ if (!parent) throw new Error("Trace has no parent");
4754
+ const { allPortsFound, portsWithSelectors: connectedPorts } = trace._findConnectedPorts();
4755
+ const { netsWithSelectors } = trace._findConnectedNets();
4756
+ if (!allPortsFound) return;
4757
+ const portIds = connectedPorts.map((p) => p.port.schematic_port_id).sort();
4758
+ const portPairKey = portIds.join(",");
4759
+ const board = trace.root?._getBoard();
4760
+ if (board?._connectedSchematicPortPairs) {
4761
+ if (board._connectedSchematicPortPairs.has(portPairKey)) {
4762
+ return;
4763
+ }
4764
+ }
4765
+ const connection = {
4766
+ name: trace.source_trace_id,
4767
+ pointsToConnect: []
4768
+ };
4769
+ const obstacles = getSchematicObstaclesForTrace(trace);
4770
+ const portsWithPosition = connectedPorts.map(({ port }) => ({
4771
+ port,
4772
+ position: port._getGlobalSchematicPositionAfterLayout(),
4773
+ schematic_port_id: port.schematic_port_id ?? void 0,
4774
+ facingDirection: port.facingDirection
4775
+ }));
4776
+ const isPortAndNetConnection = portsWithPosition.length === 1 && netsWithSelectors.length === 1;
4777
+ if (isPortAndNetConnection) {
4778
+ const net = netsWithSelectors[0].net;
4779
+ const { port, position: anchorPos } = portsWithPosition[0];
4780
+ let connectedNetLabel = trace.getSubcircuit().selectAll("netlabel").find((nl) => {
4781
+ const conn = nl._parsedProps.connection ?? nl._parsedProps.connectsTo;
4782
+ if (!conn) return false;
4783
+ if (Array.isArray(conn)) {
4784
+ return conn.some((selector) => {
4785
+ const targetPort2 = trace.getSubcircuit().selectOne(selector, {
4786
+ port: true
4787
+ });
4788
+ return targetPort2 === port;
4789
+ });
4790
+ }
4791
+ const targetPort = trace.getSubcircuit().selectOne(conn, {
4792
+ port: true
4793
+ });
4794
+ return targetPort === port;
4795
+ });
4796
+ if (!connectedNetLabel) {
4797
+ const dbNetLabel = db.schematic_net_label.getWhere({
4798
+ source_trace_id: trace.source_trace_id
4799
+ });
4800
+ if (dbNetLabel) {
4801
+ connectedNetLabel = dbNetLabel;
4802
+ }
4803
+ }
4804
+ if (connectedNetLabel) {
4805
+ const labelPos = "_getGlobalSchematicPositionBeforeLayout" in connectedNetLabel ? connectedNetLabel._getGlobalSchematicPositionBeforeLayout() : connectedNetLabel.anchor_position;
4806
+ const edges2 = [];
4807
+ if (anchorPos.x === labelPos.x || anchorPos.y === labelPos.y) {
4808
+ edges2.push({ from: anchorPos, to: labelPos });
4809
+ } else {
4810
+ edges2.push({ from: anchorPos, to: { x: labelPos.x, y: anchorPos.y } });
4811
+ edges2.push({ from: { x: labelPos.x, y: anchorPos.y }, to: labelPos });
4812
+ }
4813
+ const dbTrace2 = db.schematic_trace.insert({
4814
+ source_trace_id: trace.source_trace_id,
4815
+ edges: edges2,
4816
+ junctions: []
4817
+ });
4818
+ trace.schematic_trace_id = dbTrace2.schematic_trace_id;
4819
+ return;
4820
+ }
4821
+ if (trace.props.schDisplayLabel) {
4822
+ const side2 = getEnteringEdgeFromDirection(port.facingDirection) ?? "bottom";
4823
+ db.schematic_net_label.insert({
4824
+ text: trace.props.schDisplayLabel,
4825
+ source_net_id: net.source_net_id,
4826
+ anchor_position: anchorPos,
4827
+ center: computeSchematicNetLabelCenter({
4828
+ anchor_position: anchorPos,
4829
+ anchor_side: side2,
4830
+ text: trace.props.schDisplayLabel
4831
+ }),
4832
+ anchor_side: side2
4833
+ });
4834
+ return;
4835
+ }
4836
+ const side = getEnteringEdgeFromDirection(port.facingDirection) ?? "bottom";
4837
+ const netLabel = db.schematic_net_label.insert({
4838
+ text: net._parsedProps.name,
4839
+ source_net_id: net.source_net_id,
4840
+ anchor_position: anchorPos,
4841
+ center: computeSchematicNetLabelCenter({
4842
+ anchor_position: anchorPos,
4843
+ anchor_side: side,
4844
+ text: net._parsedProps.name
4845
+ }),
4846
+ anchor_side: side
4847
+ });
4848
+ return;
4849
+ }
4850
+ if (trace.props.schDisplayLabel) {
4851
+ if ("from" in trace.props && "to" in trace.props || "path" in trace.props) {
4852
+ trace._doInitialSchematicTraceRenderWithDisplayLabel();
4853
+ return;
4854
+ }
4855
+ }
4856
+ if (portsWithPosition.length < 2) {
4857
+ return;
4858
+ }
4859
+ connection.pointsToConnect = portsWithPosition.map(({ position }) => ({
4860
+ ...position,
4861
+ layer: "top"
4862
+ }));
4863
+ const bounds = computeObstacleBounds(obstacles);
4864
+ const BOUNDS_MARGIN = 2;
4865
+ const simpleRouteJsonInput = {
4866
+ minTraceWidth: 0.1,
4867
+ obstacles,
4868
+ connections: [connection],
4869
+ bounds: {
4870
+ minX: bounds.minX - BOUNDS_MARGIN,
4871
+ maxX: bounds.maxX + BOUNDS_MARGIN,
4872
+ minY: bounds.minY - BOUNDS_MARGIN,
4873
+ maxY: bounds.maxY + BOUNDS_MARGIN
4874
+ },
4875
+ layerCount: 1
4876
+ };
4877
+ let Autorouter = MultilayerIjump;
4878
+ let skipOtherTraceInteraction = false;
4879
+ if (trace.getSubcircuit().props._schDirectLineRoutingEnabled) {
4880
+ Autorouter = DirectLineRouter;
4881
+ skipOtherTraceInteraction = true;
4882
+ }
4883
+ const autorouter = new Autorouter({
4884
+ input: simpleRouteJsonInput,
4885
+ MAX_ITERATIONS: 100,
4886
+ OBSTACLE_MARGIN: 0.1,
4887
+ isRemovePathLoopsEnabled: true,
4888
+ isShortenPathWithShortcutsEnabled: true,
4889
+ marginsWithCosts: [
4890
+ {
4891
+ margin: 1,
4892
+ enterCost: 0,
4893
+ travelCostFactor: 1
4894
+ },
4895
+ {
4896
+ margin: 0.3,
4897
+ enterCost: 0,
4898
+ travelCostFactor: 1
4899
+ },
4900
+ {
4901
+ margin: 0.2,
4902
+ enterCost: 0,
4903
+ travelCostFactor: 2
4904
+ },
4905
+ {
4906
+ margin: 0.1,
4907
+ enterCost: 0,
4908
+ travelCostFactor: 3
4909
+ }
4910
+ ]
4911
+ });
4912
+ let results = autorouter.solveAndMapToTraces();
4913
+ if (results.length === 0) {
4914
+ if (trace._isSymbolToChipConnection() || trace._isSymbolToSymbolConnection() || trace._isChipToChipConnection()) {
4915
+ trace._doInitialSchematicTraceRenderWithDisplayLabel();
4916
+ return;
4917
+ }
4918
+ const directLineRouter = new DirectLineRouter({
4919
+ input: simpleRouteJsonInput
4920
+ });
4921
+ results = directLineRouter.solveAndMapToTraces();
4922
+ skipOtherTraceInteraction = true;
4923
+ }
4924
+ const [{ route }] = results;
4925
+ let edges = [];
4926
+ for (let i = 0; i < route.length - 1; i++) {
4927
+ edges.push({
4928
+ from: route[i],
4929
+ to: route[i + 1]
4930
+ });
4931
+ }
4932
+ const source_trace_id = trace.source_trace_id;
4933
+ let junctions = [];
4934
+ if (!skipOtherTraceInteraction) {
4935
+ pushEdgesOfSchematicTraceToPreventOverlap({ edges, db, source_trace_id });
4936
+ const otherEdges = getOtherSchematicTraces({
4937
+ db,
4938
+ source_trace_id,
4939
+ differentNetOnly: true
4940
+ }).flatMap((t) => t.edges);
4941
+ edges = createSchematicTraceCrossingSegments({ edges, otherEdges });
4942
+ junctions = createSchematicTraceJunctions({
4943
+ edges,
4944
+ db,
4945
+ source_trace_id: trace.source_trace_id
4946
+ });
4947
+ }
4948
+ const lastEdge = edges[edges.length - 1];
4949
+ const lastEdgePort = portsWithPosition[portsWithPosition.length - 1];
4950
+ const lastDominantDirection = getDominantDirection(lastEdge);
4951
+ edges.push(...getStubEdges({ lastEdge, lastEdgePort, lastDominantDirection }));
4952
+ const firstEdge = edges[0];
4953
+ const firstEdgePort = portsWithPosition[0];
4954
+ const firstDominantDirection = getDominantDirection(firstEdge);
4955
+ edges.unshift(
4956
+ ...getStubEdges({
4957
+ firstEdge,
4958
+ firstEdgePort,
4959
+ firstDominantDirection
4960
+ })
4961
+ );
4962
+ if (!trace.source_trace_id) {
4963
+ throw new Error("Missing source_trace_id for schematic trace insertion.");
4964
+ }
4965
+ if (trace.getSubcircuit()._parsedProps.schTraceAutoLabelEnabled && countComplexElements(junctions, edges) >= 5 && (trace._isSymbolToChipConnection() || trace._isSymbolToSymbolConnection() || trace._isChipToChipConnection())) {
4966
+ trace._doInitialSchematicTraceRenderWithDisplayLabel();
4967
+ return;
4968
+ }
4969
+ const dbTrace = db.schematic_trace.insert({
4970
+ source_trace_id: trace.source_trace_id,
4971
+ edges,
4972
+ junctions
4973
+ });
4974
+ trace.schematic_trace_id = dbTrace.schematic_trace_id;
4975
+ if (board?._connectedSchematicPortPairs)
4976
+ board._connectedSchematicPortPairs.add(portPairKey);
4977
+ };
4978
+
4979
+ // lib/components/primitive-components/Trace/Trace_doInitialPcbTraceRender.ts
4980
+ import { MultilayerIjump as MultilayerIjump2 } from "@tscircuit/infgrid-ijump-astar";
4981
+ import "circuit-json";
4982
+ import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map";
4983
+
4984
+ // lib/components/primitive-components/Trace/trace-utils/compute-trace-length.ts
4985
+ function getTraceLength(route) {
4986
+ let totalLength = 0;
4987
+ for (let i = 0; i < route.length; i++) {
4988
+ const point = route[i];
4989
+ if (point.route_type === "wire") {
4990
+ const nextPoint = route[i + 1];
4991
+ if (nextPoint) {
4992
+ const dx = nextPoint.x - point.x;
4993
+ const dy = nextPoint.y - point.y;
4994
+ totalLength += Math.sqrt(dx * dx + dy * dy);
4995
+ }
4996
+ } else if (point.route_type === "via") {
4997
+ totalLength += 1.6;
4998
+ }
4999
+ }
5000
+ return totalLength;
5001
+ }
5002
+
5003
+ // lib/components/primitive-components/Trace/Trace_doInitialPcbTraceRender.ts
5004
+ var portToObjective = (port) => {
5005
+ const portPosition = port._getGlobalPcbPositionAfterLayout();
5006
+ return {
5007
+ ...portPosition,
5008
+ layers: port.getAvailablePcbLayers()
5009
+ };
5010
+ };
5011
+ var SHOULD_USE_SINGLE_LAYER_ROUTING = false;
5012
+ function Trace_doInitialPcbTraceRender(trace) {
5013
+ if (trace.root?.pcbDisabled) return;
5014
+ const { db } = trace.root;
5015
+ const { _parsedProps: props, parent } = trace;
5016
+ const subcircuit = trace.getSubcircuit();
5017
+ if (!parent) throw new Error("Trace has no parent");
5018
+ if (subcircuit._parsedProps.routingDisabled) {
5019
+ return;
5020
+ }
5021
+ const cachedRoute = subcircuit._parsedProps.pcbRouteCache?.pcbTraces;
5022
+ if (cachedRoute) {
5023
+ const pcb_trace2 = db.pcb_trace.insert({
5024
+ route: cachedRoute.flatMap((trace2) => trace2.route),
5025
+ source_trace_id: trace.source_trace_id,
5026
+ subcircuit_id: subcircuit?.subcircuit_id ?? void 0,
5027
+ pcb_group_id: trace.getGroup()?.pcb_group_id ?? void 0
5028
+ });
5029
+ trace.pcb_trace_id = pcb_trace2.pcb_trace_id;
5030
+ return;
5031
+ }
5032
+ if (!subcircuit._shouldUseTraceByTraceRouting()) {
5033
+ return;
5034
+ }
5035
+ const { allPortsFound, ports } = trace._findConnectedPorts();
5036
+ const portsConnectedOnPcbViaNet = [];
5037
+ if (!allPortsFound) return;
5038
+ const portsWithoutMatchedPcbPrimitive = [];
5039
+ for (const port of ports) {
5040
+ if (!port._hasMatchedPcbPrimitive()) {
5041
+ portsWithoutMatchedPcbPrimitive.push(port);
5042
+ }
5043
+ }
5044
+ if (portsWithoutMatchedPcbPrimitive.length > 0) {
5045
+ db.pcb_trace_error.insert({
5046
+ error_type: "pcb_trace_error",
5047
+ source_trace_id: trace.source_trace_id,
5048
+ message: `Some ports did not have a matching PCB primitive (e.g. a pad or plated hole), this can happen if a footprint is missing. As a result, ${trace} wasn't routed. Missing ports: ${portsWithoutMatchedPcbPrimitive.map((p) => p.getString()).join(", ")}`,
5049
+ pcb_trace_id: trace.pcb_trace_id,
5050
+ pcb_component_ids: [],
5051
+ pcb_port_ids: portsWithoutMatchedPcbPrimitive.map((p) => p.pcb_port_id).filter(Boolean)
5052
+ });
5053
+ return;
5054
+ }
5055
+ const nets = trace._findConnectedNets().netsWithSelectors;
5056
+ if (ports.length === 0 && nets.length === 2) {
5057
+ trace.renderError(
5058
+ `Trace connects two nets, we haven't implemented a way to route this yet`
5059
+ );
5060
+ return;
5061
+ } else if (ports.length === 1 && nets.length === 1) {
5062
+ const port = ports[0];
5063
+ const portsInNet = nets[0].net.getAllConnectedPorts();
5064
+ const otherPortsInNet = portsInNet.filter((p) => p !== port);
5065
+ if (otherPortsInNet.length === 0) {
5066
+ console.log(
5067
+ "Nothing to connect this port to, the net is empty. TODO should emit a warning!"
5068
+ );
5069
+ return;
5070
+ }
5071
+ const closestPortInNet = getClosest(port, otherPortsInNet);
5072
+ portsConnectedOnPcbViaNet.push(closestPortInNet);
5073
+ ports.push(closestPortInNet);
5074
+ } else if (ports.length > 1 && nets.length >= 1) {
5075
+ trace.renderError(
5076
+ `Trace has more than one port and one or more nets, we don't currently support this type of complex trace routing`
5077
+ );
5078
+ return;
5079
+ }
5080
+ const hints = ports.flatMap(
5081
+ (port) => port.matchedComponents.filter((c) => c.componentName === "TraceHint")
5082
+ );
5083
+ const pcbRouteHints = (trace._parsedProps.pcbRouteHints ?? []).concat(
5084
+ hints.flatMap((h) => h.getPcbRouteHints())
5085
+ );
5086
+ if (ports.length > 2) {
5087
+ trace.renderError(
5088
+ `Trace has more than two ports (${ports.map((p) => p.getString()).join(
5089
+ ", "
5090
+ )}), routing between more than two ports for a single trace is not implemented`
5091
+ );
5092
+ return;
5093
+ }
5094
+ const alreadyRoutedTraces = trace.getSubcircuit().selectAll("trace").filter(
5095
+ (trace2) => trace2.renderPhaseStates.PcbTraceRender.initialized
5096
+ );
5097
+ const isAlreadyRouted = alreadyRoutedTraces.some(
5098
+ (trace2) => trace2._portsRoutedOnPcb.length === ports.length && trace2._portsRoutedOnPcb.every(
5099
+ (portRoutedByOtherTrace) => ports.includes(portRoutedByOtherTrace)
5100
+ )
5101
+ );
5102
+ if (isAlreadyRouted) {
5103
+ return;
5104
+ }
5105
+ let orderedRouteObjectives = [];
5106
+ if (pcbRouteHints.length === 0) {
5107
+ orderedRouteObjectives = [
5108
+ portToObjective(ports[0]),
5109
+ portToObjective(ports[1])
5110
+ ];
5111
+ } else {
5112
+ orderedRouteObjectives = [
5113
+ portToObjective(ports[0]),
5114
+ ...pcbRouteHints,
5115
+ portToObjective(ports[1])
5116
+ ];
5117
+ }
5118
+ const candidateLayerCombinations = findPossibleTraceLayerCombinations(
5119
+ orderedRouteObjectives
5120
+ );
5121
+ if (SHOULD_USE_SINGLE_LAYER_ROUTING && candidateLayerCombinations.length === 0) {
5122
+ trace.renderError(
5123
+ `Could not find a common layer (using hints) for trace ${trace.getString()}`
5124
+ );
5125
+ return;
5126
+ }
5127
+ const connMap = getFullConnectivityMapFromCircuitJson(
5128
+ trace.root.db.toArray()
5129
+ );
5130
+ const [obstacles, errGettingObstacles] = tryNow(
5131
+ () => getObstaclesFromCircuitJson(trace.root.db.toArray())
5132
+ // Remove as any when autorouting-dataset gets updated
5133
+ );
5134
+ if (errGettingObstacles) {
5135
+ trace.renderError({
5136
+ type: "pcb_trace_error",
5137
+ error_type: "pcb_trace_error",
5138
+ pcb_trace_error_id: trace.pcb_trace_id,
5139
+ message: `Error getting obstacles for autorouting: ${errGettingObstacles.message}`,
5140
+ source_trace_id: trace.source_trace_id,
5141
+ center: { x: 0, y: 0 },
5142
+ pcb_port_ids: ports.map((p) => p.pcb_port_id),
5143
+ pcb_trace_id: trace.pcb_trace_id,
5144
+ pcb_component_ids: []
5145
+ });
5146
+ return;
5147
+ }
5148
+ for (const obstacle of obstacles) {
5149
+ const connectedTo = obstacle.connectedTo;
5150
+ if (connectedTo.length > 0) {
5151
+ const netId = connMap.getNetConnectedToId(obstacle.connectedTo[0]);
5152
+ if (netId) {
5153
+ obstacle.connectedTo.push(netId);
5154
+ }
5155
+ }
5156
+ }
5157
+ let orderedRoutePoints = [];
5158
+ if (candidateLayerCombinations.length === 0) {
5159
+ orderedRoutePoints = orderedRouteObjectives;
5160
+ } else {
5161
+ const candidateLayerSelections = candidateLayerCombinations[0].layer_path;
5162
+ orderedRoutePoints = orderedRouteObjectives.map((t, idx) => {
5163
+ if (t.via) {
5164
+ return {
5165
+ ...t,
5166
+ via_to_layer: candidateLayerSelections[idx]
5167
+ };
5168
+ }
5169
+ return { ...t, layers: [candidateLayerSelections[idx]] };
5170
+ });
5171
+ }
5172
+ ;
5173
+ orderedRoutePoints[0].pcb_port_id = ports[0].pcb_port_id;
5174
+ orderedRoutePoints[orderedRoutePoints.length - 1].pcb_port_id = ports[1].pcb_port_id;
5175
+ const routes = [];
5176
+ for (const [a, b] of pairs(orderedRoutePoints)) {
5177
+ const dominantLayer = "via_to_layer" in a ? a.via_to_layer : null;
5178
+ const BOUNDS_MARGIN = 2;
5179
+ const aLayer = "layers" in a && a.layers.length === 1 ? a.layers[0] : dominantLayer ?? "top";
5180
+ const bLayer = "layers" in b && b.layers.length === 1 ? b.layers[0] : dominantLayer ?? "top";
5181
+ const pcbPortA = "pcb_port_id" in a ? a.pcb_port_id : null;
5182
+ const pcbPortB = "pcb_port_id" in b ? b.pcb_port_id : null;
5183
+ const minTraceWidth = trace.getSubcircuit()._parsedProps.minTraceWidth ?? 0.16;
5184
+ const ijump = new MultilayerIjump2({
5185
+ OBSTACLE_MARGIN: minTraceWidth * 2,
5186
+ isRemovePathLoopsEnabled: true,
5187
+ optimizeWithGoalBoxes: Boolean(pcbPortA && pcbPortB),
5188
+ connMap,
5189
+ input: {
5190
+ obstacles,
5191
+ minTraceWidth,
5192
+ connections: [
5193
+ {
5194
+ name: trace.source_trace_id,
5195
+ pointsToConnect: [
5196
+ { ...a, layer: aLayer, pcb_port_id: pcbPortA },
5197
+ { ...b, layer: bLayer, pcb_port_id: pcbPortB }
5198
+ ]
5199
+ }
5200
+ ],
5201
+ layerCount: 2,
5202
+ bounds: {
5203
+ minX: Math.min(a.x, b.x) - BOUNDS_MARGIN,
5204
+ maxX: Math.max(a.x, b.x) + BOUNDS_MARGIN,
5205
+ minY: Math.min(a.y, b.y) - BOUNDS_MARGIN,
5206
+ maxY: Math.max(a.y, b.y) + BOUNDS_MARGIN
5207
+ }
5208
+ }
5209
+ });
5210
+ let traces = null;
5211
+ try {
5212
+ traces = ijump.solveAndMapToTraces();
5213
+ } catch (e) {
5214
+ trace.renderError({
5215
+ type: "pcb_trace_error",
5216
+ pcb_trace_error_id: trace.source_trace_id,
5217
+ error_type: "pcb_trace_error",
5218
+ message: `error solving route: ${e.message}`,
5219
+ source_trace_id: trace.pcb_trace_id,
5220
+ center: { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 },
5221
+ pcb_port_ids: ports.map((p) => p.pcb_port_id),
5222
+ pcb_trace_id: trace.pcb_trace_id,
5223
+ pcb_component_ids: ports.map((p) => p.pcb_component_id)
5224
+ });
5225
+ }
5226
+ if (!traces) return;
5227
+ if (traces.length === 0) {
5228
+ trace.renderError({
5229
+ type: "pcb_trace_error",
5230
+ error_type: "pcb_trace_error",
5231
+ pcb_trace_error_id: trace.pcb_trace_id,
5232
+ message: `Could not find a route for ${trace}`,
5233
+ source_trace_id: trace.source_trace_id,
5234
+ center: { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 },
5235
+ pcb_port_ids: ports.map((p) => p.pcb_port_id),
5236
+ pcb_trace_id: trace.pcb_trace_id,
5237
+ pcb_component_ids: ports.map((p) => p.pcb_component_id)
5238
+ });
5239
+ return;
5240
+ }
5241
+ const [autoroutedTrace] = traces;
5242
+ if (dominantLayer) {
5243
+ autoroutedTrace.route = autoroutedTrace.route.map((p) => {
5244
+ if (p.route_type === "wire" && !p.layer) {
5245
+ p.layer = dominantLayer;
5246
+ }
5247
+ return p;
5248
+ });
5249
+ }
5250
+ if (pcbPortA && autoroutedTrace.route[0].route_type === "wire") {
5251
+ autoroutedTrace.route[0].start_pcb_port_id = pcbPortA;
5252
+ }
5253
+ const lastRoutePoint = autoroutedTrace.route[autoroutedTrace.route.length - 1];
5254
+ if (pcbPortB && lastRoutePoint.route_type === "wire") {
5255
+ lastRoutePoint.end_pcb_port_id = pcbPortB;
5256
+ }
5257
+ routes.push(autoroutedTrace.route);
5258
+ }
5259
+ const mergedRoute = mergeRoutes(routes);
5260
+ const traceLength = getTraceLength(mergedRoute);
5261
+ const pcb_trace = db.pcb_trace.insert({
5262
+ route: mergedRoute,
5263
+ source_trace_id: trace.source_trace_id,
5264
+ subcircuit_id: trace.getSubcircuit()?.subcircuit_id,
5265
+ trace_length: traceLength
5266
+ });
5267
+ trace._portsRoutedOnPcb = ports;
5268
+ trace.pcb_trace_id = pcb_trace.pcb_trace_id;
5269
+ for (const point of mergedRoute) {
5270
+ if (point.route_type === "via") {
5271
+ db.pcb_via.insert({
5272
+ pcb_trace_id: pcb_trace.pcb_trace_id,
5273
+ x: point.x,
5274
+ y: point.y,
5275
+ hole_diameter: 0.3,
5276
+ outer_diameter: 0.6,
5277
+ layers: [point.from_layer, point.to_layer],
5278
+ from_layer: point.from_layer,
5279
+ to_layer: point.to_layer
5280
+ });
5281
+ }
5282
+ }
5283
+ trace._insertErrorIfTraceIsOutsideBoard(mergedRoute, ports);
5284
+ }
5285
+
5286
+ // lib/components/primitive-components/Trace/Trace__doInitialSchematicTraceRenderWithDisplayLabel.ts
5287
+ function Trace__doInitialSchematicTraceRenderWithDisplayLabel(trace) {
5288
+ if (trace.root?.schematicDisabled) return;
5289
+ const { db } = trace.root;
5290
+ const { _parsedProps: props, parent } = trace;
5291
+ if (!parent) throw new Error("Trace has no parent");
5292
+ const { allPortsFound, portsWithSelectors: connectedPorts } = trace._findConnectedPorts();
5293
+ if (!allPortsFound) return;
5294
+ const portsWithPosition = connectedPorts.map(({ port }) => ({
5295
+ port,
5296
+ position: port._getGlobalSchematicPositionAfterLayout(),
5297
+ schematic_port_id: port.schematic_port_id,
5298
+ facingDirection: port.facingDirection
5299
+ }));
5300
+ if (portsWithPosition.length < 2) {
5301
+ throw new Error("Expected at least two ports in portsWithPosition.");
5302
+ }
5303
+ let fromPortName;
5304
+ let toPortName;
5305
+ const fromAnchorPos = portsWithPosition[0].position;
5306
+ const fromPort = portsWithPosition[0].port;
5307
+ if ("path" in trace.props) {
5308
+ if (trace.props.path.length !== 2) {
5309
+ throw new Error("Invalid 'path': Must contain exactly two elements.");
5310
+ }
5311
+ ;
5312
+ [fromPortName, toPortName] = trace.props.path;
5313
+ } else {
5314
+ if (!("from" in trace.props && "to" in trace.props)) {
5315
+ throw new Error("Missing 'from' or 'to' properties in props.");
5316
+ }
5317
+ fromPortName = trace.props.from;
5318
+ toPortName = trace.props.to;
5319
+ }
5320
+ if (!fromPort.source_port_id) {
5321
+ throw new Error(
5322
+ `Missing source_port_id for the 'from' port (${fromPortName}).`
5323
+ );
5324
+ }
5325
+ const toAnchorPos = portsWithPosition[1].position;
5326
+ const toPort = portsWithPosition[1].port;
5327
+ if (!toPort.source_port_id) {
5328
+ throw new Error(`Missing source_port_id for the 'to' port (${toPortName}).`);
5329
+ }
5330
+ const existingFromNetLabel = db.schematic_net_label.list().find((label) => label.source_net_id === fromPort.source_port_id);
5331
+ const existingToNetLabel = db.schematic_net_label.list().find((label) => label.source_net_id === toPort.source_port_id);
5332
+ const [firstPort, secondPort] = connectedPorts.map(({ port }) => port);
5333
+ const isFirstPortSchematicBox = firstPort.parent?.config.shouldRenderAsSchematicBox;
5334
+ const pinFullName = isFirstPortSchematicBox ? `${firstPort?.parent?.props.name}_${firstPort?.props.name}` : `${secondPort?.parent?.props.name}_${secondPort?.props.name}`;
5335
+ const netLabelText = trace.props.schDisplayLabel ?? pinFullName;
5336
+ if (existingFromNetLabel && existingFromNetLabel.text !== netLabelText) {
5337
+ existingFromNetLabel.text = `${netLabelText} / ${existingFromNetLabel.text}`;
5338
+ }
5339
+ if (existingToNetLabel && existingToNetLabel?.text !== netLabelText) {
5340
+ existingToNetLabel.text = `${netLabelText} / ${existingToNetLabel.text}`;
5341
+ }
5342
+ if (!existingToNetLabel) {
5343
+ const toSide = getEnteringEdgeFromDirection(toPort.facingDirection) ?? "bottom";
5344
+ db.schematic_net_label.insert({
5345
+ text: trace.props.schDisplayLabel ?? pinFullName,
5346
+ source_net_id: toPort.source_port_id,
5347
+ anchor_position: toAnchorPos,
5348
+ center: computeSchematicNetLabelCenter({
5349
+ anchor_position: toAnchorPos,
5350
+ anchor_side: toSide,
5351
+ text: trace.props.schDisplayLabel ?? pinFullName
5352
+ }),
5353
+ anchor_side: toSide
5354
+ });
5355
+ }
5356
+ if (!existingFromNetLabel) {
5357
+ const fromSide = getEnteringEdgeFromDirection(fromPort.facingDirection) ?? "bottom";
5358
+ db.schematic_net_label.insert({
5359
+ text: trace.props.schDisplayLabel ?? pinFullName,
5360
+ source_net_id: fromPort.source_port_id,
5361
+ anchor_position: fromAnchorPos,
5362
+ center: computeSchematicNetLabelCenter({
5363
+ anchor_position: fromAnchorPos,
5364
+ anchor_side: fromSide,
5365
+ text: trace.props.schDisplayLabel ?? pinFullName
5366
+ }),
5367
+ anchor_side: fromSide
5368
+ });
5369
+ }
5370
+ }
5371
+
5372
+ // lib/components/primitive-components/Trace/Trace__findConnectedPorts.ts
5373
+ function Trace__findConnectedPorts(trace) {
5374
+ const { db } = trace.root;
5375
+ const { _parsedProps: props, parent } = trace;
5376
+ if (!parent) throw new Error("Trace has no parent");
5377
+ const portSelectors = trace.getTracePortPathSelectors();
5378
+ const portsWithSelectors = portSelectors.map((selector) => ({
5379
+ selector,
5380
+ port: trace.getSubcircuit().selectOne(selector, { type: "port" }) ?? null
5381
+ }));
5382
+ for (const { selector, port } of portsWithSelectors) {
5383
+ if (!port) {
5384
+ let parentSelector;
5385
+ let portToken;
5386
+ const dotIndex = selector.lastIndexOf(".");
5387
+ if (dotIndex !== -1 && dotIndex > selector.lastIndexOf(" ")) {
5388
+ parentSelector = selector.slice(0, dotIndex);
5389
+ portToken = selector.slice(dotIndex + 1);
5390
+ } else {
5391
+ const match = selector.match(/^(.*[ >])?([^ >]+)$/);
5392
+ parentSelector = match?.[1]?.trim() ?? "";
5393
+ portToken = match?.[2] ?? selector;
5394
+ }
5395
+ let targetComponent = parentSelector ? trace.getSubcircuit().selectOne(parentSelector) : null;
5396
+ if (!targetComponent && parentSelector && !/[.#\[]/.test(parentSelector)) {
5397
+ targetComponent = trace.getSubcircuit().selectOne(`.${parentSelector}`);
5398
+ }
5399
+ if (!targetComponent) {
5400
+ if (parentSelector) {
5401
+ trace.renderError(
5402
+ `Could not find port for selector "${selector}". Component "${parentSelector}" not found`
5403
+ );
5404
+ } else {
5405
+ trace.renderError(`Could not find port for selector "${selector}"`);
5406
+ }
5407
+ } else {
5408
+ const ports = targetComponent.children.filter(
5409
+ (c) => c.componentName === "Port"
5410
+ );
5411
+ const portLabel = portToken.includes(".") ? portToken.split(".").pop() ?? "" : portToken;
5412
+ const portNames = ports.flatMap((c) => c.getNameAndAliases());
5413
+ const hasCustomLabels = portNames.some((n) => !/^(pin\d+|\d+)$/.test(n));
5414
+ const labelList = Array.from(new Set(portNames)).join(", ");
5415
+ let detail;
5416
+ if (ports.length === 0) {
5417
+ detail = "It has no ports";
5418
+ } else if (!hasCustomLabels) {
5419
+ detail = `It has ${ports.length} pins and no pinLabels (consider adding pinLabels)`;
5420
+ } else {
5421
+ detail = `It has [${labelList}]`;
5422
+ }
5423
+ trace.renderError(
5424
+ `Could not find port for selector "${selector}". Component "${targetComponent.props.name ?? parentSelector}" found, but does not have pin "${portLabel}". ${detail}`
5425
+ );
5426
+ }
5427
+ }
5428
+ }
5429
+ if (portsWithSelectors.some((p) => !p.port)) {
5430
+ return { allPortsFound: false };
5431
+ }
5432
+ return {
5433
+ allPortsFound: true,
5434
+ portsWithSelectors,
5435
+ ports: portsWithSelectors.map(({ port }) => port)
5436
+ };
5437
+ }
5438
+
5439
+ // lib/components/primitive-components/Trace/Trace.ts
5440
+ var Trace3 = class extends PrimitiveComponent2 {
5441
+ source_trace_id = null;
5442
+ pcb_trace_id = null;
5443
+ schematic_trace_id = null;
5444
+ _portsRoutedOnPcb;
5445
+ subcircuit_connectivity_map_key = null;
5446
+ _traceConnectionHash = null;
5447
+ constructor(props) {
5448
+ super(props);
5449
+ this._portsRoutedOnPcb = [];
5450
+ }
5451
+ get config() {
5452
+ return {
5453
+ zodProps: traceProps,
5454
+ componentName: "Trace"
5455
+ };
5456
+ }
5457
+ _getTracePortOrNetSelectorListFromProps() {
5458
+ if ("from" in this.props && "to" in this.props) {
5459
+ return [
5460
+ typeof this.props.from === "string" ? this.props.from : this.props.from.getPortSelector(),
5461
+ typeof this.props.to === "string" ? this.props.to : this.props.to.getPortSelector()
5462
+ ];
5463
+ }
5464
+ if ("path" in this.props) {
5465
+ return this.props.path.map(
5466
+ (p) => typeof p === "string" ? p : p.getPortSelector()
5467
+ );
4873
5468
  }
4874
- return {
4875
- allPortsFound: true,
4876
- portsWithSelectors,
4877
- ports: portsWithSelectors.map(({ port }) => port)
4878
- };
5469
+ return [];
5470
+ }
5471
+ getTracePortPathSelectors() {
5472
+ return this._getTracePortOrNetSelectorListFromProps().filter(
5473
+ (selector) => !selector.includes("net.")
5474
+ );
5475
+ }
5476
+ getTracePathNetSelectors() {
5477
+ return this._getTracePortOrNetSelectorListFromProps().filter(
5478
+ (selector) => selector.includes("net.")
5479
+ );
5480
+ }
5481
+ _findConnectedPorts() {
5482
+ return Trace__findConnectedPorts(this);
4879
5483
  }
4880
5484
  _resolveNet(selector) {
4881
5485
  const direct = this.getSubcircuit().selectOne(selector, {
@@ -4997,363 +5601,10 @@ var Trace2 = class extends PrimitiveComponent2 {
4997
5601
  }
4998
5602
  }
4999
5603
  doInitialPcbTraceRender() {
5000
- if (this.root?.pcbDisabled) return;
5001
- const { db } = this.root;
5002
- const { _parsedProps: props, parent } = this;
5003
- const subcircuit = this.getSubcircuit();
5004
- if (!parent) throw new Error("Trace has no parent");
5005
- if (subcircuit._parsedProps.routingDisabled) {
5006
- return;
5007
- }
5008
- const cachedRoute = subcircuit._parsedProps.pcbRouteCache?.pcbTraces;
5009
- if (cachedRoute) {
5010
- const pcb_trace2 = db.pcb_trace.insert({
5011
- route: cachedRoute.flatMap((trace) => trace.route),
5012
- source_trace_id: this.source_trace_id,
5013
- subcircuit_id: subcircuit?.subcircuit_id ?? void 0,
5014
- pcb_group_id: this.getGroup()?.pcb_group_id ?? void 0
5015
- });
5016
- this.pcb_trace_id = pcb_trace2.pcb_trace_id;
5017
- return;
5018
- }
5019
- if (!subcircuit._shouldUseTraceByTraceRouting()) {
5020
- return;
5021
- }
5022
- const { allPortsFound, ports } = this._findConnectedPorts();
5023
- const portsConnectedOnPcbViaNet = [];
5024
- if (!allPortsFound) return;
5025
- const portsWithoutMatchedPcbPrimitive = [];
5026
- for (const port of ports) {
5027
- if (!port._hasMatchedPcbPrimitive()) {
5028
- portsWithoutMatchedPcbPrimitive.push(port);
5029
- }
5030
- }
5031
- if (portsWithoutMatchedPcbPrimitive.length > 0) {
5032
- db.pcb_trace_error.insert({
5033
- error_type: "pcb_trace_error",
5034
- source_trace_id: this.source_trace_id,
5035
- message: `Some ports did not have a matching PCB primitive (e.g. a pad or plated hole), this can happen if a footprint is missing. As a result, ${this} wasn't routed. Missing ports: ${portsWithoutMatchedPcbPrimitive.map((p) => p.getString()).join(", ")}`,
5036
- pcb_trace_id: this.pcb_trace_id,
5037
- pcb_component_ids: [],
5038
- pcb_port_ids: portsWithoutMatchedPcbPrimitive.map((p) => p.pcb_port_id).filter(Boolean)
5039
- });
5040
- return;
5041
- }
5042
- const nets = this._findConnectedNets().netsWithSelectors;
5043
- if (ports.length === 0 && nets.length === 2) {
5044
- this.renderError(
5045
- `Trace connects two nets, we haven't implemented a way to route this yet`
5046
- );
5047
- return;
5048
- } else if (ports.length === 1 && nets.length === 1) {
5049
- const port = ports[0];
5050
- const portsInNet = nets[0].net.getAllConnectedPorts();
5051
- const otherPortsInNet = portsInNet.filter((p) => p !== port);
5052
- if (otherPortsInNet.length === 0) {
5053
- console.log(
5054
- "Nothing to connect this port to, the net is empty. TODO should emit a warning!"
5055
- );
5056
- return;
5057
- }
5058
- const closestPortInNet = getClosest(port, otherPortsInNet);
5059
- portsConnectedOnPcbViaNet.push(closestPortInNet);
5060
- ports.push(closestPortInNet);
5061
- } else if (ports.length > 1 && nets.length >= 1) {
5062
- this.renderError(
5063
- `Trace has more than one port and one or more nets, we don't currently support this type of complex trace routing`
5064
- );
5065
- return;
5066
- }
5067
- const hints = ports.flatMap(
5068
- (port) => port.matchedComponents.filter((c) => c.componentName === "TraceHint")
5069
- );
5070
- const pcbRouteHints = (this._parsedProps.pcbRouteHints ?? []).concat(
5071
- hints.flatMap((h) => h.getPcbRouteHints())
5072
- );
5073
- if (ports.length > 2) {
5074
- this.renderError(
5075
- `Trace has more than two ports (${ports.map((p) => p.getString()).join(
5076
- ", "
5077
- )}), routing between more than two ports for a single trace is not implemented`
5078
- );
5079
- return;
5080
- }
5081
- const alreadyRoutedTraces = this.getSubcircuit().selectAll("trace").filter(
5082
- (trace) => trace.renderPhaseStates.PcbTraceRender.initialized
5083
- );
5084
- const isAlreadyRouted = alreadyRoutedTraces.some(
5085
- (trace) => trace._portsRoutedOnPcb.length === ports.length && trace._portsRoutedOnPcb.every(
5086
- (portRoutedByOtherTrace) => ports.includes(portRoutedByOtherTrace)
5087
- )
5088
- );
5089
- if (isAlreadyRouted) {
5090
- return;
5091
- }
5092
- let orderedRouteObjectives = [];
5093
- if (pcbRouteHints.length === 0) {
5094
- orderedRouteObjectives = [
5095
- portToObjective(ports[0]),
5096
- portToObjective(ports[1])
5097
- ];
5098
- } else {
5099
- orderedRouteObjectives = [
5100
- portToObjective(ports[0]),
5101
- ...pcbRouteHints,
5102
- portToObjective(ports[1])
5103
- ];
5104
- }
5105
- const candidateLayerCombinations = findPossibleTraceLayerCombinations(
5106
- orderedRouteObjectives
5107
- );
5108
- if (SHOULD_USE_SINGLE_LAYER_ROUTING && candidateLayerCombinations.length === 0) {
5109
- this.renderError(
5110
- `Could not find a common layer (using hints) for trace ${this.getString()}`
5111
- );
5112
- return;
5113
- }
5114
- const connMap = getFullConnectivityMapFromCircuitJson(
5115
- this.root.db.toArray()
5116
- );
5117
- const [obstacles, errGettingObstacles] = tryNow(
5118
- () => getObstaclesFromCircuitJson(this.root.db.toArray())
5119
- // Remove as any when autorouting-dataset gets updated
5120
- );
5121
- if (errGettingObstacles) {
5122
- this.renderError({
5123
- type: "pcb_trace_error",
5124
- error_type: "pcb_trace_error",
5125
- pcb_trace_error_id: this.pcb_trace_id,
5126
- message: `Error getting obstacles for autorouting: ${errGettingObstacles.message}`,
5127
- source_trace_id: this.source_trace_id,
5128
- center: { x: 0, y: 0 },
5129
- pcb_port_ids: ports.map((p) => p.pcb_port_id),
5130
- pcb_trace_id: this.pcb_trace_id,
5131
- pcb_component_ids: []
5132
- });
5133
- return;
5134
- }
5135
- for (const obstacle of obstacles) {
5136
- const connectedTo = obstacle.connectedTo;
5137
- if (connectedTo.length > 0) {
5138
- const netId = connMap.getNetConnectedToId(obstacle.connectedTo[0]);
5139
- if (netId) {
5140
- obstacle.connectedTo.push(netId);
5141
- }
5142
- }
5143
- }
5144
- let orderedRoutePoints = [];
5145
- if (candidateLayerCombinations.length === 0) {
5146
- orderedRoutePoints = orderedRouteObjectives;
5147
- } else {
5148
- const candidateLayerSelections = candidateLayerCombinations[0].layer_path;
5149
- orderedRoutePoints = orderedRouteObjectives.map((t, idx) => {
5150
- if (t.via) {
5151
- return {
5152
- ...t,
5153
- via_to_layer: candidateLayerSelections[idx]
5154
- };
5155
- }
5156
- return { ...t, layers: [candidateLayerSelections[idx]] };
5157
- });
5158
- }
5159
- ;
5160
- orderedRoutePoints[0].pcb_port_id = ports[0].pcb_port_id;
5161
- orderedRoutePoints[orderedRoutePoints.length - 1].pcb_port_id = ports[1].pcb_port_id;
5162
- const routes = [];
5163
- for (const [a, b] of pairs(orderedRoutePoints)) {
5164
- const dominantLayer = "via_to_layer" in a ? a.via_to_layer : null;
5165
- const BOUNDS_MARGIN = 2;
5166
- const aLayer = "layers" in a && a.layers.length === 1 ? a.layers[0] : dominantLayer ?? "top";
5167
- const bLayer = "layers" in b && b.layers.length === 1 ? b.layers[0] : dominantLayer ?? "top";
5168
- const pcbPortA = "pcb_port_id" in a ? a.pcb_port_id : null;
5169
- const pcbPortB = "pcb_port_id" in b ? b.pcb_port_id : null;
5170
- const minTraceWidth = this.getSubcircuit()._parsedProps.minTraceWidth ?? 0.16;
5171
- const ijump = new MultilayerIjump({
5172
- OBSTACLE_MARGIN: minTraceWidth * 2,
5173
- isRemovePathLoopsEnabled: true,
5174
- optimizeWithGoalBoxes: Boolean(pcbPortA && pcbPortB),
5175
- connMap,
5176
- input: {
5177
- obstacles,
5178
- minTraceWidth,
5179
- connections: [
5180
- {
5181
- name: this.source_trace_id,
5182
- pointsToConnect: [
5183
- { ...a, layer: aLayer, pcb_port_id: pcbPortA },
5184
- { ...b, layer: bLayer, pcb_port_id: pcbPortB }
5185
- ]
5186
- }
5187
- ],
5188
- layerCount: 2,
5189
- bounds: {
5190
- minX: Math.min(a.x, b.x) - BOUNDS_MARGIN,
5191
- maxX: Math.max(a.x, b.x) + BOUNDS_MARGIN,
5192
- minY: Math.min(a.y, b.y) - BOUNDS_MARGIN,
5193
- maxY: Math.max(a.y, b.y) + BOUNDS_MARGIN
5194
- }
5195
- }
5196
- });
5197
- let traces = null;
5198
- try {
5199
- traces = ijump.solveAndMapToTraces();
5200
- } catch (e) {
5201
- this.renderError({
5202
- type: "pcb_trace_error",
5203
- pcb_trace_error_id: this.source_trace_id,
5204
- error_type: "pcb_trace_error",
5205
- message: `error solving route: ${e.message}`,
5206
- source_trace_id: this.pcb_trace_id,
5207
- center: { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 },
5208
- pcb_port_ids: ports.map((p) => p.pcb_port_id),
5209
- pcb_trace_id: this.pcb_trace_id,
5210
- pcb_component_ids: ports.map((p) => p.pcb_component_id)
5211
- });
5212
- }
5213
- if (!traces) return;
5214
- if (traces.length === 0) {
5215
- this.renderError({
5216
- type: "pcb_trace_error",
5217
- error_type: "pcb_trace_error",
5218
- pcb_trace_error_id: this.pcb_trace_id,
5219
- message: `Could not find a route for ${this}`,
5220
- source_trace_id: this.source_trace_id,
5221
- center: { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 },
5222
- pcb_port_ids: ports.map((p) => p.pcb_port_id),
5223
- pcb_trace_id: this.pcb_trace_id,
5224
- pcb_component_ids: ports.map((p) => p.pcb_component_id)
5225
- });
5226
- return;
5227
- }
5228
- const [trace] = traces;
5229
- if (dominantLayer) {
5230
- trace.route = trace.route.map((p) => {
5231
- if (p.route_type === "wire" && !p.layer) {
5232
- p.layer = dominantLayer;
5233
- }
5234
- return p;
5235
- });
5236
- }
5237
- if (pcbPortA && trace.route[0].route_type === "wire") {
5238
- trace.route[0].start_pcb_port_id = pcbPortA;
5239
- }
5240
- const lastRoutePoint = trace.route[trace.route.length - 1];
5241
- if (pcbPortB && lastRoutePoint.route_type === "wire") {
5242
- lastRoutePoint.end_pcb_port_id = pcbPortB;
5243
- }
5244
- routes.push(trace.route);
5245
- }
5246
- const mergedRoute = mergeRoutes(routes);
5247
- const traceLength = getTraceLength(mergedRoute);
5248
- const pcb_trace = db.pcb_trace.insert({
5249
- route: mergedRoute,
5250
- source_trace_id: this.source_trace_id,
5251
- subcircuit_id: this.getSubcircuit()?.subcircuit_id,
5252
- trace_length: traceLength
5253
- });
5254
- this._portsRoutedOnPcb = ports;
5255
- this.pcb_trace_id = pcb_trace.pcb_trace_id;
5256
- for (const point of mergedRoute) {
5257
- if (point.route_type === "via") {
5258
- db.pcb_via.insert({
5259
- pcb_trace_id: pcb_trace.pcb_trace_id,
5260
- x: point.x,
5261
- y: point.y,
5262
- hole_diameter: 0.3,
5263
- outer_diameter: 0.6,
5264
- layers: [point.from_layer, point.to_layer],
5265
- from_layer: point.from_layer,
5266
- to_layer: point.to_layer
5267
- });
5268
- }
5269
- }
5270
- this._insertErrorIfTraceIsOutsideBoard(mergedRoute, ports);
5604
+ Trace_doInitialPcbTraceRender(this);
5271
5605
  }
5272
5606
  _doInitialSchematicTraceRenderWithDisplayLabel() {
5273
- if (this.root?.schematicDisabled) return;
5274
- const { db } = this.root;
5275
- const { _parsedProps: props, parent } = this;
5276
- if (!parent) throw new Error("Trace has no parent");
5277
- const { allPortsFound, portsWithSelectors: connectedPorts } = this._findConnectedPorts();
5278
- if (!allPortsFound) return;
5279
- const portsWithPosition = connectedPorts.map(({ port }) => ({
5280
- port,
5281
- position: port._getGlobalSchematicPositionAfterLayout(),
5282
- schematic_port_id: port.schematic_port_id,
5283
- facingDirection: port.facingDirection
5284
- }));
5285
- if (portsWithPosition.length < 2) {
5286
- throw new Error("Expected at least two ports in portsWithPosition.");
5287
- }
5288
- let fromPortName;
5289
- let toPortName;
5290
- const fromAnchorPos = portsWithPosition[0].position;
5291
- const fromPort = portsWithPosition[0].port;
5292
- if ("path" in this.props) {
5293
- if (this.props.path.length !== 2) {
5294
- throw new Error("Invalid 'path': Must contain exactly two elements.");
5295
- }
5296
- ;
5297
- [fromPortName, toPortName] = this.props.path;
5298
- } else {
5299
- if (!("from" in this.props && "to" in this.props)) {
5300
- throw new Error("Missing 'from' or 'to' properties in props.");
5301
- }
5302
- fromPortName = this.props.from;
5303
- toPortName = this.props.to;
5304
- }
5305
- if (!fromPort.source_port_id) {
5306
- throw new Error(
5307
- `Missing source_port_id for the 'from' port (${fromPortName}).`
5308
- );
5309
- }
5310
- const toAnchorPos = portsWithPosition[1].position;
5311
- const toPort = portsWithPosition[1].port;
5312
- if (!toPort.source_port_id) {
5313
- throw new Error(
5314
- `Missing source_port_id for the 'to' port (${toPortName}).`
5315
- );
5316
- }
5317
- const existingFromNetLabel = db.schematic_net_label.list().find((label) => label.source_net_id === fromPort.source_port_id);
5318
- const existingToNetLabel = db.schematic_net_label.list().find((label) => label.source_net_id === toPort.source_port_id);
5319
- const [firstPort, secondPort] = connectedPorts.map(({ port }) => port);
5320
- const isFirstPortSchematicBox = firstPort.parent?.config.shouldRenderAsSchematicBox;
5321
- const pinFullName = isFirstPortSchematicBox ? `${firstPort?.parent?.props.name}_${firstPort?.props.name}` : `${secondPort?.parent?.props.name}_${secondPort?.props.name}`;
5322
- const netLabelText = this.props.schDisplayLabel ?? pinFullName;
5323
- if (existingFromNetLabel && existingFromNetLabel.text !== netLabelText) {
5324
- existingFromNetLabel.text = `${netLabelText} / ${existingFromNetLabel.text}`;
5325
- }
5326
- if (existingToNetLabel && existingToNetLabel?.text !== netLabelText) {
5327
- existingToNetLabel.text = `${netLabelText} / ${existingToNetLabel.text}`;
5328
- }
5329
- if (!existingToNetLabel) {
5330
- const toSide = getEnteringEdgeFromDirection(toPort.facingDirection) ?? "bottom";
5331
- db.schematic_net_label.insert({
5332
- text: this.props.schDisplayLabel ?? pinFullName,
5333
- source_net_id: toPort.source_port_id,
5334
- anchor_position: toAnchorPos,
5335
- center: computeSchematicNetLabelCenter({
5336
- anchor_position: toAnchorPos,
5337
- anchor_side: toSide,
5338
- text: this.props.schDisplayLabel ?? pinFullName
5339
- }),
5340
- anchor_side: toSide
5341
- });
5342
- }
5343
- if (!existingFromNetLabel) {
5344
- const fromSide = getEnteringEdgeFromDirection(fromPort.facingDirection) ?? "bottom";
5345
- db.schematic_net_label.insert({
5346
- text: this.props.schDisplayLabel ?? pinFullName,
5347
- source_net_id: fromPort.source_port_id,
5348
- anchor_position: fromAnchorPos,
5349
- center: computeSchematicNetLabelCenter({
5350
- anchor_position: fromAnchorPos,
5351
- anchor_side: fromSide,
5352
- text: this.props.schDisplayLabel ?? pinFullName
5353
- }),
5354
- anchor_side: fromSide
5355
- });
5356
- }
5607
+ Trace__doInitialSchematicTraceRenderWithDisplayLabel(this);
5357
5608
  }
5358
5609
  _isSymbolToChipConnection() {
5359
5610
  const { allPortsFound, ports } = this._findConnectedPorts();
@@ -5383,235 +5634,7 @@ var Trace2 = class extends PrimitiveComponent2 {
5383
5634
  return isPort1Chip && isPort2Chip;
5384
5635
  }
5385
5636
  doInitialSchematicTraceRender() {
5386
- if (this.root?.schematicDisabled) return;
5387
- const { db } = this.root;
5388
- const { _parsedProps: props, parent } = this;
5389
- if (!parent) throw new Error("Trace has no parent");
5390
- const { allPortsFound, portsWithSelectors: connectedPorts } = this._findConnectedPorts();
5391
- const { netsWithSelectors } = this._findConnectedNets();
5392
- if (!allPortsFound) return;
5393
- const portIds = connectedPorts.map((p) => p.port.schematic_port_id).sort();
5394
- const portPairKey = portIds.join(",");
5395
- const board = this.root?._getBoard();
5396
- if (board?._connectedSchematicPortPairs) {
5397
- if (board._connectedSchematicPortPairs.has(portPairKey)) {
5398
- return;
5399
- }
5400
- }
5401
- const connection = {
5402
- name: this.source_trace_id,
5403
- pointsToConnect: []
5404
- };
5405
- const obstacles = getSchematicObstaclesForTrace(this);
5406
- const portsWithPosition = connectedPorts.map(({ port }) => ({
5407
- port,
5408
- position: port._getGlobalSchematicPositionAfterLayout(),
5409
- schematic_port_id: port.schematic_port_id ?? void 0,
5410
- facingDirection: port.facingDirection
5411
- }));
5412
- const isPortAndNetConnection = portsWithPosition.length === 1 && netsWithSelectors.length === 1;
5413
- if (isPortAndNetConnection) {
5414
- const net = netsWithSelectors[0].net;
5415
- const { port, position: anchorPos } = portsWithPosition[0];
5416
- let connectedNetLabel = this.getSubcircuit().selectAll("netlabel").find((nl) => {
5417
- const conn = nl._parsedProps.connection ?? nl._parsedProps.connectsTo;
5418
- if (!conn) return false;
5419
- if (Array.isArray(conn)) {
5420
- return conn.some((selector) => {
5421
- const targetPort2 = this.getSubcircuit().selectOne(selector, {
5422
- port: true
5423
- });
5424
- return targetPort2 === port;
5425
- });
5426
- }
5427
- const targetPort = this.getSubcircuit().selectOne(conn, {
5428
- port: true
5429
- });
5430
- return targetPort === port;
5431
- });
5432
- if (!connectedNetLabel) {
5433
- const dbNetLabel = db.schematic_net_label.getWhere({
5434
- source_trace_id: this.source_trace_id
5435
- });
5436
- if (dbNetLabel) {
5437
- connectedNetLabel = dbNetLabel;
5438
- }
5439
- }
5440
- if (connectedNetLabel) {
5441
- const labelPos = "_getGlobalSchematicPositionBeforeLayout" in connectedNetLabel ? connectedNetLabel._getGlobalSchematicPositionBeforeLayout() : connectedNetLabel.anchor_position;
5442
- const edges2 = [];
5443
- if (anchorPos.x === labelPos.x || anchorPos.y === labelPos.y) {
5444
- edges2.push({ from: anchorPos, to: labelPos });
5445
- } else {
5446
- edges2.push({ from: anchorPos, to: { x: labelPos.x, y: anchorPos.y } });
5447
- edges2.push({ from: { x: labelPos.x, y: anchorPos.y }, to: labelPos });
5448
- }
5449
- const trace2 = db.schematic_trace.insert({
5450
- source_trace_id: this.source_trace_id,
5451
- edges: edges2,
5452
- junctions: []
5453
- });
5454
- this.schematic_trace_id = trace2.schematic_trace_id;
5455
- return;
5456
- }
5457
- if (this.props.schDisplayLabel) {
5458
- const side2 = getEnteringEdgeFromDirection(port.facingDirection) ?? "bottom";
5459
- db.schematic_net_label.insert({
5460
- text: this.props.schDisplayLabel,
5461
- source_net_id: net.source_net_id,
5462
- anchor_position: anchorPos,
5463
- center: computeSchematicNetLabelCenter({
5464
- anchor_position: anchorPos,
5465
- anchor_side: side2,
5466
- text: this.props.schDisplayLabel
5467
- }),
5468
- anchor_side: side2
5469
- });
5470
- return;
5471
- }
5472
- const side = getEnteringEdgeFromDirection(port.facingDirection) ?? "bottom";
5473
- const netLabel = db.schematic_net_label.insert({
5474
- text: net._parsedProps.name,
5475
- source_net_id: net.source_net_id,
5476
- anchor_position: anchorPos,
5477
- center: computeSchematicNetLabelCenter({
5478
- anchor_position: anchorPos,
5479
- anchor_side: side,
5480
- text: net._parsedProps.name
5481
- }),
5482
- anchor_side: side
5483
- });
5484
- return;
5485
- }
5486
- if (this.props.schDisplayLabel) {
5487
- if ("from" in this.props && "to" in this.props || "path" in this.props) {
5488
- this._doInitialSchematicTraceRenderWithDisplayLabel();
5489
- return;
5490
- }
5491
- }
5492
- if (portsWithPosition.length < 2) {
5493
- return;
5494
- }
5495
- connection.pointsToConnect = portsWithPosition.map(({ position }) => ({
5496
- ...position,
5497
- layer: "top"
5498
- }));
5499
- const bounds = computeObstacleBounds(obstacles);
5500
- const BOUNDS_MARGIN = 2;
5501
- const simpleRouteJsonInput = {
5502
- minTraceWidth: 0.1,
5503
- obstacles,
5504
- connections: [connection],
5505
- bounds: {
5506
- minX: bounds.minX - BOUNDS_MARGIN,
5507
- maxX: bounds.maxX + BOUNDS_MARGIN,
5508
- minY: bounds.minY - BOUNDS_MARGIN,
5509
- maxY: bounds.maxY + BOUNDS_MARGIN
5510
- },
5511
- layerCount: 1
5512
- };
5513
- let Autorouter = MultilayerIjump;
5514
- let skipOtherTraceInteraction = false;
5515
- if (this.getSubcircuit().props._schDirectLineRoutingEnabled) {
5516
- Autorouter = DirectLineRouter;
5517
- skipOtherTraceInteraction = true;
5518
- }
5519
- const autorouter = new Autorouter({
5520
- input: simpleRouteJsonInput,
5521
- MAX_ITERATIONS: 100,
5522
- OBSTACLE_MARGIN: 0.1,
5523
- isRemovePathLoopsEnabled: true,
5524
- isShortenPathWithShortcutsEnabled: true,
5525
- marginsWithCosts: [
5526
- {
5527
- margin: 1,
5528
- enterCost: 0,
5529
- travelCostFactor: 1
5530
- },
5531
- {
5532
- margin: 0.3,
5533
- enterCost: 0,
5534
- travelCostFactor: 1
5535
- },
5536
- {
5537
- margin: 0.2,
5538
- enterCost: 0,
5539
- travelCostFactor: 2
5540
- },
5541
- {
5542
- margin: 0.1,
5543
- enterCost: 0,
5544
- travelCostFactor: 3
5545
- }
5546
- ]
5547
- });
5548
- let results = autorouter.solveAndMapToTraces();
5549
- if (results.length === 0) {
5550
- if (this._isSymbolToChipConnection() || this._isSymbolToSymbolConnection() || this._isChipToChipConnection()) {
5551
- this._doInitialSchematicTraceRenderWithDisplayLabel();
5552
- return;
5553
- }
5554
- const directLineRouter = new DirectLineRouter({
5555
- input: simpleRouteJsonInput
5556
- });
5557
- results = directLineRouter.solveAndMapToTraces();
5558
- skipOtherTraceInteraction = true;
5559
- }
5560
- const [{ route }] = results;
5561
- let edges = [];
5562
- for (let i = 0; i < route.length - 1; i++) {
5563
- edges.push({
5564
- from: route[i],
5565
- to: route[i + 1]
5566
- });
5567
- }
5568
- const source_trace_id = this.source_trace_id;
5569
- let junctions = [];
5570
- if (!skipOtherTraceInteraction) {
5571
- pushEdgesOfSchematicTraceToPreventOverlap({ edges, db, source_trace_id });
5572
- const otherEdges = getOtherSchematicTraces({
5573
- db,
5574
- source_trace_id,
5575
- differentNetOnly: true
5576
- }).flatMap((t) => t.edges);
5577
- edges = createSchematicTraceCrossingSegments({ edges, otherEdges });
5578
- junctions = createSchematicTraceJunctions({
5579
- edges,
5580
- db,
5581
- source_trace_id: this.source_trace_id
5582
- });
5583
- }
5584
- const lastEdge = edges[edges.length - 1];
5585
- const lastEdgePort = portsWithPosition[portsWithPosition.length - 1];
5586
- const lastDominantDirection = getDominantDirection(lastEdge);
5587
- edges.push(
5588
- ...getStubEdges({ lastEdge, lastEdgePort, lastDominantDirection })
5589
- );
5590
- const firstEdge = edges[0];
5591
- const firstEdgePort = portsWithPosition[0];
5592
- const firstDominantDirection = getDominantDirection(firstEdge);
5593
- edges.unshift(
5594
- ...getStubEdges({
5595
- firstEdge,
5596
- firstEdgePort,
5597
- firstDominantDirection
5598
- })
5599
- );
5600
- if (!this.source_trace_id) {
5601
- throw new Error("Missing source_trace_id for schematic trace insertion.");
5602
- }
5603
- if (this.getSubcircuit()._parsedProps.schTraceAutoLabelEnabled && countComplexElements(junctions, edges) >= 5 && (this._isSymbolToChipConnection() || this._isSymbolToSymbolConnection() || this._isChipToChipConnection())) {
5604
- this._doInitialSchematicTraceRenderWithDisplayLabel();
5605
- return;
5606
- }
5607
- const trace = db.schematic_trace.insert({
5608
- source_trace_id: this.source_trace_id,
5609
- edges,
5610
- junctions
5611
- });
5612
- this.schematic_trace_id = trace.schematic_trace_id;
5613
- if (board?._connectedSchematicPortPairs)
5614
- board._connectedSchematicPortPairs.add(portPairKey);
5637
+ Trace_doInitialSchematicTraceRender(this);
5615
5638
  }
5616
5639
  };
5617
5640
 
@@ -6456,7 +6479,7 @@ var NormalComponent = class extends PrimitiveComponent2 {
6456
6479
  const targets = Array.isArray(target) ? target : [target];
6457
6480
  for (const targetPath of targets) {
6458
6481
  this.add(
6459
- new Trace2({
6482
+ new Trace3({
6460
6483
  from: `${this.getSubcircuitSelector()} > port.${pinName}`,
6461
6484
  to: targetPath
6462
6485
  })
@@ -6638,7 +6661,7 @@ var CapacityMeshAutorouter = class {
6638
6661
 
6639
6662
  // lib/components/primitive-components/Group/Group.ts
6640
6663
  import "circuit-json";
6641
- import Debug5 from "debug";
6664
+ import Debug6 from "debug";
6642
6665
  import "zod";
6643
6666
 
6644
6667
  // lib/components/primitive-components/TraceHint.ts
@@ -6887,7 +6910,7 @@ var applyEditEvents = ({
6887
6910
  // lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson.ts
6888
6911
  import { su as su4 } from "@tscircuit/circuit-json-util";
6889
6912
  import {
6890
- getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson2
6913
+ getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson3
6891
6914
  } from "circuit-json-to-connectivity-map";
6892
6915
 
6893
6916
  // lib/utils/autorouting/getAncestorSubcircuitIds.ts
@@ -6938,7 +6961,7 @@ var getSimpleRouteJsonFromCircuitJson = ({
6938
6961
  );
6939
6962
  const board = db.pcb_board.list()[0];
6940
6963
  db = su4(subcircuitElements);
6941
- const connMap = getFullConnectivityMapFromCircuitJson2(subcircuitElements);
6964
+ const connMap = getFullConnectivityMapFromCircuitJson3(subcircuitElements);
6942
6965
  const obstacles = getObstaclesFromCircuitJson(
6943
6966
  [
6944
6967
  ...db.pcb_component.list(),
@@ -7287,64 +7310,60 @@ import {
7287
7310
  // lib/components/primitive-components/Group/Group_doInitialSchematicLayoutMatchAdapt.ts
7288
7311
  import "@tscircuit/circuit-json-util";
7289
7312
  import "circuit-json-to-connectivity-map";
7290
- import corpus from "@tscircuit/schematic-corpus";
7291
- import { convertCircuitJsonToBpc } from "circuit-json-to-bpc";
7313
+ import { corpusNoNetLabel } from "@tscircuit/schematic-corpus";
7292
7314
  import {
7293
- layoutSchematicGraph
7315
+ convertCircuitJsonToBpc
7316
+ } from "circuit-json-to-bpc";
7317
+ import {
7318
+ layoutSchematicGraph,
7319
+ getGraphicsForBpcGraph
7294
7320
  } from "bpc-graph";
7321
+ import "graphics-debug";
7322
+ import "circuit-to-svg";
7323
+ import Debug5 from "debug";
7324
+ var debug4 = Debug5("Group_doInitialSchematicLayoutMatchAdapt");
7295
7325
  function Group_doInitialSchematicLayoutMatchAdapt(group) {
7296
7326
  const { db } = group.root;
7297
7327
  const subtreeCircuitJson = structuredClone(db.toArray());
7298
- const existingLabels = new Set(
7299
- subtreeCircuitJson.filter((e) => e.type === "schematic_net_label").map((e) => `${e.anchor_position?.x},${e.anchor_position?.y}`)
7300
- );
7301
- const oppositeSideFromFacing = {
7302
- left: "right",
7303
- right: "left",
7304
- top: "bottom",
7305
- bottom: "top"
7306
- };
7307
- const generatedNetLabels = /* @__PURE__ */ new Map();
7308
- for (const sp of subtreeCircuitJson.filter(
7309
- (e) => e.type === "schematic_port"
7310
- )) {
7311
- const key = `${sp.center.x},${sp.center.y}`;
7312
- if (existingLabels.has(key)) continue;
7313
- const srcPort = db.source_port.get(sp.source_port_id);
7314
- const srcNet = db.source_net.getWhere({
7315
- subcircuit_connectivity_map_key: srcPort?.subcircuit_connectivity_map_key
7316
- });
7317
- if (!srcNet) {
7318
- console.error(`No source net found for port: ${sp.source_port_id}`);
7319
- continue;
7320
- }
7321
- const srcTrace = db.source_trace.getWhere({
7322
- subcircuit_connectivity_map_key: srcPort?.subcircuit_connectivity_map_key
7323
- });
7324
- const schematic_net_label_id = `netlabel_for_${sp.schematic_port_id}`;
7325
- const source_net = db.source_net.get(srcNet.source_net_id);
7326
- const schematic_net_label = {
7327
- type: "schematic_net_label",
7328
- schematic_net_label_id,
7329
- text: source_net.name,
7330
- source_net_id: srcNet.source_net_id,
7331
- source_trace_id: srcTrace?.source_trace_id,
7332
- anchor_position: { ...sp.center },
7333
- center: { ...sp.center },
7334
- anchor_side: oppositeSideFromFacing[sp.facing_direction] ?? "right"
7335
- };
7336
- generatedNetLabels.set(schematic_net_label_id, {
7337
- schematic_net_label,
7338
- schematic_port: sp
7339
- });
7340
- subtreeCircuitJson.push(schematic_net_label);
7328
+ const bpcGraphBeforeGeneratedNetLabels = convertCircuitJsonToBpc(subtreeCircuitJson);
7329
+ console.log("Writing bpcGraphBeforeGeneratedNetLabels.svg");
7330
+ if (debug4.enabled) {
7331
+ global.debugGraphics?.push(
7332
+ getGraphicsForBpcGraph(bpcGraphBeforeGeneratedNetLabels, {
7333
+ title: "floatingBpcGraph"
7334
+ })
7335
+ );
7341
7336
  }
7342
- const targetBpcGraph = convertCircuitJsonToBpc(subtreeCircuitJson);
7337
+ const targetBpcGraph = convertCircuitJsonToBpc(
7338
+ subtreeCircuitJson
7339
+ // .concat(implicitNetLabels),
7340
+ );
7343
7341
  const laidOutBpcGraph = layoutSchematicGraph(targetBpcGraph, {
7344
7342
  singletonKeys: ["vcc/2", "gnd/2"],
7345
7343
  centerPinColors: ["netlabel_center", "component_center"],
7346
- corpus
7344
+ floatingBoxIdsWithMutablePinOffsets: new Set(
7345
+ targetBpcGraph.boxes.filter((box) => {
7346
+ const boxPins = targetBpcGraph.pins.filter(
7347
+ (p) => p.boxId === box.boxId
7348
+ );
7349
+ const nonCenterBoxPins = boxPins.filter(
7350
+ (bp) => !bp.color.includes("center")
7351
+ );
7352
+ if (nonCenterBoxPins.length <= 2) {
7353
+ return true;
7354
+ }
7355
+ return false;
7356
+ }).map((b) => b.boxId)
7357
+ ),
7358
+ corpus: corpusNoNetLabel
7347
7359
  });
7360
+ if (debug4.enabled) {
7361
+ global.debugGraphics?.push(
7362
+ getGraphicsForBpcGraph(laidOutBpcGraph, {
7363
+ title: "laidOutBpcGraph"
7364
+ })
7365
+ );
7366
+ }
7348
7367
  const groupOffset = group._getGlobalSchematicPositionBeforeLayout();
7349
7368
  for (const box of laidOutBpcGraph.boxes) {
7350
7369
  if (!box.center) continue;
@@ -7394,36 +7413,6 @@ function Group_doInitialSchematicLayoutMatchAdapt(group) {
7394
7413
  };
7395
7414
  continue;
7396
7415
  }
7397
- if (generatedNetLabels.has(box.boxId)) {
7398
- const { schematic_net_label: generatedNetLabel, schematic_port } = generatedNetLabels.get(box.boxId);
7399
- const pins = laidOutBpcGraph.pins.filter((p) => p.boxId === box.boxId);
7400
- const center = pins.find((p) => p.color === "netlabel_center");
7401
- const anchor = pins.find((p) => p.color !== "netlabel_center");
7402
- const color = anchor.color;
7403
- const symbolName = color === "vcc" ? "vcc" : color === "gnd" ? "gnd" : void 0;
7404
- const anchorSide = color === "vcc" ? "bottom" : color === "gnd" ? "top" : oppositeSideFromFacing[schematic_port.facing_direction] ?? "right";
7405
- const source_net = db.source_net.get(generatedNetLabel.source_net_id);
7406
- const schematic_net_label2 = {
7407
- type: "schematic_net_label",
7408
- schematic_net_label_id: `netlabel_for_${box.boxId}`,
7409
- text: source_net.name,
7410
- // no text; just a placeholder box for Match-Adapt
7411
- anchor_position: {
7412
- x: box.center.x + groupOffset.x + center.offset.x,
7413
- y: box.center.y + groupOffset.y + center.offset.y
7414
- },
7415
- center: {
7416
- x: box.center.x + groupOffset.x + center.offset.x,
7417
- y: box.center.y + groupOffset.y + center.offset.y
7418
- },
7419
- anchor_side: anchorSide,
7420
- symbol_name: symbolName,
7421
- source_net_id: generatedNetLabel.source_net_id,
7422
- source_trace_id: generatedNetLabel.source_trace_id
7423
- };
7424
- db.schematic_net_label.insert(schematic_net_label2);
7425
- continue;
7426
- }
7427
7416
  console.error(
7428
7417
  `No schematic element found for box: ${box.boxId}. This is a bug in the matchAdapt binding with @tscircuit/core`
7429
7418
  );
@@ -7924,20 +7913,20 @@ var Group = class extends NormalComponent {
7924
7913
  return false;
7925
7914
  }
7926
7915
  _hasTracesToRoute() {
7927
- const debug4 = Debug5("tscircuit:core:_hasTracesToRoute");
7916
+ const debug5 = Debug6("tscircuit:core:_hasTracesToRoute");
7928
7917
  const traces = this.selectAll("trace");
7929
- debug4(`[${this.getString()}] has ${traces.length} traces to route`);
7918
+ debug5(`[${this.getString()}] has ${traces.length} traces to route`);
7930
7919
  return traces.length > 0;
7931
7920
  }
7932
7921
  async _runEffectMakeHttpAutoroutingRequest() {
7933
7922
  const { db } = this.root;
7934
- const debug4 = Debug5("tscircuit:core:_runEffectMakeHttpAutoroutingRequest");
7923
+ const debug5 = Debug6("tscircuit:core:_runEffectMakeHttpAutoroutingRequest");
7935
7924
  const props = this._parsedProps;
7936
7925
  const autorouterConfig = this._getAutorouterConfig();
7937
7926
  const serverUrl = autorouterConfig.serverUrl;
7938
7927
  const serverMode = autorouterConfig.serverMode;
7939
7928
  const fetchWithDebug = (url, options) => {
7940
- debug4("fetching", url);
7929
+ debug5("fetching", url);
7941
7930
  if (options.headers) {
7942
7931
  options.headers["Tscircuit-Core-Version"] = this.root?.getCoreVersion();
7943
7932
  }
@@ -8053,8 +8042,8 @@ var Group = class extends NormalComponent {
8053
8042
  async _runLocalAutorouting() {
8054
8043
  const { db } = this.root;
8055
8044
  const props = this._parsedProps;
8056
- const debug4 = Debug5("tscircuit:core:_runLocalAutorouting");
8057
- debug4(`[${this.getString()}] starting local autorouting`);
8045
+ const debug5 = Debug6("tscircuit:core:_runLocalAutorouting");
8046
+ debug5(`[${this.getString()}] starting local autorouting`);
8058
8047
  const autorouterConfig = this._getAutorouterConfig();
8059
8048
  const { simpleRouteJson } = getSimpleRouteJsonFromCircuitJson({
8060
8049
  db,
@@ -8079,11 +8068,11 @@ var Group = class extends NormalComponent {
8079
8068
  const routingPromise = new Promise(
8080
8069
  (resolve, reject) => {
8081
8070
  autorouter.on("complete", (event) => {
8082
- debug4(`[${this.getString()}] local autorouting complete`);
8071
+ debug5(`[${this.getString()}] local autorouting complete`);
8083
8072
  resolve(event.traces);
8084
8073
  });
8085
8074
  autorouter.on("error", (event) => {
8086
- debug4(
8075
+ debug5(
8087
8076
  `[${this.getString()}] local autorouting error: ${event.error.message}`
8088
8077
  );
8089
8078
  reject(event.error);
@@ -8140,30 +8129,30 @@ var Group = class extends NormalComponent {
8140
8129
  }
8141
8130
  }
8142
8131
  doInitialPcbTraceRender() {
8143
- const debug4 = Debug5("tscircuit:core:doInitialPcbTraceRender");
8132
+ const debug5 = Debug6("tscircuit:core:doInitialPcbTraceRender");
8144
8133
  if (!this.isSubcircuit) return;
8145
8134
  if (this.root?.pcbDisabled) return;
8146
8135
  if (this.getInheritedProperty("routingDisabled")) return;
8147
8136
  if (this._shouldUseTraceByTraceRouting()) return;
8148
8137
  if (!this._areChildSubcircuitsRouted()) {
8149
- debug4(
8138
+ debug5(
8150
8139
  `[${this.getString()}] child subcircuits are not routed, skipping async autorouting until subcircuits routed`
8151
8140
  );
8152
8141
  return;
8153
8142
  }
8154
- debug4(
8143
+ debug5(
8155
8144
  `[${this.getString()}] no child subcircuits to wait for, initiating async routing`
8156
8145
  );
8157
8146
  if (!this._hasTracesToRoute()) return;
8158
8147
  this._startAsyncAutorouting();
8159
8148
  }
8160
8149
  updatePcbTraceRender() {
8161
- const debug4 = Debug5("tscircuit:core:updatePcbTraceRender");
8162
- debug4(`[${this.getString()}] updating...`);
8150
+ const debug5 = Debug6("tscircuit:core:updatePcbTraceRender");
8151
+ debug5(`[${this.getString()}] updating...`);
8163
8152
  if (!this.isSubcircuit) return;
8164
8153
  if (this._shouldRouteAsync() && this._hasTracesToRoute() && !this._hasStartedAsyncAutorouting) {
8165
8154
  if (this._areChildSubcircuitsRouted()) {
8166
- debug4(
8155
+ debug5(
8167
8156
  `[${this.getString()}] child subcircuits are now routed, starting async autorouting`
8168
8157
  );
8169
8158
  this._startAsyncAutorouting();
@@ -8174,14 +8163,14 @@ var Group = class extends NormalComponent {
8174
8163
  if (this._shouldUseTraceByTraceRouting()) return;
8175
8164
  const { db } = this.root;
8176
8165
  if (this._asyncAutoroutingResult.output_simple_route_json) {
8177
- debug4(
8166
+ debug5(
8178
8167
  `[${this.getString()}] updating PCB traces from simple route json (${this._asyncAutoroutingResult.output_simple_route_json.traces?.length} traces)`
8179
8168
  );
8180
8169
  this._updatePcbTraceRenderFromSimpleRouteJson();
8181
8170
  return;
8182
8171
  }
8183
8172
  if (this._asyncAutoroutingResult.output_pcb_traces) {
8184
- debug4(
8173
+ debug5(
8185
8174
  `[${this.getString()}] updating PCB traces from ${this._asyncAutoroutingResult.output_pcb_traces.length} traces`
8186
8175
  );
8187
8176
  this._updatePcbTraceRenderFromPcbTraces();
@@ -8653,13 +8642,13 @@ var Capacitor = class extends NormalComponent {
8653
8642
  doInitialCreateTracesFromProps() {
8654
8643
  if (this.props.decouplingFor && this.props.decouplingTo) {
8655
8644
  this.add(
8656
- new Trace2({
8645
+ new Trace3({
8657
8646
  from: `${this.getSubcircuitSelector()} > port.1`,
8658
8647
  to: this.props.decouplingFor
8659
8648
  })
8660
8649
  );
8661
8650
  this.add(
8662
- new Trace2({
8651
+ new Trace3({
8663
8652
  from: `${this.getSubcircuitSelector()} > port.2`,
8664
8653
  to: this.props.decouplingTo
8665
8654
  })
@@ -8735,7 +8724,7 @@ var Chip = class extends NormalComponent {
8735
8724
  if (props.externallyConnectedPins) {
8736
8725
  for (const [pin1, pin2] of props.externallyConnectedPins) {
8737
8726
  this.add(
8738
- new Trace2({
8727
+ new Trace3({
8739
8728
  from: `${this.getSubcircuitSelector()} > port.${pin1}`,
8740
8729
  to: `${this.getSubcircuitSelector()} > port.${pin2}`
8741
8730
  })
@@ -9192,13 +9181,13 @@ var Resistor = class extends NormalComponent {
9192
9181
  doInitialCreateTracesFromProps() {
9193
9182
  if (this.props.pullupFor && this.props.pullupTo) {
9194
9183
  this.add(
9195
- new Trace2({
9184
+ new Trace3({
9196
9185
  from: `${this.getSubcircuitSelector()} > port.1`,
9197
9186
  to: this.props.pullupFor
9198
9187
  })
9199
9188
  );
9200
9189
  this.add(
9201
- new Trace2({
9190
+ new Trace3({
9202
9191
  from: `${this.getSubcircuitSelector()} > port.2`,
9203
9192
  to: this.props.pullupTo
9204
9193
  })
@@ -9206,13 +9195,13 @@ var Resistor = class extends NormalComponent {
9206
9195
  }
9207
9196
  if (this.props.pulldownFor && this.props.pulldownTo) {
9208
9197
  this.add(
9209
- new Trace2({
9198
+ new Trace3({
9210
9199
  from: `${this.getSubcircuitSelector()} > port.1`,
9211
9200
  to: this.props.pulldownFor
9212
9201
  })
9213
9202
  );
9214
9203
  this.add(
9215
- new Trace2({
9204
+ new Trace3({
9216
9205
  from: `${this.getSubcircuitSelector()} > port.2`,
9217
9206
  to: this.props.pulldownTo
9218
9207
  })
@@ -9611,7 +9600,7 @@ var NetLabel = class extends PrimitiveComponent2 {
9611
9600
  if (!connectsTo) return;
9612
9601
  for (const connection of connectsTo) {
9613
9602
  this.add(
9614
- new Trace2({
9603
+ new Trace3({
9615
9604
  from: connection,
9616
9605
  to: `net.${this._getNetName()}`
9617
9606
  })
@@ -10725,7 +10714,7 @@ import { identity as identity5 } from "transformation-matrix";
10725
10714
  var package_default = {
10726
10715
  name: "@tscircuit/core",
10727
10716
  type: "module",
10728
- version: "0.0.549",
10717
+ version: "0.0.551",
10729
10718
  types: "dist/index.d.ts",
10730
10719
  main: "dist/index.js",
10731
10720
  module: "dist/index.js",
@@ -10759,7 +10748,7 @@ var package_default = {
10759
10748
  "@tscircuit/math-utils": "^0.0.18",
10760
10749
  "@tscircuit/props": "^0.0.251",
10761
10750
  "@tscircuit/schematic-autolayout": "^0.0.6",
10762
- "@tscircuit/schematic-corpus": "^0.0.50",
10751
+ "@tscircuit/schematic-corpus": "^0.0.52",
10763
10752
  "@tscircuit/schematic-match-adapt": "^0.0.16",
10764
10753
  "@tscircuit/simple-3d-svg": "^0.0.6",
10765
10754
  "@types/bun": "^1.2.16",
@@ -10767,18 +10756,18 @@ var package_default = {
10767
10756
  "@types/react": "^19.0.1",
10768
10757
  "@types/react-dom": "^19.0.2",
10769
10758
  "@types/react-reconciler": "^0.28.9",
10770
- "bpc-graph": "^0.0.50",
10759
+ "bpc-graph": "^0.0.53",
10771
10760
  "bun-match-svg": "0.0.12",
10772
10761
  "calculate-elbow": "^0.0.5",
10773
10762
  "chokidar-cli": "^3.0.0",
10774
10763
  "circuit-json": "^0.0.215",
10775
- "circuit-json-to-bpc": "^0.0.7",
10764
+ "circuit-json-to-bpc": "^0.0.13",
10776
10765
  "circuit-json-to-connectivity-map": "^0.0.22",
10777
10766
  "circuit-json-to-simple-3d": "^0.0.2",
10778
10767
  "circuit-to-svg": "^0.0.162",
10779
10768
  concurrently: "^9.1.2",
10780
10769
  debug: "^4.3.6",
10781
- "graphics-debug": "^0.0.57",
10770
+ "graphics-debug": "^0.0.60",
10782
10771
  howfat: "^0.3.8",
10783
10772
  "live-server": "^1.2.2",
10784
10773
  "looks-same": "^9.0.1",
@@ -11258,7 +11247,7 @@ export {
11258
11247
  Subcircuit,
11259
11248
  Switch,
11260
11249
  TestPoint,
11261
- Trace2 as Trace,
11250
+ Trace3 as Trace,
11262
11251
  TraceHint,
11263
11252
  Transistor,
11264
11253
  Via,