@tscircuit/core 0.0.550 → 0.0.551

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 +1150 -1127
  3. package/package.json +1 -1
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,135 +4370,1129 @@ 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
- };
4788
- }
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
- ];
4795
- }
4796
- if ("path" in this.props) {
4797
- return this.props.path.map(
4798
- (p) => typeof p === "string" ? p : p.getPortSelector()
4799
- );
4800
- }
4801
- return [];
4802
- }
4803
- getTracePortPathSelectors() {
4804
- return this._getTracePortOrNetSelectorListFromProps().filter(
4805
- (selector) => !selector.includes("net.")
4806
- );
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;
4807
4389
  }
4808
- getTracePathNetSelectors() {
4809
- return this._getTracePortOrNetSelectorListFromProps().filter(
4810
- (selector) => selector.includes("net.")
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
4811
4396
  );
4812
- }
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
- }
4397
+ const isSameNet = otherSourceTrace?.subcircuit_connectivity_map_key === mySourceTrace.subcircuit_connectivity_map_key;
4398
+ if (differentNetOnly && isSameNet) {
4399
+ continue;
4870
4400
  }
4871
- if (portsWithSelectors.some((p) => !p.port)) {
4872
- return { allPortsFound: false };
4401
+ if (sameNetOnly && !isSameNet) {
4402
+ continue;
4873
4403
  }
4874
- return {
4875
- allPortsFound: true,
4876
- portsWithSelectors,
4877
- ports: portsWithSelectors.map(({ port }) => port)
4878
- };
4879
- }
4880
- _resolveNet(selector) {
4881
- const direct = this.getSubcircuit().selectOne(selector, {
4882
- type: "net"
4883
- });
4884
- if (direct) return direct;
4885
- const match = selector.match(/^net\.(.+)$/);
4886
- const netName = match ? match[1] : null;
4887
- if (!netName) return null;
4888
- const allDescendants = this.root._getBoard().getDescendants();
4889
- return allDescendants.find(
4890
- (d) => d.componentName === "Net" && d._parsedProps.name === netName
4891
- ) || null;
4404
+ traces.push(otherSchematicTrace);
4405
+ }
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
+ );
4421
+ }
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
+ );
5468
+ }
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);
5483
+ }
5484
+ _resolveNet(selector) {
5485
+ const direct = this.getSubcircuit().selectOne(selector, {
5486
+ type: "net"
5487
+ });
5488
+ if (direct) return direct;
5489
+ const match = selector.match(/^net\.(.+)$/);
5490
+ const netName = match ? match[1] : null;
5491
+ if (!netName) return null;
5492
+ const allDescendants = this.root._getBoard().getDescendants();
5493
+ return allDescendants.find(
5494
+ (d) => d.componentName === "Net" && d._parsedProps.name === netName
5495
+ ) || null;
4892
5496
  }
4893
5497
  _findConnectedNets() {
4894
5498
  const netsWithSelectors = this.getTracePathNetSelectors().map(
@@ -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
  })
@@ -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(),
@@ -8653,13 +8676,13 @@ var Capacitor = class extends NormalComponent {
8653
8676
  doInitialCreateTracesFromProps() {
8654
8677
  if (this.props.decouplingFor && this.props.decouplingTo) {
8655
8678
  this.add(
8656
- new Trace2({
8679
+ new Trace3({
8657
8680
  from: `${this.getSubcircuitSelector()} > port.1`,
8658
8681
  to: this.props.decouplingFor
8659
8682
  })
8660
8683
  );
8661
8684
  this.add(
8662
- new Trace2({
8685
+ new Trace3({
8663
8686
  from: `${this.getSubcircuitSelector()} > port.2`,
8664
8687
  to: this.props.decouplingTo
8665
8688
  })
@@ -8735,7 +8758,7 @@ var Chip = class extends NormalComponent {
8735
8758
  if (props.externallyConnectedPins) {
8736
8759
  for (const [pin1, pin2] of props.externallyConnectedPins) {
8737
8760
  this.add(
8738
- new Trace2({
8761
+ new Trace3({
8739
8762
  from: `${this.getSubcircuitSelector()} > port.${pin1}`,
8740
8763
  to: `${this.getSubcircuitSelector()} > port.${pin2}`
8741
8764
  })
@@ -9192,13 +9215,13 @@ var Resistor = class extends NormalComponent {
9192
9215
  doInitialCreateTracesFromProps() {
9193
9216
  if (this.props.pullupFor && this.props.pullupTo) {
9194
9217
  this.add(
9195
- new Trace2({
9218
+ new Trace3({
9196
9219
  from: `${this.getSubcircuitSelector()} > port.1`,
9197
9220
  to: this.props.pullupFor
9198
9221
  })
9199
9222
  );
9200
9223
  this.add(
9201
- new Trace2({
9224
+ new Trace3({
9202
9225
  from: `${this.getSubcircuitSelector()} > port.2`,
9203
9226
  to: this.props.pullupTo
9204
9227
  })
@@ -9206,13 +9229,13 @@ var Resistor = class extends NormalComponent {
9206
9229
  }
9207
9230
  if (this.props.pulldownFor && this.props.pulldownTo) {
9208
9231
  this.add(
9209
- new Trace2({
9232
+ new Trace3({
9210
9233
  from: `${this.getSubcircuitSelector()} > port.1`,
9211
9234
  to: this.props.pulldownFor
9212
9235
  })
9213
9236
  );
9214
9237
  this.add(
9215
- new Trace2({
9238
+ new Trace3({
9216
9239
  from: `${this.getSubcircuitSelector()} > port.2`,
9217
9240
  to: this.props.pulldownTo
9218
9241
  })
@@ -9611,7 +9634,7 @@ var NetLabel = class extends PrimitiveComponent2 {
9611
9634
  if (!connectsTo) return;
9612
9635
  for (const connection of connectsTo) {
9613
9636
  this.add(
9614
- new Trace2({
9637
+ new Trace3({
9615
9638
  from: connection,
9616
9639
  to: `net.${this._getNetName()}`
9617
9640
  })
@@ -10725,7 +10748,7 @@ import { identity as identity5 } from "transformation-matrix";
10725
10748
  var package_default = {
10726
10749
  name: "@tscircuit/core",
10727
10750
  type: "module",
10728
- version: "0.0.549",
10751
+ version: "0.0.550",
10729
10752
  types: "dist/index.d.ts",
10730
10753
  main: "dist/index.js",
10731
10754
  module: "dist/index.js",
@@ -11258,7 +11281,7 @@ export {
11258
11281
  Subcircuit,
11259
11282
  Switch,
11260
11283
  TestPoint,
11261
- Trace2 as Trace,
11284
+ Trace3 as Trace,
11262
11285
  TraceHint,
11263
11286
  Transistor,
11264
11287
  Via,