@tscircuit/matchpack 0.0.12 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +45 -1
- package/dist/index.js +182 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -47,6 +47,7 @@ type Chip = {
|
|
|
47
47
|
chipId: ChipId;
|
|
48
48
|
pins: PinId[];
|
|
49
49
|
size: Point;
|
|
50
|
+
isDecouplingCap?: boolean;
|
|
50
51
|
availableRotations?: Array<0 | 90 | 180 | 270>;
|
|
51
52
|
};
|
|
52
53
|
type Group = {
|
|
@@ -73,6 +74,7 @@ type InputProblem = {
|
|
|
73
74
|
chipGap: number;
|
|
74
75
|
/** The minimum gap between two partitions */
|
|
75
76
|
partitionGap: number;
|
|
77
|
+
inferDecouplingCaps?: boolean;
|
|
76
78
|
};
|
|
77
79
|
|
|
78
80
|
/**
|
|
@@ -104,6 +106,47 @@ declare class ChipPartitionsSolver extends BaseSolver {
|
|
|
104
106
|
visualize(): GraphicsObject;
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Identifies decoupling capacitor groups based on specific criteria:
|
|
111
|
+
* 1. Component has exactly 2 pins and restricted rotation (0/180 only or no rotation)
|
|
112
|
+
* 2. One pin indirectly connected to net with "y+" restriction, one to "y-" restriction
|
|
113
|
+
* 3. At least one pin directly connected to another chip
|
|
114
|
+
*/
|
|
115
|
+
|
|
116
|
+
interface DecouplingCapGroup {
|
|
117
|
+
decouplingCapGroupId: string;
|
|
118
|
+
mainChipId: ChipId;
|
|
119
|
+
decouplingCapChipIds: ChipId[];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Identify decoupling capacitor groups based on specific criteria:
|
|
123
|
+
* 1. Component has exactly 2 pins and restricted rotation (0/180 only or no rotation)
|
|
124
|
+
* 2. One pin indirectly connected to net with "y+" restriction, one to "y-" restriction
|
|
125
|
+
* 3. At least one pin directly connected to a chip (the main chip, typically a microcontroller)
|
|
126
|
+
*/
|
|
127
|
+
declare class IdentifyDecouplingCapsSolver extends BaseSolver {
|
|
128
|
+
inputProblem: InputProblem;
|
|
129
|
+
queuedChips: Chip[];
|
|
130
|
+
outputDecouplingCapGroups: DecouplingCapGroup[];
|
|
131
|
+
/** Quick lookup of groups by main chip for accumulation */
|
|
132
|
+
private groupsByMainChipId;
|
|
133
|
+
constructor(inputProblem: InputProblem);
|
|
134
|
+
/** Determine if chip is a 2-pin component with restricted rotation */
|
|
135
|
+
private isTwoPinRestrictedRotation;
|
|
136
|
+
/** Check that the two pins are on opposite Y sides (y+ and y-) */
|
|
137
|
+
private pinsOnOppositeYSides;
|
|
138
|
+
/** Get chips strongly connected (direct pin-to-pin) to this pin */
|
|
139
|
+
private getStronglyConnectedNeighborChips;
|
|
140
|
+
/** Find the main chip id for a decoupling capacitor candidate */
|
|
141
|
+
private findMainChipIdForCap;
|
|
142
|
+
/** Adds a decoupling capacitor to the group for the given main chip */
|
|
143
|
+
private addToGroup;
|
|
144
|
+
_step(): void;
|
|
145
|
+
visualize(): GraphicsObject;
|
|
146
|
+
getConstructorParams(): [InputProblem];
|
|
147
|
+
computeProgress(): number;
|
|
148
|
+
}
|
|
149
|
+
|
|
107
150
|
type Placement = Point & {
|
|
108
151
|
ccwRotationDegrees: number;
|
|
109
152
|
};
|
|
@@ -186,6 +229,7 @@ type PipelineStep<T extends new (...args: any[]) => BaseSolver> = {
|
|
|
186
229
|
onSolved?: (instance: LayoutPipelineSolver) => void;
|
|
187
230
|
};
|
|
188
231
|
declare class LayoutPipelineSolver extends BaseSolver {
|
|
232
|
+
identifyDecouplingCapsSolver?: IdentifyDecouplingCapsSolver;
|
|
189
233
|
chipPartitionsSolver?: ChipPartitionsSolver;
|
|
190
234
|
packInnerPartitionsSolver?: PackInnerPartitionsSolver;
|
|
191
235
|
partitionPackingSolver?: PartitionPackingSolver;
|
|
@@ -196,7 +240,7 @@ declare class LayoutPipelineSolver extends BaseSolver {
|
|
|
196
240
|
inputProblem: InputProblem;
|
|
197
241
|
chipPartitions?: ChipPartitionsSolver["partitions"];
|
|
198
242
|
packedPartitions?: PackedPartition[];
|
|
199
|
-
pipelineDef: (PipelineStep<typeof ChipPartitionsSolver> | PipelineStep<typeof PackInnerPartitionsSolver> | PipelineStep<typeof PartitionPackingSolver>)[];
|
|
243
|
+
pipelineDef: (PipelineStep<typeof IdentifyDecouplingCapsSolver> | PipelineStep<typeof ChipPartitionsSolver> | PipelineStep<typeof PackInnerPartitionsSolver> | PipelineStep<typeof PartitionPackingSolver>)[];
|
|
200
244
|
constructor(inputProblem: InputProblem);
|
|
201
245
|
currentPipelineStepIndex: number;
|
|
202
246
|
_step(): void;
|
package/dist/index.js
CHANGED
|
@@ -239,7 +239,19 @@ function visualizeInputProblem(inputProblem, basicLayout) {
|
|
|
239
239
|
|
|
240
240
|
// lib/solvers/LayoutPipelineSolver/doBasicInputProblemLayout.ts
|
|
241
241
|
import { pack } from "calculate-packing";
|
|
242
|
+
|
|
243
|
+
// lib/solvers/LayoutPipelineSolver/hashInputProblem.ts
|
|
244
|
+
var hashInputProblem = (inputProblem) => {
|
|
245
|
+
return JSON.stringify(inputProblem);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// lib/solvers/LayoutPipelineSolver/doBasicInputProblemLayout.ts
|
|
249
|
+
var cachedProblems = /* @__PURE__ */ new Map();
|
|
242
250
|
function doBasicInputProblemLayout(inputProblem) {
|
|
251
|
+
const problemHash = hashInputProblem(inputProblem);
|
|
252
|
+
if (cachedProblems.has(problemHash)) {
|
|
253
|
+
return structuredClone(cachedProblems.get(problemHash));
|
|
254
|
+
}
|
|
243
255
|
const components = Object.entries(inputProblem.chipMap).map(
|
|
244
256
|
([chipId, chip]) => {
|
|
245
257
|
const chipPins = chip.pins.map((pinId) => inputProblem.chipPinMap[pinId]);
|
|
@@ -292,11 +304,13 @@ function doBasicInputProblemLayout(inputProblem) {
|
|
|
292
304
|
ccwRotationDegrees: component.ccwRotationOffset
|
|
293
305
|
};
|
|
294
306
|
}
|
|
295
|
-
|
|
307
|
+
const outputLayout = {
|
|
296
308
|
chipPlacements,
|
|
297
309
|
groupPlacements: {}
|
|
298
310
|
// No groups for now
|
|
299
311
|
};
|
|
312
|
+
cachedProblems.set(problemHash, outputLayout);
|
|
313
|
+
return structuredClone(outputLayout);
|
|
300
314
|
}
|
|
301
315
|
|
|
302
316
|
// lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts
|
|
@@ -452,6 +466,158 @@ var ChipPartitionsSolver = class extends BaseSolver {
|
|
|
452
466
|
}
|
|
453
467
|
};
|
|
454
468
|
|
|
469
|
+
// lib/utils/getColorFromString.ts
|
|
470
|
+
var getColorFromString = (string, alpha = 1) => {
|
|
471
|
+
const hash = string.split("").reduce((acc, char) => {
|
|
472
|
+
return acc * 31 + char.charCodeAt(0);
|
|
473
|
+
}, 0);
|
|
474
|
+
return `hsl(${hash % 360}, 100%, 50%, ${alpha})`;
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// lib/solvers/IdentifyDecouplingCapsSolver/IdentifyDecouplingCapsSolver.ts
|
|
478
|
+
var IdentifyDecouplingCapsSolver = class extends BaseSolver {
|
|
479
|
+
inputProblem;
|
|
480
|
+
queuedChips;
|
|
481
|
+
outputDecouplingCapGroups = [];
|
|
482
|
+
/** Quick lookup of groups by main chip for accumulation */
|
|
483
|
+
groupsByMainChipId = /* @__PURE__ */ new Map();
|
|
484
|
+
constructor(inputProblem) {
|
|
485
|
+
super();
|
|
486
|
+
this.inputProblem = inputProblem;
|
|
487
|
+
this.queuedChips = Object.values(inputProblem.chipMap);
|
|
488
|
+
}
|
|
489
|
+
/** Determine if chip is a 2-pin component with restricted rotation */
|
|
490
|
+
isTwoPinRestrictedRotation(chip) {
|
|
491
|
+
if (chip.pins.length !== 2) return false;
|
|
492
|
+
if (!chip.availableRotations) return false;
|
|
493
|
+
const allowed = /* @__PURE__ */ new Set([0, 180]);
|
|
494
|
+
return chip.availableRotations.length > 0 && chip.availableRotations.every((r) => allowed.has(r));
|
|
495
|
+
}
|
|
496
|
+
/** Check that the two pins are on opposite Y sides (y+ and y-) */
|
|
497
|
+
pinsOnOppositeYSides(chip) {
|
|
498
|
+
if (chip.pins.length !== 2) return false;
|
|
499
|
+
const [p1, p2] = chip.pins;
|
|
500
|
+
const cp1 = this.inputProblem.chipPinMap[p1];
|
|
501
|
+
const cp2 = this.inputProblem.chipPinMap[p2];
|
|
502
|
+
if (!cp1 || !cp2) return false;
|
|
503
|
+
const sides = /* @__PURE__ */ new Set([cp1.side, cp2.side]);
|
|
504
|
+
return sides.has("y+") && sides.has("y-");
|
|
505
|
+
}
|
|
506
|
+
/** Get chips strongly connected (direct pin-to-pin) to this pin */
|
|
507
|
+
getStronglyConnectedNeighborChips(pinId) {
|
|
508
|
+
const neighbors = /* @__PURE__ */ new Set();
|
|
509
|
+
for (const [connKey, connected] of Object.entries(
|
|
510
|
+
this.inputProblem.pinStrongConnMap
|
|
511
|
+
)) {
|
|
512
|
+
if (!connected) continue;
|
|
513
|
+
const [a, b] = connKey.split("-");
|
|
514
|
+
if (a === pinId) {
|
|
515
|
+
const otherChipId = b.split(".")[0];
|
|
516
|
+
neighbors.add(otherChipId);
|
|
517
|
+
} else if (b === pinId) {
|
|
518
|
+
const otherChipId = a.split(".")[0];
|
|
519
|
+
neighbors.add(otherChipId);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return neighbors;
|
|
523
|
+
}
|
|
524
|
+
/** Find the main chip id for a decoupling capacitor candidate */
|
|
525
|
+
findMainChipIdForCap(capChip) {
|
|
526
|
+
const strongNeighbors = /* @__PURE__ */ new Map();
|
|
527
|
+
for (const pinId of capChip.pins) {
|
|
528
|
+
const neighbors = this.getStronglyConnectedNeighborChips(pinId);
|
|
529
|
+
for (const n of neighbors) {
|
|
530
|
+
if (n === capChip.chipId) continue;
|
|
531
|
+
strongNeighbors.set(n, (strongNeighbors.get(n) || 0) + 1);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (strongNeighbors.size === 0) return null;
|
|
535
|
+
let best = null;
|
|
536
|
+
for (const [id, score] of strongNeighbors.entries()) {
|
|
537
|
+
if (!best || score > best.score || score === best.score && id < best.id)
|
|
538
|
+
best = { id, score };
|
|
539
|
+
}
|
|
540
|
+
return best ? best.id : null;
|
|
541
|
+
}
|
|
542
|
+
/** Adds a decoupling capacitor to the group for the given main chip */
|
|
543
|
+
addToGroup(mainChipId, capChipId) {
|
|
544
|
+
let group = this.groupsByMainChipId.get(mainChipId);
|
|
545
|
+
if (!group) {
|
|
546
|
+
group = {
|
|
547
|
+
decouplingCapGroupId: `decap_group_${mainChipId}`,
|
|
548
|
+
mainChipId,
|
|
549
|
+
decouplingCapChipIds: []
|
|
550
|
+
};
|
|
551
|
+
this.groupsByMainChipId.set(mainChipId, group);
|
|
552
|
+
this.outputDecouplingCapGroups.push(group);
|
|
553
|
+
}
|
|
554
|
+
if (!group.decouplingCapChipIds.includes(capChipId)) {
|
|
555
|
+
group.decouplingCapChipIds.push(capChipId);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
_step() {
|
|
559
|
+
const currentChip = this.queuedChips.shift();
|
|
560
|
+
if (!currentChip) {
|
|
561
|
+
this.solved = true;
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const candidate = this.isTwoPinRestrictedRotation(currentChip) && this.pinsOnOppositeYSides(currentChip);
|
|
565
|
+
if (!candidate) return;
|
|
566
|
+
const mainChipId = this.findMainChipIdForCap(currentChip);
|
|
567
|
+
if (!mainChipId) return;
|
|
568
|
+
this.addToGroup(mainChipId, currentChip.chipId);
|
|
569
|
+
}
|
|
570
|
+
visualize() {
|
|
571
|
+
const basicLayout = doBasicInputProblemLayout(this.inputProblem);
|
|
572
|
+
const graphics = visualizeInputProblem(
|
|
573
|
+
this.inputProblem,
|
|
574
|
+
basicLayout
|
|
575
|
+
);
|
|
576
|
+
const chipRoleMap = /* @__PURE__ */ new Map();
|
|
577
|
+
for (const group of this.outputDecouplingCapGroups) {
|
|
578
|
+
const color = getColorFromString(group.mainChipId, 0.8);
|
|
579
|
+
chipRoleMap.set(group.mainChipId, { type: "main", color });
|
|
580
|
+
for (const capChipId of group.decouplingCapChipIds) {
|
|
581
|
+
chipRoleMap.set(capChipId, { type: "decap", color });
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
for (const rect of graphics.rects || []) {
|
|
585
|
+
rect.fill = "rgba(0,0,0,0.5)";
|
|
586
|
+
}
|
|
587
|
+
for (const rect of graphics.rects || []) {
|
|
588
|
+
const chipId = rect.label;
|
|
589
|
+
const role = chipRoleMap.get(chipId);
|
|
590
|
+
if (!role) continue;
|
|
591
|
+
const alpha = role.type === "main" ? 0.18 : 0.36;
|
|
592
|
+
rect.label = `${rect.label}
|
|
593
|
+
${role.type}`;
|
|
594
|
+
rect.fill = getColorFromString(role.color, alpha);
|
|
595
|
+
}
|
|
596
|
+
if (!graphics.texts) graphics.texts = [];
|
|
597
|
+
for (const rect of graphics.rects || []) {
|
|
598
|
+
const chipId = rect.label;
|
|
599
|
+
const role = chipRoleMap.get(chipId);
|
|
600
|
+
if (!role) continue;
|
|
601
|
+
graphics.texts.push({
|
|
602
|
+
x: rect.center.x,
|
|
603
|
+
y: rect.center.y - (rect.height || 0) / 2,
|
|
604
|
+
text: role.type === "main" ? "MAIN" : "DECAP",
|
|
605
|
+
fillColor: role.type === "main" ? role.color : "rgba(0,0,0,0.6)",
|
|
606
|
+
fontSize: 8
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
return graphics;
|
|
610
|
+
}
|
|
611
|
+
getConstructorParams() {
|
|
612
|
+
return [this.inputProblem];
|
|
613
|
+
}
|
|
614
|
+
computeProgress() {
|
|
615
|
+
const total = Object.keys(this.inputProblem.chipMap).length || 1;
|
|
616
|
+
const processed = total - this.queuedChips.length;
|
|
617
|
+
return Math.min(1, Math.max(0, processed / total));
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
|
|
455
621
|
// lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts
|
|
456
622
|
import { PackSolver2 } from "calculate-packing";
|
|
457
623
|
|
|
@@ -1023,6 +1189,7 @@ function definePipelineStep(solverName, solverClass, getConstructorParams, opts
|
|
|
1023
1189
|
};
|
|
1024
1190
|
}
|
|
1025
1191
|
var LayoutPipelineSolver = class extends BaseSolver {
|
|
1192
|
+
identifyDecouplingCapsSolver;
|
|
1026
1193
|
chipPartitionsSolver;
|
|
1027
1194
|
packInnerPartitionsSolver;
|
|
1028
1195
|
partitionPackingSolver;
|
|
@@ -1034,6 +1201,15 @@ var LayoutPipelineSolver = class extends BaseSolver {
|
|
|
1034
1201
|
chipPartitions;
|
|
1035
1202
|
packedPartitions;
|
|
1036
1203
|
pipelineDef = [
|
|
1204
|
+
definePipelineStep(
|
|
1205
|
+
"identifyDecouplingCapsSolver",
|
|
1206
|
+
IdentifyDecouplingCapsSolver,
|
|
1207
|
+
() => [this.inputProblem],
|
|
1208
|
+
{
|
|
1209
|
+
onSolved: (_layoutSolver) => {
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
),
|
|
1037
1213
|
definePipelineStep(
|
|
1038
1214
|
"chipPartitionsSolver",
|
|
1039
1215
|
ChipPartitionsSolver,
|
|
@@ -1121,6 +1297,7 @@ var LayoutPipelineSolver = class extends BaseSolver {
|
|
|
1121
1297
|
if (this.solved && this.partitionPackingSolver?.solved) {
|
|
1122
1298
|
return this.partitionPackingSolver.visualize();
|
|
1123
1299
|
}
|
|
1300
|
+
const identifyDecouplingCapsViz = this.identifyDecouplingCapsSolver?.visualize();
|
|
1124
1301
|
const chipPartitionsViz = this.chipPartitionsSolver?.visualize();
|
|
1125
1302
|
const packInnerPartitionsViz = this.packInnerPartitionsSolver?.visualize();
|
|
1126
1303
|
const partitionPackingViz = this.partitionPackingSolver?.visualize();
|
|
@@ -1128,6 +1305,7 @@ var LayoutPipelineSolver = class extends BaseSolver {
|
|
|
1128
1305
|
const inputViz = visualizeInputProblem(this.inputProblem, basicLayout);
|
|
1129
1306
|
const visualizations = [
|
|
1130
1307
|
inputViz,
|
|
1308
|
+
identifyDecouplingCapsViz,
|
|
1131
1309
|
chipPartitionsViz,
|
|
1132
1310
|
packInnerPartitionsViz,
|
|
1133
1311
|
partitionPackingViz
|
|
@@ -1175,6 +1353,9 @@ var LayoutPipelineSolver = class extends BaseSolver {
|
|
|
1175
1353
|
if (this.chipPartitionsSolver?.solved) {
|
|
1176
1354
|
return this.chipPartitionsSolver.visualize();
|
|
1177
1355
|
}
|
|
1356
|
+
if (this.identifyDecouplingCapsSolver?.solved) {
|
|
1357
|
+
return this.identifyDecouplingCapsSolver.visualize();
|
|
1358
|
+
}
|
|
1178
1359
|
return super.preview();
|
|
1179
1360
|
}
|
|
1180
1361
|
/**
|