@tscircuit/core 0.0.1281 → 0.0.1282

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 (2) hide show
  1. package/dist/index.js +734 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3843,7 +3843,7 @@ var Trace_doInitialSchematicTraceRender = (trace) => {
3843
3843
  elbowEdges.push({ from: path[j], to: path[j + 1] });
3844
3844
  }
3845
3845
  }
3846
- const doesSegmentIntersectRect = (edge, rect) => {
3846
+ const doesSegmentIntersectRect2 = (edge, rect) => {
3847
3847
  const halfW = rect.width / 2;
3848
3848
  const halfH = rect.height / 2;
3849
3849
  const left = rect.center.x - halfW;
@@ -3876,7 +3876,7 @@ var Trace_doInitialSchematicTraceRender = (trace) => {
3876
3876
  };
3877
3877
  for (const edge of elbowEdges) {
3878
3878
  for (const obstacle of obstacles) {
3879
- if (doesSegmentIntersectRect(edge, obstacle)) {
3879
+ if (doesSegmentIntersectRect2(edge, obstacle)) {
3880
3880
  return null;
3881
3881
  }
3882
3882
  }
@@ -21867,7 +21867,7 @@ import { identity as identity5 } from "transformation-matrix";
21867
21867
  var package_default = {
21868
21868
  name: "@tscircuit/core",
21869
21869
  type: "module",
21870
- version: "0.0.1280",
21870
+ version: "0.0.1281",
21871
21871
  types: "dist/index.d.ts",
21872
21872
  main: "dist/index.js",
21873
21873
  module: "dist/index.js",
@@ -24866,7 +24866,733 @@ var PcbNoteDimension = class extends PrimitiveComponent2 {
24866
24866
 
24867
24867
  // lib/components/primitive-components/Breakout/Breakout.ts
24868
24868
  import "@tscircuit/props";
24869
- import { BreakoutPointSolver } from "@tscircuit/breakout-point-solver";
24869
+
24870
+ // node_modules/@tscircuit/breakout-point-solver/lib/BreakoutPointSolver.ts
24871
+ import { BaseSolver } from "@tscircuit/solver-utils";
24872
+
24873
+ // node_modules/@tscircuit/breakout-point-solver/lib/boundary/get-breakout-boundary-intersection.ts
24874
+ import {
24875
+ distance as distance14,
24876
+ getSegmentIntersection
24877
+ } from "@tscircuit/math-utils";
24878
+ var getBreakoutBoundaryIntersection = ({
24879
+ from,
24880
+ to,
24881
+ bounds
24882
+ }) => {
24883
+ if (from.x === to.x && from.y === to.y) return null;
24884
+ const boundarySegments = [
24885
+ [
24886
+ { x: bounds.minX, y: bounds.minY },
24887
+ { x: bounds.maxX, y: bounds.minY }
24888
+ ],
24889
+ [
24890
+ { x: bounds.maxX, y: bounds.minY },
24891
+ { x: bounds.maxX, y: bounds.maxY }
24892
+ ],
24893
+ [
24894
+ { x: bounds.maxX, y: bounds.maxY },
24895
+ { x: bounds.minX, y: bounds.maxY }
24896
+ ],
24897
+ [
24898
+ { x: bounds.minX, y: bounds.maxY },
24899
+ { x: bounds.minX, y: bounds.minY }
24900
+ ]
24901
+ ];
24902
+ const candidates = boundarySegments.map(([start, end]) => getSegmentIntersection(from, to, start, end)).filter((point6) => point6 !== null);
24903
+ candidates.sort((a, b) => distance14(from, a) - distance14(from, b));
24904
+ return candidates[0] ?? null;
24905
+ };
24906
+
24907
+ // node_modules/@tscircuit/breakout-point-solver/lib/boundary/get-available-breakout-boundary-point.ts
24908
+ import { distance as distance15 } from "@tscircuit/math-utils";
24909
+
24910
+ // node_modules/@tscircuit/breakout-point-solver/lib/pad/breakout-pad-collisions.ts
24911
+ import {
24912
+ doesSegmentIntersectRect,
24913
+ getBoundFromCenteredRect
24914
+ } from "@tscircuit/math-utils";
24915
+ var degreesToRadians = (degrees) => degrees * Math.PI / 180;
24916
+ var rotatePoint = (point6, radians) => {
24917
+ const cos = Math.cos(radians);
24918
+ const sin = Math.sin(radians);
24919
+ return {
24920
+ x: point6.x * cos - point6.y * sin,
24921
+ y: point6.x * sin + point6.y * cos
24922
+ };
24923
+ };
24924
+ var getLocalPadPoint = (point6, pad) => {
24925
+ const translatedPoint = {
24926
+ x: point6.x - pad.center.x,
24927
+ y: point6.y - pad.center.y
24928
+ };
24929
+ return rotatePoint(
24930
+ translatedPoint,
24931
+ -degreesToRadians(pad.ccwRotationDegrees ?? 0)
24932
+ );
24933
+ };
24934
+ var getInflatedPadRect = (pad) => {
24935
+ const clearance = pad.clearance ?? 0;
24936
+ return getBoundFromCenteredRect({
24937
+ center: { x: 0, y: 0 },
24938
+ width: pad.width + clearance * 2,
24939
+ height: pad.height + clearance * 2
24940
+ });
24941
+ };
24942
+ var isBreakoutPadIgnoredForSourcePort = ({
24943
+ pad,
24944
+ sourcePortId
24945
+ }) => {
24946
+ if (!pad.sourcePortIds) return false;
24947
+ return pad.sourcePortIds.includes(sourcePortId);
24948
+ };
24949
+ var isBreakoutPadIgnoredForLayer = ({
24950
+ pad,
24951
+ layer
24952
+ }) => {
24953
+ if (!pad.layer || !layer) return false;
24954
+ return pad.layer !== layer;
24955
+ };
24956
+ var doesBreakoutSegmentIntersectPad = ({
24957
+ from,
24958
+ to,
24959
+ pad
24960
+ }) => doesSegmentIntersectRect(
24961
+ getLocalPadPoint(from, pad),
24962
+ getLocalPadPoint(to, pad),
24963
+ getInflatedPadRect(pad)
24964
+ );
24965
+ var doesBreakoutSegmentIntersectNonIgnoredPads = ({
24966
+ from,
24967
+ to,
24968
+ pads,
24969
+ ignoredSourcePortIds,
24970
+ layer
24971
+ }) => {
24972
+ for (const pad of pads) {
24973
+ if (ignoredSourcePortIds.some(
24974
+ (sourcePortId) => isBreakoutPadIgnoredForSourcePort({
24975
+ pad,
24976
+ sourcePortId
24977
+ })
24978
+ ) || isBreakoutPadIgnoredForLayer({
24979
+ pad,
24980
+ layer
24981
+ })) {
24982
+ continue;
24983
+ }
24984
+ if (doesBreakoutSegmentIntersectPad({ from, to, pad })) {
24985
+ return true;
24986
+ }
24987
+ }
24988
+ return false;
24989
+ };
24990
+ var doesBreakoutSegmentIntersectPads = ({
24991
+ from,
24992
+ to,
24993
+ pads,
24994
+ sourcePortId,
24995
+ layer
24996
+ }) => doesBreakoutSegmentIntersectNonIgnoredPads({
24997
+ from,
24998
+ to,
24999
+ pads,
25000
+ ignoredSourcePortIds: [sourcePortId],
25001
+ layer
25002
+ });
25003
+
25004
+ // node_modules/@tscircuit/breakout-point-solver/lib/boundary/get-available-breakout-boundary-point.ts
25005
+ var BOUNDARY_POINT_DISTANCE_TOLERANCE = 1e-6;
25006
+ var isInsideRequiredSpacing = ({
25007
+ candidate,
25008
+ usedPoint,
25009
+ boundaryPointSpacing
25010
+ }) => distance15(usedPoint, candidate) < boundaryPointSpacing - BOUNDARY_POINT_DISTANCE_TOLERANCE;
25011
+ var getBoundsEdge = (point6, bounds) => {
25012
+ if (Math.abs(point6.x - bounds.minX) < BOUNDARY_POINT_DISTANCE_TOLERANCE)
25013
+ return "left";
25014
+ if (Math.abs(point6.x - bounds.maxX) < BOUNDARY_POINT_DISTANCE_TOLERANCE)
25015
+ return "right";
25016
+ if (Math.abs(point6.y - bounds.minY) < BOUNDARY_POINT_DISTANCE_TOLERANCE)
25017
+ return "bottom";
25018
+ if (Math.abs(point6.y - bounds.maxY) < BOUNDARY_POINT_DISTANCE_TOLERANCE)
25019
+ return "top";
25020
+ return null;
25021
+ };
25022
+ var getBoundsEdgeCandidates = ({
25023
+ edge,
25024
+ bounds,
25025
+ step
25026
+ }) => {
25027
+ const candidates = [];
25028
+ if (edge === "left" || edge === "right") {
25029
+ const x = edge === "left" ? bounds.minX : bounds.maxX;
25030
+ for (let y = bounds.minY; y <= bounds.maxY + step / 2; y += step) {
25031
+ candidates.push({ x, y: Math.min(y, bounds.maxY) });
25032
+ }
25033
+ } else {
25034
+ const y = edge === "bottom" ? bounds.minY : bounds.maxY;
25035
+ for (let x = bounds.minX; x <= bounds.maxX + step / 2; x += step) {
25036
+ candidates.push({ x: Math.min(x, bounds.maxX), y });
25037
+ }
25038
+ }
25039
+ return candidates;
25040
+ };
25041
+ var getAllBoundsCandidates = ({
25042
+ bounds,
25043
+ step
25044
+ }) => {
25045
+ const candidatesByCoordinate = /* @__PURE__ */ new Map();
25046
+ for (const edge of ["left", "right", "bottom", "top"]) {
25047
+ for (const candidate of getBoundsEdgeCandidates({ edge, bounds, step })) {
25048
+ candidatesByCoordinate.set(`${candidate.x}:${candidate.y}`, candidate);
25049
+ }
25050
+ }
25051
+ return [...candidatesByCoordinate.values()];
25052
+ };
25053
+ var getBoundaryCandidateSearchStep = ({
25054
+ bounds,
25055
+ boundaryPointSpacing
25056
+ }) => {
25057
+ if (boundaryPointSpacing > 0) return boundaryPointSpacing;
25058
+ return Math.min(bounds.maxX - bounds.minX, bounds.maxY - bounds.minY) / 40;
25059
+ };
25060
+ var hasBoundarySpacingConflict = ({
25061
+ candidate,
25062
+ usedBoundaryPoints,
25063
+ boundaryPointSpacing
25064
+ }) => {
25065
+ for (const usedPoint of usedBoundaryPoints) {
25066
+ if (isInsideRequiredSpacing({
25067
+ candidate,
25068
+ usedPoint,
25069
+ boundaryPointSpacing
25070
+ })) {
25071
+ return true;
25072
+ }
25073
+ }
25074
+ return false;
25075
+ };
25076
+ var isBoundaryCandidateBlocked = ({
25077
+ candidate,
25078
+ routeFrom,
25079
+ pads,
25080
+ sourcePortId,
25081
+ layer
25082
+ }) => {
25083
+ if (!routeFrom || !pads || !sourcePortId) return false;
25084
+ return doesBreakoutSegmentIntersectPads({
25085
+ from: routeFrom,
25086
+ to: candidate,
25087
+ pads,
25088
+ sourcePortId,
25089
+ layer
25090
+ });
25091
+ };
25092
+ var isCandidateAvailable = ({
25093
+ candidate,
25094
+ usedBoundaryPoints,
25095
+ boundaryPointSpacing,
25096
+ routeFrom,
25097
+ pads,
25098
+ sourcePortId,
25099
+ layer
25100
+ }) => {
25101
+ if (hasBoundarySpacingConflict({
25102
+ candidate,
25103
+ usedBoundaryPoints,
25104
+ boundaryPointSpacing
25105
+ })) {
25106
+ return false;
25107
+ }
25108
+ return !isBoundaryCandidateBlocked({
25109
+ candidate,
25110
+ routeFrom,
25111
+ pads,
25112
+ sourcePortId,
25113
+ layer
25114
+ });
25115
+ };
25116
+ var hasOutsideAccessConflict = ({
25117
+ candidate,
25118
+ outsidePorts,
25119
+ pads,
25120
+ sourcePortId,
25121
+ layer
25122
+ }) => {
25123
+ if (!outsidePorts || !pads || !sourcePortId) return false;
25124
+ for (const outsidePort of outsidePorts) {
25125
+ if (doesBreakoutSegmentIntersectNonIgnoredPads({
25126
+ from: candidate,
25127
+ to: outsidePort.position,
25128
+ pads,
25129
+ ignoredSourcePortIds: [sourcePortId, outsidePort.sourcePortId],
25130
+ layer
25131
+ })) {
25132
+ return true;
25133
+ }
25134
+ }
25135
+ return false;
25136
+ };
25137
+ var isCandidateAvailableForOutsidePorts = ({
25138
+ candidate,
25139
+ bounds,
25140
+ usedBoundaryPoints,
25141
+ boundaryPointSpacing,
25142
+ routeFrom,
25143
+ pads,
25144
+ sourcePortId,
25145
+ outsidePorts,
25146
+ layer
25147
+ }) => {
25148
+ if (!isCandidateAvailable({
25149
+ candidate,
25150
+ usedBoundaryPoints,
25151
+ boundaryPointSpacing,
25152
+ routeFrom,
25153
+ pads,
25154
+ sourcePortId,
25155
+ layer
25156
+ })) {
25157
+ return false;
25158
+ }
25159
+ return !hasOutsideAccessConflict({
25160
+ candidate,
25161
+ outsidePorts,
25162
+ pads,
25163
+ sourcePortId,
25164
+ layer
25165
+ });
25166
+ };
25167
+ var getAvailableBreakoutBoundaryPoint = ({
25168
+ idealPoint,
25169
+ bounds,
25170
+ usedBoundaryPoints,
25171
+ boundaryPointSpacing,
25172
+ routeFrom,
25173
+ pads,
25174
+ sourcePortId,
25175
+ layer
25176
+ }) => {
25177
+ if (isCandidateAvailable({
25178
+ candidate: idealPoint,
25179
+ usedBoundaryPoints,
25180
+ boundaryPointSpacing,
25181
+ routeFrom,
25182
+ pads,
25183
+ sourcePortId,
25184
+ layer
25185
+ })) {
25186
+ return idealPoint;
25187
+ }
25188
+ const edge = getBoundsEdge(idealPoint, bounds);
25189
+ if (!edge) return null;
25190
+ const step = getBoundaryCandidateSearchStep({
25191
+ bounds,
25192
+ boundaryPointSpacing
25193
+ });
25194
+ if (step <= 0) return null;
25195
+ const edgeCandidates = getBoundsEdgeCandidates({
25196
+ edge,
25197
+ bounds,
25198
+ step
25199
+ });
25200
+ edgeCandidates.sort(
25201
+ (a, b) => distance15(a, idealPoint) - distance15(b, idealPoint)
25202
+ );
25203
+ for (const candidate of edgeCandidates) {
25204
+ if (isCandidateAvailable({
25205
+ candidate,
25206
+ usedBoundaryPoints,
25207
+ boundaryPointSpacing,
25208
+ routeFrom,
25209
+ pads,
25210
+ sourcePortId,
25211
+ layer
25212
+ })) {
25213
+ return candidate;
25214
+ }
25215
+ }
25216
+ const candidates = getAllBoundsCandidates({ bounds, step });
25217
+ candidates.sort((a, b) => distance15(a, idealPoint) - distance15(b, idealPoint));
25218
+ for (const candidate of candidates) {
25219
+ if (isCandidateAvailable({
25220
+ candidate,
25221
+ usedBoundaryPoints,
25222
+ boundaryPointSpacing,
25223
+ routeFrom,
25224
+ pads,
25225
+ sourcePortId,
25226
+ layer
25227
+ })) {
25228
+ return candidate;
25229
+ }
25230
+ }
25231
+ return null;
25232
+ };
25233
+ var getAvailableBreakoutBoundaryPointForOutsidePorts = ({
25234
+ idealPoints,
25235
+ bounds,
25236
+ usedBoundaryPoints,
25237
+ boundaryPointSpacing,
25238
+ routeFrom,
25239
+ pads,
25240
+ sourcePortId,
25241
+ outsidePorts,
25242
+ layer
25243
+ }) => {
25244
+ const step = getBoundaryCandidateSearchStep({
25245
+ bounds,
25246
+ boundaryPointSpacing
25247
+ });
25248
+ if (step <= 0) return null;
25249
+ for (const idealPoint of idealPoints) {
25250
+ if (isCandidateAvailableForOutsidePorts({
25251
+ candidate: idealPoint,
25252
+ bounds,
25253
+ usedBoundaryPoints,
25254
+ boundaryPointSpacing,
25255
+ routeFrom,
25256
+ pads,
25257
+ sourcePortId,
25258
+ outsidePorts,
25259
+ layer
25260
+ })) {
25261
+ return idealPoint;
25262
+ }
25263
+ const edge = getBoundsEdge(idealPoint, bounds);
25264
+ if (!edge) continue;
25265
+ const edgeCandidates = getBoundsEdgeCandidates({ edge, bounds, step });
25266
+ edgeCandidates.sort(
25267
+ (a, b) => distance15(a, idealPoint) - distance15(b, idealPoint)
25268
+ );
25269
+ for (const candidate of edgeCandidates) {
25270
+ if (isCandidateAvailableForOutsidePorts({
25271
+ candidate,
25272
+ bounds,
25273
+ usedBoundaryPoints,
25274
+ boundaryPointSpacing,
25275
+ routeFrom,
25276
+ pads,
25277
+ sourcePortId,
25278
+ outsidePorts,
25279
+ layer
25280
+ })) {
25281
+ return candidate;
25282
+ }
25283
+ }
25284
+ }
25285
+ for (const idealPoint of idealPoints) {
25286
+ const candidates = getAllBoundsCandidates({ bounds, step });
25287
+ candidates.sort((a, b) => distance15(a, idealPoint) - distance15(b, idealPoint));
25288
+ for (const candidate of candidates) {
25289
+ if (isCandidateAvailableForOutsidePorts({
25290
+ candidate,
25291
+ bounds,
25292
+ usedBoundaryPoints,
25293
+ boundaryPointSpacing,
25294
+ routeFrom,
25295
+ pads,
25296
+ sourcePortId,
25297
+ outsidePorts,
25298
+ layer
25299
+ })) {
25300
+ return candidate;
25301
+ }
25302
+ }
25303
+ }
25304
+ return null;
25305
+ };
25306
+
25307
+ // node_modules/@tscircuit/breakout-point-solver/lib/BreakoutPointSolver.ts
25308
+ var getLayerVisualStyle = (layer) => {
25309
+ if (layer === "bottom") {
25310
+ return {
25311
+ insidePadFill: "rgba(2, 132, 199, 0.28)",
25312
+ insidePadStroke: "#0369a1",
25313
+ insidePointColor: "#0369a1",
25314
+ outsidePadFill: "rgba(30, 64, 175, 0.2)",
25315
+ outsidePadStroke: "#1e40af",
25316
+ outsidePointColor: "#1e40af",
25317
+ padFill: "rgba(37, 99, 235, 0.18)",
25318
+ padStroke: "#1d4ed8",
25319
+ componentFill: "rgba(245, 158, 11, 0.1)",
25320
+ componentStroke: "#b45309",
25321
+ traceStroke: "#2563eb",
25322
+ breakoutPointColor: "#0f766e"
25323
+ };
25324
+ }
25325
+ return {
25326
+ insidePadFill: "rgba(46, 125, 50, 0.28)",
25327
+ insidePadStroke: "#1b5e20",
25328
+ insidePointColor: "#1b5e20",
25329
+ outsidePadFill: "rgba(106, 27, 154, 0.2)",
25330
+ outsidePadStroke: "#6a1b9a",
25331
+ outsidePointColor: "#6a1b9a",
25332
+ padFill: "rgba(220, 38, 38, 0.22)",
25333
+ padStroke: "#b91c1c",
25334
+ componentFill: "rgba(245, 158, 11, 0.1)",
25335
+ componentStroke: "#b45309",
25336
+ traceStroke: "#7e8794",
25337
+ breakoutPointColor: "#0d47a1"
25338
+ };
25339
+ };
25340
+ var getPortPadRect = ({
25341
+ port,
25342
+ fill,
25343
+ stroke,
25344
+ fallbackLabel
25345
+ }) => {
25346
+ if (port.width === void 0 || port.height === void 0) return null;
25347
+ return {
25348
+ center: port.position,
25349
+ width: port.width,
25350
+ height: port.height,
25351
+ ccwRotationDegrees: port.ccwRotationDegrees,
25352
+ fill,
25353
+ stroke,
25354
+ label: port.label ?? fallbackLabel
25355
+ };
25356
+ };
25357
+ var getAveragePortPosition = (ports) => {
25358
+ if (ports.length === 0) return null;
25359
+ const total = ports.reduce(
25360
+ (sum, port) => ({
25361
+ x: sum.x + port.position.x,
25362
+ y: sum.y + port.position.y
25363
+ }),
25364
+ { x: 0, y: 0 }
25365
+ );
25366
+ return {
25367
+ x: total.x / ports.length,
25368
+ y: total.y / ports.length
25369
+ };
25370
+ };
25371
+ var getOutsideTarget = (trace) => getAveragePortPosition(trace.outsidePorts);
25372
+ var getInsidePortKey = (port) => `${port.sourcePortId}:${port.layer ?? "top"}`;
25373
+ var getOutsidePortsForInsidePort = ({
25374
+ traces,
25375
+ insidePort
25376
+ }) => {
25377
+ const outsidePorts = [];
25378
+ const insidePortKey = getInsidePortKey(insidePort);
25379
+ for (const trace of traces) {
25380
+ const hasMatchingInsidePort = trace.insidePorts.some(
25381
+ (port) => getInsidePortKey(port) === insidePortKey
25382
+ );
25383
+ if (!hasMatchingInsidePort) continue;
25384
+ outsidePorts.push(...trace.outsidePorts);
25385
+ }
25386
+ return outsidePorts;
25387
+ };
25388
+ var getIdealBoundaryPoints = ({
25389
+ insidePort,
25390
+ outsidePorts,
25391
+ bounds
25392
+ }) => {
25393
+ const idealBoundaryPoints = [];
25394
+ for (const outsidePort of outsidePorts) {
25395
+ const idealBoundaryPoint = getBreakoutBoundaryIntersection({
25396
+ from: insidePort.position,
25397
+ to: outsidePort.position,
25398
+ bounds
25399
+ });
25400
+ if (idealBoundaryPoint) idealBoundaryPoints.push(idealBoundaryPoint);
25401
+ }
25402
+ return idealBoundaryPoints;
25403
+ };
25404
+ var BreakoutPointSolver = class extends BaseSolver {
25405
+ input;
25406
+ output = { breakoutPoints: [] };
25407
+ constructor(input) {
25408
+ super();
25409
+ this.input = input;
25410
+ }
25411
+ _step() {
25412
+ const breakoutPoints = [];
25413
+ const boundaryPointsByInsidePort = /* @__PURE__ */ new Map();
25414
+ for (const trace of this.input.traces) {
25415
+ for (const insidePort of trace.insidePorts) {
25416
+ const insidePortKey = getInsidePortKey(insidePort);
25417
+ let boundaryPoint = boundaryPointsByInsidePort.get(insidePortKey) ?? null;
25418
+ if (!boundaryPoint) {
25419
+ const outsidePorts = getOutsidePortsForInsidePort({
25420
+ traces: this.input.traces,
25421
+ insidePort
25422
+ });
25423
+ const idealBoundaryPoints = getIdealBoundaryPoints({
25424
+ insidePort,
25425
+ outsidePorts,
25426
+ bounds: this.input.bounds
25427
+ });
25428
+ if (idealBoundaryPoints.length === 0) continue;
25429
+ const usedBoundaryPoints = [
25430
+ ...this.input.usedBoundaryPoints ?? [],
25431
+ ...boundaryPointsByInsidePort.values()
25432
+ ];
25433
+ if (outsidePorts.length > 1) {
25434
+ boundaryPoint = getAvailableBreakoutBoundaryPointForOutsidePorts({
25435
+ idealPoints: idealBoundaryPoints,
25436
+ bounds: this.input.bounds,
25437
+ usedBoundaryPoints,
25438
+ boundaryPointSpacing: this.input.boundaryPointSpacing ?? 0,
25439
+ routeFrom: insidePort.position,
25440
+ pads: this.input.pads,
25441
+ sourcePortId: insidePort.sourcePortId,
25442
+ outsidePorts,
25443
+ layer: insidePort.layer
25444
+ });
25445
+ }
25446
+ boundaryPoint ??= getAvailableBreakoutBoundaryPoint({
25447
+ idealPoint: idealBoundaryPoints[0],
25448
+ bounds: this.input.bounds,
25449
+ usedBoundaryPoints,
25450
+ boundaryPointSpacing: this.input.boundaryPointSpacing ?? 0,
25451
+ routeFrom: insidePort.position,
25452
+ pads: this.input.pads,
25453
+ sourcePortId: insidePort.sourcePortId,
25454
+ layer: insidePort.layer
25455
+ });
25456
+ if (!boundaryPoint) continue;
25457
+ boundaryPointsByInsidePort.set(insidePortKey, boundaryPoint);
25458
+ }
25459
+ breakoutPoints.push({
25460
+ sourcePortId: insidePort.sourcePortId,
25461
+ sourceTraceId: trace.sourceTraceId,
25462
+ x: boundaryPoint.x,
25463
+ y: boundaryPoint.y,
25464
+ ...insidePort.layer ? { layer: insidePort.layer } : {}
25465
+ });
25466
+ }
25467
+ }
25468
+ this.output = { breakoutPoints };
25469
+ this.solved = true;
25470
+ }
25471
+ getConstructorParams() {
25472
+ return [this.input];
25473
+ }
25474
+ getOutput() {
25475
+ return this.output;
25476
+ }
25477
+ visualize() {
25478
+ const { bounds } = this.input;
25479
+ const width = bounds.maxX - bounds.minX;
25480
+ const height = bounds.maxY - bounds.minY;
25481
+ const center = {
25482
+ x: bounds.minX + width / 2,
25483
+ y: bounds.minY + height / 2
25484
+ };
25485
+ return {
25486
+ title: "BreakoutPointSolver - generated breakout points",
25487
+ rects: [
25488
+ {
25489
+ center,
25490
+ width,
25491
+ height,
25492
+ fill: "rgba(210, 225, 255, 0.25)",
25493
+ stroke: "#315fba",
25494
+ label: "breakout bounds"
25495
+ },
25496
+ ...(this.input.components ?? []).map((component) => ({
25497
+ center: component.center,
25498
+ width: component.width,
25499
+ height: component.height,
25500
+ ccwRotationDegrees: component.ccwRotationDegrees,
25501
+ fill: getLayerVisualStyle(component.layer).componentFill,
25502
+ stroke: getLayerVisualStyle(component.layer).componentStroke,
25503
+ label: component.label ?? "component"
25504
+ })),
25505
+ ...(this.input.pads ?? []).map((pad) => ({
25506
+ center: pad.center,
25507
+ width: pad.width + (pad.clearance ?? 0) * 2,
25508
+ height: pad.height + (pad.clearance ?? 0) * 2,
25509
+ ccwRotationDegrees: pad.ccwRotationDegrees,
25510
+ fill: getLayerVisualStyle(pad.layer).padFill,
25511
+ stroke: getLayerVisualStyle(pad.layer).padStroke,
25512
+ label: pad.label ?? "pad"
25513
+ })),
25514
+ ...this.input.traces.flatMap(
25515
+ (trace) => trace.insidePorts.flatMap((port) => {
25516
+ const rect = getPortPadRect({
25517
+ port,
25518
+ fill: getLayerVisualStyle(port.layer).insidePadFill,
25519
+ stroke: getLayerVisualStyle(port.layer).insidePadStroke,
25520
+ fallbackLabel: `inside pad ${port.sourcePortId}`
25521
+ });
25522
+ return rect ? [rect] : [];
25523
+ })
25524
+ ),
25525
+ ...this.input.traces.flatMap(
25526
+ (trace) => trace.outsidePorts.flatMap((port) => {
25527
+ const rect = getPortPadRect({
25528
+ port,
25529
+ fill: getLayerVisualStyle(port.layer).outsidePadFill,
25530
+ stroke: getLayerVisualStyle(port.layer).outsidePadStroke,
25531
+ fallbackLabel: `outside pad ${port.sourcePortId}`
25532
+ });
25533
+ return rect ? [rect] : [];
25534
+ })
25535
+ )
25536
+ ],
25537
+ lines: this.input.traces.flatMap(
25538
+ (trace) => trace.insidePorts.flatMap((insidePort) => {
25539
+ const outsideTarget = getOutsideTarget(trace);
25540
+ if (!outsideTarget) return [];
25541
+ const breakoutPoint = this.output.breakoutPoints.find(
25542
+ (point6) => point6.sourceTraceId === trace.sourceTraceId && point6.sourcePortId === insidePort.sourcePortId
25543
+ );
25544
+ if (!breakoutPoint) return [];
25545
+ return [
25546
+ {
25547
+ points: [
25548
+ insidePort.position,
25549
+ { x: breakoutPoint.x, y: breakoutPoint.y }
25550
+ ],
25551
+ strokeColor: getLayerVisualStyle(insidePort.layer).traceStroke,
25552
+ label: `breakout segment ${trace.sourceTraceId}`
25553
+ },
25554
+ {
25555
+ points: [
25556
+ { x: breakoutPoint.x, y: breakoutPoint.y },
25557
+ outsideTarget
25558
+ ],
25559
+ strokeColor: getLayerVisualStyle(insidePort.layer).traceStroke,
25560
+ strokeDash: "0.15 0.15",
25561
+ label: `target guide ${trace.sourceTraceId}`
25562
+ }
25563
+ ];
25564
+ })
25565
+ ),
25566
+ points: [
25567
+ ...this.input.traces.flatMap(
25568
+ (trace) => trace.insidePorts.map((port) => ({
25569
+ ...port.position,
25570
+ color: getLayerVisualStyle(port.layer).insidePointColor,
25571
+ label: `inside pad ${port.sourcePortId}`
25572
+ }))
25573
+ ),
25574
+ ...this.input.traces.flatMap(
25575
+ (trace) => trace.outsidePorts.map((port) => ({
25576
+ ...port.position,
25577
+ color: getLayerVisualStyle(port.layer).outsidePointColor,
25578
+ label: `outside target ${port.sourcePortId}`
25579
+ }))
25580
+ ),
25581
+ ...this.output.breakoutPoints.map((point6) => ({
25582
+ x: point6.x,
25583
+ y: point6.y,
25584
+ color: getLayerVisualStyle(point6.layer).breakoutPointColor,
25585
+ label: `selected breakout ${point6.sourcePortId}`
25586
+ })),
25587
+ ...(this.input.usedBoundaryPoints ?? []).map((point6) => ({
25588
+ ...point6,
25589
+ color: "#ef6c00",
25590
+ label: "pre-existing breakout point"
25591
+ }))
25592
+ ]
25593
+ };
25594
+ }
25595
+ };
24870
25596
 
24871
25597
  // lib/components/primitive-components/BaseBreakoutPoint.ts
24872
25598
  import { pcbLayoutProps } from "@tscircuit/props";
@@ -25380,7 +26106,7 @@ var NetLabel = class extends PrimitiveComponent2 {
25380
26106
 
25381
26107
  // lib/components/primitive-components/Fiducial.ts
25382
26108
  import "zod";
25383
- import { distance as distance14 } from "circuit-json";
26109
+ import { distance as distance16 } from "circuit-json";
25384
26110
  import { fiducialProps } from "@tscircuit/props";
25385
26111
  var Fiducial = class extends PrimitiveComponent2 {
25386
26112
  pcb_smtpad_id = null;
@@ -25405,15 +26131,15 @@ var Fiducial = class extends PrimitiveComponent2 {
25405
26131
  shape: "circle",
25406
26132
  x: position.x,
25407
26133
  y: position.y,
25408
- radius: distance14.parse(props.padDiameter) / 2,
25409
- soldermask_margin: props.soldermaskPullback ? distance14.parse(props.soldermaskPullback) : distance14.parse(props.padDiameter) / 2,
26134
+ radius: distance16.parse(props.padDiameter) / 2,
26135
+ soldermask_margin: props.soldermaskPullback ? distance16.parse(props.soldermaskPullback) : distance16.parse(props.padDiameter) / 2,
25410
26136
  is_covered_with_solder_mask: true
25411
26137
  });
25412
26138
  this.pcb_smtpad_id = pcb_smtpad.pcb_smtpad_id;
25413
26139
  }
25414
26140
  getPcbSize() {
25415
26141
  const { _parsedProps: props } = this;
25416
- const d = distance14.parse(props.padDiameter);
26142
+ const d = distance16.parse(props.padDiameter);
25417
26143
  return { width: d, height: d };
25418
26144
  }
25419
26145
  _setPositionFromLayout(newCenter) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.1281",
4
+ "version": "0.0.1282",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",