@tscircuit/core 0.0.1280 → 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.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +746 -9
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -78583,6 +78583,7 @@ declare class SmtPad extends PrimitiveComponent<typeof smtPadProps> {
|
|
|
78583
78583
|
doInitialPortMatching(): void;
|
|
78584
78584
|
doInitialPcbPrimitiveRender(): void;
|
|
78585
78585
|
doInitialPcbPortAttachment(): void;
|
|
78586
|
+
getAvailablePcbLayers(): string[];
|
|
78586
78587
|
_getPcbCircuitJsonBounds(): {
|
|
78587
78588
|
center: {
|
|
78588
78589
|
x: number;
|
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
|
|
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 (
|
|
3879
|
+
if (doesSegmentIntersectRect2(edge, obstacle)) {
|
|
3880
3880
|
return null;
|
|
3881
3881
|
}
|
|
3882
3882
|
}
|
|
@@ -8191,7 +8191,10 @@ var SmtPad = class extends PrimitiveComponent2 {
|
|
|
8191
8191
|
const isRotated90Degrees = Math.abs(normalizedRotationDegrees - 90) < rotationTolerance || Math.abs(normalizedRotationDegrees - 270) < rotationTolerance;
|
|
8192
8192
|
let finalRotationDegrees = Math.abs(normalizedRotationDegrees - 360) < rotationTolerance ? 0 : normalizedRotationDegrees;
|
|
8193
8193
|
const transformRotationBeforeFlip = finalRotationDegrees;
|
|
8194
|
-
const { maybeFlipLayer, isFlipped } = this._getPcbPrimitiveFlippedHelpers();
|
|
8194
|
+
const { maybeFlipLayer: maybeFlipDefaultLayer, isFlipped } = this._getPcbPrimitiveFlippedHelpers();
|
|
8195
|
+
const primitiveContainer = this.getPrimitiveContainer();
|
|
8196
|
+
const primitiveContainerLayer = primitiveContainer?.props?.layer !== void 0 ? primitiveContainer?._parsedProps.layer : void 0;
|
|
8197
|
+
const maybeFlipLayer = (layer) => primitiveContainerLayer ?? maybeFlipDefaultLayer(layer);
|
|
8195
8198
|
if (isFlipped) {
|
|
8196
8199
|
finalRotationDegrees = (360 - finalRotationDegrees + 360) % 360;
|
|
8197
8200
|
}
|
|
@@ -8403,6 +8406,14 @@ var SmtPad = class extends PrimitiveComponent2 {
|
|
|
8403
8406
|
pcb_port_id: this.matchedPort?.pcb_port_id
|
|
8404
8407
|
});
|
|
8405
8408
|
}
|
|
8409
|
+
getAvailablePcbLayers() {
|
|
8410
|
+
const { maybeFlipLayer } = this._getPcbPrimitiveFlippedHelpers();
|
|
8411
|
+
const primitiveContainer = this.getPrimitiveContainer();
|
|
8412
|
+
const primitiveContainerLayer = primitiveContainer?.props?.layer !== void 0 ? primitiveContainer?._parsedProps.layer : void 0;
|
|
8413
|
+
return [
|
|
8414
|
+
primitiveContainerLayer ?? maybeFlipLayer(this._parsedProps.layer ?? "top")
|
|
8415
|
+
];
|
|
8416
|
+
}
|
|
8406
8417
|
_getPcbCircuitJsonBounds() {
|
|
8407
8418
|
const { db } = this.root;
|
|
8408
8419
|
const smtpad = db.pcb_smtpad.get(this.pcb_smtpad_id);
|
|
@@ -21856,7 +21867,7 @@ import { identity as identity5 } from "transformation-matrix";
|
|
|
21856
21867
|
var package_default = {
|
|
21857
21868
|
name: "@tscircuit/core",
|
|
21858
21869
|
type: "module",
|
|
21859
|
-
version: "0.0.
|
|
21870
|
+
version: "0.0.1281",
|
|
21860
21871
|
types: "dist/index.d.ts",
|
|
21861
21872
|
main: "dist/index.js",
|
|
21862
21873
|
module: "dist/index.js",
|
|
@@ -24855,7 +24866,733 @@ var PcbNoteDimension = class extends PrimitiveComponent2 {
|
|
|
24855
24866
|
|
|
24856
24867
|
// lib/components/primitive-components/Breakout/Breakout.ts
|
|
24857
24868
|
import "@tscircuit/props";
|
|
24858
|
-
|
|
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
|
+
};
|
|
24859
25596
|
|
|
24860
25597
|
// lib/components/primitive-components/BaseBreakoutPoint.ts
|
|
24861
25598
|
import { pcbLayoutProps } from "@tscircuit/props";
|
|
@@ -25369,7 +26106,7 @@ var NetLabel = class extends PrimitiveComponent2 {
|
|
|
25369
26106
|
|
|
25370
26107
|
// lib/components/primitive-components/Fiducial.ts
|
|
25371
26108
|
import "zod";
|
|
25372
|
-
import { distance as
|
|
26109
|
+
import { distance as distance16 } from "circuit-json";
|
|
25373
26110
|
import { fiducialProps } from "@tscircuit/props";
|
|
25374
26111
|
var Fiducial = class extends PrimitiveComponent2 {
|
|
25375
26112
|
pcb_smtpad_id = null;
|
|
@@ -25394,15 +26131,15 @@ var Fiducial = class extends PrimitiveComponent2 {
|
|
|
25394
26131
|
shape: "circle",
|
|
25395
26132
|
x: position.x,
|
|
25396
26133
|
y: position.y,
|
|
25397
|
-
radius:
|
|
25398
|
-
soldermask_margin: props.soldermaskPullback ?
|
|
26134
|
+
radius: distance16.parse(props.padDiameter) / 2,
|
|
26135
|
+
soldermask_margin: props.soldermaskPullback ? distance16.parse(props.soldermaskPullback) : distance16.parse(props.padDiameter) / 2,
|
|
25399
26136
|
is_covered_with_solder_mask: true
|
|
25400
26137
|
});
|
|
25401
26138
|
this.pcb_smtpad_id = pcb_smtpad.pcb_smtpad_id;
|
|
25402
26139
|
}
|
|
25403
26140
|
getPcbSize() {
|
|
25404
26141
|
const { _parsedProps: props } = this;
|
|
25405
|
-
const d =
|
|
26142
|
+
const d = distance16.parse(props.padDiameter);
|
|
25406
26143
|
return { width: d, height: d };
|
|
25407
26144
|
}
|
|
25408
26145
|
_setPositionFromLayout(newCenter) {
|