Skip to content

Commit 4f70627

Browse files
Updates
1 parent d6effca commit 4f70627

1 file changed

Lines changed: 78 additions & 26 deletions

File tree

visualizer.js

Lines changed: 78 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -613,32 +613,80 @@ class Visualizer {
613613
* network complement IS the desired output, no inverter needed
614614
* 7. Any NOT(VAR) leaves get dedicated input inverter sub-circuits
615615
*/
616+
617+
/**
618+
* Measure a PDN/PUN tree to determine how much space it needs.
619+
* width = total parallel leaf devices (horizontal extent)
620+
* depth = longest series chain (vertical extent)
621+
*/
622+
measureTree(node) {
623+
if (!node) return { width: 1, depth: 1 };
624+
if (node.type === 'device') return { width: 1, depth: 1 };
625+
if (node.type === 'series') {
626+
let totalDepth = 0;
627+
let maxWidth = 0;
628+
for (const child of node.children) {
629+
const m = this.measureTree(child);
630+
totalDepth += m.depth;
631+
maxWidth = Math.max(maxWidth, m.width);
632+
}
633+
return { width: maxWidth, depth: totalDepth };
634+
}
635+
if (node.type === 'parallel') {
636+
let totalWidth = 0;
637+
let maxDepth = 0;
638+
for (const child of node.children) {
639+
const m = this.measureTree(child);
640+
totalWidth += m.width;
641+
maxDepth = Math.max(maxDepth, m.depth);
642+
}
643+
return { width: totalWidth, depth: maxDepth };
644+
}
645+
return { width: 1, depth: 1 };
646+
}
647+
616648
renderCMOSDiagram(svgElement) {
617649
svgElement.innerHTML = '';
618650
const ns = 'http://www.w3.org/2000/svg';
619-
svgElement.setAttribute('viewBox', '0 0 1800 1200');
651+
// Prepare: strip outer complement, expand, track inverted inputs
652+
const { coreFn, needsOutputInverter, invertedInputs } = this.prepareForCMOS(this.ast);
653+
const pdnTree = this.buildPDNTree(coreFn);
654+
const punTree = this.dualizePDNtoPUN(pdnTree);
655+
656+
// Measure trees to compute dynamic layout
657+
const pdnSize = this.measureTree(pdnTree);
658+
const punSize = this.measureTree(punTree);
659+
const maxWidth = Math.max(pdnSize.width, punSize.width, 1);
660+
const totalDepth = pdnSize.depth + punSize.depth;
661+
662+
// Dynamic spacing — scales with complexity
663+
const transistorSpacingH = Math.max(200, 160 + maxWidth * 20);
664+
const transistorSpacingV = Math.max(140, 120 + totalDepth * 15);
665+
666+
// Dynamic canvas — grows with circuit, infinite room
667+
const numInverters = invertedInputs.size;
668+
const leftMargin = numInverters > 0 ? 400 : 100;
669+
const neededWidth = leftMargin + maxWidth * transistorSpacingH + (needsOutputInverter ? 900 : 500);
670+
const neededHeight = 300 + totalDepth * transistorSpacingV + 500;
671+
const canvasW = Math.max(1800, neededWidth);
672+
const canvasH = Math.max(1200, neededHeight);
673+
674+
svgElement.setAttribute('viewBox', `0 0 ${canvasW} ${canvasH}`);
620675

621676
const bg = document.createElementNS(ns, 'rect');
622-
bg.setAttribute('width', '1800');
623-
bg.setAttribute('height', '1200');
677+
bg.setAttribute('width', String(canvasW));
678+
bg.setAttribute('height', String(canvasH));
624679
bg.setAttribute('fill', 'white');
625680
svgElement.appendChild(bg);
626681

627682
const g = document.createElementNS(ns, 'g');
628683
g.setAttribute('id', 'cmosGroup');
629684

630-
// Layout parameters
631-
const mainCenterX = 600;
685+
// Dynamic positions based on circuit size
686+
const mainCenterX = leftMargin + (maxWidth * transistorSpacingH) / 2 + 100;
632687
const vddY = 80;
633-
const gndY = 1120;
634-
const outputY = 600;
635-
const transistorSpacingH = 180;
636-
const transistorSpacingV = 140;
637-
638-
// Prepare: strip outer complement, expand, track inverted inputs
639-
const { coreFn, needsOutputInverter, invertedInputs } = this.prepareForCMOS(this.ast);
640-
const pdnTree = this.buildPDNTree(coreFn);
641-
const punTree = this.dualizePDNtoPUN(pdnTree);
688+
const gndY = canvasH - 80;
689+
const outputY = (vddY + gndY) / 2;
642690

643691
// ============================================
644692
// MAIN NETWORK
@@ -695,8 +743,8 @@ class Visualizer {
695743
// ============================================
696744
if (invertedInputs.size > 0) {
697745
const invVars = Array.from(invertedInputs);
698-
const invSpacing = Math.min(350, (gndY - vddY - 200) / Math.max(invVars.length, 1));
699-
const invX = 200;
746+
const invSpacing = Math.max(300, Math.min(400, (gndY - vddY - 200) / Math.max(invVars.length, 1)));
747+
const invX = Math.max(200, mainCenterX - maxWidth * transistorSpacingH / 2 - 200);
700748
invVars.forEach((varName, i) => {
701749
const invCenterY = vddY + 180 + i * invSpacing;
702750
this.drawInputInverterCircuit(g, invX, invCenterY, varName);
@@ -707,7 +755,7 @@ class Visualizer {
707755
// OUTPUT SECTION (conditional inverter)
708756
// ============================================
709757
if (needsOutputInverter) {
710-
const inverterX = mainCenterX + 500;
758+
const inverterX = mainCenterX + Math.max(500, maxWidth * transistorSpacingH / 2 + 300);
711759
const gateConnectionX = inverterX - 70;
712760

713761
// Horizontal connection to inverter gate
@@ -821,7 +869,7 @@ class Visualizer {
821869
g.appendChild(finalLabel);
822870
} else {
823871
// No output inverter — direct output from main network
824-
const finalX = mainCenterX + 300;
872+
const finalX = mainCenterX + Math.max(300, maxWidth * transistorSpacingH / 2 + 200);
825873

826874
const outLine = document.createElementNS(ns, 'line');
827875
outLine.setAttribute('x1', mainCenterX);
@@ -875,7 +923,7 @@ class Visualizer {
875923

876924
for (let i = 0; i < node.children.length; i++) {
877925
const child = node.children[i];
878-
const result = this.renderCMOSTree(group, child, x, currentY, hSpacing * 0.7, vSpacing);
926+
const result = this.renderCMOSTree(group, child, x, currentY, hSpacing, vSpacing);
879927

880928
if (i === 0) {
881929
firstResult = result;
@@ -905,17 +953,21 @@ class Visualizer {
905953
};
906954
}
907955

908-
// Parallel: place children side by side, connect tops and bottoms
956+
// Parallel: place children side by side — allocate width proportional to each child's needs
909957
if (node.type === 'parallel') {
910-
const numChildren = node.children.length;
911-
const totalWidth = (numChildren - 1) * hSpacing;
912-
const startX = x - totalWidth / 2;
958+
const childMeasures = node.children.map(c => this.measureTree(c));
959+
const totalLeaves = childMeasures.reduce((s, m) => s + m.width, 0);
960+
const totalWidth = Math.max(totalLeaves, node.children.length) * hSpacing;
913961

914962
const results = [];
915-
for (let i = 0; i < numChildren; i++) {
916-
const childX = startX + i * hSpacing;
917-
const result = this.renderCMOSTree(group, node.children[i], childX, y, hSpacing * 0.6, vSpacing);
963+
let currentX = x - totalWidth / 2;
964+
for (let i = 0; i < node.children.length; i++) {
965+
const childW = childMeasures[i].width;
966+
const slotWidth = (childW / totalLeaves) * totalWidth;
967+
const childX = currentX + slotWidth / 2;
968+
const result = this.renderCMOSTree(group, node.children[i], childX, y, hSpacing, vSpacing);
918969
results.push(result);
970+
currentX += slotWidth;
919971
}
920972

921973
// Find bounds

0 commit comments

Comments
 (0)