@tscircuit/core 0.0.1858 → 0.0.1860
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 +7 -2
- package/dist/index.js +579 -14
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { FanoutSolver } from '@tscircuit/fanout-solver';
|
|
|
18
18
|
import { ImplicitCopperPourPipelineSolver } from '@tscircuit/implicit-copper-pour-solver/lib/index';
|
|
19
19
|
import { LayoutPipelineSolver } from '@tscircuit/matchpack';
|
|
20
20
|
import { SchematicTracePipelineSolver } from '@tscircuit/schematic-trace-solver';
|
|
21
|
+
import { ViaStitchSolver } from '@tscircuit/via-stitch-solver';
|
|
21
22
|
import { PackSolver2 } from 'calculate-packing';
|
|
22
23
|
import { Box, Bounds } from '@tscircuit/math-utils';
|
|
23
24
|
import { ConnectivityMap } from 'circuit-json-to-connectivity-map';
|
|
@@ -42,6 +43,7 @@ declare const SOLVERS: {
|
|
|
42
43
|
FanoutSolver: typeof FanoutSolver;
|
|
43
44
|
ImplicitCopperPourPipelineSolver: typeof ImplicitCopperPourPipelineSolver;
|
|
44
45
|
SchematicTracePipelineSolver: typeof SchematicTracePipelineSolver;
|
|
46
|
+
ViaStitchSolver: typeof ViaStitchSolver;
|
|
45
47
|
};
|
|
46
48
|
type SolverName = keyof typeof SOLVERS;
|
|
47
49
|
|
|
@@ -358,9 +360,9 @@ interface IIsolatedCircuit {
|
|
|
358
360
|
interface IRootCircuit extends IIsolatedCircuit {
|
|
359
361
|
}
|
|
360
362
|
|
|
361
|
-
declare const orderedRenderPhases: readonly ["ReactSubtreesRender", "RenderIsolatedSubcircuits", "InflateSubcircuitCircuitJson", "SourceNameDuplicateComponentRemoval", "PcbFootprintStringRender", "FetchPartFootprint", "ResolveFootprintPinLabels", "InitializePortsFromChildren", "CreateNetsFromProps", "AssignFallbackProps", "CreateTracesFromProps", "CreateTracesFromNetLabels", "CreateTraceHintsFromProps", "CreateAutoplacedBreakoutPoints", "SourceGroupRender", "AssignNameToUnnamedComponents", "SourceRender", "CheckRefDesConvention", "SourceComponentPropertyValidation", "SourceParentAttachment", "PortMatching", "OptimizeSelectorCache", "SourceTraceRender", "SourceAddConnectivityMapKey", "SourceDesignRuleChecks", "SimulationRender", "SchematicComponentRender", "SchematicPortRender", "SymbolContainerRender", "SchematicPrimitiveRender", "SchematicSymbolResize", "SchematicComponentSizeCalculation", "SchematicLayout", "SchematicSectionRender", "SchematicTraceRender", "SchematicSheetRender", "SchematicReplaceNetLabelsWithSymbols", "SchematicLabelNetsWithConflictingNames", "PanelBoardLayout", "ValidatePcbCoordinates", "PcbComponentRender", "PcbPrimitiveRender", "PcbFootprintLayout", "PcbPortRender", "PcbPortAttachment", "PcbComponentSizeCalculation", "PcbComponentAnchorAlignment", "PcbCalcPlacementResolution", "PcbLayout", "PcbBoardAutoSize", "PanelLayout", "PcbAutoplaceBreakoutPoints", "PcbTraceHintRender", "PcbManualTraceRender", "PcbPlacementDesignRuleChecks", "PcbTraceRender", "PcbRouteNetIslands", "PcbImplicitCopperPourRender", "PcbCopperPourRender", "PcbDesignRuleChecks", "SilkscreenOverlapAdjustment", "CadModelRender", "PartsEngineRender", "PartOrientationAnalysis", "SupplierFootprintMismatchWarning", "SimulationSpiceEngineRender"];
|
|
363
|
+
declare const orderedRenderPhases: readonly ["ReactSubtreesRender", "RenderIsolatedSubcircuits", "InflateSubcircuitCircuitJson", "SourceNameDuplicateComponentRemoval", "PcbFootprintStringRender", "FetchPartFootprint", "ResolveFootprintPinLabels", "InitializePortsFromChildren", "CreateNetsFromProps", "AssignFallbackProps", "CreateTracesFromProps", "CreateTracesFromNetLabels", "CreateTraceHintsFromProps", "CreateAutoplacedBreakoutPoints", "SourceGroupRender", "AssignNameToUnnamedComponents", "SourceRender", "CheckRefDesConvention", "SourceComponentPropertyValidation", "SourceParentAttachment", "PortMatching", "OptimizeSelectorCache", "SourceTraceRender", "SourceAddConnectivityMapKey", "SourceDesignRuleChecks", "SimulationRender", "SchematicComponentRender", "SchematicPortRender", "SymbolContainerRender", "SchematicPrimitiveRender", "SchematicSymbolResize", "SchematicComponentSizeCalculation", "SchematicLayout", "SchematicSectionRender", "SchematicTraceRender", "SchematicSheetRender", "SchematicReplaceNetLabelsWithSymbols", "SchematicLabelNetsWithConflictingNames", "PanelBoardLayout", "ValidatePcbCoordinates", "PcbComponentRender", "PcbPrimitiveRender", "PcbFootprintLayout", "PcbPortRender", "PcbPortAttachment", "PcbComponentSizeCalculation", "PcbComponentAnchorAlignment", "PcbCalcPlacementResolution", "PcbLayout", "PcbBoardAutoSize", "PanelLayout", "PcbAutoplaceBreakoutPoints", "PcbTraceHintRender", "PcbManualTraceRender", "PcbPlacementDesignRuleChecks", "PcbTraceRender", "PcbRouteNetIslands", "PcbImplicitCopperPourRender", "PcbCopperPourRender", "PcbViaStitchRender", "PcbDesignRuleChecks", "SilkscreenOverlapAdjustment", "CadModelRender", "PartsEngineRender", "PartOrientationAnalysis", "SupplierFootprintMismatchWarning", "SimulationSpiceEngineRender"];
|
|
362
364
|
type RenderPhase = (typeof orderedRenderPhases)[number];
|
|
363
|
-
declare const renderPhaseIndexMap: Map<"ReactSubtreesRender" | "RenderIsolatedSubcircuits" | "InflateSubcircuitCircuitJson" | "SourceNameDuplicateComponentRemoval" | "PcbFootprintStringRender" | "FetchPartFootprint" | "ResolveFootprintPinLabels" | "InitializePortsFromChildren" | "CreateNetsFromProps" | "AssignFallbackProps" | "CreateTracesFromProps" | "CreateTracesFromNetLabels" | "CreateTraceHintsFromProps" | "CreateAutoplacedBreakoutPoints" | "SourceGroupRender" | "AssignNameToUnnamedComponents" | "SourceRender" | "CheckRefDesConvention" | "SourceComponentPropertyValidation" | "SourceParentAttachment" | "PortMatching" | "OptimizeSelectorCache" | "SourceTraceRender" | "SourceAddConnectivityMapKey" | "SourceDesignRuleChecks" | "SimulationRender" | "SchematicComponentRender" | "SchematicPortRender" | "SymbolContainerRender" | "SchematicPrimitiveRender" | "SchematicSymbolResize" | "SchematicComponentSizeCalculation" | "SchematicLayout" | "SchematicSectionRender" | "SchematicTraceRender" | "SchematicSheetRender" | "SchematicReplaceNetLabelsWithSymbols" | "SchematicLabelNetsWithConflictingNames" | "PanelBoardLayout" | "ValidatePcbCoordinates" | "PcbComponentRender" | "PcbPrimitiveRender" | "PcbFootprintLayout" | "PcbPortRender" | "PcbPortAttachment" | "PcbComponentSizeCalculation" | "PcbComponentAnchorAlignment" | "PcbCalcPlacementResolution" | "PcbLayout" | "PcbBoardAutoSize" | "PanelLayout" | "PcbAutoplaceBreakoutPoints" | "PcbTraceHintRender" | "PcbManualTraceRender" | "PcbPlacementDesignRuleChecks" | "PcbTraceRender" | "PcbRouteNetIslands" | "PcbImplicitCopperPourRender" | "PcbCopperPourRender" | "PcbDesignRuleChecks" | "SilkscreenOverlapAdjustment" | "CadModelRender" | "PartsEngineRender" | "PartOrientationAnalysis" | "SupplierFootprintMismatchWarning" | "SimulationSpiceEngineRender", number>;
|
|
365
|
+
declare const renderPhaseIndexMap: Map<"ReactSubtreesRender" | "RenderIsolatedSubcircuits" | "InflateSubcircuitCircuitJson" | "SourceNameDuplicateComponentRemoval" | "PcbFootprintStringRender" | "FetchPartFootprint" | "ResolveFootprintPinLabels" | "InitializePortsFromChildren" | "CreateNetsFromProps" | "AssignFallbackProps" | "CreateTracesFromProps" | "CreateTracesFromNetLabels" | "CreateTraceHintsFromProps" | "CreateAutoplacedBreakoutPoints" | "SourceGroupRender" | "AssignNameToUnnamedComponents" | "SourceRender" | "CheckRefDesConvention" | "SourceComponentPropertyValidation" | "SourceParentAttachment" | "PortMatching" | "OptimizeSelectorCache" | "SourceTraceRender" | "SourceAddConnectivityMapKey" | "SourceDesignRuleChecks" | "SimulationRender" | "SchematicComponentRender" | "SchematicPortRender" | "SymbolContainerRender" | "SchematicPrimitiveRender" | "SchematicSymbolResize" | "SchematicComponentSizeCalculation" | "SchematicLayout" | "SchematicSectionRender" | "SchematicTraceRender" | "SchematicSheetRender" | "SchematicReplaceNetLabelsWithSymbols" | "SchematicLabelNetsWithConflictingNames" | "PanelBoardLayout" | "ValidatePcbCoordinates" | "PcbComponentRender" | "PcbPrimitiveRender" | "PcbFootprintLayout" | "PcbPortRender" | "PcbPortAttachment" | "PcbComponentSizeCalculation" | "PcbComponentAnchorAlignment" | "PcbCalcPlacementResolution" | "PcbLayout" | "PcbBoardAutoSize" | "PanelLayout" | "PcbAutoplaceBreakoutPoints" | "PcbTraceHintRender" | "PcbManualTraceRender" | "PcbPlacementDesignRuleChecks" | "PcbTraceRender" | "PcbRouteNetIslands" | "PcbImplicitCopperPourRender" | "PcbCopperPourRender" | "PcbViaStitchRender" | "PcbDesignRuleChecks" | "SilkscreenOverlapAdjustment" | "CadModelRender" | "PartsEngineRender" | "PartOrientationAnalysis" | "SupplierFootprintMismatchWarning" | "SimulationSpiceEngineRender", number>;
|
|
364
366
|
type RenderPhaseFn<K extends RenderPhase = RenderPhase> = `doInitial${K}` | `update${K}` | `remove${K}`;
|
|
365
367
|
type RenderPhaseStates = Record<RenderPhase, {
|
|
366
368
|
initialized: boolean;
|
|
@@ -522,6 +524,8 @@ declare class IsolatedCircuit {
|
|
|
522
524
|
pcbDisabled: boolean;
|
|
523
525
|
pcbRoutingDisabled: boolean;
|
|
524
526
|
_featureMspSchematicTraceRouting: boolean;
|
|
527
|
+
/** Experimental automatic via stitching; opt in before rendering. */
|
|
528
|
+
_featurePcbViaStitching: boolean;
|
|
525
529
|
/**
|
|
526
530
|
* The IsolatedCircuit name is usually set by the platform, it's not required but
|
|
527
531
|
* if supplied can identify the circuit in certain effects, e.g. it is passed
|
|
@@ -107902,6 +107906,7 @@ declare class CopperPour extends PrimitiveComponent<typeof copperPourProps> {
|
|
|
107902
107906
|
};
|
|
107903
107907
|
doInitialCreateNetsFromProps(): void;
|
|
107904
107908
|
doInitialPcbCopperPourRender(): void;
|
|
107909
|
+
doInitialPcbViaStitchRender(): void;
|
|
107905
107910
|
}
|
|
107906
107911
|
|
|
107907
107912
|
declare class CopperText extends PrimitiveComponent<typeof copperTextProps> {
|
package/dist/index.js
CHANGED
|
@@ -452,6 +452,7 @@ var orderedRenderPhases = [
|
|
|
452
452
|
"PcbRouteNetIslands",
|
|
453
453
|
"PcbImplicitCopperPourRender",
|
|
454
454
|
"PcbCopperPourRender",
|
|
455
|
+
"PcbViaStitchRender",
|
|
455
456
|
"PcbDesignRuleChecks",
|
|
456
457
|
"SilkscreenOverlapAdjustment",
|
|
457
458
|
"CadModelRender",
|
|
@@ -513,6 +514,15 @@ var asyncPhaseDependencies = {
|
|
|
513
514
|
"PcbTraceRender",
|
|
514
515
|
"PcbRouteNetIslands"
|
|
515
516
|
],
|
|
517
|
+
PcbViaStitchRender: [
|
|
518
|
+
"PcbFootprintStringRender",
|
|
519
|
+
"FetchPartFootprint",
|
|
520
|
+
"PcbPlacementDesignRuleChecks",
|
|
521
|
+
"PcbTraceRender",
|
|
522
|
+
"PcbRouteNetIslands",
|
|
523
|
+
"PcbImplicitCopperPourRender",
|
|
524
|
+
"PcbCopperPourRender"
|
|
525
|
+
],
|
|
516
526
|
PcbPlacementDesignRuleChecks: [
|
|
517
527
|
"PcbFootprintStringRender",
|
|
518
528
|
"FetchPartFootprint"
|
|
@@ -529,7 +539,8 @@ var asyncPhaseDependencies = {
|
|
|
529
539
|
"PcbPlacementDesignRuleChecks",
|
|
530
540
|
"PcbTraceRender",
|
|
531
541
|
"PcbImplicitCopperPourRender",
|
|
532
|
-
"PcbCopperPourRender"
|
|
542
|
+
"PcbCopperPourRender",
|
|
543
|
+
"PcbViaStitchRender"
|
|
533
544
|
],
|
|
534
545
|
SilkscreenOverlapAdjustment: [
|
|
535
546
|
"PcbFootprintStringRender",
|
|
@@ -3316,6 +3327,26 @@ var getObstaclesFromCircuitJson = (circuitJson, connMap) => {
|
|
|
3316
3327
|
height: element.outer_diameter,
|
|
3317
3328
|
connectedTo: withNetId([element.pcb_plated_hole_id])
|
|
3318
3329
|
});
|
|
3330
|
+
} else if (element.shape === "pill_hole_with_rect_pad" || element.shape === "rotated_pill_hole_with_rect_pad") {
|
|
3331
|
+
const rotatedRect = {
|
|
3332
|
+
center: { x: element.x, y: element.y },
|
|
3333
|
+
width: element.rect_pad_width,
|
|
3334
|
+
height: element.rect_pad_height,
|
|
3335
|
+
rotation: element.shape === "rotated_pill_hole_with_rect_pad" ? element.rect_ccw_rotation : 0
|
|
3336
|
+
};
|
|
3337
|
+
const axisAlignedRect = getAxisAlignedRectFromRotatedRect(rotatedRect);
|
|
3338
|
+
const rect = axisAlignedRect ?? rotatedRect;
|
|
3339
|
+
obstacles.push({
|
|
3340
|
+
circuitJsonMetadata: pcbPlatedHoleCircuitJsonMetadata,
|
|
3341
|
+
componentId: pcbComponentId,
|
|
3342
|
+
type: "rect",
|
|
3343
|
+
layers: everyLayer,
|
|
3344
|
+
center: rect.center,
|
|
3345
|
+
width: rect.width,
|
|
3346
|
+
height: rect.height,
|
|
3347
|
+
ccwRotationDegrees: axisAlignedRect ? void 0 : rotatedRect.rotation,
|
|
3348
|
+
connectedTo: withNetId([element.pcb_plated_hole_id])
|
|
3349
|
+
});
|
|
3319
3350
|
} else if (element.shape === "circular_hole_with_rect_pad") {
|
|
3320
3351
|
obstacles.push({
|
|
3321
3352
|
circuitJsonMetadata: pcbPlatedHoleCircuitJsonMetadata,
|
|
@@ -40819,6 +40850,480 @@ var ImplicitCopperPourPipelineSolver = class extends BasePipelineSolver {
|
|
|
40819
40850
|
// lib/solvers.ts
|
|
40820
40851
|
import { LayoutPipelineSolver } from "@tscircuit/matchpack";
|
|
40821
40852
|
import { SchematicTracePipelineSolver } from "@tscircuit/schematic-trace-solver";
|
|
40853
|
+
|
|
40854
|
+
// node_modules/@tscircuit/via-stitch-solver/lib/solvers/ViaStitchSolver.ts
|
|
40855
|
+
import { BaseSolver as BaseSolver3 } from "@tscircuit/solver-utils";
|
|
40856
|
+
|
|
40857
|
+
// node_modules/@tscircuit/via-stitch-solver/lib/geometry/brep-point-containment.ts
|
|
40858
|
+
var POINT_EPSILON = 1e-8;
|
|
40859
|
+
var isPointOnSegment = (point6, start, end) => {
|
|
40860
|
+
const cross3 = (point6.y - start.y) * (end.x - start.x) - (point6.x - start.x) * (end.y - start.y);
|
|
40861
|
+
if (Math.abs(cross3) > POINT_EPSILON) return false;
|
|
40862
|
+
const dot = (point6.x - start.x) * (end.x - start.x) + (point6.y - start.y) * (end.y - start.y);
|
|
40863
|
+
if (dot < -POINT_EPSILON) return false;
|
|
40864
|
+
const squaredLength = (end.x - start.x) ** 2 + (end.y - start.y) ** 2;
|
|
40865
|
+
return dot <= squaredLength + POINT_EPSILON;
|
|
40866
|
+
};
|
|
40867
|
+
var isPointInRing = (point6, ring) => {
|
|
40868
|
+
let isInside = false;
|
|
40869
|
+
for (let currentIndex = 0, previousIndex = ring.vertices.length - 1; currentIndex < ring.vertices.length; previousIndex = currentIndex++) {
|
|
40870
|
+
const current = ring.vertices[currentIndex];
|
|
40871
|
+
const previous = ring.vertices[previousIndex];
|
|
40872
|
+
if (isPointOnSegment(point6, previous, current)) return true;
|
|
40873
|
+
const crossesRay = current.y > point6.y !== previous.y > point6.y && point6.x < (previous.x - current.x) * (point6.y - current.y) / (previous.y - current.y) + current.x;
|
|
40874
|
+
if (crossesRay) isInside = !isInside;
|
|
40875
|
+
}
|
|
40876
|
+
return isInside;
|
|
40877
|
+
};
|
|
40878
|
+
var isPointInShape = (point6, shape) => isPointInRing(point6, shape.outer_ring) && !shape.inner_rings.some((innerRing) => isPointInRing(point6, innerRing));
|
|
40879
|
+
var isPointInShapeUnion = (point6, shapes) => shapes.some((shape) => isPointInShape(point6, shape));
|
|
40880
|
+
var getShapeUnionBounds = (shapes) => {
|
|
40881
|
+
const vertices = shapes.flatMap((shape) => shape.outer_ring.vertices);
|
|
40882
|
+
if (vertices.length === 0) return void 0;
|
|
40883
|
+
return {
|
|
40884
|
+
minX: Math.min(...vertices.map((vertex) => vertex.x)),
|
|
40885
|
+
maxX: Math.max(...vertices.map((vertex) => vertex.x)),
|
|
40886
|
+
minY: Math.min(...vertices.map((vertex) => vertex.y)),
|
|
40887
|
+
maxY: Math.max(...vertices.map((vertex) => vertex.y))
|
|
40888
|
+
};
|
|
40889
|
+
};
|
|
40890
|
+
var isViaAnnulusInsideShapeUnion = ({
|
|
40891
|
+
center,
|
|
40892
|
+
radius,
|
|
40893
|
+
shapes
|
|
40894
|
+
}) => {
|
|
40895
|
+
const samplePoints = [center];
|
|
40896
|
+
for (let sampleIndex = 0; sampleIndex < 32; sampleIndex++) {
|
|
40897
|
+
const angle = sampleIndex / 32 * Math.PI * 2;
|
|
40898
|
+
samplePoints.push({
|
|
40899
|
+
x: center.x + Math.cos(angle) * radius,
|
|
40900
|
+
y: center.y + Math.sin(angle) * radius
|
|
40901
|
+
});
|
|
40902
|
+
}
|
|
40903
|
+
return samplePoints.every(
|
|
40904
|
+
(samplePoint) => isPointInShapeUnion(samplePoint, shapes)
|
|
40905
|
+
);
|
|
40906
|
+
};
|
|
40907
|
+
|
|
40908
|
+
// node_modules/@tscircuit/via-stitch-solver/lib/geometry/circuit-element-obstacles.ts
|
|
40909
|
+
var layerMatches = (layer, layers) => layers.some((candidateLayer) => String(candidateLayer) === String(layer));
|
|
40910
|
+
var componentToObstacle = (component) => {
|
|
40911
|
+
if (component.do_not_place || !component.obstructs_within_bounds) {
|
|
40912
|
+
return void 0;
|
|
40913
|
+
}
|
|
40914
|
+
return {
|
|
40915
|
+
kind: "rect",
|
|
40916
|
+
center: component.center,
|
|
40917
|
+
width: component.width,
|
|
40918
|
+
height: component.height,
|
|
40919
|
+
ccwRotation: component.rotation
|
|
40920
|
+
};
|
|
40921
|
+
};
|
|
40922
|
+
var smtPadToObstacle = (smtPad) => {
|
|
40923
|
+
if (smtPad.shape === "circle") {
|
|
40924
|
+
return {
|
|
40925
|
+
kind: "circle",
|
|
40926
|
+
center: { x: smtPad.x, y: smtPad.y },
|
|
40927
|
+
radius: smtPad.radius
|
|
40928
|
+
};
|
|
40929
|
+
}
|
|
40930
|
+
if (smtPad.shape === "polygon") {
|
|
40931
|
+
return { kind: "polygon", points: smtPad.points };
|
|
40932
|
+
}
|
|
40933
|
+
return {
|
|
40934
|
+
kind: "rect",
|
|
40935
|
+
center: { x: smtPad.x, y: smtPad.y },
|
|
40936
|
+
width: smtPad.width,
|
|
40937
|
+
height: smtPad.height,
|
|
40938
|
+
ccwRotation: smtPad.shape === "rotated_rect" || smtPad.shape === "rotated_pill" ? smtPad.ccw_rotation : 0
|
|
40939
|
+
};
|
|
40940
|
+
};
|
|
40941
|
+
var platedHoleToObstacle = (platedHole) => {
|
|
40942
|
+
if (platedHole.shape === "circle") {
|
|
40943
|
+
return {
|
|
40944
|
+
kind: "circle",
|
|
40945
|
+
center: { x: platedHole.x, y: platedHole.y },
|
|
40946
|
+
radius: platedHole.outer_diameter / 2
|
|
40947
|
+
};
|
|
40948
|
+
}
|
|
40949
|
+
if ("outer_width" in platedHole) {
|
|
40950
|
+
return {
|
|
40951
|
+
kind: "rect",
|
|
40952
|
+
center: { x: platedHole.x, y: platedHole.y },
|
|
40953
|
+
width: platedHole.outer_width,
|
|
40954
|
+
height: platedHole.outer_height,
|
|
40955
|
+
ccwRotation: platedHole.ccw_rotation
|
|
40956
|
+
};
|
|
40957
|
+
}
|
|
40958
|
+
if (platedHole.shape === "hole_with_polygon_pad") {
|
|
40959
|
+
const rotationRadians = (platedHole.ccw_rotation ?? 0) * Math.PI / 180;
|
|
40960
|
+
const cosine = Math.cos(rotationRadians);
|
|
40961
|
+
const sine = Math.sin(rotationRadians);
|
|
40962
|
+
return {
|
|
40963
|
+
kind: "polygon",
|
|
40964
|
+
points: platedHole.pad_outline.map((point6) => ({
|
|
40965
|
+
x: platedHole.x + point6.x * cosine - point6.y * sine,
|
|
40966
|
+
y: platedHole.y + point6.x * sine + point6.y * cosine
|
|
40967
|
+
}))
|
|
40968
|
+
};
|
|
40969
|
+
}
|
|
40970
|
+
return {
|
|
40971
|
+
kind: "rect",
|
|
40972
|
+
center: { x: platedHole.x, y: platedHole.y },
|
|
40973
|
+
width: platedHole.rect_pad_width,
|
|
40974
|
+
height: platedHole.rect_pad_height,
|
|
40975
|
+
ccwRotation: "rect_ccw_rotation" in platedHole ? platedHole.rect_ccw_rotation ?? 0 : 0
|
|
40976
|
+
};
|
|
40977
|
+
};
|
|
40978
|
+
var holeToObstacle = (hole) => {
|
|
40979
|
+
if ("hole_diameter" in hole) {
|
|
40980
|
+
if (hole.hole_shape === "circle") {
|
|
40981
|
+
return {
|
|
40982
|
+
kind: "circle",
|
|
40983
|
+
center: { x: hole.x, y: hole.y },
|
|
40984
|
+
radius: hole.hole_diameter / 2
|
|
40985
|
+
};
|
|
40986
|
+
}
|
|
40987
|
+
return {
|
|
40988
|
+
kind: "rect",
|
|
40989
|
+
center: { x: hole.x, y: hole.y },
|
|
40990
|
+
width: hole.hole_diameter,
|
|
40991
|
+
height: hole.hole_diameter,
|
|
40992
|
+
ccwRotation: 0
|
|
40993
|
+
};
|
|
40994
|
+
}
|
|
40995
|
+
return {
|
|
40996
|
+
kind: "rect",
|
|
40997
|
+
center: { x: hole.x, y: hole.y },
|
|
40998
|
+
width: hole.hole_width,
|
|
40999
|
+
height: hole.hole_height,
|
|
41000
|
+
ccwRotation: hole.hole_shape === "rotated_pill" ? hole.ccw_rotation : 0
|
|
41001
|
+
};
|
|
41002
|
+
};
|
|
41003
|
+
var getDistanceToSegment = (point6, start, end) => {
|
|
41004
|
+
const segmentX = end.x - start.x;
|
|
41005
|
+
const segmentY = end.y - start.y;
|
|
41006
|
+
const squaredLength = segmentX ** 2 + segmentY ** 2;
|
|
41007
|
+
if (squaredLength === 0)
|
|
41008
|
+
return Math.hypot(point6.x - start.x, point6.y - start.y);
|
|
41009
|
+
const projection = Math.max(
|
|
41010
|
+
0,
|
|
41011
|
+
Math.min(
|
|
41012
|
+
1,
|
|
41013
|
+
((point6.x - start.x) * segmentX + (point6.y - start.y) * segmentY) / squaredLength
|
|
41014
|
+
)
|
|
41015
|
+
);
|
|
41016
|
+
return Math.hypot(
|
|
41017
|
+
point6.x - (start.x + projection * segmentX),
|
|
41018
|
+
point6.y - (start.y + projection * segmentY)
|
|
41019
|
+
);
|
|
41020
|
+
};
|
|
41021
|
+
var isPointInPolygon = (point6, polygon) => {
|
|
41022
|
+
let isInside = false;
|
|
41023
|
+
for (let currentIndex = 0, previousIndex = polygon.length - 1; currentIndex < polygon.length; previousIndex = currentIndex++) {
|
|
41024
|
+
const current = polygon[currentIndex];
|
|
41025
|
+
const previous = polygon[previousIndex];
|
|
41026
|
+
const crossesRay = current.y > point6.y !== previous.y > point6.y && point6.x < (previous.x - current.x) * (point6.y - current.y) / (previous.y - current.y) + current.x;
|
|
41027
|
+
if (crossesRay) isInside = !isInside;
|
|
41028
|
+
}
|
|
41029
|
+
return isInside;
|
|
41030
|
+
};
|
|
41031
|
+
var doesCircleIntersectObstacle = (center, radius, obstacle) => {
|
|
41032
|
+
if (obstacle.kind === "circle") {
|
|
41033
|
+
return Math.hypot(center.x - obstacle.center.x, center.y - obstacle.center.y) < radius + obstacle.radius;
|
|
41034
|
+
}
|
|
41035
|
+
if (obstacle.kind === "polygon") {
|
|
41036
|
+
if (isPointInPolygon(center, obstacle.points)) return true;
|
|
41037
|
+
return obstacle.points.some(
|
|
41038
|
+
(point6, pointIndex) => Boolean(
|
|
41039
|
+
getDistanceToSegment(
|
|
41040
|
+
center,
|
|
41041
|
+
point6,
|
|
41042
|
+
obstacle.points[(pointIndex + 1) % obstacle.points.length]
|
|
41043
|
+
) < radius
|
|
41044
|
+
)
|
|
41045
|
+
);
|
|
41046
|
+
}
|
|
41047
|
+
const rotationRadians = -obstacle.ccwRotation * Math.PI / 180;
|
|
41048
|
+
const deltaX = center.x - obstacle.center.x;
|
|
41049
|
+
const deltaY = center.y - obstacle.center.y;
|
|
41050
|
+
const localX = deltaX * Math.cos(rotationRadians) - deltaY * Math.sin(rotationRadians);
|
|
41051
|
+
const localY = deltaX * Math.sin(rotationRadians) + deltaY * Math.cos(rotationRadians);
|
|
41052
|
+
const outsideX = Math.max(Math.abs(localX) - obstacle.width / 2, 0);
|
|
41053
|
+
const outsideY = Math.max(Math.abs(localY) - obstacle.height / 2, 0);
|
|
41054
|
+
return Math.hypot(outsideX, outsideY) < radius;
|
|
41055
|
+
};
|
|
41056
|
+
var getStitchingObstacles = (circuitJson, layers) => circuitJson.flatMap((element) => {
|
|
41057
|
+
if (element.type === "pcb_component" && layerMatches(element.layer, layers)) {
|
|
41058
|
+
const obstacle = componentToObstacle(element);
|
|
41059
|
+
return obstacle ? [obstacle] : [];
|
|
41060
|
+
}
|
|
41061
|
+
if (element.type === "pcb_smtpad" && layerMatches(element.layer, layers)) {
|
|
41062
|
+
return [smtPadToObstacle(element)];
|
|
41063
|
+
}
|
|
41064
|
+
if (element.type === "pcb_plated_hole" && element.layers.some((layer) => layerMatches(layer, layers))) {
|
|
41065
|
+
return [platedHoleToObstacle(element)];
|
|
41066
|
+
}
|
|
41067
|
+
if (element.type === "pcb_hole") return [holeToObstacle(element)];
|
|
41068
|
+
return [];
|
|
41069
|
+
});
|
|
41070
|
+
var doesViaIntersectStitchingObstacle = ({
|
|
41071
|
+
center,
|
|
41072
|
+
radius,
|
|
41073
|
+
obstacles
|
|
41074
|
+
}) => obstacles.some(
|
|
41075
|
+
(obstacle) => doesCircleIntersectObstacle(center, radius, obstacle)
|
|
41076
|
+
);
|
|
41077
|
+
|
|
41078
|
+
// node_modules/@tscircuit/via-stitch-solver/lib/solvers/ViaStitchSolver.ts
|
|
41079
|
+
var resolveOptions = (options = {}) => {
|
|
41080
|
+
const resolvedOptions = {
|
|
41081
|
+
sourceNetIds: options.sourceNetIds ? new Set(options.sourceNetIds) : void 0,
|
|
41082
|
+
layers: options.layers ?? ["top", "bottom"],
|
|
41083
|
+
viaPitch: options.viaPitch ?? 1,
|
|
41084
|
+
viaHoleDiameter: options.viaHoleDiameter ?? 0.3,
|
|
41085
|
+
viaOuterDiameter: options.viaOuterDiameter ?? 0.6,
|
|
41086
|
+
pourEdgeClearance: options.pourEdgeClearance ?? 0.2,
|
|
41087
|
+
obstacleClearance: options.obstacleClearance ?? 0.2,
|
|
41088
|
+
minimumViaSeparation: options.minimumViaSeparation ?? Math.max(options.viaOuterDiameter ?? 0.6, 0.8),
|
|
41089
|
+
gridOrigin: options.gridOrigin ?? { x: 0, y: 0 },
|
|
41090
|
+
isTented: options.isTented ?? true
|
|
41091
|
+
};
|
|
41092
|
+
for (const [optionName, optionValue] of [
|
|
41093
|
+
["viaPitch", resolvedOptions.viaPitch],
|
|
41094
|
+
["viaHoleDiameter", resolvedOptions.viaHoleDiameter],
|
|
41095
|
+
["viaOuterDiameter", resolvedOptions.viaOuterDiameter],
|
|
41096
|
+
["minimumViaSeparation", resolvedOptions.minimumViaSeparation]
|
|
41097
|
+
]) {
|
|
41098
|
+
if (!Number.isFinite(optionValue) || optionValue <= 0) {
|
|
41099
|
+
throw new Error(`${optionName} must be a finite number greater than zero`);
|
|
41100
|
+
}
|
|
41101
|
+
}
|
|
41102
|
+
if (!Number.isFinite(resolvedOptions.pourEdgeClearance) || resolvedOptions.pourEdgeClearance < 0) {
|
|
41103
|
+
throw new Error("pourEdgeClearance must be a finite non-negative number");
|
|
41104
|
+
}
|
|
41105
|
+
if (!Number.isFinite(resolvedOptions.obstacleClearance) || resolvedOptions.obstacleClearance < 0) {
|
|
41106
|
+
throw new Error("obstacleClearance must be a finite non-negative number");
|
|
41107
|
+
}
|
|
41108
|
+
if (!Number.isFinite(resolvedOptions.gridOrigin.x) || !Number.isFinite(resolvedOptions.gridOrigin.y)) {
|
|
41109
|
+
throw new Error("gridOrigin coordinates must be finite numbers");
|
|
41110
|
+
}
|
|
41111
|
+
if (resolvedOptions.viaHoleDiameter >= resolvedOptions.viaOuterDiameter) {
|
|
41112
|
+
throw new Error("viaHoleDiameter must be smaller than viaOuterDiameter");
|
|
41113
|
+
}
|
|
41114
|
+
if (String(resolvedOptions.layers[0]) === String(resolvedOptions.layers[1])) {
|
|
41115
|
+
throw new Error("layers must contain two distinct PCB layers");
|
|
41116
|
+
}
|
|
41117
|
+
return resolvedOptions;
|
|
41118
|
+
};
|
|
41119
|
+
var isBrepCopperPour = (element) => element.type === "pcb_copper_pour" && element.shape === "brep";
|
|
41120
|
+
var getCopperPourPairContexts = (input, options) => {
|
|
41121
|
+
const sourceNetsById = new Map(
|
|
41122
|
+
input.circuitJson.filter((element) => element.type === "source_net").map((sourceNet) => [sourceNet.source_net_id, sourceNet])
|
|
41123
|
+
);
|
|
41124
|
+
const poursBySourceNetId = /* @__PURE__ */ new Map();
|
|
41125
|
+
for (const element of input.circuitJson) {
|
|
41126
|
+
if (!isBrepCopperPour(element) || !element.source_net_id) continue;
|
|
41127
|
+
if (options.sourceNetIds && !options.sourceNetIds.has(element.source_net_id)) {
|
|
41128
|
+
continue;
|
|
41129
|
+
}
|
|
41130
|
+
const sourceNetPours = poursBySourceNetId.get(element.source_net_id) ?? [];
|
|
41131
|
+
sourceNetPours.push(element);
|
|
41132
|
+
poursBySourceNetId.set(element.source_net_id, sourceNetPours);
|
|
41133
|
+
}
|
|
41134
|
+
const [fromLayer, toLayer] = options.layers;
|
|
41135
|
+
const contexts = [];
|
|
41136
|
+
for (const [sourceNetId, sourceNetPours] of poursBySourceNetId) {
|
|
41137
|
+
const fromLayerPours = sourceNetPours.filter(
|
|
41138
|
+
(copperPour) => String(copperPour.layer) === String(fromLayer)
|
|
41139
|
+
);
|
|
41140
|
+
const toLayerPours = sourceNetPours.filter(
|
|
41141
|
+
(copperPour) => String(copperPour.layer) === String(toLayer)
|
|
41142
|
+
);
|
|
41143
|
+
if (fromLayerPours.length === 0 || toLayerPours.length === 0) continue;
|
|
41144
|
+
contexts.push({
|
|
41145
|
+
sourceNetId,
|
|
41146
|
+
sourceNet: sourceNetsById.get(sourceNetId),
|
|
41147
|
+
fromLayerPours,
|
|
41148
|
+
toLayerPours
|
|
41149
|
+
});
|
|
41150
|
+
}
|
|
41151
|
+
return contexts;
|
|
41152
|
+
};
|
|
41153
|
+
var getPcbTraceViaRadius = (routePoint, fallbackRadius) => {
|
|
41154
|
+
const routePointWithDiameter = routePoint;
|
|
41155
|
+
return (routePointWithDiameter.outer_diameter ?? fallbackRadius * 2) / 2;
|
|
41156
|
+
};
|
|
41157
|
+
var isTooCloseToOccupiedVia = ({
|
|
41158
|
+
center,
|
|
41159
|
+
radius,
|
|
41160
|
+
occupiedVias,
|
|
41161
|
+
minimumViaSeparation
|
|
41162
|
+
}) => occupiedVias.some(
|
|
41163
|
+
(occupiedVia) => Math.hypot(
|
|
41164
|
+
center.x - occupiedVia.center.x,
|
|
41165
|
+
center.y - occupiedVia.center.y
|
|
41166
|
+
) < Math.max(minimumViaSeparation, radius + occupiedVia.radius)
|
|
41167
|
+
);
|
|
41168
|
+
var getGridCoordinates = ({
|
|
41169
|
+
minimum,
|
|
41170
|
+
maximum,
|
|
41171
|
+
origin,
|
|
41172
|
+
pitch
|
|
41173
|
+
}) => {
|
|
41174
|
+
const coordinates = [];
|
|
41175
|
+
const firstGridIndex = Math.ceil((minimum - origin) / pitch);
|
|
41176
|
+
const lastGridIndex = Math.floor((maximum - origin) / pitch);
|
|
41177
|
+
for (let gridIndex = firstGridIndex; gridIndex <= lastGridIndex; gridIndex++) {
|
|
41178
|
+
coordinates.push(origin + gridIndex * pitch);
|
|
41179
|
+
}
|
|
41180
|
+
return coordinates;
|
|
41181
|
+
};
|
|
41182
|
+
var ViaStitchSolver = class extends BaseSolver3 {
|
|
41183
|
+
constructor(input) {
|
|
41184
|
+
super();
|
|
41185
|
+
this.input = input;
|
|
41186
|
+
this.options = resolveOptions(input.options);
|
|
41187
|
+
this.copperPourPairContexts = getCopperPourPairContexts(input, this.options);
|
|
41188
|
+
this.stitchingObstacles = getStitchingObstacles(
|
|
41189
|
+
input.circuitJson,
|
|
41190
|
+
this.options.layers
|
|
41191
|
+
);
|
|
41192
|
+
const fallbackViaRadius = this.options.viaOuterDiameter / 2;
|
|
41193
|
+
for (const element of input.circuitJson) {
|
|
41194
|
+
if (element.type === "pcb_via") {
|
|
41195
|
+
const pcbVia = element;
|
|
41196
|
+
this.existingPcbViaIds.add(pcbVia.pcb_via_id);
|
|
41197
|
+
this.occupiedVias.push({
|
|
41198
|
+
center: { x: pcbVia.x, y: pcbVia.y },
|
|
41199
|
+
radius: pcbVia.outer_diameter / 2
|
|
41200
|
+
});
|
|
41201
|
+
} else if (element.type === "pcb_trace") {
|
|
41202
|
+
for (const routePoint of element.route) {
|
|
41203
|
+
if (routePoint.route_type !== "via") continue;
|
|
41204
|
+
this.occupiedVias.push({
|
|
41205
|
+
center: { x: routePoint.x, y: routePoint.y },
|
|
41206
|
+
radius: getPcbTraceViaRadius(routePoint, fallbackViaRadius)
|
|
41207
|
+
});
|
|
41208
|
+
}
|
|
41209
|
+
}
|
|
41210
|
+
}
|
|
41211
|
+
}
|
|
41212
|
+
input;
|
|
41213
|
+
options;
|
|
41214
|
+
copperPourPairContexts;
|
|
41215
|
+
pcbVias = [];
|
|
41216
|
+
occupiedVias = [];
|
|
41217
|
+
existingPcbViaIds = /* @__PURE__ */ new Set();
|
|
41218
|
+
stitchingObstacles;
|
|
41219
|
+
nextCopperPourPairIndex = 0;
|
|
41220
|
+
nextViaId = 0;
|
|
41221
|
+
_step() {
|
|
41222
|
+
const copperPourPairContext = this.copperPourPairContexts[this.nextCopperPourPairIndex];
|
|
41223
|
+
if (!copperPourPairContext) {
|
|
41224
|
+
this.solved = true;
|
|
41225
|
+
this.progress = 1;
|
|
41226
|
+
return;
|
|
41227
|
+
}
|
|
41228
|
+
this.processCopperPourPair(copperPourPairContext);
|
|
41229
|
+
this.nextCopperPourPairIndex += 1;
|
|
41230
|
+
this.progress = this.copperPourPairContexts.length === 0 ? 1 : this.nextCopperPourPairIndex / this.copperPourPairContexts.length;
|
|
41231
|
+
this.solved = this.nextCopperPourPairIndex === this.copperPourPairContexts.length;
|
|
41232
|
+
}
|
|
41233
|
+
processCopperPourPair(context) {
|
|
41234
|
+
const [fromLayer, toLayer] = this.options.layers;
|
|
41235
|
+
const fromLayerShapes = context.fromLayerPours.map(
|
|
41236
|
+
(copperPour) => copperPour.brep_shape
|
|
41237
|
+
);
|
|
41238
|
+
const toLayerShapes = context.toLayerPours.map(
|
|
41239
|
+
(copperPour) => copperPour.brep_shape
|
|
41240
|
+
);
|
|
41241
|
+
const fromBounds = getShapeUnionBounds(fromLayerShapes);
|
|
41242
|
+
const toBounds = getShapeUnionBounds(toLayerShapes);
|
|
41243
|
+
if (!fromBounds || !toBounds) return;
|
|
41244
|
+
const overlapBounds = {
|
|
41245
|
+
minX: Math.max(fromBounds.minX, toBounds.minX),
|
|
41246
|
+
maxX: Math.min(fromBounds.maxX, toBounds.maxX),
|
|
41247
|
+
minY: Math.max(fromBounds.minY, toBounds.minY),
|
|
41248
|
+
maxY: Math.min(fromBounds.maxY, toBounds.maxY)
|
|
41249
|
+
};
|
|
41250
|
+
if (overlapBounds.minX > overlapBounds.maxX || overlapBounds.minY > overlapBounds.maxY) {
|
|
41251
|
+
return;
|
|
41252
|
+
}
|
|
41253
|
+
const viaRadius = this.options.viaOuterDiameter / 2;
|
|
41254
|
+
const requiredCopperRadius = viaRadius + this.options.pourEdgeClearance;
|
|
41255
|
+
const requiredObstacleRadius = viaRadius + this.options.obstacleClearance;
|
|
41256
|
+
const xCoordinates = getGridCoordinates({
|
|
41257
|
+
minimum: overlapBounds.minX + requiredCopperRadius,
|
|
41258
|
+
maximum: overlapBounds.maxX - requiredCopperRadius,
|
|
41259
|
+
origin: this.options.gridOrigin.x,
|
|
41260
|
+
pitch: this.options.viaPitch
|
|
41261
|
+
});
|
|
41262
|
+
const yCoordinates = getGridCoordinates({
|
|
41263
|
+
minimum: overlapBounds.minY + requiredCopperRadius,
|
|
41264
|
+
maximum: overlapBounds.maxY - requiredCopperRadius,
|
|
41265
|
+
origin: this.options.gridOrigin.y,
|
|
41266
|
+
pitch: this.options.viaPitch
|
|
41267
|
+
});
|
|
41268
|
+
for (const y of yCoordinates) {
|
|
41269
|
+
for (const x of xCoordinates) {
|
|
41270
|
+
const center = { x, y };
|
|
41271
|
+
if (isTooCloseToOccupiedVia({
|
|
41272
|
+
center,
|
|
41273
|
+
radius: viaRadius,
|
|
41274
|
+
occupiedVias: this.occupiedVias,
|
|
41275
|
+
minimumViaSeparation: this.options.minimumViaSeparation
|
|
41276
|
+
}) || doesViaIntersectStitchingObstacle({
|
|
41277
|
+
center,
|
|
41278
|
+
radius: requiredObstacleRadius,
|
|
41279
|
+
obstacles: this.stitchingObstacles
|
|
41280
|
+
}) || !isViaAnnulusInsideShapeUnion({
|
|
41281
|
+
center,
|
|
41282
|
+
radius: requiredCopperRadius,
|
|
41283
|
+
shapes: fromLayerShapes
|
|
41284
|
+
}) || !isViaAnnulusInsideShapeUnion({
|
|
41285
|
+
center,
|
|
41286
|
+
radius: requiredCopperRadius,
|
|
41287
|
+
shapes: toLayerShapes
|
|
41288
|
+
})) {
|
|
41289
|
+
continue;
|
|
41290
|
+
}
|
|
41291
|
+
let pcbViaId = `via_stitch_via_${this.nextViaId++}`;
|
|
41292
|
+
while (this.existingPcbViaIds.has(pcbViaId)) {
|
|
41293
|
+
pcbViaId = `via_stitch_via_${this.nextViaId++}`;
|
|
41294
|
+
}
|
|
41295
|
+
const referencePour = context.fromLayerPours[0];
|
|
41296
|
+
const pcbVia = {
|
|
41297
|
+
type: "pcb_via",
|
|
41298
|
+
pcb_via_id: pcbViaId,
|
|
41299
|
+
x,
|
|
41300
|
+
y,
|
|
41301
|
+
hole_diameter: this.options.viaHoleDiameter,
|
|
41302
|
+
outer_diameter: this.options.viaOuterDiameter,
|
|
41303
|
+
layers: [fromLayer, toLayer],
|
|
41304
|
+
from_layer: fromLayer,
|
|
41305
|
+
to_layer: toLayer,
|
|
41306
|
+
source_net_id: context.sourceNetId,
|
|
41307
|
+
subcircuit_id: referencePour.subcircuit_id,
|
|
41308
|
+
pcb_group_id: referencePour.pcb_group_id,
|
|
41309
|
+
subcircuit_connectivity_map_key: context.sourceNet?.subcircuit_connectivity_map_key,
|
|
41310
|
+
is_tented: this.options.isTented
|
|
41311
|
+
};
|
|
41312
|
+
this.pcbVias.push(pcbVia);
|
|
41313
|
+
this.existingPcbViaIds.add(pcbViaId);
|
|
41314
|
+
this.occupiedVias.push({ center, radius: viaRadius });
|
|
41315
|
+
}
|
|
41316
|
+
}
|
|
41317
|
+
}
|
|
41318
|
+
getOutput() {
|
|
41319
|
+
return {
|
|
41320
|
+
processedCopperPourPairCount: this.copperPourPairContexts.length,
|
|
41321
|
+
pcbVias: this.pcbVias
|
|
41322
|
+
};
|
|
41323
|
+
}
|
|
41324
|
+
};
|
|
41325
|
+
|
|
41326
|
+
// lib/solvers.ts
|
|
40822
41327
|
import { PackSolver2 } from "calculate-packing";
|
|
40823
41328
|
var SOLVERS = {
|
|
40824
41329
|
PackSolver2,
|
|
@@ -40838,7 +41343,8 @@ var SOLVERS = {
|
|
|
40838
41343
|
CreateFdmEnclosureSolver,
|
|
40839
41344
|
FanoutSolver,
|
|
40840
41345
|
ImplicitCopperPourPipelineSolver,
|
|
40841
|
-
SchematicTracePipelineSolver
|
|
41346
|
+
SchematicTracePipelineSolver,
|
|
41347
|
+
ViaStitchSolver
|
|
40842
41348
|
};
|
|
40843
41349
|
|
|
40844
41350
|
// lib/utils/autorouting/LocalCacheEngineCacheProvider.ts
|
|
@@ -50925,7 +51431,7 @@ function Group_getRoutingPhasePlans(group) {
|
|
|
50925
51431
|
var package_default = {
|
|
50926
51432
|
name: "@tscircuit/core",
|
|
50927
51433
|
type: "module",
|
|
50928
|
-
version: "0.0.
|
|
51434
|
+
version: "0.0.1859",
|
|
50929
51435
|
types: "dist/index.d.ts",
|
|
50930
51436
|
main: "dist/index.js",
|
|
50931
51437
|
module: "dist/index.js",
|
|
@@ -50983,6 +51489,7 @@ var package_default = {
|
|
|
50983
51489
|
"@tscircuit/schematic-trace-solver": "^0.0.186",
|
|
50984
51490
|
"@tscircuit/solver-utils": "^0.0.16",
|
|
50985
51491
|
"@tscircuit/soup-util": "^0.0.41",
|
|
51492
|
+
"@tscircuit/via-stitch-solver": "github:tscircuit/via-stitch-solver#75653b6440e75584b052f03595afd34283ae7558",
|
|
50986
51493
|
"@tscircuit/winding-breakout-point-solver": "github:tscircuit/winding-breakout-point-solver#202dbe9eb0088633b6fa094120dba6f2f41f8803",
|
|
50987
51494
|
"@types/bun": "^1.2.16",
|
|
50988
51495
|
"@types/debug": "^4.1.12",
|
|
@@ -51654,7 +52161,7 @@ function getAccumulatedPcbTracesWithStageOutputReplacements({
|
|
|
51654
52161
|
// lib/components/primitive-components/Group/get-source-trace-id-for-routed-trace.ts
|
|
51655
52162
|
import { distance as distance14, pointToSegmentDistance as pointToSegmentDistance3 } from "@tscircuit/math-utils";
|
|
51656
52163
|
import { ConnectivityMap as ConnectivityMap5 } from "circuit-json-to-connectivity-map";
|
|
51657
|
-
var
|
|
52164
|
+
var POINT_EPSILON2 = 1e-6;
|
|
51658
52165
|
function isPointOnWireSegment({
|
|
51659
52166
|
point: point6,
|
|
51660
52167
|
segmentStart,
|
|
@@ -51663,7 +52170,7 @@ function isPointOnWireSegment({
|
|
|
51663
52170
|
if (point6.layer && (segmentStart.layer !== point6.layer || segmentEnd.layer !== point6.layer)) {
|
|
51664
52171
|
return false;
|
|
51665
52172
|
}
|
|
51666
|
-
return pointToSegmentDistance3(point6, segmentStart, segmentEnd) <=
|
|
52173
|
+
return pointToSegmentDistance3(point6, segmentStart, segmentEnd) <= POINT_EPSILON2;
|
|
51667
52174
|
}
|
|
51668
52175
|
function isPointInSmtPad({
|
|
51669
52176
|
point: point6,
|
|
@@ -55449,6 +55956,8 @@ var IsolatedCircuit = class {
|
|
|
55449
55956
|
pcbDisabled = false;
|
|
55450
55957
|
pcbRoutingDisabled = false;
|
|
55451
55958
|
_featureMspSchematicTraceRouting = true;
|
|
55959
|
+
/** Experimental automatic via stitching; opt in before rendering. */
|
|
55960
|
+
_featurePcbViaStitching = false;
|
|
55452
55961
|
/**
|
|
55453
55962
|
* The IsolatedCircuit name is usually set by the platform, it's not required but
|
|
55454
55963
|
* if supplied can identify the circuit in certain effects, e.g. it is passed
|
|
@@ -55873,7 +56382,7 @@ import {
|
|
|
55873
56382
|
// lib/components/primitive-components/CopperPour/utils/mark-trace-segments-inside-copper-pour.ts
|
|
55874
56383
|
var EPSILON14 = 1e-9;
|
|
55875
56384
|
var isWireRoutePoint = (routePoint) => routePoint.route_type === "wire";
|
|
55876
|
-
var
|
|
56385
|
+
var isPointOnSegment2 = (p, a, b) => {
|
|
55877
56386
|
const cross3 = (p.y - a.y) * (b.x - a.x) - (p.x - a.x) * (b.y - a.y);
|
|
55878
56387
|
if (Math.abs(cross3) > EPSILON14) return false;
|
|
55879
56388
|
const dot = (p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y);
|
|
@@ -55882,12 +56391,12 @@ var isPointOnSegment = (p, a, b) => {
|
|
|
55882
56391
|
if (dot - squaredLength > EPSILON14) return false;
|
|
55883
56392
|
return true;
|
|
55884
56393
|
};
|
|
55885
|
-
var
|
|
56394
|
+
var isPointInRing2 = (point6, ring) => {
|
|
55886
56395
|
if (ring.length < 3) return false;
|
|
55887
56396
|
let inside = false;
|
|
55888
56397
|
let previous = ring[ring.length - 1];
|
|
55889
56398
|
for (const current of ring) {
|
|
55890
|
-
if (
|
|
56399
|
+
if (isPointOnSegment2(point6, previous, current)) return true;
|
|
55891
56400
|
const intersects = current.y > point6.y !== previous.y > point6.y && point6.x < (previous.x - current.x) * (point6.y - current.y) / (previous.y - current.y) + current.x;
|
|
55892
56401
|
if (intersects) inside = !inside;
|
|
55893
56402
|
previous = current;
|
|
@@ -55910,10 +56419,10 @@ var isPointInBrepPour = (p, pour) => {
|
|
|
55910
56419
|
x: v.x,
|
|
55911
56420
|
y: v.y
|
|
55912
56421
|
}));
|
|
55913
|
-
if (!
|
|
56422
|
+
if (!isPointInRing2(p, outerRing)) return false;
|
|
55914
56423
|
for (const innerRing of pour.brep_shape.inner_rings) {
|
|
55915
56424
|
const points = innerRing.vertices.map((v) => ({ x: v.x, y: v.y }));
|
|
55916
|
-
if (
|
|
56425
|
+
if (isPointInRing2(p, points)) return false;
|
|
55917
56426
|
}
|
|
55918
56427
|
return true;
|
|
55919
56428
|
};
|
|
@@ -56100,6 +56609,59 @@ function CopperPour_doInitialPcbCopperPourRender(copperPour) {
|
|
|
56100
56609
|
});
|
|
56101
56610
|
}
|
|
56102
56611
|
|
|
56612
|
+
// lib/components/primitive-components/CopperPour/CopperPour_doInitialPcbViaStitchRender.ts
|
|
56613
|
+
var renderViaStitchingForCopperPours = (copperPour) => {
|
|
56614
|
+
const subcircuit = copperPour.getSubcircuit();
|
|
56615
|
+
if (!subcircuit.root) return;
|
|
56616
|
+
const copperPours = subcircuit.selectAll("copperpour").filter(
|
|
56617
|
+
(candidateCopperPour) => candidateCopperPour.getSubcircuit() === subcircuit
|
|
56618
|
+
);
|
|
56619
|
+
if (copperPours[0] !== copperPour) return;
|
|
56620
|
+
const sourceNetIds = copperPours.flatMap((candidateCopperPour) => {
|
|
56621
|
+
const net = subcircuit.selectOne(
|
|
56622
|
+
candidateCopperPour._parsedProps.connectsTo
|
|
56623
|
+
);
|
|
56624
|
+
return net?.source_net_id ? [net.source_net_id] : [];
|
|
56625
|
+
});
|
|
56626
|
+
if (sourceNetIds.length === 0) return;
|
|
56627
|
+
const { db } = subcircuit.root;
|
|
56628
|
+
const pcbStyle = copperPour.getInheritedMergedProperty("pcbStyle");
|
|
56629
|
+
const { holeDiameter, padDiameter } = getViaDiameterDefaults(pcbStyle);
|
|
56630
|
+
const boardComponent = copperPour._getBoard();
|
|
56631
|
+
const pcbBoard = boardComponent?.pcb_board_id ? db.pcb_board.get(boardComponent.pcb_board_id) : void 0;
|
|
56632
|
+
const solverInput = {
|
|
56633
|
+
circuitJson: db.toArray(),
|
|
56634
|
+
options: {
|
|
56635
|
+
sourceNetIds: [...new Set(sourceNetIds)],
|
|
56636
|
+
viaHoleDiameter: pcbBoard?.min_via_hole_diameter ?? holeDiameter,
|
|
56637
|
+
viaOuterDiameter: pcbBoard?.min_via_pad_diameter ?? padDiameter
|
|
56638
|
+
}
|
|
56639
|
+
};
|
|
56640
|
+
const solver = new ViaStitchSolver(solverInput);
|
|
56641
|
+
subcircuit.root.emit("solver:started", {
|
|
56642
|
+
type: "solver:started",
|
|
56643
|
+
solverName: "ViaStitchSolver",
|
|
56644
|
+
solverParams: solverInput,
|
|
56645
|
+
solverConstructorArgs: [solverInput],
|
|
56646
|
+
componentName: subcircuit.getString()
|
|
56647
|
+
});
|
|
56648
|
+
solver.solve();
|
|
56649
|
+
if (solver.failed) {
|
|
56650
|
+
throw new Error(solver.error ?? "Via stitching solver failed");
|
|
56651
|
+
}
|
|
56652
|
+
for (const pcbVia of solver.getOutput().pcbVias) {
|
|
56653
|
+
const { pcb_via_id: _solverViaId, ...pcbViaInput } = pcbVia;
|
|
56654
|
+
db.pcb_via.insert(pcbViaInput);
|
|
56655
|
+
}
|
|
56656
|
+
};
|
|
56657
|
+
function CopperPour_doInitialPcbViaStitchRender(copperPour) {
|
|
56658
|
+
if (copperPour.root?.pcbDisabled || !copperPour.root?._featurePcbViaStitching)
|
|
56659
|
+
return;
|
|
56660
|
+
copperPour._queueAsyncEffect("PcbViaStitchRender", async () => {
|
|
56661
|
+
renderViaStitchingForCopperPours(copperPour);
|
|
56662
|
+
});
|
|
56663
|
+
}
|
|
56664
|
+
|
|
56103
56665
|
// lib/components/primitive-components/CopperPour/CopperPour.ts
|
|
56104
56666
|
var CopperPour = class extends PrimitiveComponent2 {
|
|
56105
56667
|
isPcbPrimitive = true;
|
|
@@ -56120,6 +56682,9 @@ var CopperPour = class extends PrimitiveComponent2 {
|
|
|
56120
56682
|
doInitialPcbCopperPourRender() {
|
|
56121
56683
|
CopperPour_doInitialPcbCopperPourRender(this);
|
|
56122
56684
|
}
|
|
56685
|
+
doInitialPcbViaStitchRender() {
|
|
56686
|
+
CopperPour_doInitialPcbViaStitchRender(this);
|
|
56687
|
+
}
|
|
56123
56688
|
};
|
|
56124
56689
|
|
|
56125
56690
|
// lib/components/normal-components/Board_doInitialPcbImplicitCopperPourRender.ts
|
|
@@ -60225,7 +60790,7 @@ var validateWindingBreakoutInput = (input) => {
|
|
|
60225
60790
|
};
|
|
60226
60791
|
|
|
60227
60792
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/GatePlacementSolver.ts
|
|
60228
|
-
import { BaseSolver as
|
|
60793
|
+
import { BaseSolver as BaseSolver4 } from "@tscircuit/solver-utils";
|
|
60229
60794
|
|
|
60230
60795
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/gate-placement/place-breakout-gates.ts
|
|
60231
60796
|
var GEOMETRY_EPSILON = 1e-9;
|
|
@@ -60755,7 +61320,7 @@ var getSharedGateSlotCount = (points) => new Set(
|
|
|
60755
61320
|
(point6) => `${point6.regionId}:${point6.x.toFixed(9)}:${point6.y.toFixed(9)}`
|
|
60756
61321
|
)
|
|
60757
61322
|
).size;
|
|
60758
|
-
var GatePlacementSolver = class extends
|
|
61323
|
+
var GatePlacementSolver = class extends BaseSolver4 {
|
|
60759
61324
|
constructor(params) {
|
|
60760
61325
|
super();
|
|
60761
61326
|
this.params = params;
|
|
@@ -60894,7 +61459,7 @@ var GatePlacementSolver = class extends BaseSolver3 {
|
|
|
60894
61459
|
};
|
|
60895
61460
|
|
|
60896
61461
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/ReferenceOrderingSolver.ts
|
|
60897
|
-
import { BaseSolver as
|
|
61462
|
+
import { BaseSolver as BaseSolver5 } from "@tscircuit/solver-utils";
|
|
60898
61463
|
|
|
60899
61464
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/ordering/get-natural-winding-order.ts
|
|
60900
61465
|
var getNaturalWindingOrder = (region, connections) => connections.map((connection) => {
|
|
@@ -60961,7 +61526,7 @@ var getReferenceWindingOrder = (naturalOrder, atomicGroups, buses) => {
|
|
|
60961
61526
|
};
|
|
60962
61527
|
|
|
60963
61528
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/ReferenceOrderingSolver.ts
|
|
60964
|
-
var ReferenceOrderingSolver = class extends
|
|
61529
|
+
var ReferenceOrderingSolver = class extends BaseSolver5 {
|
|
60965
61530
|
constructor(params) {
|
|
60966
61531
|
super();
|
|
60967
61532
|
this.params = params;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.1860",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"@tscircuit/schematic-trace-solver": "^0.0.186",
|
|
60
60
|
"@tscircuit/solver-utils": "^0.0.16",
|
|
61
61
|
"@tscircuit/soup-util": "^0.0.41",
|
|
62
|
+
"@tscircuit/via-stitch-solver": "github:tscircuit/via-stitch-solver#75653b6440e75584b052f03595afd34283ae7558",
|
|
62
63
|
"@tscircuit/winding-breakout-point-solver": "github:tscircuit/winding-breakout-point-solver#202dbe9eb0088633b6fa094120dba6f2f41f8803",
|
|
63
64
|
"@types/bun": "^1.2.16",
|
|
64
65
|
"@types/debug": "^4.1.12",
|