@tscircuit/cli 0.1.1981 → 0.1.1982
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/cli/main.js +1319 -876
- package/dist/lib/index.js +1 -1
- package/package.json +2 -2
package/dist/cli/main.js
CHANGED
|
@@ -136163,7 +136163,7 @@ var package_default = {
|
|
|
136163
136163
|
"@tscircuit/check-shorts": "https://jscdn.tscircuit.com/@tscircuit/check-shorts/0.0.19.tgz",
|
|
136164
136164
|
"@tscircuit/circuit-json-placement-analysis": "^0.0.9",
|
|
136165
136165
|
"@tscircuit/circuit-json-routing-analysis": "^0.0.8",
|
|
136166
|
-
"@tscircuit/circuit-json-schematic-placement-analysis": "github:tscircuit/circuit-json-schematic-placement-analysis#
|
|
136166
|
+
"@tscircuit/circuit-json-schematic-placement-analysis": "github:tscircuit/circuit-json-schematic-placement-analysis#db2cd1ca2ce51490e3230e53c3d7b361eeb14446",
|
|
136167
136167
|
"@tscircuit/circuit-json-util": "^0.0.105",
|
|
136168
136168
|
"@tscircuit/eval": "^0.0.1016",
|
|
136169
136169
|
"@tscircuit/fake-snippets": "^0.0.182",
|
|
@@ -301798,118 +301798,27 @@ var registerCheckRoutingDifficulty = (program2) => {
|
|
|
301798
301798
|
});
|
|
301799
301799
|
};
|
|
301800
301800
|
|
|
301801
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/
|
|
301802
|
-
import {
|
|
301803
|
-
BasePipelineSolver as BasePipelineSolver2,
|
|
301804
|
-
definePipelineStep as definePipelineStep3
|
|
301805
|
-
} from "@tscircuit/solver-utils";
|
|
301806
|
-
|
|
301807
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/placements.ts
|
|
301808
|
-
import { cju as cju7 } from "@tscircuit/circuit-json-util";
|
|
301809
|
-
|
|
301810
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/schematic-sheets.ts
|
|
301811
|
-
var getSchematicSheetNamesById = (circuitJson) => {
|
|
301812
|
-
const map = new Map;
|
|
301813
|
-
for (const element of circuitJson) {
|
|
301814
|
-
if (element.type === "schematic_sheet" && element.name) {
|
|
301815
|
-
map.set(element.schematic_sheet_id, element.name);
|
|
301816
|
-
}
|
|
301817
|
-
}
|
|
301818
|
-
return map;
|
|
301819
|
-
};
|
|
301801
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/CapacitorOrientationSolver/CapacitorOrientationSolver.ts
|
|
301802
|
+
import { BaseSolver as BaseSolver5 } from "@tscircuit/solver-utils";
|
|
301820
301803
|
|
|
301821
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/
|
|
301822
|
-
var
|
|
301823
|
-
|
|
301824
|
-
|
|
301825
|
-
|
|
301826
|
-
if (!sourceComponentId)
|
|
301827
|
-
return;
|
|
301828
|
-
return cju7(circuitJson).source_component.get(sourceComponentId)?.name;
|
|
301829
|
-
};
|
|
301830
|
-
var getSourceComponentMetadata = (schematicBox, circuitJson) => {
|
|
301831
|
-
if (!schematicBox.schematic_component_id)
|
|
301832
|
-
return {};
|
|
301833
|
-
const util2 = cju7(circuitJson);
|
|
301834
|
-
const sc2 = util2.schematic_component.get(schematicBox.schematic_component_id);
|
|
301835
|
-
if (!sc2?.source_component_id)
|
|
301836
|
-
return {};
|
|
301837
|
-
const sourceComponent = util2.source_component.get(sc2.source_component_id);
|
|
301838
|
-
return {
|
|
301839
|
-
sourceComponentId: sc2.source_component_id,
|
|
301840
|
-
sourceComponentName: sourceComponent?.name
|
|
301841
|
-
};
|
|
301842
|
-
};
|
|
301843
|
-
var schematicComponentToPlacement = (schematicComponent, circuitJson, schematicSheetNameById, schematicBox) => {
|
|
301844
|
-
let schematicSheetId = getSchematicSheetId(schematicComponent);
|
|
301845
|
-
if (schematicSheetId === undefined && schematicBox) {
|
|
301846
|
-
schematicSheetId = getSchematicSheetId(schematicBox);
|
|
301847
|
-
}
|
|
301848
|
-
let schematicSheetName;
|
|
301849
|
-
if (schematicSheetId) {
|
|
301850
|
-
schematicSheetName = schematicSheetNameById.get(schematicSheetId);
|
|
301851
|
-
}
|
|
301852
|
-
const placement = {
|
|
301853
|
-
lineItemType: "SchematicBoxPlacement",
|
|
301854
|
-
positionAnchor: "center",
|
|
301855
|
-
schX: schematicComponent.center.x,
|
|
301856
|
-
schY: schematicComponent.center.y,
|
|
301857
|
-
width: schematicBox?.width ?? schematicComponent.size.width,
|
|
301858
|
-
height: schematicBox?.height ?? schematicComponent.size.height,
|
|
301859
|
-
sourceComponentId: schematicComponent.source_component_id,
|
|
301860
|
-
sourceComponentName: getSourceComponentName(circuitJson, schematicComponent.source_component_id),
|
|
301861
|
-
schematicComponentId: schematicComponent.schematic_component_id,
|
|
301862
|
-
schematicSymbolId: schematicBox?.schematic_symbol_id ?? schematicComponent.schematic_symbol_id,
|
|
301863
|
-
subcircuitId: schematicComponent.subcircuit_id ?? schematicBox?.subcircuit_id
|
|
301864
|
-
};
|
|
301865
|
-
if (schematicSheetId) {
|
|
301866
|
-
placement.schematicSheetId = schematicSheetId;
|
|
301867
|
-
}
|
|
301868
|
-
if (schematicSheetName) {
|
|
301869
|
-
placement.schematicSheetName = schematicSheetName;
|
|
301870
|
-
}
|
|
301871
|
-
return placement;
|
|
301804
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/format.ts
|
|
301805
|
+
var fmtNumber5 = (value) => {
|
|
301806
|
+
if (Number.isInteger(value))
|
|
301807
|
+
return String(value);
|
|
301808
|
+
return value.toFixed(3).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
|
|
301872
301809
|
};
|
|
301873
|
-
var
|
|
301874
|
-
const
|
|
301875
|
-
|
|
301876
|
-
if (schematicSheetId) {
|
|
301877
|
-
schematicSheetName = schematicSheetNameById.get(schematicSheetId);
|
|
301878
|
-
}
|
|
301879
|
-
const placement = {
|
|
301880
|
-
lineItemType: "SchematicBoxPlacement",
|
|
301881
|
-
positionAnchor: "center",
|
|
301882
|
-
schX: schematicBox.x,
|
|
301883
|
-
schY: schematicBox.y,
|
|
301884
|
-
width: schematicBox.width,
|
|
301885
|
-
height: schematicBox.height,
|
|
301886
|
-
...getSourceComponentMetadata(schematicBox, circuitJson),
|
|
301887
|
-
schematicComponentId: schematicBox.schematic_component_id,
|
|
301888
|
-
schematicSymbolId: schematicBox.schematic_symbol_id,
|
|
301889
|
-
subcircuitId: schematicBox.subcircuit_id
|
|
301890
|
-
};
|
|
301891
|
-
if (schematicSheetId) {
|
|
301892
|
-
placement.schematicSheetId = schematicSheetId;
|
|
301893
|
-
}
|
|
301894
|
-
if (schematicSheetName) {
|
|
301895
|
-
placement.schematicSheetName = schematicSheetName;
|
|
301896
|
-
}
|
|
301897
|
-
return placement;
|
|
301810
|
+
var fmtDelta = (value) => {
|
|
301811
|
+
const formatted = fmtNumber5(value);
|
|
301812
|
+
return value > 0 ? `+${formatted}` : formatted;
|
|
301898
301813
|
};
|
|
301899
|
-
var
|
|
301900
|
-
|
|
301901
|
-
|
|
301902
|
-
|
|
301903
|
-
const
|
|
301904
|
-
|
|
301905
|
-
...schematicBoxes.filter((sb3) => !sb3.schematic_component_id || !schematicComponentIds.has(sb3.schematic_component_id)).map((sb3) => schematicBoxToPlacement(sb3, circuitJson, schematicSheetNameById))
|
|
301906
|
-
];
|
|
301907
|
-
return { circuitJson, componentPlacements };
|
|
301814
|
+
var escapeAttr3 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
301815
|
+
var addAttr = (attrs, key, value, options) => {
|
|
301816
|
+
if (value === undefined)
|
|
301817
|
+
return;
|
|
301818
|
+
const stringValue = typeof value === "number" ? options?.formatDelta ? fmtDelta(value) : fmtNumber5(value) : options?.escape === false ? value : escapeAttr3(value);
|
|
301819
|
+
attrs.push(`${key}="${stringValue}"`);
|
|
301908
301820
|
};
|
|
301909
301821
|
|
|
301910
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/CapacitorOrientationSolver/CapacitorOrientationSolver.ts
|
|
301911
|
-
import { BaseSolver as BaseSolver5 } from "@tscircuit/solver-utils";
|
|
301912
|
-
|
|
301913
301822
|
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/graphics.ts
|
|
301914
301823
|
import { mergeGraphics as mergeGraphics2 } from "graphics-debug";
|
|
301915
301824
|
function mergeGraphicsObjects2(objects) {
|
|
@@ -301942,24 +301851,6 @@ function visualizeCircuitJson(circuitJson) {
|
|
|
301942
301851
|
return { rects };
|
|
301943
301852
|
}
|
|
301944
301853
|
|
|
301945
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/format.ts
|
|
301946
|
-
var fmtNumber5 = (value) => {
|
|
301947
|
-
if (Number.isInteger(value))
|
|
301948
|
-
return String(value);
|
|
301949
|
-
return value.toFixed(3).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
|
|
301950
|
-
};
|
|
301951
|
-
var fmtDelta = (value) => {
|
|
301952
|
-
const formatted = fmtNumber5(value);
|
|
301953
|
-
return value > 0 ? `+${formatted}` : formatted;
|
|
301954
|
-
};
|
|
301955
|
-
var escapeAttr3 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
301956
|
-
var addAttr = (attrs, key, value, options) => {
|
|
301957
|
-
if (value === undefined)
|
|
301958
|
-
return;
|
|
301959
|
-
const stringValue = typeof value === "number" ? options?.formatDelta ? fmtDelta(value) : fmtNumber5(value) : options?.escape === false ? value : escapeAttr3(value);
|
|
301960
|
-
attrs.push(`${key}="${stringValue}"`);
|
|
301961
|
-
};
|
|
301962
|
-
|
|
301963
301854
|
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/CapacitorOrientationSolver/CapacitorOrientationSolver.ts
|
|
301964
301855
|
class CapacitorOrientationSolver extends BaseSolver5 {
|
|
301965
301856
|
static ORIENTATION_MESSAGE = 'Use schOrientation="vertical" on this capacitor to fix the symbol orientation';
|
|
@@ -301993,7 +301884,9 @@ class CapacitorOrientationSolver extends BaseSolver5 {
|
|
|
301993
301884
|
height: p3.height,
|
|
301994
301885
|
sourceComponentId: p3.sourceComponentId,
|
|
301995
301886
|
sourceComponentName: p3.sourceComponentName,
|
|
301996
|
-
schematicComponentId: p3.schematicComponentId
|
|
301887
|
+
schematicComponentId: p3.schematicComponentId,
|
|
301888
|
+
schematicSheetId: p3.schematicSheetId,
|
|
301889
|
+
schematicSheetName: p3.schematicSheetName
|
|
301997
301890
|
}));
|
|
301998
301891
|
}
|
|
301999
301892
|
_step() {
|
|
@@ -302046,7 +301939,9 @@ class CapacitorOrientationSolver extends BaseSolver5 {
|
|
|
302046
301939
|
height: placement.height,
|
|
302047
301940
|
sourceComponentId: placement.sourceComponentId,
|
|
302048
301941
|
sourceComponentName: placement.sourceComponentName,
|
|
302049
|
-
schematicComponentId: placement.schematicComponentId
|
|
301942
|
+
schematicComponentId: placement.schematicComponentId,
|
|
301943
|
+
schematicSheetId: placement.schematicSheetId,
|
|
301944
|
+
schematicSheetName: placement.schematicSheetName
|
|
302050
301945
|
};
|
|
302051
301946
|
}
|
|
302052
301947
|
getIssueForPlacement(placement) {
|
|
@@ -302075,129 +301970,672 @@ class CapacitorOrientationSolver extends BaseSolver5 {
|
|
|
302075
301970
|
}
|
|
302076
301971
|
}
|
|
302077
301972
|
|
|
302078
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/
|
|
301973
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/ComponentNetLabelCollisionSolver/ComponentNetLabelCollisionSolver.ts
|
|
302079
301974
|
import { BaseSolver as BaseSolver7 } from "@tscircuit/solver-utils";
|
|
302080
|
-
|
|
301975
|
+
|
|
301976
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/geometry.ts
|
|
301977
|
+
function centeredRect(cx3, cy3, w4, h4) {
|
|
301978
|
+
return {
|
|
301979
|
+
left: cx3 - w4 / 2,
|
|
301980
|
+
right: cx3 + w4 / 2,
|
|
301981
|
+
top: cy3 + h4 / 2,
|
|
301982
|
+
bottom: cy3 - h4 / 2
|
|
301983
|
+
};
|
|
301984
|
+
}
|
|
301985
|
+
function rectOverlap(a2, b3) {
|
|
301986
|
+
const ow2 = Math.min(a2.right, b3.right) - Math.max(a2.left, b3.left);
|
|
301987
|
+
const oh2 = Math.min(a2.top, b3.top) - Math.max(a2.bottom, b3.bottom);
|
|
301988
|
+
return ow2 > 0 && oh2 > 0 ? { ow: ow2, oh: oh2 } : null;
|
|
301989
|
+
}
|
|
301990
|
+
|
|
301991
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/ComponentNetLabelCollisionSolver/ComponentNetLabelCollisionSolver.ts
|
|
301992
|
+
class ComponentNetLabelCollisionSolver extends BaseSolver7 {
|
|
302081
301993
|
params;
|
|
301994
|
+
LABEL_HALF_HEIGHT = 0.1;
|
|
301995
|
+
LABEL_BOUNDS_SLACK = 0.1;
|
|
301996
|
+
SCH_CHAR_WIDTH = 0.13;
|
|
302082
301997
|
placements;
|
|
301998
|
+
netLabelsByComponentId;
|
|
301999
|
+
rawCollisions = [];
|
|
302083
302000
|
firstIndex = 0;
|
|
302084
302001
|
secondIndex = 1;
|
|
302085
302002
|
constructor(params2) {
|
|
302086
302003
|
super();
|
|
302087
302004
|
this.params = params2;
|
|
302088
302005
|
this.placements = params2.ctx.componentPlacements;
|
|
302006
|
+
this.netLabelsByComponentId = this.buildNetLabelsByComponentId(params2.ctx.circuitJson);
|
|
302089
302007
|
this.solved = this.placements.length < 2;
|
|
302090
302008
|
}
|
|
302091
302009
|
_step() {
|
|
302092
302010
|
if (this.firstIndex >= this.placements.length - 1) {
|
|
302011
|
+
this.buildAndPushIssues();
|
|
302093
302012
|
this.solved = true;
|
|
302094
302013
|
return;
|
|
302095
302014
|
}
|
|
302096
|
-
const
|
|
302097
|
-
|
|
302098
|
-
|
|
302015
|
+
const compA = this.placements[this.firstIndex];
|
|
302016
|
+
const compB = this.placements[this.secondIndex];
|
|
302017
|
+
this.detectPair(compA, compB);
|
|
302099
302018
|
this.secondIndex++;
|
|
302100
302019
|
if (this.secondIndex >= this.placements.length) {
|
|
302101
302020
|
this.firstIndex++;
|
|
302102
302021
|
this.secondIndex = this.firstIndex + 1;
|
|
302103
302022
|
}
|
|
302104
|
-
this.solved = this.firstIndex >= this.placements.length - 1;
|
|
302105
302023
|
}
|
|
302106
|
-
|
|
302107
|
-
|
|
302108
|
-
|
|
302109
|
-
|
|
302110
|
-
|
|
302111
|
-
|
|
302024
|
+
detectPair(compA, compB) {
|
|
302025
|
+
if (compA.schematicSheetId !== compB.schematicSheetId)
|
|
302026
|
+
return;
|
|
302027
|
+
this.rawCollisions.push(...this.detectLabelLabel(compA, compB));
|
|
302028
|
+
this.rawCollisions.push(...this.detectBoxLabel(compA, compB));
|
|
302029
|
+
this.rawCollisions.push(...this.detectBoxLabel(compB, compA));
|
|
302030
|
+
}
|
|
302031
|
+
detectLabelLabel(firstComponent, secondComponent) {
|
|
302032
|
+
let leftComp = firstComponent;
|
|
302033
|
+
let rightComp = secondComponent;
|
|
302034
|
+
if (firstComponent.schX > secondComponent.schX) {
|
|
302035
|
+
leftComp = secondComponent;
|
|
302036
|
+
rightComp = firstComponent;
|
|
302037
|
+
}
|
|
302038
|
+
const leftId = leftComp.schematicComponentId;
|
|
302039
|
+
const rightId = rightComp.schematicComponentId;
|
|
302040
|
+
if (!leftId || !rightId)
|
|
302041
|
+
return [];
|
|
302042
|
+
const leftLabels = this.netLabelsByComponentId.get(leftId) ?? [];
|
|
302043
|
+
const rightLabels = this.netLabelsByComponentId.get(rightId) ?? [];
|
|
302044
|
+
if (leftLabels.length === 0 || rightLabels.length === 0)
|
|
302045
|
+
return [];
|
|
302046
|
+
const hits = [];
|
|
302047
|
+
for (const leftLabel of leftLabels) {
|
|
302048
|
+
for (const rightLabel of rightLabels) {
|
|
302049
|
+
const leftBounds = this.getNetLabelBounds(leftLabel);
|
|
302050
|
+
const rightBounds = this.getNetLabelBounds(rightLabel);
|
|
302051
|
+
if (rectOverlap(leftBounds, rightBounds)) {
|
|
302052
|
+
hits.push({
|
|
302053
|
+
type: "label-label",
|
|
302054
|
+
leftComp,
|
|
302055
|
+
rightComp,
|
|
302056
|
+
leftId,
|
|
302057
|
+
rightId,
|
|
302058
|
+
xSeparation: leftBounds.right - rightBounds.left + 0.1
|
|
302059
|
+
});
|
|
302060
|
+
}
|
|
302061
|
+
}
|
|
302062
|
+
}
|
|
302063
|
+
return hits;
|
|
302064
|
+
}
|
|
302065
|
+
detectBoxLabel(boxComp, labelComp) {
|
|
302066
|
+
const boxId = boxComp.schematicComponentId;
|
|
302067
|
+
const labelId = labelComp.schematicComponentId;
|
|
302068
|
+
if (!boxId || !labelId)
|
|
302069
|
+
return [];
|
|
302070
|
+
const labels = this.netLabelsByComponentId.get(labelId) ?? [];
|
|
302071
|
+
if (labels.length === 0)
|
|
302072
|
+
return [];
|
|
302073
|
+
const boxBounds = centeredRect(boxComp.schX, boxComp.schY, boxComp.width, boxComp.height);
|
|
302074
|
+
const boxIsLeft = boxComp.schX <= labelComp.schX;
|
|
302075
|
+
const hits = [];
|
|
302076
|
+
for (const label of labels) {
|
|
302077
|
+
const labelBounds = this.getNetLabelBounds(label);
|
|
302078
|
+
if (!rectOverlap(boxBounds, labelBounds))
|
|
302079
|
+
continue;
|
|
302080
|
+
let xSeparation;
|
|
302081
|
+
if (boxIsLeft) {
|
|
302082
|
+
xSeparation = boxBounds.right - labelBounds.left + 0.1;
|
|
302083
|
+
} else {
|
|
302084
|
+
xSeparation = labelBounds.right - boxBounds.left + 0.1;
|
|
302085
|
+
}
|
|
302086
|
+
hits.push({
|
|
302087
|
+
type: "box-label",
|
|
302088
|
+
boxComp,
|
|
302089
|
+
labelComp,
|
|
302090
|
+
boxId,
|
|
302091
|
+
labelId,
|
|
302092
|
+
xSeparation
|
|
302093
|
+
});
|
|
302094
|
+
}
|
|
302095
|
+
return hits;
|
|
302096
|
+
}
|
|
302097
|
+
buildAndPushIssues() {
|
|
302098
|
+
if (this.rawCollisions.length === 0)
|
|
302099
|
+
return;
|
|
302100
|
+
const collisionsBySheet = new Map;
|
|
302101
|
+
for (const collision of this.rawCollisions) {
|
|
302102
|
+
const placement = collision.type === "label-label" ? collision.leftComp : collision.boxComp;
|
|
302103
|
+
const sheetKey = placement.schematicSheetId ?? "";
|
|
302104
|
+
const sheetCollisions = collisionsBySheet.get(sheetKey);
|
|
302105
|
+
if (sheetCollisions)
|
|
302106
|
+
sheetCollisions.push(collision);
|
|
302107
|
+
else
|
|
302108
|
+
collisionsBySheet.set(sheetKey, [collision]);
|
|
302109
|
+
}
|
|
302110
|
+
for (const collisions of collisionsBySheet.values()) {
|
|
302111
|
+
this.buildAndPushIssueForSheet(collisions);
|
|
302112
|
+
}
|
|
302113
|
+
}
|
|
302114
|
+
buildAndPushIssueForSheet(collisions) {
|
|
302115
|
+
const globalFixes = this.computeGlobalFixes(collisions);
|
|
302116
|
+
if (globalFixes.size === 0)
|
|
302117
|
+
return;
|
|
302118
|
+
const seenPairs = new Set;
|
|
302119
|
+
const pairs3 = [];
|
|
302120
|
+
for (const collision of collisions) {
|
|
302121
|
+
let comp1Name;
|
|
302122
|
+
let comp2Name;
|
|
302123
|
+
if (collision.type === "label-label") {
|
|
302124
|
+
comp1Name = collision.leftComp.sourceComponentName ?? "";
|
|
302125
|
+
comp2Name = collision.rightComp.sourceComponentName ?? "";
|
|
302126
|
+
} else {
|
|
302127
|
+
comp1Name = collision.boxComp.sourceComponentName ?? "";
|
|
302128
|
+
comp2Name = collision.labelComp.sourceComponentName ?? "";
|
|
302129
|
+
}
|
|
302130
|
+
const key = `${comp1Name}/${comp2Name}`;
|
|
302131
|
+
if (!seenPairs.has(key)) {
|
|
302132
|
+
seenPairs.add(key);
|
|
302133
|
+
pairs3.push({ comp1Name, comp2Name });
|
|
302134
|
+
}
|
|
302135
|
+
}
|
|
302136
|
+
const firstCollision = collisions[0];
|
|
302137
|
+
const firstPlacement = firstCollision.type === "label-label" ? firstCollision.leftComp : firstCollision.boxComp;
|
|
302138
|
+
this.params.issues.push({
|
|
302139
|
+
lineItemType: "NetLabelCollision",
|
|
302140
|
+
schematicSheetId: firstPlacement.schematicSheetId,
|
|
302141
|
+
schematicSheetName: firstPlacement.schematicSheetName,
|
|
302142
|
+
pairs: pairs3,
|
|
302143
|
+
moves: Array.from(globalFixes.values())
|
|
302144
|
+
});
|
|
302145
|
+
}
|
|
302146
|
+
computeGlobalFixes(collisions) {
|
|
302147
|
+
const compById = new Map;
|
|
302148
|
+
for (const placement of this.placements) {
|
|
302149
|
+
if (placement.schematicComponentId)
|
|
302150
|
+
compById.set(placement.schematicComponentId, placement);
|
|
302151
|
+
}
|
|
302152
|
+
const constraintMap = new Map;
|
|
302153
|
+
const addConstraint = (leftId, rightId, xSeparation) => {
|
|
302154
|
+
const leftComp = compById.get(leftId);
|
|
302155
|
+
const rightComp = compById.get(rightId);
|
|
302156
|
+
if (!leftComp || !rightComp)
|
|
302157
|
+
return;
|
|
302158
|
+
const minSep = rightComp.schX - leftComp.schX + xSeparation;
|
|
302159
|
+
const key = `${leftId}|${rightId}`;
|
|
302160
|
+
constraintMap.set(key, Math.max(constraintMap.get(key) ?? 0, minSep));
|
|
302112
302161
|
};
|
|
302162
|
+
for (const collision of collisions) {
|
|
302163
|
+
if (collision.type === "label-label") {
|
|
302164
|
+
addConstraint(collision.leftId, collision.rightId, collision.xSeparation);
|
|
302165
|
+
} else if (collision.boxComp.schX <= collision.labelComp.schX) {
|
|
302166
|
+
addConstraint(collision.boxId, collision.labelId, collision.xSeparation);
|
|
302167
|
+
} else {
|
|
302168
|
+
addConstraint(collision.labelId, collision.boxId, collision.xSeparation);
|
|
302169
|
+
}
|
|
302170
|
+
}
|
|
302171
|
+
const constraints = [...constraintMap.entries()].map(([key, minSep]) => {
|
|
302172
|
+
const pipeIndex = key.indexOf("|");
|
|
302173
|
+
return {
|
|
302174
|
+
leftId: key.slice(0, pipeIndex),
|
|
302175
|
+
rightId: key.slice(pipeIndex + 1),
|
|
302176
|
+
minSep
|
|
302177
|
+
};
|
|
302178
|
+
});
|
|
302179
|
+
const allIds = new Set;
|
|
302180
|
+
const adjacency = new Map;
|
|
302181
|
+
for (const { leftId, rightId } of constraints) {
|
|
302182
|
+
allIds.add(leftId);
|
|
302183
|
+
allIds.add(rightId);
|
|
302184
|
+
if (!adjacency.has(leftId))
|
|
302185
|
+
adjacency.set(leftId, new Set);
|
|
302186
|
+
if (!adjacency.has(rightId))
|
|
302187
|
+
adjacency.set(rightId, new Set);
|
|
302188
|
+
adjacency.get(leftId).add(rightId);
|
|
302189
|
+
adjacency.get(rightId).add(leftId);
|
|
302190
|
+
}
|
|
302191
|
+
const visited = new Set;
|
|
302192
|
+
const result = new Map;
|
|
302193
|
+
for (const startId of allIds) {
|
|
302194
|
+
if (visited.has(startId))
|
|
302195
|
+
continue;
|
|
302196
|
+
const group = [];
|
|
302197
|
+
const queue = [startId];
|
|
302198
|
+
visited.add(startId);
|
|
302199
|
+
while (queue.length) {
|
|
302200
|
+
const compId = queue.shift();
|
|
302201
|
+
group.push(compId);
|
|
302202
|
+
for (const neighbor of adjacency.get(compId) ?? []) {
|
|
302203
|
+
if (!visited.has(neighbor)) {
|
|
302204
|
+
visited.add(neighbor);
|
|
302205
|
+
queue.push(neighbor);
|
|
302206
|
+
}
|
|
302207
|
+
}
|
|
302208
|
+
}
|
|
302209
|
+
group.sort((idA, idB) => (compById.get(idA)?.schX ?? 0) - (compById.get(idB)?.schX ?? 0));
|
|
302210
|
+
const groupIds = new Set(group);
|
|
302211
|
+
const groupConstraints = constraints.filter((c3) => groupIds.has(c3.leftId) && groupIds.has(c3.rightId));
|
|
302212
|
+
const assigned = new Map;
|
|
302213
|
+
for (const compId of group) {
|
|
302214
|
+
let newX = compById.get(compId)?.schX ?? 0;
|
|
302215
|
+
for (const { leftId, rightId, minSep } of groupConstraints) {
|
|
302216
|
+
if (rightId === compId && assigned.has(leftId)) {
|
|
302217
|
+
newX = Math.max(newX, assigned.get(leftId) + minSep);
|
|
302218
|
+
}
|
|
302219
|
+
}
|
|
302220
|
+
assigned.set(compId, newX);
|
|
302221
|
+
}
|
|
302222
|
+
const totalPush = [...assigned.entries()].reduce((sum, [compId, newX]) => sum + (newX - (compById.get(compId)?.schX ?? 0)), 0);
|
|
302223
|
+
if (totalPush > 0.000000001) {
|
|
302224
|
+
const pullBack = totalPush / group.length;
|
|
302225
|
+
const shifted = new Map;
|
|
302226
|
+
for (const compId of group) {
|
|
302227
|
+
let newX = (compById.get(compId)?.schX ?? 0) - pullBack;
|
|
302228
|
+
for (const { leftId, rightId, minSep } of groupConstraints) {
|
|
302229
|
+
if (rightId === compId && shifted.has(leftId)) {
|
|
302230
|
+
newX = Math.max(newX, shifted.get(leftId) + minSep);
|
|
302231
|
+
}
|
|
302232
|
+
}
|
|
302233
|
+
shifted.set(compId, newX);
|
|
302234
|
+
}
|
|
302235
|
+
const maxDisplacement = (positions) => [...positions.entries()].reduce((currentMax, [compId, newX]) => Math.max(currentMax, Math.abs(newX - (compById.get(compId)?.schX ?? 0))), 0);
|
|
302236
|
+
if (maxDisplacement(shifted) < maxDisplacement(assigned)) {
|
|
302237
|
+
for (const [compId, newX] of shifted)
|
|
302238
|
+
assigned.set(compId, newX);
|
|
302239
|
+
}
|
|
302240
|
+
}
|
|
302241
|
+
for (const [compId, newX] of assigned) {
|
|
302242
|
+
const comp = compById.get(compId);
|
|
302243
|
+
if (!comp || Math.abs(newX - comp.schX) < 0.000000001)
|
|
302244
|
+
continue;
|
|
302245
|
+
result.set(compId, {
|
|
302246
|
+
componentName: comp.sourceComponentName ?? compId,
|
|
302247
|
+
newSchX: Math.round(newX * 100) / 100,
|
|
302248
|
+
newSchY: Math.round(comp.schY * 100) / 100
|
|
302249
|
+
});
|
|
302250
|
+
}
|
|
302251
|
+
}
|
|
302252
|
+
return result;
|
|
302113
302253
|
}
|
|
302114
|
-
|
|
302115
|
-
|
|
302116
|
-
|
|
302117
|
-
|
|
302118
|
-
|
|
302119
|
-
|
|
302120
|
-
|
|
302121
|
-
|
|
302122
|
-
|
|
302123
|
-
|
|
302124
|
-
|
|
302125
|
-
|
|
302126
|
-
|
|
302127
|
-
|
|
302128
|
-
|
|
302129
|
-
|
|
302130
|
-
|
|
302131
|
-
|
|
302132
|
-
|
|
302133
|
-
|
|
302134
|
-
|
|
302135
|
-
|
|
302136
|
-
|
|
302137
|
-
|
|
302138
|
-
|
|
302139
|
-
|
|
302254
|
+
buildNetLabelsByComponentId(circuitJson) {
|
|
302255
|
+
const MATCH_EPSILON = 0.0001;
|
|
302256
|
+
const portPositions = [];
|
|
302257
|
+
for (const element of circuitJson) {
|
|
302258
|
+
if (element.type !== "schematic_port")
|
|
302259
|
+
continue;
|
|
302260
|
+
const port = element;
|
|
302261
|
+
if (!port.schematic_component_id)
|
|
302262
|
+
continue;
|
|
302263
|
+
portPositions.push({
|
|
302264
|
+
componentId: port.schematic_component_id,
|
|
302265
|
+
cx: port.center.x,
|
|
302266
|
+
cy: port.center.y,
|
|
302267
|
+
schematicSheetId: port.schematic_sheet_id
|
|
302268
|
+
});
|
|
302269
|
+
}
|
|
302270
|
+
const result = new Map;
|
|
302271
|
+
for (const element of circuitJson) {
|
|
302272
|
+
if (element.type !== "schematic_net_label")
|
|
302273
|
+
continue;
|
|
302274
|
+
const label = element;
|
|
302275
|
+
if (!label.anchor_position)
|
|
302276
|
+
continue;
|
|
302277
|
+
const { x: anchorX, y: anchorY } = label.anchor_position;
|
|
302278
|
+
const matches = portPositions.filter((port) => port.schematicSheetId === label.schematic_sheet_id && Math.hypot(port.cx - anchorX, port.cy - anchorY) < MATCH_EPSILON);
|
|
302279
|
+
if (matches.length === 0)
|
|
302280
|
+
continue;
|
|
302281
|
+
const componentIds = new Set(matches.map((match) => match.componentId));
|
|
302282
|
+
if (componentIds.size !== 1)
|
|
302283
|
+
continue;
|
|
302284
|
+
const componentId = matches[0].componentId;
|
|
302285
|
+
const labels = result.get(componentId) ?? [];
|
|
302286
|
+
labels.push(label);
|
|
302287
|
+
result.set(componentId, labels);
|
|
302288
|
+
}
|
|
302289
|
+
return result;
|
|
302290
|
+
}
|
|
302291
|
+
getNetLabelBounds(label) {
|
|
302292
|
+
const anchorSide = label.anchor_side;
|
|
302293
|
+
const isVertical3 = anchorSide === "top" || anchorSide === "bottom";
|
|
302294
|
+
if (isVertical3) {
|
|
302295
|
+
const anchorY = label.anchor_position?.y ?? label.center.y;
|
|
302296
|
+
const textHalfExtent = (label.text?.length ?? 8) * this.SCH_CHAR_WIDTH / 2 + this.LABEL_BOUNDS_SLACK;
|
|
302297
|
+
const left = label.center.x - this.LABEL_HALF_HEIGHT;
|
|
302298
|
+
const right = label.center.x + this.LABEL_HALF_HEIGHT;
|
|
302299
|
+
if (anchorSide === "top") {
|
|
302300
|
+
return {
|
|
302301
|
+
left,
|
|
302302
|
+
right,
|
|
302303
|
+
top: anchorY,
|
|
302304
|
+
bottom: anchorY - textHalfExtent * 2
|
|
302305
|
+
};
|
|
302140
302306
|
}
|
|
302141
|
-
|
|
302307
|
+
return { left, right, top: anchorY + textHalfExtent * 2, bottom: anchorY };
|
|
302308
|
+
}
|
|
302309
|
+
const anchorX = label.anchor_position?.x ?? label.center.x;
|
|
302310
|
+
const halfWidth = Math.abs(label.center.x - anchorX);
|
|
302311
|
+
const farHalfWidth = halfWidth + this.LABEL_BOUNDS_SLACK;
|
|
302312
|
+
const top = label.center.y + this.LABEL_HALF_HEIGHT;
|
|
302313
|
+
const bottom = label.center.y - this.LABEL_HALF_HEIGHT;
|
|
302314
|
+
if (label.center.x >= anchorX) {
|
|
302315
|
+
return {
|
|
302316
|
+
left: anchorX,
|
|
302317
|
+
right: label.center.x + farHalfWidth,
|
|
302318
|
+
top,
|
|
302319
|
+
bottom
|
|
302320
|
+
};
|
|
302321
|
+
}
|
|
302322
|
+
return { left: label.center.x - farHalfWidth, right: anchorX, top, bottom };
|
|
302142
302323
|
}
|
|
302143
|
-
static
|
|
302144
|
-
const
|
|
302145
|
-
|
|
302146
|
-
addAttr(attrs, "component2Name", issue.secondComponent.sourceComponentName);
|
|
302147
|
-
addAttr(attrs, "component1SchX", issue.firstComponent.schX);
|
|
302148
|
-
addAttr(attrs, "component1SchY", issue.firstComponent.schY);
|
|
302149
|
-
addAttr(attrs, "component2SchX", issue.secondComponent.schX);
|
|
302150
|
-
addAttr(attrs, "component2SchY", issue.secondComponent.schY);
|
|
302151
|
-
addAttr(attrs, "overlapWidth", issue.overlapWidth);
|
|
302152
|
-
addAttr(attrs, "overlapHeight", issue.overlapHeight);
|
|
302324
|
+
static netLabelCollisionToString(issue) {
|
|
302325
|
+
const pairAttrs = issue.pairs.map((pair, i2) => `pair${i2 + 1}="${pair.comp1Name}/${pair.comp2Name}"`).join(" ");
|
|
302326
|
+
const moves = issue.moves.map((move) => ` <Move componentName="${move.componentName}" newSchX="${move.newSchX}" newSchY="${move.newSchY}" />`);
|
|
302153
302327
|
return [
|
|
302154
|
-
`<
|
|
302155
|
-
|
|
302156
|
-
|
|
302328
|
+
`<ComponentNetLabelCollision ${pairAttrs}>`,
|
|
302329
|
+
` <SuggestedFix note="Apply all moves simultaneously. Set schAutoLayoutEnabled on your circuit.">`,
|
|
302330
|
+
...moves,
|
|
302331
|
+
` </SuggestedFix>`,
|
|
302332
|
+
`</ComponentNetLabelCollision>`
|
|
302157
302333
|
].join(`
|
|
302158
302334
|
`);
|
|
302159
302335
|
}
|
|
302160
|
-
|
|
302161
|
-
|
|
302162
|
-
|
|
302163
|
-
|
|
302164
|
-
|
|
302165
|
-
|
|
302336
|
+
}
|
|
302337
|
+
|
|
302338
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/ComponentPinAlignmentSolver/ComponentPinAlignmentSolver.ts
|
|
302339
|
+
import { BaseSolver as BaseSolver8 } from "@tscircuit/solver-utils";
|
|
302340
|
+
class ComponentPinAlignmentSolver extends BaseSolver8 {
|
|
302341
|
+
static ALIGNMENT_EPSILON = 0.01;
|
|
302342
|
+
ctx;
|
|
302343
|
+
out;
|
|
302344
|
+
constructor({
|
|
302345
|
+
ctx,
|
|
302346
|
+
issues
|
|
302347
|
+
}) {
|
|
302348
|
+
super();
|
|
302349
|
+
this.ctx = ctx;
|
|
302350
|
+
this.out = issues;
|
|
302351
|
+
}
|
|
302352
|
+
_step() {
|
|
302353
|
+
const portsById = new Map(this.ctx.circuitJson.filter((el3) => el3.type === "schematic_port").map((port) => [port.schematic_port_id, port]));
|
|
302354
|
+
const placementBySchematicComponentId = new Map(this.ctx.componentPlacements.flatMap((placement) => placement.schematicComponentId ? [[placement.schematicComponentId, placement]] : []));
|
|
302355
|
+
const pinPairsByComponentPair = new Map;
|
|
302356
|
+
for (const trace of this.ctx.circuitJson.filter((el3) => el3.type === "schematic_trace")) {
|
|
302357
|
+
const pinPair = this.getTracePinPair(trace, portsById);
|
|
302358
|
+
if (!pinPair)
|
|
302359
|
+
continue;
|
|
302360
|
+
const firstComponentId = pinPair.firstPort.schematic_component_id;
|
|
302361
|
+
const secondComponentId = pinPair.secondPort.schematic_component_id;
|
|
302362
|
+
if (!firstComponentId || !secondComponentId || firstComponentId === secondComponentId) {
|
|
302363
|
+
continue;
|
|
302364
|
+
}
|
|
302365
|
+
const orderedPair = firstComponentId < secondComponentId ? pinPair : {
|
|
302366
|
+
firstPort: pinPair.secondPort,
|
|
302367
|
+
secondPort: pinPair.firstPort
|
|
302368
|
+
};
|
|
302369
|
+
const key = [firstComponentId, secondComponentId].sort().join("\x00");
|
|
302370
|
+
const pairs3 = pinPairsByComponentPair.get(key) ?? [];
|
|
302371
|
+
const isDuplicate = pairs3.some((pair) => pair.firstPort.schematic_port_id === orderedPair.firstPort.schematic_port_id && pair.secondPort.schematic_port_id === orderedPair.secondPort.schematic_port_id);
|
|
302372
|
+
if (!isDuplicate)
|
|
302373
|
+
pairs3.push(orderedPair);
|
|
302374
|
+
pinPairsByComponentPair.set(key, pairs3);
|
|
302166
302375
|
}
|
|
302167
|
-
|
|
302168
|
-
|
|
302169
|
-
|
|
302376
|
+
for (const pinPairs of pinPairsByComponentPair.values()) {
|
|
302377
|
+
const issue = this.findVerticalShiftIssue(pinPairs, placementBySchematicComponentId);
|
|
302378
|
+
if (issue)
|
|
302379
|
+
this.out.push(issue);
|
|
302170
302380
|
}
|
|
302171
|
-
|
|
302381
|
+
this.solved = true;
|
|
302172
302382
|
}
|
|
302173
|
-
|
|
302174
|
-
|
|
302175
|
-
|
|
302176
|
-
|
|
302177
|
-
|
|
302178
|
-
const
|
|
302179
|
-
const
|
|
302180
|
-
|
|
302181
|
-
|
|
302383
|
+
getTracePinPair(trace, portsById) {
|
|
302384
|
+
const firstEdge = trace.edges?.[0];
|
|
302385
|
+
const lastEdge = trace.edges?.at(-1);
|
|
302386
|
+
if (!firstEdge || !lastEdge)
|
|
302387
|
+
return;
|
|
302388
|
+
const firstPortId = firstEdge.from_schematic_port_id ?? firstEdge.to_schematic_port_id;
|
|
302389
|
+
const secondPortId = lastEdge.to_schematic_port_id ?? lastEdge.from_schematic_port_id;
|
|
302390
|
+
const ports = [...portsById.values()];
|
|
302391
|
+
const firstPort = firstPortId ? portsById.get(firstPortId) : this.findPortAtPoint(ports, firstEdge.from, trace.schematic_sheet_id);
|
|
302392
|
+
const secondPort = secondPortId ? portsById.get(secondPortId) : this.findPortAtPoint(ports, lastEdge.to, trace.schematic_sheet_id);
|
|
302393
|
+
if (!firstPort || !secondPort)
|
|
302394
|
+
return;
|
|
302395
|
+
if (firstPort.schematic_port_id === secondPort.schematic_port_id)
|
|
302396
|
+
return;
|
|
302397
|
+
if (firstPort.schematic_sheet_id !== secondPort.schematic_sheet_id)
|
|
302398
|
+
return;
|
|
302399
|
+
return { firstPort, secondPort };
|
|
302400
|
+
}
|
|
302401
|
+
findPortAtPoint(ports, point9, schematicSheetId) {
|
|
302402
|
+
const { ALIGNMENT_EPSILON } = ComponentPinAlignmentSolver;
|
|
302403
|
+
return ports.find((port) => port.schematic_sheet_id === schematicSheetId && Math.abs(port.center.x - point9.x) <= ALIGNMENT_EPSILON && Math.abs(port.center.y - point9.y) <= ALIGNMENT_EPSILON);
|
|
302404
|
+
}
|
|
302405
|
+
findVerticalShiftIssue(pinPairs, placementBySchematicComponentId) {
|
|
302406
|
+
const firstPair = pinPairs[0];
|
|
302407
|
+
if (!firstPair)
|
|
302408
|
+
return;
|
|
302409
|
+
const firstPlacement = placementBySchematicComponentId.get(firstPair.firstPort.schematic_component_id);
|
|
302410
|
+
const secondPlacement = placementBySchematicComponentId.get(firstPair.secondPort.schematic_component_id);
|
|
302411
|
+
if (!firstPlacement || !secondPlacement)
|
|
302412
|
+
return;
|
|
302413
|
+
const [leftPlacement, rightPlacement] = firstPlacement.schX <= secondPlacement.schX ? [firstPlacement, secondPlacement] : [secondPlacement, firstPlacement];
|
|
302414
|
+
const horizontalPairs = pinPairs.flatMap((pair) => {
|
|
302415
|
+
const [leftPort, rightPort] = pair.firstPort.center.x <= pair.secondPort.center.x ? [pair.firstPort, pair.secondPort] : [pair.secondPort, pair.firstPort];
|
|
302416
|
+
return leftPort.facing_direction === "right" && rightPort.facing_direction === "left" ? [{ leftPort, rightPort }] : [];
|
|
302417
|
+
});
|
|
302418
|
+
if (horizontalPairs.length < 2)
|
|
302419
|
+
return;
|
|
302420
|
+
const { ALIGNMENT_EPSILON } = ComponentPinAlignmentSolver;
|
|
302421
|
+
const currentlyAlignedPinCount = horizontalPairs.filter(({ leftPort, rightPort }) => Math.abs(leftPort.center.y - rightPort.center.y) <= ALIGNMENT_EPSILON).length;
|
|
302422
|
+
const candidateGroups = [];
|
|
302423
|
+
for (const pair of horizontalPairs) {
|
|
302424
|
+
const deltaSchY2 = pair.leftPort.center.y - pair.rightPort.center.y;
|
|
302425
|
+
if (Math.abs(deltaSchY2) <= ALIGNMENT_EPSILON)
|
|
302426
|
+
continue;
|
|
302427
|
+
const group = candidateGroups.find((candidate) => Math.abs(candidate.deltaSchY - deltaSchY2) <= ALIGNMENT_EPSILON);
|
|
302428
|
+
if (group)
|
|
302429
|
+
group.pairs.push(pair);
|
|
302430
|
+
else
|
|
302431
|
+
candidateGroups.push({ deltaSchY: deltaSchY2, pairs: [pair] });
|
|
302432
|
+
}
|
|
302433
|
+
const bestCandidate = candidateGroups.sort((a2, b3) => b3.pairs.length - a2.pairs.length || Math.abs(a2.deltaSchY) - Math.abs(b3.deltaSchY))[0];
|
|
302434
|
+
if (!bestCandidate || bestCandidate.pairs.length < 2 || bestCandidate.pairs.length <= currentlyAlignedPinCount) {
|
|
302435
|
+
return;
|
|
302436
|
+
}
|
|
302437
|
+
const targetName = rightPlacement.sourceComponentName ?? rightPlacement.schematicComponentId ?? "component";
|
|
302438
|
+
const deltaSchY = bestCandidate.deltaSchY;
|
|
302439
|
+
const newSchY = rightPlacement.schY + deltaSchY;
|
|
302182
302440
|
return {
|
|
302183
|
-
lineItemType: "
|
|
302184
|
-
firstComponent,
|
|
302185
|
-
secondComponent,
|
|
302186
|
-
|
|
302187
|
-
|
|
302188
|
-
|
|
302189
|
-
|
|
302190
|
-
|
|
302191
|
-
|
|
302192
|
-
|
|
302193
|
-
|
|
302441
|
+
lineItemType: "ComponentPinsWouldAlignWithVerticalShift",
|
|
302442
|
+
firstComponent: leftPlacement,
|
|
302443
|
+
secondComponent: rightPlacement,
|
|
302444
|
+
targetComponent: rightPlacement,
|
|
302445
|
+
deltaSchY,
|
|
302446
|
+
newSchY,
|
|
302447
|
+
currentlyAlignedPinCount,
|
|
302448
|
+
alignedPinCount: bestCandidate.pairs.length,
|
|
302449
|
+
alignedPinPairs: bestCandidate.pairs.map(({ leftPort, rightPort }) => ({
|
|
302450
|
+
firstPin: this.getPinLabel(leftPort),
|
|
302451
|
+
secondPin: this.getPinLabel(rightPort)
|
|
302452
|
+
})),
|
|
302453
|
+
message: `shift ${targetName} vertically by ${fmtDelta(deltaSchY)} to align ${bestCandidate.pairs.length} connected pin pairs`
|
|
302194
302454
|
};
|
|
302195
302455
|
}
|
|
302456
|
+
getPinLabel(port) {
|
|
302457
|
+
return port.display_pin_label ?? port.pin_number?.toString();
|
|
302458
|
+
}
|
|
302459
|
+
static issueToString(issue) {
|
|
302460
|
+
const attrs = [];
|
|
302461
|
+
addAttr(attrs, "firstComponentName", issue.firstComponent.sourceComponentName);
|
|
302462
|
+
addAttr(attrs, "secondComponentName", issue.secondComponent.sourceComponentName);
|
|
302463
|
+
addAttr(attrs, "targetComponentName", issue.targetComponent.sourceComponentName);
|
|
302464
|
+
addAttr(attrs, "deltaSchY", issue.deltaSchY);
|
|
302465
|
+
addAttr(attrs, "newSchY", issue.newSchY);
|
|
302466
|
+
addAttr(attrs, "currentlyAlignedPinCount", issue.currentlyAlignedPinCount);
|
|
302467
|
+
addAttr(attrs, "alignedPinCount", issue.alignedPinCount);
|
|
302468
|
+
addAttr(attrs, "message", issue.message);
|
|
302469
|
+
return `<ComponentPinsWouldAlignWithVerticalShift ${attrs.join(" ")} />`;
|
|
302470
|
+
}
|
|
302471
|
+
}
|
|
302472
|
+
|
|
302473
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/DiodeResistorAlignmentSolver/DiodeResistorAlignmentSolver.ts
|
|
302474
|
+
import { BaseSolver as BaseSolver9 } from "@tscircuit/solver-utils";
|
|
302475
|
+
class DiodeResistorAlignmentSolver extends BaseSolver9 {
|
|
302476
|
+
static DIODE_FTYPES = new Set(["simple_led", "simple_diode"]);
|
|
302477
|
+
ctx;
|
|
302478
|
+
out;
|
|
302479
|
+
schematicTraces;
|
|
302480
|
+
currentIndex = 0;
|
|
302481
|
+
sourceComponentFtypeById;
|
|
302482
|
+
sourceComponentIdBySourcePortId;
|
|
302483
|
+
schematicPorts;
|
|
302484
|
+
schematicBoxBySourceComponentId;
|
|
302485
|
+
constructor({
|
|
302486
|
+
ctx,
|
|
302487
|
+
issues
|
|
302488
|
+
}) {
|
|
302489
|
+
super();
|
|
302490
|
+
this.ctx = ctx;
|
|
302491
|
+
this.out = issues;
|
|
302492
|
+
const { circuitJson } = ctx;
|
|
302493
|
+
this.sourceComponentFtypeById = this.buildSourceComponentFtypeById(circuitJson);
|
|
302494
|
+
this.sourceComponentIdBySourcePortId = this.buildSourceComponentIdBySourcePortId(circuitJson);
|
|
302495
|
+
this.schematicPorts = circuitJson.filter((el3) => el3.type === "schematic_port");
|
|
302496
|
+
this.schematicBoxBySourceComponentId = this.buildSchematicBoxBySourceComponentId();
|
|
302497
|
+
this.schematicTraces = circuitJson.filter((el3) => el3.type === "schematic_trace");
|
|
302498
|
+
this.solved = this.schematicTraces.length === 0;
|
|
302499
|
+
}
|
|
302500
|
+
_step() {
|
|
302501
|
+
const trace = this.schematicTraces[this.currentIndex];
|
|
302502
|
+
if (!trace) {
|
|
302503
|
+
this.solved = true;
|
|
302504
|
+
return;
|
|
302505
|
+
}
|
|
302506
|
+
this.currentIndex++;
|
|
302507
|
+
this.solved = this.currentIndex >= this.schematicTraces.length;
|
|
302508
|
+
if (!trace.edges || trace.edges.length === 0)
|
|
302509
|
+
return;
|
|
302510
|
+
const firstEdge = trace.edges[0];
|
|
302511
|
+
const lastEdge = trace.edges[trace.edges.length - 1];
|
|
302512
|
+
if (!firstEdge || !lastEdge)
|
|
302513
|
+
return;
|
|
302514
|
+
const start = firstEdge.from;
|
|
302515
|
+
const end = lastEdge.to;
|
|
302516
|
+
const schematicSheetId = trace.schematic_sheet_id;
|
|
302517
|
+
const startSourceCompId = this.findSourceComponentIdNearPoint(start, schematicSheetId);
|
|
302518
|
+
const endSourceCompId = this.findSourceComponentIdNearPoint(end, schematicSheetId);
|
|
302519
|
+
if (!startSourceCompId || !endSourceCompId)
|
|
302520
|
+
return;
|
|
302521
|
+
const startFtype = this.sourceComponentFtypeById.get(startSourceCompId);
|
|
302522
|
+
const endFtype = this.sourceComponentFtypeById.get(endSourceCompId);
|
|
302523
|
+
const { DIODE_FTYPES } = DiodeResistorAlignmentSolver;
|
|
302524
|
+
const isDiodeResistorPair = DIODE_FTYPES.has(startFtype) && endFtype === "simple_resistor" || startFtype === "simple_resistor" && DIODE_FTYPES.has(endFtype);
|
|
302525
|
+
if (!isDiodeResistorPair)
|
|
302526
|
+
return;
|
|
302527
|
+
const diodeCompId = DIODE_FTYPES.has(startFtype) ? startSourceCompId : endSourceCompId;
|
|
302528
|
+
const resistorCompId = startFtype === "simple_resistor" ? startSourceCompId : endSourceCompId;
|
|
302529
|
+
const diodeBox = this.schematicBoxBySourceComponentId.get(diodeCompId);
|
|
302530
|
+
const resistorBox = this.schematicBoxBySourceComponentId.get(resistorCompId);
|
|
302531
|
+
if (!diodeBox || !resistorBox)
|
|
302532
|
+
return;
|
|
302533
|
+
const diodePort = this.findNearestPort(DIODE_FTYPES.has(startFtype) ? start : end, schematicSheetId);
|
|
302534
|
+
const resistorPort = this.findNearestPort(startFtype === "simple_resistor" ? start : end, schematicSheetId);
|
|
302535
|
+
if (!diodePort?.center || !resistorPort?.center)
|
|
302536
|
+
return;
|
|
302537
|
+
const diodeName = diodeBox.sourceComponentName ?? diodeCompId;
|
|
302538
|
+
const resistorName = resistorBox.sourceComponentName ?? resistorCompId;
|
|
302539
|
+
const diodePin = diodePort?.display_pin_label ?? diodePort?.pin_number?.toString();
|
|
302540
|
+
const resistorPin = resistorPort?.display_pin_label ?? resistorPort?.pin_number?.toString();
|
|
302541
|
+
const diodeFacing = diodePort?.facing_direction;
|
|
302542
|
+
const resistorFacing = resistorPort?.facing_direction;
|
|
302543
|
+
const diodePinDesc = diodePin ? `${diodeName}.${diodePin}` : diodeName;
|
|
302544
|
+
const resistorPinDesc = resistorPin ? `${resistorName}.${resistorPin}` : resistorName;
|
|
302545
|
+
const makeIssue2 = (message) => ({
|
|
302546
|
+
lineItemType: "DiodeResistorNotAligned",
|
|
302547
|
+
diodeSchematicBox: diodeBox,
|
|
302548
|
+
resistorSchematicBox: resistorBox,
|
|
302549
|
+
diodePin,
|
|
302550
|
+
resistorPin,
|
|
302551
|
+
diodePinFacingDirection: diodeFacing,
|
|
302552
|
+
resistorPinFacingDirection: resistorFacing,
|
|
302553
|
+
message
|
|
302554
|
+
});
|
|
302555
|
+
if (!DiodeResistorAlignmentSolver.isCoLinear(diodePort.center, resistorPort.center)) {
|
|
302556
|
+
this.out.push(makeIssue2(`trace has corners — align ${diodeName} and ${resistorName} on same axis and rotate so ${diodePinDesc} faces ${resistorPinDesc}`));
|
|
302557
|
+
return;
|
|
302558
|
+
}
|
|
302559
|
+
if (diodeFacing && resistorFacing && !DiodeResistorAlignmentSolver.pinsFacingEachOther(diodePort.center, diodeFacing, resistorPort.center, resistorFacing)) {
|
|
302560
|
+
this.out.push(makeIssue2(`${diodePinDesc} and ${resistorPinDesc} face away from each other — rotate ${diodeName} so ${diodePinDesc} faces ${resistorPinDesc}`));
|
|
302561
|
+
}
|
|
302562
|
+
}
|
|
302563
|
+
static isCoLinear(a2, b3, epsilon = 0.01) {
|
|
302564
|
+
return Math.abs(a2.x - b3.x) < epsilon || Math.abs(a2.y - b3.y) < epsilon;
|
|
302565
|
+
}
|
|
302566
|
+
static pinsFacingEachOther(aCenter, aFacing, bCenter, bFacing) {
|
|
302567
|
+
const dx3 = bCenter.x - aCenter.x;
|
|
302568
|
+
const dy3 = bCenter.y - aCenter.y;
|
|
302569
|
+
const aToward = aFacing === "right" && dx3 > 0 || aFacing === "left" && dx3 < 0 || aFacing === "up" && dy3 > 0 || aFacing === "down" && dy3 < 0;
|
|
302570
|
+
const bToward = bFacing === "right" && dx3 < 0 || bFacing === "left" && dx3 > 0 || bFacing === "up" && dy3 < 0 || bFacing === "down" && dy3 > 0;
|
|
302571
|
+
return aToward && bToward;
|
|
302572
|
+
}
|
|
302573
|
+
static dist(a2, b3) {
|
|
302574
|
+
return Math.sqrt((a2.x - b3.x) ** 2 + (a2.y - b3.y) ** 2);
|
|
302575
|
+
}
|
|
302576
|
+
findNearestPort(point9, schematicSheetId) {
|
|
302577
|
+
let nearest;
|
|
302578
|
+
let minDist = Infinity;
|
|
302579
|
+
for (const port of this.schematicPorts) {
|
|
302580
|
+
if (port.schematic_sheet_id !== schematicSheetId)
|
|
302581
|
+
continue;
|
|
302582
|
+
if (!port.center)
|
|
302583
|
+
continue;
|
|
302584
|
+
const d3 = DiodeResistorAlignmentSolver.dist(point9, port.center);
|
|
302585
|
+
if (d3 < minDist) {
|
|
302586
|
+
minDist = d3;
|
|
302587
|
+
nearest = port;
|
|
302588
|
+
}
|
|
302589
|
+
}
|
|
302590
|
+
return nearest;
|
|
302591
|
+
}
|
|
302592
|
+
findSourceComponentIdNearPoint(point9, schematicSheetId) {
|
|
302593
|
+
const nearest = this.findNearestPort(point9, schematicSheetId);
|
|
302594
|
+
if (!nearest || !nearest.source_port_id)
|
|
302595
|
+
return;
|
|
302596
|
+
return this.sourceComponentIdBySourcePortId.get(nearest.source_port_id);
|
|
302597
|
+
}
|
|
302598
|
+
buildSourceComponentFtypeById(circuitJson) {
|
|
302599
|
+
const map = new Map;
|
|
302600
|
+
for (const el3 of circuitJson) {
|
|
302601
|
+
if (el3.type === "source_component" && "source_component_id" in el3 && "ftype" in el3 && typeof el3.ftype === "string") {
|
|
302602
|
+
map.set(el3.source_component_id, el3.ftype);
|
|
302603
|
+
}
|
|
302604
|
+
}
|
|
302605
|
+
return map;
|
|
302606
|
+
}
|
|
302607
|
+
buildSourceComponentIdBySourcePortId(circuitJson) {
|
|
302608
|
+
const map = new Map;
|
|
302609
|
+
for (const el3 of circuitJson) {
|
|
302610
|
+
if (el3.type === "source_port" && "source_port_id" in el3 && "source_component_id" in el3 && typeof el3.source_port_id === "string" && typeof el3.source_component_id === "string") {
|
|
302611
|
+
map.set(el3.source_port_id, el3.source_component_id);
|
|
302612
|
+
}
|
|
302613
|
+
}
|
|
302614
|
+
return map;
|
|
302615
|
+
}
|
|
302616
|
+
buildSchematicBoxBySourceComponentId() {
|
|
302617
|
+
const map = new Map;
|
|
302618
|
+
for (const placement of this.ctx.componentPlacements) {
|
|
302619
|
+
if (placement.sourceComponentId) {
|
|
302620
|
+
map.set(placement.sourceComponentId, placement);
|
|
302621
|
+
}
|
|
302622
|
+
}
|
|
302623
|
+
return map;
|
|
302624
|
+
}
|
|
302625
|
+
static issueToString(issue) {
|
|
302626
|
+
const attrs = [];
|
|
302627
|
+
addAttr(attrs, "diodeComponentName", issue.diodeSchematicBox.sourceComponentName);
|
|
302628
|
+
addAttr(attrs, "diodePin", issue.diodePin);
|
|
302629
|
+
addAttr(attrs, "resistorComponentName", issue.resistorSchematicBox.sourceComponentName);
|
|
302630
|
+
addAttr(attrs, "resistorPin", issue.resistorPin);
|
|
302631
|
+
addAttr(attrs, "message", issue.message);
|
|
302632
|
+
return `<DiodeResistorNotAligned ${attrs.join(" ")} />`;
|
|
302633
|
+
}
|
|
302196
302634
|
}
|
|
302197
302635
|
|
|
302198
302636
|
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/SchematicBoxInnerLabelCollisionSolver/SchematicBoxInnerLabelCollisionSolver.ts
|
|
302199
|
-
import { BaseSolver as
|
|
302200
|
-
class SchematicBoxInnerLabelCollisionSolver extends
|
|
302637
|
+
import { BaseSolver as BaseSolver10 } from "@tscircuit/solver-utils";
|
|
302638
|
+
class SchematicBoxInnerLabelCollisionSolver extends BaseSolver10 {
|
|
302201
302639
|
params;
|
|
302202
302640
|
MESSAGE = "Inner labels are colliding. Increase the schWidth or schHeight.";
|
|
302203
302641
|
PIN_LABEL_EDGE_PADDING = 0.1;
|
|
@@ -302394,9 +302832,129 @@ class SchematicBoxInnerLabelCollisionSolver extends BaseSolver8 {
|
|
|
302394
302832
|
}
|
|
302395
302833
|
}
|
|
302396
302834
|
|
|
302835
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/SchematicBoxOverlapSolver/SchematicBoxOverlapSolver.ts
|
|
302836
|
+
import { BaseSolver as BaseSolver11 } from "@tscircuit/solver-utils";
|
|
302837
|
+
class SchematicBoxOverlapSolver extends BaseSolver11 {
|
|
302838
|
+
params;
|
|
302839
|
+
placements;
|
|
302840
|
+
firstIndex = 0;
|
|
302841
|
+
secondIndex = 1;
|
|
302842
|
+
constructor(params2) {
|
|
302843
|
+
super();
|
|
302844
|
+
this.params = params2;
|
|
302845
|
+
this.placements = params2.ctx.componentPlacements;
|
|
302846
|
+
this.solved = this.placements.length < 2;
|
|
302847
|
+
}
|
|
302848
|
+
_step() {
|
|
302849
|
+
if (this.firstIndex >= this.placements.length - 1) {
|
|
302850
|
+
this.solved = true;
|
|
302851
|
+
return;
|
|
302852
|
+
}
|
|
302853
|
+
const overlap = this.getComponentOverlap(this.placements[this.firstIndex], this.placements[this.secondIndex]);
|
|
302854
|
+
if (overlap)
|
|
302855
|
+
this.params.issues.push(overlap);
|
|
302856
|
+
this.secondIndex++;
|
|
302857
|
+
if (this.secondIndex >= this.placements.length) {
|
|
302858
|
+
this.firstIndex++;
|
|
302859
|
+
this.secondIndex = this.firstIndex + 1;
|
|
302860
|
+
}
|
|
302861
|
+
this.solved = this.firstIndex >= this.placements.length - 1;
|
|
302862
|
+
}
|
|
302863
|
+
getCenteredRectBounds(box2) {
|
|
302864
|
+
return {
|
|
302865
|
+
left: box2.schX - box2.width / 2,
|
|
302866
|
+
right: box2.schX + box2.width / 2,
|
|
302867
|
+
top: box2.schY - box2.height / 2,
|
|
302868
|
+
bottom: box2.schY + box2.height / 2
|
|
302869
|
+
};
|
|
302870
|
+
}
|
|
302871
|
+
getOverlapCorrectionSuggestions({
|
|
302872
|
+
firstComponent,
|
|
302873
|
+
secondComponent,
|
|
302874
|
+
overlapWidth,
|
|
302875
|
+
overlapHeight
|
|
302876
|
+
}) {
|
|
302877
|
+
const firstArea = firstComponent.width * firstComponent.height;
|
|
302878
|
+
const secondArea = secondComponent.width * secondComponent.height;
|
|
302879
|
+
const target = firstArea <= secondArea ? firstComponent : secondComponent;
|
|
302880
|
+
const other = target === firstComponent ? secondComponent : firstComponent;
|
|
302881
|
+
const deltaSchX = target.schX <= other.schX ? -overlapWidth : overlapWidth;
|
|
302882
|
+
const deltaSchY = target.schY <= other.schY ? -overlapHeight : overlapHeight;
|
|
302883
|
+
return [
|
|
302884
|
+
{
|
|
302885
|
+
targetComponentName: target.sourceComponentName,
|
|
302886
|
+
deltaSchX,
|
|
302887
|
+
deltaSchY: 0,
|
|
302888
|
+
newSchX: target.schX + deltaSchX,
|
|
302889
|
+
newSchY: target.schY
|
|
302890
|
+
},
|
|
302891
|
+
{
|
|
302892
|
+
targetComponentName: target.sourceComponentName,
|
|
302893
|
+
deltaSchX: 0,
|
|
302894
|
+
deltaSchY,
|
|
302895
|
+
newSchX: target.schX,
|
|
302896
|
+
newSchY: target.schY + deltaSchY
|
|
302897
|
+
}
|
|
302898
|
+
];
|
|
302899
|
+
}
|
|
302900
|
+
static issueToString(issue) {
|
|
302901
|
+
const attrs = [];
|
|
302902
|
+
addAttr(attrs, "component1Name", issue.firstComponent.sourceComponentName);
|
|
302903
|
+
addAttr(attrs, "component2Name", issue.secondComponent.sourceComponentName);
|
|
302904
|
+
addAttr(attrs, "component1SchX", issue.firstComponent.schX);
|
|
302905
|
+
addAttr(attrs, "component1SchY", issue.firstComponent.schY);
|
|
302906
|
+
addAttr(attrs, "component2SchX", issue.secondComponent.schX);
|
|
302907
|
+
addAttr(attrs, "component2SchY", issue.secondComponent.schY);
|
|
302908
|
+
addAttr(attrs, "overlapWidth", issue.overlapWidth);
|
|
302909
|
+
addAttr(attrs, "overlapHeight", issue.overlapHeight);
|
|
302910
|
+
return [
|
|
302911
|
+
`<ComponentOverlap ${attrs.join(" ")}>`,
|
|
302912
|
+
...issue.correctionSuggestions.map(SchematicBoxOverlapSolver.correctionSuggestionToString),
|
|
302913
|
+
"</ComponentOverlap>"
|
|
302914
|
+
].join(`
|
|
302915
|
+
`);
|
|
302916
|
+
}
|
|
302917
|
+
static correctionSuggestionToString(suggestion) {
|
|
302918
|
+
const attrs = [];
|
|
302919
|
+
addAttr(attrs, "target", suggestion.targetComponentName);
|
|
302920
|
+
if (suggestion.deltaSchX !== 0) {
|
|
302921
|
+
addAttr(attrs, "newSchX", suggestion.newSchX);
|
|
302922
|
+
addAttr(attrs, "deltaSchX", suggestion.deltaSchX, { formatDelta: true });
|
|
302923
|
+
}
|
|
302924
|
+
if (suggestion.deltaSchY !== 0) {
|
|
302925
|
+
addAttr(attrs, "newSchY", suggestion.newSchY);
|
|
302926
|
+
addAttr(attrs, "deltaSchY", suggestion.deltaSchY, { formatDelta: true });
|
|
302927
|
+
}
|
|
302928
|
+
return `<OverlapCorrectionSuggestion ${attrs.join(" ")} />`;
|
|
302929
|
+
}
|
|
302930
|
+
getComponentOverlap(firstComponent, secondComponent) {
|
|
302931
|
+
if (firstComponent.schematicSheetId !== secondComponent.schematicSheetId)
|
|
302932
|
+
return null;
|
|
302933
|
+
const a2 = this.getCenteredRectBounds(firstComponent);
|
|
302934
|
+
const b3 = this.getCenteredRectBounds(secondComponent);
|
|
302935
|
+
const overlapWidth = Math.min(a2.right, b3.right) - Math.max(a2.left, b3.left);
|
|
302936
|
+
const overlapHeight = Math.min(a2.bottom, b3.bottom) - Math.max(a2.top, b3.top);
|
|
302937
|
+
if (overlapWidth <= 0 || overlapHeight <= 0)
|
|
302938
|
+
return null;
|
|
302939
|
+
return {
|
|
302940
|
+
lineItemType: "ComponentOverlap",
|
|
302941
|
+
firstComponent,
|
|
302942
|
+
secondComponent,
|
|
302943
|
+
overlapWidth,
|
|
302944
|
+
overlapHeight,
|
|
302945
|
+
correctionSuggestions: this.getOverlapCorrectionSuggestions({
|
|
302946
|
+
firstComponent,
|
|
302947
|
+
secondComponent,
|
|
302948
|
+
overlapWidth,
|
|
302949
|
+
overlapHeight
|
|
302950
|
+
})
|
|
302951
|
+
};
|
|
302952
|
+
}
|
|
302953
|
+
}
|
|
302954
|
+
|
|
302397
302955
|
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/SchematicBoxTooWideSolver/SchematicBoxTooWideSolver.ts
|
|
302398
|
-
import { BaseSolver as
|
|
302399
|
-
class SchematicBoxTooWideSolver extends
|
|
302956
|
+
import { BaseSolver as BaseSolver12 } from "@tscircuit/solver-utils";
|
|
302957
|
+
class SchematicBoxTooWideSolver extends BaseSolver12 {
|
|
302400
302958
|
params;
|
|
302401
302959
|
SCHEMATIC_BOX_TOO_WIDE_MESSAGE = "Shrink schematic box width";
|
|
302402
302960
|
PIN_HEADER_MAX_ALLOWED_GAP = 0.1;
|
|
@@ -302571,8 +303129,8 @@ class SchematicBoxTooWideSolver extends BaseSolver9 {
|
|
|
302571
303129
|
}
|
|
302572
303130
|
|
|
302573
303131
|
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/SchematicPinPaddingToEdgeSolver/SchematicPinPaddingToEdgeSolver.ts
|
|
302574
|
-
import { BaseSolver as
|
|
302575
|
-
class SchematicPinPaddingToEdgeSolver extends
|
|
303132
|
+
import { BaseSolver as BaseSolver13 } from "@tscircuit/solver-utils";
|
|
303133
|
+
class SchematicPinPaddingToEdgeSolver extends BaseSolver13 {
|
|
302576
303134
|
params;
|
|
302577
303135
|
MESSAGE = "Move schematic pins closer to the box edge or change the schematic box";
|
|
302578
303136
|
PIN_NAME_CHARACTER_WIDTH = 0.095;
|
|
@@ -302800,9 +303358,349 @@ class SchematicPinPaddingToEdgeSolver extends BaseSolver10 {
|
|
|
302800
303358
|
}
|
|
302801
303359
|
}
|
|
302802
303360
|
|
|
303361
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/SchematicPlacementPipeline/SchematicPlacementPipeline.ts
|
|
303362
|
+
import {
|
|
303363
|
+
BasePipelineSolver as BasePipelineSolver2,
|
|
303364
|
+
definePipelineStep as definePipelineStep3
|
|
303365
|
+
} from "@tscircuit/solver-utils";
|
|
303366
|
+
|
|
303367
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/placements.ts
|
|
303368
|
+
import { cju as cju7 } from "@tscircuit/circuit-json-util";
|
|
303369
|
+
|
|
303370
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/schematic-sheets.ts
|
|
303371
|
+
var getSchematicSheetNamesById = (circuitJson) => {
|
|
303372
|
+
const map = new Map;
|
|
303373
|
+
for (const element of circuitJson) {
|
|
303374
|
+
if (element.type === "schematic_sheet" && element.name) {
|
|
303375
|
+
map.set(element.schematic_sheet_id, element.name);
|
|
303376
|
+
}
|
|
303377
|
+
}
|
|
303378
|
+
return map;
|
|
303379
|
+
};
|
|
303380
|
+
|
|
303381
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/placements.ts
|
|
303382
|
+
var isSchematicBox = (el3) => el3.type === "schematic_box";
|
|
303383
|
+
var isSchematicComponent = (el3) => el3.type === "schematic_component";
|
|
303384
|
+
var getSchematicSheetId = (el3) => el3.schematic_sheet_id;
|
|
303385
|
+
var getSourceComponentName = (circuitJson, sourceComponentId) => {
|
|
303386
|
+
if (!sourceComponentId)
|
|
303387
|
+
return;
|
|
303388
|
+
return cju7(circuitJson).source_component.get(sourceComponentId)?.name;
|
|
303389
|
+
};
|
|
303390
|
+
var getSourceComponentMetadata = (schematicBox, circuitJson) => {
|
|
303391
|
+
if (!schematicBox.schematic_component_id)
|
|
303392
|
+
return {};
|
|
303393
|
+
const util2 = cju7(circuitJson);
|
|
303394
|
+
const sc2 = util2.schematic_component.get(schematicBox.schematic_component_id);
|
|
303395
|
+
if (!sc2?.source_component_id)
|
|
303396
|
+
return {};
|
|
303397
|
+
const sourceComponent = util2.source_component.get(sc2.source_component_id);
|
|
303398
|
+
return {
|
|
303399
|
+
sourceComponentId: sc2.source_component_id,
|
|
303400
|
+
sourceComponentName: sourceComponent?.name
|
|
303401
|
+
};
|
|
303402
|
+
};
|
|
303403
|
+
var schematicComponentToPlacement = (schematicComponent, circuitJson, schematicSheetNameById, schematicBox) => {
|
|
303404
|
+
let schematicSheetId = getSchematicSheetId(schematicComponent);
|
|
303405
|
+
if (schematicSheetId === undefined && schematicBox) {
|
|
303406
|
+
schematicSheetId = getSchematicSheetId(schematicBox);
|
|
303407
|
+
}
|
|
303408
|
+
let schematicSheetName;
|
|
303409
|
+
if (schematicSheetId) {
|
|
303410
|
+
schematicSheetName = schematicSheetNameById.get(schematicSheetId);
|
|
303411
|
+
}
|
|
303412
|
+
const placement = {
|
|
303413
|
+
lineItemType: "SchematicBoxPlacement",
|
|
303414
|
+
positionAnchor: "center",
|
|
303415
|
+
schX: schematicComponent.center.x,
|
|
303416
|
+
schY: schematicComponent.center.y,
|
|
303417
|
+
width: schematicBox?.width ?? schematicComponent.size.width,
|
|
303418
|
+
height: schematicBox?.height ?? schematicComponent.size.height,
|
|
303419
|
+
sourceComponentId: schematicComponent.source_component_id,
|
|
303420
|
+
sourceComponentName: getSourceComponentName(circuitJson, schematicComponent.source_component_id),
|
|
303421
|
+
schematicComponentId: schematicComponent.schematic_component_id,
|
|
303422
|
+
schematicSymbolId: schematicBox?.schematic_symbol_id ?? schematicComponent.schematic_symbol_id,
|
|
303423
|
+
subcircuitId: schematicComponent.subcircuit_id ?? schematicBox?.subcircuit_id
|
|
303424
|
+
};
|
|
303425
|
+
if (schematicSheetId) {
|
|
303426
|
+
placement.schematicSheetId = schematicSheetId;
|
|
303427
|
+
}
|
|
303428
|
+
if (schematicSheetName) {
|
|
303429
|
+
placement.schematicSheetName = schematicSheetName;
|
|
303430
|
+
}
|
|
303431
|
+
return placement;
|
|
303432
|
+
};
|
|
303433
|
+
var schematicBoxToPlacement = (schematicBox, circuitJson, schematicSheetNameById) => {
|
|
303434
|
+
const schematicSheetId = getSchematicSheetId(schematicBox);
|
|
303435
|
+
let schematicSheetName;
|
|
303436
|
+
if (schematicSheetId) {
|
|
303437
|
+
schematicSheetName = schematicSheetNameById.get(schematicSheetId);
|
|
303438
|
+
}
|
|
303439
|
+
const placement = {
|
|
303440
|
+
lineItemType: "SchematicBoxPlacement",
|
|
303441
|
+
positionAnchor: "center",
|
|
303442
|
+
schX: schematicBox.x,
|
|
303443
|
+
schY: schematicBox.y,
|
|
303444
|
+
width: schematicBox.width,
|
|
303445
|
+
height: schematicBox.height,
|
|
303446
|
+
...getSourceComponentMetadata(schematicBox, circuitJson),
|
|
303447
|
+
schematicComponentId: schematicBox.schematic_component_id,
|
|
303448
|
+
schematicSymbolId: schematicBox.schematic_symbol_id,
|
|
303449
|
+
subcircuitId: schematicBox.subcircuit_id
|
|
303450
|
+
};
|
|
303451
|
+
if (schematicSheetId) {
|
|
303452
|
+
placement.schematicSheetId = schematicSheetId;
|
|
303453
|
+
}
|
|
303454
|
+
if (schematicSheetName) {
|
|
303455
|
+
placement.schematicSheetName = schematicSheetName;
|
|
303456
|
+
}
|
|
303457
|
+
return placement;
|
|
303458
|
+
};
|
|
303459
|
+
var buildSolverContext = (circuitJson) => {
|
|
303460
|
+
const schematicBoxes = circuitJson.filter(isSchematicBox);
|
|
303461
|
+
const schematicSheetNameById = getSchematicSheetNamesById(circuitJson);
|
|
303462
|
+
const schematicComponentIds = new Set(circuitJson.filter(isSchematicComponent).map((sc2) => sc2.schematic_component_id));
|
|
303463
|
+
const componentPlacements = [
|
|
303464
|
+
...circuitJson.filter(isSchematicComponent).map((sc2) => schematicComponentToPlacement(sc2, circuitJson, schematicSheetNameById, schematicBoxes.find((sb3) => sb3.schematic_component_id === sc2.schematic_component_id))),
|
|
303465
|
+
...schematicBoxes.filter((sb3) => !sb3.schematic_component_id || !schematicComponentIds.has(sb3.schematic_component_id)).map((sb3) => schematicBoxToPlacement(sb3, circuitJson, schematicSheetNameById))
|
|
303466
|
+
];
|
|
303467
|
+
return { circuitJson, componentPlacements };
|
|
303468
|
+
};
|
|
303469
|
+
|
|
303470
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/TraceSimplificationSolver/TraceSimplificationSolver.ts
|
|
303471
|
+
import { BaseSolver as BaseSolver14 } from "@tscircuit/solver-utils";
|
|
303472
|
+
class TraceSimplificationSolver extends BaseSolver14 {
|
|
303473
|
+
static EPSILON = 0.01;
|
|
303474
|
+
ctx;
|
|
303475
|
+
out;
|
|
303476
|
+
constructor({
|
|
303477
|
+
ctx,
|
|
303478
|
+
issues
|
|
303479
|
+
}) {
|
|
303480
|
+
super();
|
|
303481
|
+
this.ctx = ctx;
|
|
303482
|
+
this.out = issues;
|
|
303483
|
+
}
|
|
303484
|
+
_step() {
|
|
303485
|
+
const ports = this.ctx.circuitJson.filter((element) => element.type === "schematic_port");
|
|
303486
|
+
const portsById = new Map(ports.map((port) => [port.schematic_port_id, port]));
|
|
303487
|
+
const placementsByComponentId = new Map(this.ctx.componentPlacements.flatMap((placement) => placement.schematicComponentId ? [[placement.schematicComponentId, placement]] : []));
|
|
303488
|
+
const emittedMoves = new Set;
|
|
303489
|
+
for (const trace of this.ctx.circuitJson.filter((element) => element.type === "schematic_trace")) {
|
|
303490
|
+
const points = this.getTracePoints(trace);
|
|
303491
|
+
const currentTurnCount = this.countTurns(points);
|
|
303492
|
+
if (currentTurnCount !== 3)
|
|
303493
|
+
continue;
|
|
303494
|
+
const candidates = [
|
|
303495
|
+
this.getCandidate({
|
|
303496
|
+
trace,
|
|
303497
|
+
points,
|
|
303498
|
+
atStart: true,
|
|
303499
|
+
currentTurnCount,
|
|
303500
|
+
ports,
|
|
303501
|
+
portsById,
|
|
303502
|
+
placementsByComponentId
|
|
303503
|
+
}),
|
|
303504
|
+
this.getCandidate({
|
|
303505
|
+
trace,
|
|
303506
|
+
points,
|
|
303507
|
+
atStart: false,
|
|
303508
|
+
currentTurnCount,
|
|
303509
|
+
ports,
|
|
303510
|
+
portsById,
|
|
303511
|
+
placementsByComponentId
|
|
303512
|
+
})
|
|
303513
|
+
].filter((candidate) => Boolean(candidate));
|
|
303514
|
+
for (const candidate of candidates) {
|
|
303515
|
+
if (this.wouldOverlapAnotherComponent(candidate))
|
|
303516
|
+
continue;
|
|
303517
|
+
const moveKey = [
|
|
303518
|
+
candidate.target.schematicComponentId,
|
|
303519
|
+
candidate.deltaSchX.toFixed(3),
|
|
303520
|
+
candidate.deltaSchY.toFixed(3)
|
|
303521
|
+
].join("\x00");
|
|
303522
|
+
if (emittedMoves.has(moveKey))
|
|
303523
|
+
continue;
|
|
303524
|
+
emittedMoves.add(moveKey);
|
|
303525
|
+
this.out.push(this.makeIssue(trace, candidate));
|
|
303526
|
+
}
|
|
303527
|
+
}
|
|
303528
|
+
this.solved = true;
|
|
303529
|
+
}
|
|
303530
|
+
getCandidate({
|
|
303531
|
+
trace,
|
|
303532
|
+
points,
|
|
303533
|
+
atStart,
|
|
303534
|
+
currentTurnCount,
|
|
303535
|
+
ports,
|
|
303536
|
+
portsById,
|
|
303537
|
+
placementsByComponentId
|
|
303538
|
+
}) {
|
|
303539
|
+
if (points.length < 4)
|
|
303540
|
+
return;
|
|
303541
|
+
const terminalIndex = atStart ? 0 : points.length - 1;
|
|
303542
|
+
const leadIndex = atStart ? 1 : points.length - 2;
|
|
303543
|
+
const acrossIndex = atStart ? 2 : points.length - 3;
|
|
303544
|
+
const retainedIndex = atStart ? 3 : points.length - 4;
|
|
303545
|
+
const terminalPoint = points[terminalIndex];
|
|
303546
|
+
const leadPoint = points[leadIndex];
|
|
303547
|
+
const acrossPoint = points[acrossIndex];
|
|
303548
|
+
const retainedPoint = points[retainedIndex];
|
|
303549
|
+
const terminalEdge = atStart ? trace.edges[0] : trace.edges.at(-1);
|
|
303550
|
+
if (!terminalEdge)
|
|
303551
|
+
return;
|
|
303552
|
+
const portId = atStart ? terminalEdge.from_schematic_port_id : terminalEdge.to_schematic_port_id;
|
|
303553
|
+
const port = portId ? portsById.get(portId) : this.findPortAtPoint(ports, terminalPoint, trace.schematic_sheet_id);
|
|
303554
|
+
if (!port?.schematic_component_id || !port.facing_direction)
|
|
303555
|
+
return;
|
|
303556
|
+
const leadAxis = this.getAxis(terminalPoint, leadPoint);
|
|
303557
|
+
const shiftAxis = this.getAxis(leadPoint, acrossPoint);
|
|
303558
|
+
const retainedAxis = this.getAxis(acrossPoint, retainedPoint);
|
|
303559
|
+
const portAxis = port.facing_direction === "left" || port.facing_direction === "right" ? "horizontal" : "vertical";
|
|
303560
|
+
if (!leadAxis || !shiftAxis || !retainedAxis)
|
|
303561
|
+
return;
|
|
303562
|
+
if (leadAxis !== portAxis || shiftAxis === portAxis || retainedAxis !== portAxis) {
|
|
303563
|
+
return;
|
|
303564
|
+
}
|
|
303565
|
+
if (!this.isPointInFacingDirection(terminalPoint, leadPoint, port.facing_direction) || !this.isPointInFacingDirection(acrossPoint, retainedPoint, port.facing_direction)) {
|
|
303566
|
+
return;
|
|
303567
|
+
}
|
|
303568
|
+
const target = placementsByComponentId.get(port.schematic_component_id);
|
|
303569
|
+
if (!target)
|
|
303570
|
+
return;
|
|
303571
|
+
const deltaSchX = acrossPoint.x - leadPoint.x;
|
|
303572
|
+
const deltaSchY = acrossPoint.y - leadPoint.y;
|
|
303573
|
+
if (Math.abs(deltaSchX) <= TraceSimplificationSolver.EPSILON && Math.abs(deltaSchY) <= TraceSimplificationSolver.EPSILON) {
|
|
303574
|
+
return;
|
|
303575
|
+
}
|
|
303576
|
+
const movedTerminalPoint = {
|
|
303577
|
+
x: terminalPoint.x + deltaSchX,
|
|
303578
|
+
y: terminalPoint.y + deltaSchY
|
|
303579
|
+
};
|
|
303580
|
+
const suggestedPoints = atStart ? [movedTerminalPoint, ...points.slice(2)] : [...points.slice(0, -2), movedTerminalPoint];
|
|
303581
|
+
const suggestedTurnCount = this.countTurns(suggestedPoints);
|
|
303582
|
+
if (suggestedTurnCount === undefined || suggestedTurnCount !== 1 || suggestedTurnCount >= currentTurnCount) {
|
|
303583
|
+
return;
|
|
303584
|
+
}
|
|
303585
|
+
return {
|
|
303586
|
+
target,
|
|
303587
|
+
deltaSchX,
|
|
303588
|
+
deltaSchY,
|
|
303589
|
+
currentTurnCount,
|
|
303590
|
+
suggestedTurnCount
|
|
303591
|
+
};
|
|
303592
|
+
}
|
|
303593
|
+
getTracePoints(trace) {
|
|
303594
|
+
const firstEdge = trace.edges[0];
|
|
303595
|
+
if (!firstEdge)
|
|
303596
|
+
return [];
|
|
303597
|
+
const points = [firstEdge.from];
|
|
303598
|
+
for (const edge of trace.edges) {
|
|
303599
|
+
const previousPoint = points.at(-1);
|
|
303600
|
+
if (!this.pointsEqual(previousPoint, edge.from))
|
|
303601
|
+
return [];
|
|
303602
|
+
points.push(edge.to);
|
|
303603
|
+
}
|
|
303604
|
+
return points;
|
|
303605
|
+
}
|
|
303606
|
+
countTurns(points) {
|
|
303607
|
+
const axes = [];
|
|
303608
|
+
for (const [index, point9] of points.slice(1).entries()) {
|
|
303609
|
+
const previousPoint = points[index];
|
|
303610
|
+
if (this.pointsEqual(previousPoint, point9))
|
|
303611
|
+
continue;
|
|
303612
|
+
const axis = this.getAxis(previousPoint, point9);
|
|
303613
|
+
if (!axis)
|
|
303614
|
+
return;
|
|
303615
|
+
axes.push(axis);
|
|
303616
|
+
}
|
|
303617
|
+
return axes.slice(1).filter((axis, index) => axis !== axes[index]).length;
|
|
303618
|
+
}
|
|
303619
|
+
pointsEqual(a2, b3) {
|
|
303620
|
+
const { EPSILON: EPSILON3 } = TraceSimplificationSolver;
|
|
303621
|
+
return Math.abs(a2.x - b3.x) <= EPSILON3 && Math.abs(a2.y - b3.y) <= EPSILON3;
|
|
303622
|
+
}
|
|
303623
|
+
getAxis(a2, b3) {
|
|
303624
|
+
const dx3 = Math.abs(b3.x - a2.x);
|
|
303625
|
+
const dy3 = Math.abs(b3.y - a2.y);
|
|
303626
|
+
const { EPSILON: EPSILON3 } = TraceSimplificationSolver;
|
|
303627
|
+
if (dx3 <= EPSILON3 && dy3 > EPSILON3)
|
|
303628
|
+
return "vertical";
|
|
303629
|
+
if (dy3 <= EPSILON3 && dx3 > EPSILON3)
|
|
303630
|
+
return "horizontal";
|
|
303631
|
+
return;
|
|
303632
|
+
}
|
|
303633
|
+
isPointInFacingDirection(origin, point9, facingDirection) {
|
|
303634
|
+
const { EPSILON: EPSILON3 } = TraceSimplificationSolver;
|
|
303635
|
+
switch (facingDirection) {
|
|
303636
|
+
case "left":
|
|
303637
|
+
return point9.x < origin.x - EPSILON3;
|
|
303638
|
+
case "right":
|
|
303639
|
+
return point9.x > origin.x + EPSILON3;
|
|
303640
|
+
case "up":
|
|
303641
|
+
return point9.y > origin.y + EPSILON3;
|
|
303642
|
+
case "down":
|
|
303643
|
+
return point9.y < origin.y - EPSILON3;
|
|
303644
|
+
}
|
|
303645
|
+
}
|
|
303646
|
+
findPortAtPoint(ports, point9, schematicSheetId) {
|
|
303647
|
+
const { EPSILON: EPSILON3 } = TraceSimplificationSolver;
|
|
303648
|
+
return ports.find((port) => port.schematic_sheet_id === schematicSheetId && Math.abs(port.center.x - point9.x) <= EPSILON3 && Math.abs(port.center.y - point9.y) <= EPSILON3);
|
|
303649
|
+
}
|
|
303650
|
+
wouldOverlapAnotherComponent(candidate) {
|
|
303651
|
+
const movedBounds = centeredRect(candidate.target.schX + candidate.deltaSchX, candidate.target.schY + candidate.deltaSchY, candidate.target.width, candidate.target.height);
|
|
303652
|
+
return this.ctx.componentPlacements.some((placement) => {
|
|
303653
|
+
if (placement === candidate.target)
|
|
303654
|
+
return false;
|
|
303655
|
+
if (placement.schematicSheetId !== candidate.target.schematicSheetId) {
|
|
303656
|
+
return false;
|
|
303657
|
+
}
|
|
303658
|
+
return Boolean(rectOverlap(movedBounds, centeredRect(placement.schX, placement.schY, placement.width, placement.height)));
|
|
303659
|
+
});
|
|
303660
|
+
}
|
|
303661
|
+
makeIssue(trace, candidate) {
|
|
303662
|
+
const targetName = candidate.target.sourceComponentName ?? candidate.target.schematicComponentId ?? "component";
|
|
303663
|
+
const direction2 = this.getMoveDirection(candidate.deltaSchX, candidate.deltaSchY);
|
|
303664
|
+
const distance49 = Math.abs(candidate.deltaSchX || candidate.deltaSchY);
|
|
303665
|
+
const newSchX = candidate.target.schX + candidate.deltaSchX;
|
|
303666
|
+
const newSchY = candidate.target.schY + candidate.deltaSchY;
|
|
303667
|
+
return {
|
|
303668
|
+
lineItemType: "TraceCanBeSimplifiedByMovingComponent",
|
|
303669
|
+
schematicTraceId: trace.schematic_trace_id,
|
|
303670
|
+
targetComponent: candidate.target,
|
|
303671
|
+
deltaSchX: candidate.deltaSchX,
|
|
303672
|
+
deltaSchY: candidate.deltaSchY,
|
|
303673
|
+
newSchX,
|
|
303674
|
+
newSchY,
|
|
303675
|
+
currentTurnCount: candidate.currentTurnCount,
|
|
303676
|
+
suggestedTurnCount: candidate.suggestedTurnCount,
|
|
303677
|
+
message: `move ${targetName} ${direction2} by ${fmtNumber5(distance49)} (to schX=${fmtNumber5(newSchX)}, schY=${fmtNumber5(newSchY)}) to reduce this trace from ${candidate.currentTurnCount} turns to ${candidate.suggestedTurnCount}`
|
|
303678
|
+
};
|
|
303679
|
+
}
|
|
303680
|
+
getMoveDirection(deltaSchX, deltaSchY) {
|
|
303681
|
+
if (Math.abs(deltaSchX) > TraceSimplificationSolver.EPSILON) {
|
|
303682
|
+
return deltaSchX > 0 ? "right" : "left";
|
|
303683
|
+
}
|
|
303684
|
+
return deltaSchY > 0 ? "up" : "down";
|
|
303685
|
+
}
|
|
303686
|
+
static issueToString(issue) {
|
|
303687
|
+
const attrs = [];
|
|
303688
|
+
addAttr(attrs, "schematicTraceId", issue.schematicTraceId);
|
|
303689
|
+
addAttr(attrs, "targetComponentName", issue.targetComponent.sourceComponentName);
|
|
303690
|
+
addAttr(attrs, "deltaSchX", issue.deltaSchX, { formatDelta: true });
|
|
303691
|
+
addAttr(attrs, "deltaSchY", issue.deltaSchY, { formatDelta: true });
|
|
303692
|
+
addAttr(attrs, "newSchX", issue.newSchX);
|
|
303693
|
+
addAttr(attrs, "newSchY", issue.newSchY);
|
|
303694
|
+
addAttr(attrs, "currentTurnCount", issue.currentTurnCount);
|
|
303695
|
+
addAttr(attrs, "suggestedTurnCount", issue.suggestedTurnCount);
|
|
303696
|
+
addAttr(attrs, "message", issue.message);
|
|
303697
|
+
return `<TraceCanBeSimplifiedByMovingComponent ${attrs.join(" ")} />`;
|
|
303698
|
+
}
|
|
303699
|
+
}
|
|
303700
|
+
|
|
302803
303701
|
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/VerboseNetLabelSolver/VerboseNetLabelSolver.ts
|
|
302804
|
-
import { BaseSolver as
|
|
302805
|
-
class VerboseNetLabelSolver extends
|
|
303702
|
+
import { BaseSolver as BaseSolver15 } from "@tscircuit/solver-utils";
|
|
303703
|
+
class VerboseNetLabelSolver extends BaseSolver15 {
|
|
302806
303704
|
params;
|
|
302807
303705
|
VERBOSE_NET_LABEL_MESSAGE = "Create trace with schDisplayLabel";
|
|
302808
303706
|
netLabels;
|
|
@@ -302910,650 +303808,6 @@ class VerboseNetLabelSolver extends BaseSolver11 {
|
|
|
302910
303808
|
}
|
|
302911
303809
|
}
|
|
302912
303810
|
|
|
302913
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/DiodeResistorAlignmentSolver/DiodeResistorAlignmentSolver.ts
|
|
302914
|
-
import { BaseSolver as BaseSolver12 } from "@tscircuit/solver-utils";
|
|
302915
|
-
class DiodeResistorAlignmentSolver extends BaseSolver12 {
|
|
302916
|
-
static DIODE_FTYPES = new Set(["simple_led", "simple_diode"]);
|
|
302917
|
-
ctx;
|
|
302918
|
-
out;
|
|
302919
|
-
schematicTraces;
|
|
302920
|
-
currentIndex = 0;
|
|
302921
|
-
sourceComponentFtypeById;
|
|
302922
|
-
sourceComponentIdBySourcePortId;
|
|
302923
|
-
schematicPorts;
|
|
302924
|
-
schematicBoxBySourceComponentId;
|
|
302925
|
-
constructor({
|
|
302926
|
-
ctx,
|
|
302927
|
-
issues
|
|
302928
|
-
}) {
|
|
302929
|
-
super();
|
|
302930
|
-
this.ctx = ctx;
|
|
302931
|
-
this.out = issues;
|
|
302932
|
-
const { circuitJson } = ctx;
|
|
302933
|
-
this.sourceComponentFtypeById = this.buildSourceComponentFtypeById(circuitJson);
|
|
302934
|
-
this.sourceComponentIdBySourcePortId = this.buildSourceComponentIdBySourcePortId(circuitJson);
|
|
302935
|
-
this.schematicPorts = circuitJson.filter((el3) => el3.type === "schematic_port");
|
|
302936
|
-
this.schematicBoxBySourceComponentId = this.buildSchematicBoxBySourceComponentId();
|
|
302937
|
-
this.schematicTraces = circuitJson.filter((el3) => el3.type === "schematic_trace");
|
|
302938
|
-
this.solved = this.schematicTraces.length === 0;
|
|
302939
|
-
}
|
|
302940
|
-
_step() {
|
|
302941
|
-
const trace = this.schematicTraces[this.currentIndex];
|
|
302942
|
-
if (!trace) {
|
|
302943
|
-
this.solved = true;
|
|
302944
|
-
return;
|
|
302945
|
-
}
|
|
302946
|
-
this.currentIndex++;
|
|
302947
|
-
this.solved = this.currentIndex >= this.schematicTraces.length;
|
|
302948
|
-
if (!trace.edges || trace.edges.length === 0)
|
|
302949
|
-
return;
|
|
302950
|
-
const firstEdge = trace.edges[0];
|
|
302951
|
-
const lastEdge = trace.edges[trace.edges.length - 1];
|
|
302952
|
-
if (!firstEdge || !lastEdge)
|
|
302953
|
-
return;
|
|
302954
|
-
const start = firstEdge.from;
|
|
302955
|
-
const end = lastEdge.to;
|
|
302956
|
-
const schematicSheetId = trace.schematic_sheet_id;
|
|
302957
|
-
const startSourceCompId = this.findSourceComponentIdNearPoint(start, schematicSheetId);
|
|
302958
|
-
const endSourceCompId = this.findSourceComponentIdNearPoint(end, schematicSheetId);
|
|
302959
|
-
if (!startSourceCompId || !endSourceCompId)
|
|
302960
|
-
return;
|
|
302961
|
-
const startFtype = this.sourceComponentFtypeById.get(startSourceCompId);
|
|
302962
|
-
const endFtype = this.sourceComponentFtypeById.get(endSourceCompId);
|
|
302963
|
-
const { DIODE_FTYPES } = DiodeResistorAlignmentSolver;
|
|
302964
|
-
const isDiodeResistorPair = DIODE_FTYPES.has(startFtype) && endFtype === "simple_resistor" || startFtype === "simple_resistor" && DIODE_FTYPES.has(endFtype);
|
|
302965
|
-
if (!isDiodeResistorPair)
|
|
302966
|
-
return;
|
|
302967
|
-
const diodeCompId = DIODE_FTYPES.has(startFtype) ? startSourceCompId : endSourceCompId;
|
|
302968
|
-
const resistorCompId = startFtype === "simple_resistor" ? startSourceCompId : endSourceCompId;
|
|
302969
|
-
const diodeBox = this.schematicBoxBySourceComponentId.get(diodeCompId);
|
|
302970
|
-
const resistorBox = this.schematicBoxBySourceComponentId.get(resistorCompId);
|
|
302971
|
-
if (!diodeBox || !resistorBox)
|
|
302972
|
-
return;
|
|
302973
|
-
const diodePort = this.findNearestPort(DIODE_FTYPES.has(startFtype) ? start : end, schematicSheetId);
|
|
302974
|
-
const resistorPort = this.findNearestPort(startFtype === "simple_resistor" ? start : end, schematicSheetId);
|
|
302975
|
-
if (!diodePort?.center || !resistorPort?.center)
|
|
302976
|
-
return;
|
|
302977
|
-
const diodeName = diodeBox.sourceComponentName ?? diodeCompId;
|
|
302978
|
-
const resistorName = resistorBox.sourceComponentName ?? resistorCompId;
|
|
302979
|
-
const diodePin = diodePort?.display_pin_label ?? diodePort?.pin_number?.toString();
|
|
302980
|
-
const resistorPin = resistorPort?.display_pin_label ?? resistorPort?.pin_number?.toString();
|
|
302981
|
-
const diodeFacing = diodePort?.facing_direction;
|
|
302982
|
-
const resistorFacing = resistorPort?.facing_direction;
|
|
302983
|
-
const diodePinDesc = diodePin ? `${diodeName}.${diodePin}` : diodeName;
|
|
302984
|
-
const resistorPinDesc = resistorPin ? `${resistorName}.${resistorPin}` : resistorName;
|
|
302985
|
-
const makeIssue2 = (message) => ({
|
|
302986
|
-
lineItemType: "DiodeResistorNotAligned",
|
|
302987
|
-
diodeSchematicBox: diodeBox,
|
|
302988
|
-
resistorSchematicBox: resistorBox,
|
|
302989
|
-
diodePin,
|
|
302990
|
-
resistorPin,
|
|
302991
|
-
diodePinFacingDirection: diodeFacing,
|
|
302992
|
-
resistorPinFacingDirection: resistorFacing,
|
|
302993
|
-
message
|
|
302994
|
-
});
|
|
302995
|
-
if (!DiodeResistorAlignmentSolver.isCoLinear(diodePort.center, resistorPort.center)) {
|
|
302996
|
-
this.out.push(makeIssue2(`trace has corners — align ${diodeName} and ${resistorName} on same axis and rotate so ${diodePinDesc} faces ${resistorPinDesc}`));
|
|
302997
|
-
return;
|
|
302998
|
-
}
|
|
302999
|
-
if (diodeFacing && resistorFacing && !DiodeResistorAlignmentSolver.pinsFacingEachOther(diodePort.center, diodeFacing, resistorPort.center, resistorFacing)) {
|
|
303000
|
-
this.out.push(makeIssue2(`${diodePinDesc} and ${resistorPinDesc} face away from each other — rotate ${diodeName} so ${diodePinDesc} faces ${resistorPinDesc}`));
|
|
303001
|
-
}
|
|
303002
|
-
}
|
|
303003
|
-
static isCoLinear(a2, b3, epsilon = 0.01) {
|
|
303004
|
-
return Math.abs(a2.x - b3.x) < epsilon || Math.abs(a2.y - b3.y) < epsilon;
|
|
303005
|
-
}
|
|
303006
|
-
static pinsFacingEachOther(aCenter, aFacing, bCenter, bFacing) {
|
|
303007
|
-
const dx3 = bCenter.x - aCenter.x;
|
|
303008
|
-
const dy3 = bCenter.y - aCenter.y;
|
|
303009
|
-
const aToward = aFacing === "right" && dx3 > 0 || aFacing === "left" && dx3 < 0 || aFacing === "up" && dy3 > 0 || aFacing === "down" && dy3 < 0;
|
|
303010
|
-
const bToward = bFacing === "right" && dx3 < 0 || bFacing === "left" && dx3 > 0 || bFacing === "up" && dy3 < 0 || bFacing === "down" && dy3 > 0;
|
|
303011
|
-
return aToward && bToward;
|
|
303012
|
-
}
|
|
303013
|
-
static dist(a2, b3) {
|
|
303014
|
-
return Math.sqrt((a2.x - b3.x) ** 2 + (a2.y - b3.y) ** 2);
|
|
303015
|
-
}
|
|
303016
|
-
findNearestPort(point9, schematicSheetId) {
|
|
303017
|
-
let nearest;
|
|
303018
|
-
let minDist = Infinity;
|
|
303019
|
-
for (const port of this.schematicPorts) {
|
|
303020
|
-
if (port.schematic_sheet_id !== schematicSheetId)
|
|
303021
|
-
continue;
|
|
303022
|
-
if (!port.center)
|
|
303023
|
-
continue;
|
|
303024
|
-
const d3 = DiodeResistorAlignmentSolver.dist(point9, port.center);
|
|
303025
|
-
if (d3 < minDist) {
|
|
303026
|
-
minDist = d3;
|
|
303027
|
-
nearest = port;
|
|
303028
|
-
}
|
|
303029
|
-
}
|
|
303030
|
-
return nearest;
|
|
303031
|
-
}
|
|
303032
|
-
findSourceComponentIdNearPoint(point9, schematicSheetId) {
|
|
303033
|
-
const nearest = this.findNearestPort(point9, schematicSheetId);
|
|
303034
|
-
if (!nearest || !nearest.source_port_id)
|
|
303035
|
-
return;
|
|
303036
|
-
return this.sourceComponentIdBySourcePortId.get(nearest.source_port_id);
|
|
303037
|
-
}
|
|
303038
|
-
buildSourceComponentFtypeById(circuitJson) {
|
|
303039
|
-
const map = new Map;
|
|
303040
|
-
for (const el3 of circuitJson) {
|
|
303041
|
-
if (el3.type === "source_component" && "source_component_id" in el3 && "ftype" in el3 && typeof el3.ftype === "string") {
|
|
303042
|
-
map.set(el3.source_component_id, el3.ftype);
|
|
303043
|
-
}
|
|
303044
|
-
}
|
|
303045
|
-
return map;
|
|
303046
|
-
}
|
|
303047
|
-
buildSourceComponentIdBySourcePortId(circuitJson) {
|
|
303048
|
-
const map = new Map;
|
|
303049
|
-
for (const el3 of circuitJson) {
|
|
303050
|
-
if (el3.type === "source_port" && "source_port_id" in el3 && "source_component_id" in el3 && typeof el3.source_port_id === "string" && typeof el3.source_component_id === "string") {
|
|
303051
|
-
map.set(el3.source_port_id, el3.source_component_id);
|
|
303052
|
-
}
|
|
303053
|
-
}
|
|
303054
|
-
return map;
|
|
303055
|
-
}
|
|
303056
|
-
buildSchematicBoxBySourceComponentId() {
|
|
303057
|
-
const map = new Map;
|
|
303058
|
-
for (const placement of this.ctx.componentPlacements) {
|
|
303059
|
-
if (placement.sourceComponentId) {
|
|
303060
|
-
map.set(placement.sourceComponentId, placement);
|
|
303061
|
-
}
|
|
303062
|
-
}
|
|
303063
|
-
return map;
|
|
303064
|
-
}
|
|
303065
|
-
static issueToString(issue) {
|
|
303066
|
-
const attrs = [];
|
|
303067
|
-
addAttr(attrs, "diodeComponentName", issue.diodeSchematicBox.sourceComponentName);
|
|
303068
|
-
addAttr(attrs, "diodePin", issue.diodePin);
|
|
303069
|
-
addAttr(attrs, "resistorComponentName", issue.resistorSchematicBox.sourceComponentName);
|
|
303070
|
-
addAttr(attrs, "resistorPin", issue.resistorPin);
|
|
303071
|
-
addAttr(attrs, "message", issue.message);
|
|
303072
|
-
return `<DiodeResistorNotAligned ${attrs.join(" ")} />`;
|
|
303073
|
-
}
|
|
303074
|
-
}
|
|
303075
|
-
|
|
303076
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/ComponentNetLabelCollisionSolver/ComponentNetLabelCollisionSolver.ts
|
|
303077
|
-
import { BaseSolver as BaseSolver13 } from "@tscircuit/solver-utils";
|
|
303078
|
-
|
|
303079
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/geometry.ts
|
|
303080
|
-
function centeredRect(cx3, cy3, w4, h4) {
|
|
303081
|
-
return {
|
|
303082
|
-
left: cx3 - w4 / 2,
|
|
303083
|
-
right: cx3 + w4 / 2,
|
|
303084
|
-
top: cy3 + h4 / 2,
|
|
303085
|
-
bottom: cy3 - h4 / 2
|
|
303086
|
-
};
|
|
303087
|
-
}
|
|
303088
|
-
function rectOverlap(a2, b3) {
|
|
303089
|
-
const ow2 = Math.min(a2.right, b3.right) - Math.max(a2.left, b3.left);
|
|
303090
|
-
const oh2 = Math.min(a2.top, b3.top) - Math.max(a2.bottom, b3.bottom);
|
|
303091
|
-
return ow2 > 0 && oh2 > 0 ? { ow: ow2, oh: oh2 } : null;
|
|
303092
|
-
}
|
|
303093
|
-
|
|
303094
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/ComponentNetLabelCollisionSolver/ComponentNetLabelCollisionSolver.ts
|
|
303095
|
-
class ComponentNetLabelCollisionSolver extends BaseSolver13 {
|
|
303096
|
-
params;
|
|
303097
|
-
LABEL_HALF_HEIGHT = 0.1;
|
|
303098
|
-
LABEL_BOUNDS_SLACK = 0.1;
|
|
303099
|
-
SCH_CHAR_WIDTH = 0.13;
|
|
303100
|
-
placements;
|
|
303101
|
-
netLabelsByComponentId;
|
|
303102
|
-
rawCollisions = [];
|
|
303103
|
-
firstIndex = 0;
|
|
303104
|
-
secondIndex = 1;
|
|
303105
|
-
constructor(params2) {
|
|
303106
|
-
super();
|
|
303107
|
-
this.params = params2;
|
|
303108
|
-
this.placements = params2.ctx.componentPlacements;
|
|
303109
|
-
this.netLabelsByComponentId = this.buildNetLabelsByComponentId(params2.ctx.circuitJson);
|
|
303110
|
-
this.solved = this.placements.length < 2;
|
|
303111
|
-
}
|
|
303112
|
-
_step() {
|
|
303113
|
-
if (this.firstIndex >= this.placements.length - 1) {
|
|
303114
|
-
this.buildAndPushIssues();
|
|
303115
|
-
this.solved = true;
|
|
303116
|
-
return;
|
|
303117
|
-
}
|
|
303118
|
-
const compA = this.placements[this.firstIndex];
|
|
303119
|
-
const compB = this.placements[this.secondIndex];
|
|
303120
|
-
this.detectPair(compA, compB);
|
|
303121
|
-
this.secondIndex++;
|
|
303122
|
-
if (this.secondIndex >= this.placements.length) {
|
|
303123
|
-
this.firstIndex++;
|
|
303124
|
-
this.secondIndex = this.firstIndex + 1;
|
|
303125
|
-
}
|
|
303126
|
-
}
|
|
303127
|
-
detectPair(compA, compB) {
|
|
303128
|
-
if (compA.schematicSheetId !== compB.schematicSheetId)
|
|
303129
|
-
return;
|
|
303130
|
-
this.rawCollisions.push(...this.detectLabelLabel(compA, compB));
|
|
303131
|
-
this.rawCollisions.push(...this.detectBoxLabel(compA, compB));
|
|
303132
|
-
this.rawCollisions.push(...this.detectBoxLabel(compB, compA));
|
|
303133
|
-
}
|
|
303134
|
-
detectLabelLabel(firstComponent, secondComponent) {
|
|
303135
|
-
let leftComp = firstComponent;
|
|
303136
|
-
let rightComp = secondComponent;
|
|
303137
|
-
if (firstComponent.schX > secondComponent.schX) {
|
|
303138
|
-
leftComp = secondComponent;
|
|
303139
|
-
rightComp = firstComponent;
|
|
303140
|
-
}
|
|
303141
|
-
const leftId = leftComp.schematicComponentId;
|
|
303142
|
-
const rightId = rightComp.schematicComponentId;
|
|
303143
|
-
if (!leftId || !rightId)
|
|
303144
|
-
return [];
|
|
303145
|
-
const leftLabels = this.netLabelsByComponentId.get(leftId) ?? [];
|
|
303146
|
-
const rightLabels = this.netLabelsByComponentId.get(rightId) ?? [];
|
|
303147
|
-
if (leftLabels.length === 0 || rightLabels.length === 0)
|
|
303148
|
-
return [];
|
|
303149
|
-
const hits = [];
|
|
303150
|
-
for (const leftLabel of leftLabels) {
|
|
303151
|
-
for (const rightLabel of rightLabels) {
|
|
303152
|
-
const leftBounds = this.getNetLabelBounds(leftLabel);
|
|
303153
|
-
const rightBounds = this.getNetLabelBounds(rightLabel);
|
|
303154
|
-
if (rectOverlap(leftBounds, rightBounds)) {
|
|
303155
|
-
hits.push({
|
|
303156
|
-
type: "label-label",
|
|
303157
|
-
leftComp,
|
|
303158
|
-
rightComp,
|
|
303159
|
-
leftId,
|
|
303160
|
-
rightId,
|
|
303161
|
-
xSeparation: leftBounds.right - rightBounds.left + 0.1
|
|
303162
|
-
});
|
|
303163
|
-
}
|
|
303164
|
-
}
|
|
303165
|
-
}
|
|
303166
|
-
return hits;
|
|
303167
|
-
}
|
|
303168
|
-
detectBoxLabel(boxComp, labelComp) {
|
|
303169
|
-
const boxId = boxComp.schematicComponentId;
|
|
303170
|
-
const labelId = labelComp.schematicComponentId;
|
|
303171
|
-
if (!boxId || !labelId)
|
|
303172
|
-
return [];
|
|
303173
|
-
const labels = this.netLabelsByComponentId.get(labelId) ?? [];
|
|
303174
|
-
if (labels.length === 0)
|
|
303175
|
-
return [];
|
|
303176
|
-
const boxBounds = centeredRect(boxComp.schX, boxComp.schY, boxComp.width, boxComp.height);
|
|
303177
|
-
const boxIsLeft = boxComp.schX <= labelComp.schX;
|
|
303178
|
-
const hits = [];
|
|
303179
|
-
for (const label of labels) {
|
|
303180
|
-
const labelBounds = this.getNetLabelBounds(label);
|
|
303181
|
-
if (!rectOverlap(boxBounds, labelBounds))
|
|
303182
|
-
continue;
|
|
303183
|
-
let xSeparation;
|
|
303184
|
-
if (boxIsLeft) {
|
|
303185
|
-
xSeparation = boxBounds.right - labelBounds.left + 0.1;
|
|
303186
|
-
} else {
|
|
303187
|
-
xSeparation = labelBounds.right - boxBounds.left + 0.1;
|
|
303188
|
-
}
|
|
303189
|
-
hits.push({
|
|
303190
|
-
type: "box-label",
|
|
303191
|
-
boxComp,
|
|
303192
|
-
labelComp,
|
|
303193
|
-
boxId,
|
|
303194
|
-
labelId,
|
|
303195
|
-
xSeparation
|
|
303196
|
-
});
|
|
303197
|
-
}
|
|
303198
|
-
return hits;
|
|
303199
|
-
}
|
|
303200
|
-
buildAndPushIssues() {
|
|
303201
|
-
if (this.rawCollisions.length === 0)
|
|
303202
|
-
return;
|
|
303203
|
-
const globalFixes = this.computeGlobalFixes();
|
|
303204
|
-
if (globalFixes.size === 0)
|
|
303205
|
-
return;
|
|
303206
|
-
const seenPairs = new Set;
|
|
303207
|
-
const pairs3 = [];
|
|
303208
|
-
for (const collision of this.rawCollisions) {
|
|
303209
|
-
let comp1Name;
|
|
303210
|
-
let comp2Name;
|
|
303211
|
-
if (collision.type === "label-label") {
|
|
303212
|
-
comp1Name = collision.leftComp.sourceComponentName ?? "";
|
|
303213
|
-
comp2Name = collision.rightComp.sourceComponentName ?? "";
|
|
303214
|
-
} else {
|
|
303215
|
-
comp1Name = collision.boxComp.sourceComponentName ?? "";
|
|
303216
|
-
comp2Name = collision.labelComp.sourceComponentName ?? "";
|
|
303217
|
-
}
|
|
303218
|
-
const key = `${comp1Name}/${comp2Name}`;
|
|
303219
|
-
if (!seenPairs.has(key)) {
|
|
303220
|
-
seenPairs.add(key);
|
|
303221
|
-
pairs3.push({ comp1Name, comp2Name });
|
|
303222
|
-
}
|
|
303223
|
-
}
|
|
303224
|
-
this.params.issues.push({
|
|
303225
|
-
lineItemType: "NetLabelCollision",
|
|
303226
|
-
pairs: pairs3,
|
|
303227
|
-
moves: Array.from(globalFixes.values())
|
|
303228
|
-
});
|
|
303229
|
-
}
|
|
303230
|
-
computeGlobalFixes() {
|
|
303231
|
-
const compById = new Map;
|
|
303232
|
-
for (const placement of this.placements) {
|
|
303233
|
-
if (placement.schematicComponentId)
|
|
303234
|
-
compById.set(placement.schematicComponentId, placement);
|
|
303235
|
-
}
|
|
303236
|
-
const constraintMap = new Map;
|
|
303237
|
-
const addConstraint = (leftId, rightId, xSeparation) => {
|
|
303238
|
-
const leftComp = compById.get(leftId);
|
|
303239
|
-
const rightComp = compById.get(rightId);
|
|
303240
|
-
if (!leftComp || !rightComp)
|
|
303241
|
-
return;
|
|
303242
|
-
const minSep = rightComp.schX - leftComp.schX + xSeparation;
|
|
303243
|
-
const key = `${leftId}|${rightId}`;
|
|
303244
|
-
constraintMap.set(key, Math.max(constraintMap.get(key) ?? 0, minSep));
|
|
303245
|
-
};
|
|
303246
|
-
for (const collision of this.rawCollisions) {
|
|
303247
|
-
if (collision.type === "label-label") {
|
|
303248
|
-
addConstraint(collision.leftId, collision.rightId, collision.xSeparation);
|
|
303249
|
-
} else if (collision.boxComp.schX <= collision.labelComp.schX) {
|
|
303250
|
-
addConstraint(collision.boxId, collision.labelId, collision.xSeparation);
|
|
303251
|
-
} else {
|
|
303252
|
-
addConstraint(collision.labelId, collision.boxId, collision.xSeparation);
|
|
303253
|
-
}
|
|
303254
|
-
}
|
|
303255
|
-
const constraints = [...constraintMap.entries()].map(([key, minSep]) => {
|
|
303256
|
-
const pipeIndex = key.indexOf("|");
|
|
303257
|
-
return {
|
|
303258
|
-
leftId: key.slice(0, pipeIndex),
|
|
303259
|
-
rightId: key.slice(pipeIndex + 1),
|
|
303260
|
-
minSep
|
|
303261
|
-
};
|
|
303262
|
-
});
|
|
303263
|
-
const allIds = new Set;
|
|
303264
|
-
const adjacency = new Map;
|
|
303265
|
-
for (const { leftId, rightId } of constraints) {
|
|
303266
|
-
allIds.add(leftId);
|
|
303267
|
-
allIds.add(rightId);
|
|
303268
|
-
if (!adjacency.has(leftId))
|
|
303269
|
-
adjacency.set(leftId, new Set);
|
|
303270
|
-
if (!adjacency.has(rightId))
|
|
303271
|
-
adjacency.set(rightId, new Set);
|
|
303272
|
-
adjacency.get(leftId).add(rightId);
|
|
303273
|
-
adjacency.get(rightId).add(leftId);
|
|
303274
|
-
}
|
|
303275
|
-
const visited = new Set;
|
|
303276
|
-
const result = new Map;
|
|
303277
|
-
for (const startId of allIds) {
|
|
303278
|
-
if (visited.has(startId))
|
|
303279
|
-
continue;
|
|
303280
|
-
const group = [];
|
|
303281
|
-
const queue = [startId];
|
|
303282
|
-
visited.add(startId);
|
|
303283
|
-
while (queue.length) {
|
|
303284
|
-
const compId = queue.shift();
|
|
303285
|
-
group.push(compId);
|
|
303286
|
-
for (const neighbor of adjacency.get(compId) ?? []) {
|
|
303287
|
-
if (!visited.has(neighbor)) {
|
|
303288
|
-
visited.add(neighbor);
|
|
303289
|
-
queue.push(neighbor);
|
|
303290
|
-
}
|
|
303291
|
-
}
|
|
303292
|
-
}
|
|
303293
|
-
group.sort((idA, idB) => (compById.get(idA)?.schX ?? 0) - (compById.get(idB)?.schX ?? 0));
|
|
303294
|
-
const groupIds = new Set(group);
|
|
303295
|
-
const groupConstraints = constraints.filter((c3) => groupIds.has(c3.leftId) && groupIds.has(c3.rightId));
|
|
303296
|
-
const assigned = new Map;
|
|
303297
|
-
for (const compId of group) {
|
|
303298
|
-
let newX = compById.get(compId)?.schX ?? 0;
|
|
303299
|
-
for (const { leftId, rightId, minSep } of groupConstraints) {
|
|
303300
|
-
if (rightId === compId && assigned.has(leftId)) {
|
|
303301
|
-
newX = Math.max(newX, assigned.get(leftId) + minSep);
|
|
303302
|
-
}
|
|
303303
|
-
}
|
|
303304
|
-
assigned.set(compId, newX);
|
|
303305
|
-
}
|
|
303306
|
-
const totalPush = [...assigned.entries()].reduce((sum, [compId, newX]) => sum + (newX - (compById.get(compId)?.schX ?? 0)), 0);
|
|
303307
|
-
if (totalPush > 0.000000001) {
|
|
303308
|
-
const pullBack = totalPush / group.length;
|
|
303309
|
-
const shifted = new Map;
|
|
303310
|
-
for (const compId of group) {
|
|
303311
|
-
let newX = (compById.get(compId)?.schX ?? 0) - pullBack;
|
|
303312
|
-
for (const { leftId, rightId, minSep } of groupConstraints) {
|
|
303313
|
-
if (rightId === compId && shifted.has(leftId)) {
|
|
303314
|
-
newX = Math.max(newX, shifted.get(leftId) + minSep);
|
|
303315
|
-
}
|
|
303316
|
-
}
|
|
303317
|
-
shifted.set(compId, newX);
|
|
303318
|
-
}
|
|
303319
|
-
const maxDisplacement = (positions) => [...positions.entries()].reduce((currentMax, [compId, newX]) => Math.max(currentMax, Math.abs(newX - (compById.get(compId)?.schX ?? 0))), 0);
|
|
303320
|
-
if (maxDisplacement(shifted) < maxDisplacement(assigned)) {
|
|
303321
|
-
for (const [compId, newX] of shifted)
|
|
303322
|
-
assigned.set(compId, newX);
|
|
303323
|
-
}
|
|
303324
|
-
}
|
|
303325
|
-
for (const [compId, newX] of assigned) {
|
|
303326
|
-
const comp = compById.get(compId);
|
|
303327
|
-
if (!comp || Math.abs(newX - comp.schX) < 0.000000001)
|
|
303328
|
-
continue;
|
|
303329
|
-
result.set(compId, {
|
|
303330
|
-
componentName: comp.sourceComponentName ?? compId,
|
|
303331
|
-
newSchX: Math.round(newX * 100) / 100,
|
|
303332
|
-
newSchY: Math.round(comp.schY * 100) / 100
|
|
303333
|
-
});
|
|
303334
|
-
}
|
|
303335
|
-
}
|
|
303336
|
-
return result;
|
|
303337
|
-
}
|
|
303338
|
-
buildNetLabelsByComponentId(circuitJson) {
|
|
303339
|
-
const MATCH_EPSILON = 0.0001;
|
|
303340
|
-
const portPositions = [];
|
|
303341
|
-
for (const element of circuitJson) {
|
|
303342
|
-
if (element.type !== "schematic_port")
|
|
303343
|
-
continue;
|
|
303344
|
-
const port = element;
|
|
303345
|
-
if (!port.schematic_component_id)
|
|
303346
|
-
continue;
|
|
303347
|
-
portPositions.push({
|
|
303348
|
-
componentId: port.schematic_component_id,
|
|
303349
|
-
cx: port.center.x,
|
|
303350
|
-
cy: port.center.y,
|
|
303351
|
-
schematicSheetId: port.schematic_sheet_id
|
|
303352
|
-
});
|
|
303353
|
-
}
|
|
303354
|
-
const result = new Map;
|
|
303355
|
-
for (const element of circuitJson) {
|
|
303356
|
-
if (element.type !== "schematic_net_label")
|
|
303357
|
-
continue;
|
|
303358
|
-
const label = element;
|
|
303359
|
-
if (!label.anchor_position)
|
|
303360
|
-
continue;
|
|
303361
|
-
const { x: anchorX, y: anchorY } = label.anchor_position;
|
|
303362
|
-
const matches = portPositions.filter((port) => port.schematicSheetId === label.schematic_sheet_id && Math.hypot(port.cx - anchorX, port.cy - anchorY) < MATCH_EPSILON);
|
|
303363
|
-
if (matches.length === 0)
|
|
303364
|
-
continue;
|
|
303365
|
-
const componentIds = new Set(matches.map((match) => match.componentId));
|
|
303366
|
-
if (componentIds.size !== 1)
|
|
303367
|
-
continue;
|
|
303368
|
-
const componentId = matches[0].componentId;
|
|
303369
|
-
const labels = result.get(componentId) ?? [];
|
|
303370
|
-
labels.push(label);
|
|
303371
|
-
result.set(componentId, labels);
|
|
303372
|
-
}
|
|
303373
|
-
return result;
|
|
303374
|
-
}
|
|
303375
|
-
getNetLabelBounds(label) {
|
|
303376
|
-
const anchorSide = label.anchor_side;
|
|
303377
|
-
const isVertical3 = anchorSide === "top" || anchorSide === "bottom";
|
|
303378
|
-
if (isVertical3) {
|
|
303379
|
-
const anchorY = label.anchor_position?.y ?? label.center.y;
|
|
303380
|
-
const textHalfExtent = (label.text?.length ?? 8) * this.SCH_CHAR_WIDTH / 2 + this.LABEL_BOUNDS_SLACK;
|
|
303381
|
-
const left = label.center.x - this.LABEL_HALF_HEIGHT;
|
|
303382
|
-
const right = label.center.x + this.LABEL_HALF_HEIGHT;
|
|
303383
|
-
if (anchorSide === "top") {
|
|
303384
|
-
return {
|
|
303385
|
-
left,
|
|
303386
|
-
right,
|
|
303387
|
-
top: anchorY,
|
|
303388
|
-
bottom: anchorY - textHalfExtent * 2
|
|
303389
|
-
};
|
|
303390
|
-
}
|
|
303391
|
-
return { left, right, top: anchorY + textHalfExtent * 2, bottom: anchorY };
|
|
303392
|
-
}
|
|
303393
|
-
const anchorX = label.anchor_position?.x ?? label.center.x;
|
|
303394
|
-
const halfWidth = Math.abs(label.center.x - anchorX);
|
|
303395
|
-
const farHalfWidth = halfWidth + this.LABEL_BOUNDS_SLACK;
|
|
303396
|
-
const top = label.center.y + this.LABEL_HALF_HEIGHT;
|
|
303397
|
-
const bottom = label.center.y - this.LABEL_HALF_HEIGHT;
|
|
303398
|
-
if (label.center.x >= anchorX) {
|
|
303399
|
-
return {
|
|
303400
|
-
left: anchorX,
|
|
303401
|
-
right: label.center.x + farHalfWidth,
|
|
303402
|
-
top,
|
|
303403
|
-
bottom
|
|
303404
|
-
};
|
|
303405
|
-
}
|
|
303406
|
-
return { left: label.center.x - farHalfWidth, right: anchorX, top, bottom };
|
|
303407
|
-
}
|
|
303408
|
-
static netLabelCollisionToString(issue) {
|
|
303409
|
-
const pairAttrs = issue.pairs.map((pair, i2) => `pair${i2 + 1}="${pair.comp1Name}/${pair.comp2Name}"`).join(" ");
|
|
303410
|
-
const moves = issue.moves.map((move) => ` <Move componentName="${move.componentName}" newSchX="${move.newSchX}" newSchY="${move.newSchY}" />`);
|
|
303411
|
-
return [
|
|
303412
|
-
`<ComponentNetLabelCollision ${pairAttrs}>`,
|
|
303413
|
-
` <SuggestedFix note="Apply all moves simultaneously. Set schAutoLayoutEnabled on your circuit.">`,
|
|
303414
|
-
...moves,
|
|
303415
|
-
` </SuggestedFix>`,
|
|
303416
|
-
`</ComponentNetLabelCollision>`
|
|
303417
|
-
].join(`
|
|
303418
|
-
`);
|
|
303419
|
-
}
|
|
303420
|
-
}
|
|
303421
|
-
|
|
303422
|
-
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/ComponentPinAlignmentSolver/ComponentPinAlignmentSolver.ts
|
|
303423
|
-
import { BaseSolver as BaseSolver14 } from "@tscircuit/solver-utils";
|
|
303424
|
-
class ComponentPinAlignmentSolver extends BaseSolver14 {
|
|
303425
|
-
static ALIGNMENT_EPSILON = 0.01;
|
|
303426
|
-
ctx;
|
|
303427
|
-
out;
|
|
303428
|
-
constructor({
|
|
303429
|
-
ctx,
|
|
303430
|
-
issues
|
|
303431
|
-
}) {
|
|
303432
|
-
super();
|
|
303433
|
-
this.ctx = ctx;
|
|
303434
|
-
this.out = issues;
|
|
303435
|
-
}
|
|
303436
|
-
_step() {
|
|
303437
|
-
const portsById = new Map(this.ctx.circuitJson.filter((el3) => el3.type === "schematic_port").map((port) => [port.schematic_port_id, port]));
|
|
303438
|
-
const placementBySchematicComponentId = new Map(this.ctx.componentPlacements.flatMap((placement) => placement.schematicComponentId ? [[placement.schematicComponentId, placement]] : []));
|
|
303439
|
-
const pinPairsByComponentPair = new Map;
|
|
303440
|
-
for (const trace of this.ctx.circuitJson.filter((el3) => el3.type === "schematic_trace")) {
|
|
303441
|
-
const pinPair = this.getTracePinPair(trace, portsById);
|
|
303442
|
-
if (!pinPair)
|
|
303443
|
-
continue;
|
|
303444
|
-
const firstComponentId = pinPair.firstPort.schematic_component_id;
|
|
303445
|
-
const secondComponentId = pinPair.secondPort.schematic_component_id;
|
|
303446
|
-
if (!firstComponentId || !secondComponentId || firstComponentId === secondComponentId) {
|
|
303447
|
-
continue;
|
|
303448
|
-
}
|
|
303449
|
-
const orderedPair = firstComponentId < secondComponentId ? pinPair : {
|
|
303450
|
-
firstPort: pinPair.secondPort,
|
|
303451
|
-
secondPort: pinPair.firstPort
|
|
303452
|
-
};
|
|
303453
|
-
const key = [firstComponentId, secondComponentId].sort().join("\x00");
|
|
303454
|
-
const pairs3 = pinPairsByComponentPair.get(key) ?? [];
|
|
303455
|
-
const isDuplicate = pairs3.some((pair) => pair.firstPort.schematic_port_id === orderedPair.firstPort.schematic_port_id && pair.secondPort.schematic_port_id === orderedPair.secondPort.schematic_port_id);
|
|
303456
|
-
if (!isDuplicate)
|
|
303457
|
-
pairs3.push(orderedPair);
|
|
303458
|
-
pinPairsByComponentPair.set(key, pairs3);
|
|
303459
|
-
}
|
|
303460
|
-
for (const pinPairs of pinPairsByComponentPair.values()) {
|
|
303461
|
-
const issue = this.findVerticalShiftIssue(pinPairs, placementBySchematicComponentId);
|
|
303462
|
-
if (issue)
|
|
303463
|
-
this.out.push(issue);
|
|
303464
|
-
}
|
|
303465
|
-
this.solved = true;
|
|
303466
|
-
}
|
|
303467
|
-
getTracePinPair(trace, portsById) {
|
|
303468
|
-
const firstEdge = trace.edges?.[0];
|
|
303469
|
-
const lastEdge = trace.edges?.at(-1);
|
|
303470
|
-
if (!firstEdge || !lastEdge)
|
|
303471
|
-
return;
|
|
303472
|
-
const firstPortId = firstEdge.from_schematic_port_id ?? firstEdge.to_schematic_port_id;
|
|
303473
|
-
const secondPortId = lastEdge.to_schematic_port_id ?? lastEdge.from_schematic_port_id;
|
|
303474
|
-
const ports = [...portsById.values()];
|
|
303475
|
-
const firstPort = firstPortId ? portsById.get(firstPortId) : this.findPortAtPoint(ports, firstEdge.from, trace.schematic_sheet_id);
|
|
303476
|
-
const secondPort = secondPortId ? portsById.get(secondPortId) : this.findPortAtPoint(ports, lastEdge.to, trace.schematic_sheet_id);
|
|
303477
|
-
if (!firstPort || !secondPort)
|
|
303478
|
-
return;
|
|
303479
|
-
if (firstPort.schematic_port_id === secondPort.schematic_port_id)
|
|
303480
|
-
return;
|
|
303481
|
-
if (firstPort.schematic_sheet_id !== secondPort.schematic_sheet_id)
|
|
303482
|
-
return;
|
|
303483
|
-
return { firstPort, secondPort };
|
|
303484
|
-
}
|
|
303485
|
-
findPortAtPoint(ports, point9, schematicSheetId) {
|
|
303486
|
-
const { ALIGNMENT_EPSILON } = ComponentPinAlignmentSolver;
|
|
303487
|
-
return ports.find((port) => port.schematic_sheet_id === schematicSheetId && Math.abs(port.center.x - point9.x) <= ALIGNMENT_EPSILON && Math.abs(port.center.y - point9.y) <= ALIGNMENT_EPSILON);
|
|
303488
|
-
}
|
|
303489
|
-
findVerticalShiftIssue(pinPairs, placementBySchematicComponentId) {
|
|
303490
|
-
const firstPair = pinPairs[0];
|
|
303491
|
-
if (!firstPair)
|
|
303492
|
-
return;
|
|
303493
|
-
const firstPlacement = placementBySchematicComponentId.get(firstPair.firstPort.schematic_component_id);
|
|
303494
|
-
const secondPlacement = placementBySchematicComponentId.get(firstPair.secondPort.schematic_component_id);
|
|
303495
|
-
if (!firstPlacement || !secondPlacement)
|
|
303496
|
-
return;
|
|
303497
|
-
const [leftPlacement, rightPlacement] = firstPlacement.schX <= secondPlacement.schX ? [firstPlacement, secondPlacement] : [secondPlacement, firstPlacement];
|
|
303498
|
-
const horizontalPairs = pinPairs.flatMap((pair) => {
|
|
303499
|
-
const [leftPort, rightPort] = pair.firstPort.center.x <= pair.secondPort.center.x ? [pair.firstPort, pair.secondPort] : [pair.secondPort, pair.firstPort];
|
|
303500
|
-
return leftPort.facing_direction === "right" && rightPort.facing_direction === "left" ? [{ leftPort, rightPort }] : [];
|
|
303501
|
-
});
|
|
303502
|
-
if (horizontalPairs.length < 2)
|
|
303503
|
-
return;
|
|
303504
|
-
const { ALIGNMENT_EPSILON } = ComponentPinAlignmentSolver;
|
|
303505
|
-
const currentlyAlignedPinCount = horizontalPairs.filter(({ leftPort, rightPort }) => Math.abs(leftPort.center.y - rightPort.center.y) <= ALIGNMENT_EPSILON).length;
|
|
303506
|
-
const candidateGroups = [];
|
|
303507
|
-
for (const pair of horizontalPairs) {
|
|
303508
|
-
const deltaSchY2 = pair.leftPort.center.y - pair.rightPort.center.y;
|
|
303509
|
-
if (Math.abs(deltaSchY2) <= ALIGNMENT_EPSILON)
|
|
303510
|
-
continue;
|
|
303511
|
-
const group = candidateGroups.find((candidate) => Math.abs(candidate.deltaSchY - deltaSchY2) <= ALIGNMENT_EPSILON);
|
|
303512
|
-
if (group)
|
|
303513
|
-
group.pairs.push(pair);
|
|
303514
|
-
else
|
|
303515
|
-
candidateGroups.push({ deltaSchY: deltaSchY2, pairs: [pair] });
|
|
303516
|
-
}
|
|
303517
|
-
const bestCandidate = candidateGroups.sort((a2, b3) => b3.pairs.length - a2.pairs.length || Math.abs(a2.deltaSchY) - Math.abs(b3.deltaSchY))[0];
|
|
303518
|
-
if (!bestCandidate || bestCandidate.pairs.length < 2 || bestCandidate.pairs.length <= currentlyAlignedPinCount) {
|
|
303519
|
-
return;
|
|
303520
|
-
}
|
|
303521
|
-
const targetName = rightPlacement.sourceComponentName ?? rightPlacement.schematicComponentId ?? "component";
|
|
303522
|
-
const deltaSchY = bestCandidate.deltaSchY;
|
|
303523
|
-
const newSchY = rightPlacement.schY + deltaSchY;
|
|
303524
|
-
return {
|
|
303525
|
-
lineItemType: "ComponentPinsWouldAlignWithVerticalShift",
|
|
303526
|
-
firstComponent: leftPlacement,
|
|
303527
|
-
secondComponent: rightPlacement,
|
|
303528
|
-
targetComponent: rightPlacement,
|
|
303529
|
-
deltaSchY,
|
|
303530
|
-
newSchY,
|
|
303531
|
-
currentlyAlignedPinCount,
|
|
303532
|
-
alignedPinCount: bestCandidate.pairs.length,
|
|
303533
|
-
alignedPinPairs: bestCandidate.pairs.map(({ leftPort, rightPort }) => ({
|
|
303534
|
-
firstPin: this.getPinLabel(leftPort),
|
|
303535
|
-
secondPin: this.getPinLabel(rightPort)
|
|
303536
|
-
})),
|
|
303537
|
-
message: `shift ${targetName} vertically by ${fmtDelta(deltaSchY)} to align ${bestCandidate.pairs.length} connected pin pairs`
|
|
303538
|
-
};
|
|
303539
|
-
}
|
|
303540
|
-
getPinLabel(port) {
|
|
303541
|
-
return port.display_pin_label ?? port.pin_number?.toString();
|
|
303542
|
-
}
|
|
303543
|
-
static issueToString(issue) {
|
|
303544
|
-
const attrs = [];
|
|
303545
|
-
addAttr(attrs, "firstComponentName", issue.firstComponent.sourceComponentName);
|
|
303546
|
-
addAttr(attrs, "secondComponentName", issue.secondComponent.sourceComponentName);
|
|
303547
|
-
addAttr(attrs, "targetComponentName", issue.targetComponent.sourceComponentName);
|
|
303548
|
-
addAttr(attrs, "deltaSchY", issue.deltaSchY);
|
|
303549
|
-
addAttr(attrs, "newSchY", issue.newSchY);
|
|
303550
|
-
addAttr(attrs, "currentlyAlignedPinCount", issue.currentlyAlignedPinCount);
|
|
303551
|
-
addAttr(attrs, "alignedPinCount", issue.alignedPinCount);
|
|
303552
|
-
addAttr(attrs, "message", issue.message);
|
|
303553
|
-
return `<ComponentPinsWouldAlignWithVerticalShift ${attrs.join(" ")} />`;
|
|
303554
|
-
}
|
|
303555
|
-
}
|
|
303556
|
-
|
|
303557
303811
|
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/solvers/SchematicPlacementPipeline/SchematicPlacementPipeline.ts
|
|
303558
303812
|
class SchematicPlacementPipeline extends BasePipelineSolver2 {
|
|
303559
303813
|
ctx;
|
|
@@ -303583,6 +303837,9 @@ class SchematicPlacementPipeline extends BasePipelineSolver2 {
|
|
|
303583
303837
|
definePipelineStep3("ComponentPinAlignmentSolver", ComponentPinAlignmentSolver, (p3) => [
|
|
303584
303838
|
{ ctx: p3.ctx, issues: p3.issues }
|
|
303585
303839
|
]),
|
|
303840
|
+
definePipelineStep3("TraceSimplificationSolver", TraceSimplificationSolver, (p3) => [
|
|
303841
|
+
{ ctx: p3.ctx, issues: p3.issues }
|
|
303842
|
+
]),
|
|
303586
303843
|
definePipelineStep3("ComponentNetLabelCollisionSolver", ComponentNetLabelCollisionSolver, (p3) => [
|
|
303587
303844
|
{ ctx: p3.ctx, issues: p3.issues }
|
|
303588
303845
|
])
|
|
@@ -303598,11 +303855,153 @@ class SchematicPlacementPipeline extends BasePipelineSolver2 {
|
|
|
303598
303855
|
}
|
|
303599
303856
|
}
|
|
303600
303857
|
|
|
303858
|
+
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/utils/issue-context.ts
|
|
303859
|
+
var POSITION_EPSILON = 0.01;
|
|
303860
|
+
var getRelevantPlacementsForIssues = ({
|
|
303861
|
+
issues,
|
|
303862
|
+
componentPlacements,
|
|
303863
|
+
circuitJson
|
|
303864
|
+
}) => {
|
|
303865
|
+
const relevantPlacements = new Set;
|
|
303866
|
+
const placementByComponentId = new Map(componentPlacements.flatMap((placement) => placement.schematicComponentId ? [[placement.schematicComponentId, placement]] : []));
|
|
303867
|
+
const placementsByName = new Map;
|
|
303868
|
+
for (const placement of componentPlacements) {
|
|
303869
|
+
if (!placement.sourceComponentName)
|
|
303870
|
+
continue;
|
|
303871
|
+
const namedPlacements = placementsByName.get(placement.sourceComponentName);
|
|
303872
|
+
if (namedPlacements)
|
|
303873
|
+
namedPlacements.push(placement);
|
|
303874
|
+
else
|
|
303875
|
+
placementsByName.set(placement.sourceComponentName, [placement]);
|
|
303876
|
+
}
|
|
303877
|
+
const addPlacement = (placement) => {
|
|
303878
|
+
if (!placement)
|
|
303879
|
+
return;
|
|
303880
|
+
const canonicalPlacement = placement.schematicComponentId ? placementByComponentId.get(placement.schematicComponentId) : componentPlacements.find((candidate) => candidate.sourceComponentId === placement.sourceComponentId && candidate.schX === placement.schX && candidate.schY === placement.schY);
|
|
303881
|
+
if (canonicalPlacement)
|
|
303882
|
+
relevantPlacements.add(canonicalPlacement);
|
|
303883
|
+
};
|
|
303884
|
+
const addComponentName = (componentName, schematicSheetId) => {
|
|
303885
|
+
if (!componentName)
|
|
303886
|
+
return;
|
|
303887
|
+
for (const placement of placementsByName.get(componentName) ?? []) {
|
|
303888
|
+
if (schematicSheetId === undefined || placement.schematicSheetId === schematicSheetId) {
|
|
303889
|
+
relevantPlacements.add(placement);
|
|
303890
|
+
}
|
|
303891
|
+
}
|
|
303892
|
+
};
|
|
303893
|
+
for (const issue of issues) {
|
|
303894
|
+
switch (issue.lineItemType) {
|
|
303895
|
+
case "ComponentOverlap":
|
|
303896
|
+
addPlacement(issue.firstComponent);
|
|
303897
|
+
addPlacement(issue.secondComponent);
|
|
303898
|
+
break;
|
|
303899
|
+
case "SchematicBoxHasALotOfSurroundingWhitespace":
|
|
303900
|
+
case "CapacitorSymbolHorizontal":
|
|
303901
|
+
case "PinHeaderSchematicBoxTooWide":
|
|
303902
|
+
case "GenericSchematicBoxTooWide":
|
|
303903
|
+
case "SchematicBoxInnerLabelCollision":
|
|
303904
|
+
case "SchematicPinPaddingToEdgeTooLarge":
|
|
303905
|
+
addPlacement(issue.schematicBox);
|
|
303906
|
+
break;
|
|
303907
|
+
case "VerboseSchematicNetLabel":
|
|
303908
|
+
for (const pin of issue.involvedPins) {
|
|
303909
|
+
addComponentName(getComponentNameFromPin(pin), issue.schematicSheetId);
|
|
303910
|
+
}
|
|
303911
|
+
break;
|
|
303912
|
+
case "DiodeResistorNotAligned":
|
|
303913
|
+
addPlacement(issue.diodeSchematicBox);
|
|
303914
|
+
addPlacement(issue.resistorSchematicBox);
|
|
303915
|
+
break;
|
|
303916
|
+
case "ComponentPinsWouldAlignWithVerticalShift":
|
|
303917
|
+
addPlacement(issue.firstComponent);
|
|
303918
|
+
addPlacement(issue.secondComponent);
|
|
303919
|
+
addPlacement(issue.targetComponent);
|
|
303920
|
+
break;
|
|
303921
|
+
case "TraceCanBeSimplifiedByMovingComponent":
|
|
303922
|
+
addPlacement(issue.targetComponent);
|
|
303923
|
+
for (const placement of getTraceEndpointPlacements({
|
|
303924
|
+
schematicTraceId: issue.schematicTraceId,
|
|
303925
|
+
circuitJson,
|
|
303926
|
+
placementByComponentId
|
|
303927
|
+
})) {
|
|
303928
|
+
relevantPlacements.add(placement);
|
|
303929
|
+
}
|
|
303930
|
+
break;
|
|
303931
|
+
case "ComponentNetLabelCollision":
|
|
303932
|
+
addPlacement(issue.firstComponent);
|
|
303933
|
+
addPlacement(issue.secondComponent);
|
|
303934
|
+
break;
|
|
303935
|
+
case "ComponentBoxNetLabelCollision":
|
|
303936
|
+
addPlacement(issue.boxComponent);
|
|
303937
|
+
addPlacement(issue.labelComponent);
|
|
303938
|
+
break;
|
|
303939
|
+
case "NetLabelCollision":
|
|
303940
|
+
for (const pair of issue.pairs) {
|
|
303941
|
+
addComponentName(pair.comp1Name, issue.schematicSheetId);
|
|
303942
|
+
addComponentName(pair.comp2Name, issue.schematicSheetId);
|
|
303943
|
+
}
|
|
303944
|
+
for (const move of issue.moves) {
|
|
303945
|
+
addComponentName(move.componentName, issue.schematicSheetId);
|
|
303946
|
+
}
|
|
303947
|
+
break;
|
|
303948
|
+
}
|
|
303949
|
+
}
|
|
303950
|
+
return componentPlacements.filter((placement) => relevantPlacements.has(placement));
|
|
303951
|
+
};
|
|
303952
|
+
var getIssueSchematicSheetContext = (issue) => {
|
|
303953
|
+
if ("schematicSheetId" in issue || "schematicSheetName" in issue) {
|
|
303954
|
+
const schematicSheetId = "schematicSheetId" in issue && typeof issue.schematicSheetId === "string" ? issue.schematicSheetId : undefined;
|
|
303955
|
+
const schematicSheetName = "schematicSheetName" in issue && typeof issue.schematicSheetName === "string" ? issue.schematicSheetName : undefined;
|
|
303956
|
+
if (schematicSheetId || schematicSheetName) {
|
|
303957
|
+
return { schematicSheetId, schematicSheetName };
|
|
303958
|
+
}
|
|
303959
|
+
}
|
|
303960
|
+
for (const value of Object.values(issue)) {
|
|
303961
|
+
if (isSchematicBoxPlacement(value)) {
|
|
303962
|
+
return {
|
|
303963
|
+
schematicSheetId: value.schematicSheetId,
|
|
303964
|
+
schematicSheetName: value.schematicSheetName
|
|
303965
|
+
};
|
|
303966
|
+
}
|
|
303967
|
+
}
|
|
303968
|
+
return {};
|
|
303969
|
+
};
|
|
303970
|
+
var getComponentNameFromPin = (pin) => {
|
|
303971
|
+
const separatorIndex = pin.lastIndexOf(".");
|
|
303972
|
+
return separatorIndex === -1 ? pin : pin.slice(0, separatorIndex);
|
|
303973
|
+
};
|
|
303974
|
+
var isSchematicBoxPlacement = (value) => Boolean(value && typeof value === "object" && "positionAnchor" in value && value.positionAnchor === "center" && "schX" in value && "schY" in value && "width" in value && "height" in value);
|
|
303975
|
+
var getTraceEndpointPlacements = ({
|
|
303976
|
+
schematicTraceId,
|
|
303977
|
+
circuitJson,
|
|
303978
|
+
placementByComponentId
|
|
303979
|
+
}) => {
|
|
303980
|
+
const trace = circuitJson.find((element) => element.type === "schematic_trace" && element.schematic_trace_id === schematicTraceId);
|
|
303981
|
+
const firstEdge = trace?.edges[0];
|
|
303982
|
+
const lastEdge = trace?.edges.at(-1);
|
|
303983
|
+
if (!trace || !firstEdge || !lastEdge)
|
|
303984
|
+
return [];
|
|
303985
|
+
const ports = circuitJson.filter((element) => element.type === "schematic_port");
|
|
303986
|
+
const portsById = new Map(ports.map((port) => [port.schematic_port_id, port]));
|
|
303987
|
+
const endpointPorts = [
|
|
303988
|
+
firstEdge.from_schematic_port_id ? portsById.get(firstEdge.from_schematic_port_id) : findPortAtPoint(ports, firstEdge.from, trace.schematic_sheet_id),
|
|
303989
|
+
lastEdge.to_schematic_port_id ? portsById.get(lastEdge.to_schematic_port_id) : findPortAtPoint(ports, lastEdge.to, trace.schematic_sheet_id)
|
|
303990
|
+
];
|
|
303991
|
+
return endpointPorts.flatMap((port) => {
|
|
303992
|
+
const placement = port?.schematic_component_id ? placementByComponentId.get(port.schematic_component_id) : undefined;
|
|
303993
|
+
return placement ? [placement] : [];
|
|
303994
|
+
});
|
|
303995
|
+
};
|
|
303996
|
+
var findPortAtPoint = (ports, point9, schematicSheetId) => ports.find((port) => port.schematic_sheet_id === schematicSheetId && Math.abs(port.center.x - point9.x) <= POSITION_EPSILON && Math.abs(port.center.y - point9.y) <= POSITION_EPSILON);
|
|
303997
|
+
|
|
303601
303998
|
// node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/analyze-schematic-placement.ts
|
|
303602
303999
|
class SchematicPlacementAnalysis {
|
|
303603
304000
|
lineItems;
|
|
303604
|
-
|
|
304001
|
+
groupBySchematicSheet;
|
|
304002
|
+
constructor(lineItems, groupBySchematicSheet = false) {
|
|
303605
304003
|
this.lineItems = lineItems;
|
|
304004
|
+
this.groupBySchematicSheet = groupBySchematicSheet;
|
|
303606
304005
|
}
|
|
303607
304006
|
getLineItems() {
|
|
303608
304007
|
return this.lineItems;
|
|
@@ -303618,7 +304017,6 @@ class SchematicPlacementAnalysis {
|
|
|
303618
304017
|
addAttr(attrs, "schY", lineItem.schY);
|
|
303619
304018
|
addAttr(attrs, "width", lineItem.width);
|
|
303620
304019
|
addAttr(attrs, "height", lineItem.height);
|
|
303621
|
-
addAttr(attrs, "schSheetName", lineItem.schematicSheetName);
|
|
303622
304020
|
return `<SchematicBoxPlacement ${attrs.join(" ")} />`;
|
|
303623
304021
|
}
|
|
303624
304022
|
schematicIssuesToString(issue) {
|
|
@@ -303640,6 +304038,8 @@ class SchematicPlacementAnalysis {
|
|
|
303640
304038
|
return DiodeResistorAlignmentSolver.issueToString(issue);
|
|
303641
304039
|
case "ComponentPinsWouldAlignWithVerticalShift":
|
|
303642
304040
|
return ComponentPinAlignmentSolver.issueToString(issue);
|
|
304041
|
+
case "TraceCanBeSimplifiedByMovingComponent":
|
|
304042
|
+
return TraceSimplificationSolver.issueToString(issue);
|
|
303643
304043
|
case "NetLabelCollision":
|
|
303644
304044
|
return ComponentNetLabelCollisionSolver.netLabelCollisionToString(issue);
|
|
303645
304045
|
default:
|
|
@@ -303649,28 +304049,71 @@ class SchematicPlacementAnalysis {
|
|
|
303649
304049
|
toString() {
|
|
303650
304050
|
const schematicBoxPlacements = this.lineItems.filter((lineItem) => lineItem.lineItemType === "SchematicBoxPlacement");
|
|
303651
304051
|
const issues = this.lineItems.flatMap((lineItem) => lineItem.lineItemType === "SchematicPlacementIssues" ? lineItem.issues : []);
|
|
303652
|
-
|
|
303653
|
-
"
|
|
303654
|
-
|
|
303655
|
-
|
|
303656
|
-
|
|
303657
|
-
|
|
303658
|
-
|
|
303659
|
-
|
|
303660
|
-
|
|
303661
|
-
|
|
304052
|
+
if (issues.length === 0)
|
|
304053
|
+
return "";
|
|
304054
|
+
if (!this.groupBySchematicSheet) {
|
|
304055
|
+
return this.issueContextToLines(schematicBoxPlacements, issues).join(`
|
|
304056
|
+
`);
|
|
304057
|
+
}
|
|
304058
|
+
const groups = new Map;
|
|
304059
|
+
const getOrCreateGroup = (context) => {
|
|
304060
|
+
const key = context.schematicSheetId ?? context.schematicSheetName ?? "__default__";
|
|
304061
|
+
const existingGroup = groups.get(key);
|
|
304062
|
+
if (existingGroup)
|
|
304063
|
+
return existingGroup;
|
|
304064
|
+
const group = { ...context, placements: [], issues: [] };
|
|
304065
|
+
groups.set(key, group);
|
|
304066
|
+
return group;
|
|
304067
|
+
};
|
|
304068
|
+
for (const issue of issues) {
|
|
304069
|
+
getOrCreateGroup(getIssueSchematicSheetContext(issue)).issues.push(issue);
|
|
304070
|
+
}
|
|
304071
|
+
for (const placement of schematicBoxPlacements) {
|
|
304072
|
+
getOrCreateGroup({
|
|
304073
|
+
schematicSheetId: placement.schematicSheetId,
|
|
304074
|
+
schematicSheetName: placement.schematicSheetName
|
|
304075
|
+
}).placements.push(placement);
|
|
304076
|
+
}
|
|
304077
|
+
return [...groups.values()].flatMap((group) => {
|
|
304078
|
+
const sheetAttrs = [];
|
|
304079
|
+
addAttr(sheetAttrs, "name", group.schematicSheetName);
|
|
304080
|
+
addAttr(sheetAttrs, "id", group.schematicSheetId);
|
|
304081
|
+
return [
|
|
304082
|
+
`<SchematicSheet${sheetAttrs.length > 0 ? ` ${sheetAttrs.join(" ")}` : ""}>`,
|
|
304083
|
+
...this.issueContextToLines(group.placements, group.issues),
|
|
304084
|
+
"</SchematicSheet>"
|
|
304085
|
+
];
|
|
304086
|
+
}).join(`
|
|
303662
304087
|
`);
|
|
303663
304088
|
}
|
|
304089
|
+
issueContextToLines(placements, issues) {
|
|
304090
|
+
return [
|
|
304091
|
+
...placements.length > 0 ? [
|
|
304092
|
+
"<SchematicBoxPositions>",
|
|
304093
|
+
...placements.map(this.schematicBoxPlacementsToString),
|
|
304094
|
+
"</SchematicBoxPositions>"
|
|
304095
|
+
] : [],
|
|
304096
|
+
"<SchematicPlacementIssues>",
|
|
304097
|
+
...issues.map(this.schematicIssuesToString),
|
|
304098
|
+
"</SchematicPlacementIssues>"
|
|
304099
|
+
];
|
|
304100
|
+
}
|
|
303664
304101
|
}
|
|
303665
304102
|
var analyzeSchematicPlacement = (circuitJson) => {
|
|
303666
304103
|
const pipeline = new SchematicPlacementPipeline(circuitJson);
|
|
303667
304104
|
pipeline.solve();
|
|
303668
304105
|
const { issues, componentPlacements } = pipeline.getOutput();
|
|
304106
|
+
const relevantPlacements = getRelevantPlacementsForIssues({
|
|
304107
|
+
issues,
|
|
304108
|
+
componentPlacements,
|
|
304109
|
+
circuitJson
|
|
304110
|
+
});
|
|
303669
304111
|
const lineItems = [
|
|
303670
|
-
...
|
|
304112
|
+
...relevantPlacements,
|
|
303671
304113
|
...issues.length > 0 ? [{ lineItemType: "SchematicPlacementIssues", issues }] : []
|
|
303672
304114
|
];
|
|
303673
|
-
|
|
304115
|
+
const schematicSheetIds = new Set(circuitJson.flatMap((element) => ("schematic_sheet_id" in element) && typeof element.schematic_sheet_id === "string" ? [element.schematic_sheet_id] : []));
|
|
304116
|
+
return new SchematicPlacementAnalysis(lineItems, schematicSheetIds.size > 1);
|
|
303674
304117
|
};
|
|
303675
304118
|
// cli/check/schematic-placement/register.ts
|
|
303676
304119
|
var checkSchematicPlacement = async (file) => {
|