Files
logic-gates/js/examples.js
Jose Luis bc8823bcd4 feat: editable labels for INPUT/OUTPUT/CLOCK gates
Double-click any INPUT, OUTPUT, or CLOCK gate to assign a custom label.
Labels are shown inside the gate and used in waveform viewer instead of
generic IN_0/OUT_0 names. Example circuits now ship with meaningful
labels (S, R, D, EN, Q, Q̅, CLK).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 04:06:50 +01:00

301 lines
9.8 KiB
JavaScript

// Pre-built example circuits
// Each example is a { gates, connections, nextId, camera } object
const W = 100; // GATE_W
const H = 60; // GATE_H
const GAP_X = 160;
const GAP_Y = 100;
/**
* SR Flip-Flop using cross-coupled NOR gates
*
* S ──┐
* NOR─── Q
* ┌──┘ │
* │ │
* └──┐ │
* NOR─── Q̅
* R ──┘
*/
function srFlipFlop() {
const gates = [
// Inputs
{ id: 1, type: 'INPUT', x: 50, y: 80, value: 0, label: 'S' },
{ id: 2, type: 'INPUT', x: 50, y: 280, value: 0, label: 'R' },
// Cross-coupled NOR gates
{ id: 3, type: 'NOR', x: 300, y: 80, value: 0 },
{ id: 4, type: 'NOR', x: 300, y: 280, value: 0 },
// Outputs
{ id: 5, type: 'OUTPUT', x: 550, y: 80, value: 0, label: 'Q' },
{ id: 6, type: 'OUTPUT', x: 550, y: 280, value: 0, label: 'Q̅' }
];
const connections = [
// S → Top NOR input 0
{ from: 1, fromPort: 0, to: 3, toPort: 0 },
// R → Bottom NOR input 1
{ from: 2, fromPort: 0, to: 4, toPort: 1 },
// Top NOR (Q) → Bottom NOR input 0 (cross-couple)
{ from: 3, fromPort: 0, to: 4, toPort: 0 },
// Bottom NOR (Q̅) → Top NOR input 1 (cross-couple)
{ from: 4, fromPort: 0, to: 3, toPort: 1 },
// NOR outputs → visible outputs
{ from: 3, fromPort: 0, to: 5, toPort: 0 },
{ from: 4, fromPort: 0, to: 6, toPort: 0 }
];
return {
name: 'SR Flip-Flop (NOR)',
description: 'Set-Reset latch using cross-coupled NOR gates. Toggle S to set Q=1, toggle R to reset Q=0.',
gates,
connections,
nextId: 7,
camera: { camX: 0, camY: 0, zoom: 1 }
};
}
/**
* SR Flip-Flop using cross-coupled NAND gates
*
* S̅ ──┐
* NAND── Q
* ┌───┘ │
* │ │
* └───┐ │
* NAND── Q̅
* R̅ ──┘
*/
function srFlipFlopNand() {
const gates = [
// Inputs (active low for NAND SR)
{ id: 1, type: 'INPUT', x: 50, y: 80, value: 1, label: 'S̅' },
{ id: 2, type: 'INPUT', x: 50, y: 280, value: 1, label: 'R̅' },
// Cross-coupled NAND gates
{ id: 3, type: 'NAND', x: 300, y: 80, value: 0 },
{ id: 4, type: 'NAND', x: 300, y: 280, value: 0 },
// Outputs
{ id: 5, type: 'OUTPUT', x: 550, y: 80, value: 0, label: 'Q' },
{ id: 6, type: 'OUTPUT', x: 550, y: 280, value: 0, label: 'Q̅' }
];
const connections = [
// S̅ → Top NAND input 0
{ from: 1, fromPort: 0, to: 3, toPort: 0 },
// R̅ → Bottom NAND input 1
{ from: 2, fromPort: 0, to: 4, toPort: 1 },
// Top NAND (Q) → Bottom NAND input 0 (cross-couple)
{ from: 3, fromPort: 0, to: 4, toPort: 0 },
// Bottom NAND (Q̅) → Top NAND input 1 (cross-couple)
{ from: 4, fromPort: 0, to: 3, toPort: 1 },
// Outputs
{ from: 3, fromPort: 0, to: 5, toPort: 0 },
{ from: 4, fromPort: 0, to: 6, toPort: 0 }
];
return {
name: 'SR Flip-Flop (NAND)',
description: 'Set-Reset latch using cross-coupled NAND gates. Inputs are active-low: set S̅=0 to set, R̅=0 to reset.',
gates,
connections,
nextId: 7,
camera: { camX: 0, camY: 0, zoom: 1 }
};
}
/**
* Gated D Latch (1-bit memory)
*
* Uses an SR flip-flop with gating logic:
* D ──AND──┐
* E ──┤ NOR── Q
* │ │ ┌──┘│
* │ │ │ │
* │ │ └──┐│
* E ──┤ NOR── Q̅
* D─NOT─AND┘
*
* When Enable=1, Q follows D.
* When Enable=0, Q holds its value.
*/
function dLatch() {
const gates = [
// Inputs
{ id: 1, type: 'INPUT', x: 50, y: 100, value: 0, label: 'D' },
{ id: 2, type: 'INPUT', x: 50, y: 280, value: 0, label: 'EN' },
// NOT gate to invert D
{ id: 3, type: 'NOT', x: 200, y: 340, value: 0 },
// AND gates for gating
{ id: 4, type: 'AND', x: 350, y: 60, value: 0 }, // D AND E → S
{ id: 5, type: 'AND', x: 350, y: 340, value: 0 }, // NOT(D) AND E → R
// Cross-coupled NOR gates (SR latch core)
{ id: 6, type: 'NOR', x: 550, y: 60, value: 0 }, // → Q
{ id: 7, type: 'NOR', x: 550, y: 340, value: 0 }, // → Q̅
// Outputs
{ id: 8, type: 'OUTPUT', x: 750, y: 60, value: 0, label: 'Q' },
{ id: 9, type: 'OUTPUT', x: 750, y: 340, value: 0, label: 'Q̅' }
];
const connections = [
// D → AND top input, and D → NOT
{ from: 1, fromPort: 0, to: 4, toPort: 0 },
{ from: 1, fromPort: 0, to: 3, toPort: 0 },
// E → both AND gates
{ from: 2, fromPort: 0, to: 4, toPort: 1 },
{ from: 2, fromPort: 0, to: 5, toPort: 1 },
// NOT(D) → bottom AND
{ from: 3, fromPort: 0, to: 5, toPort: 0 },
// AND outputs → NOR inputs (S and R)
{ from: 4, fromPort: 0, to: 6, toPort: 0 },
{ from: 5, fromPort: 0, to: 7, toPort: 1 },
// Cross-coupling
{ from: 6, fromPort: 0, to: 7, toPort: 0 },
{ from: 7, fromPort: 0, to: 6, toPort: 1 },
// Outputs
{ from: 6, fromPort: 0, to: 8, toPort: 0 },
{ from: 7, fromPort: 0, to: 9, toPort: 0 }
];
return {
name: 'D Latch (1-bit Memory)',
description: 'Gated D latch — a 1-bit memory cell. When Enable=1, output Q follows input D. When Enable=0, Q holds its last value.',
gates,
connections,
nextId: 10,
camera: { camX: 0, camY: 0, zoom: 1 }
};
}
/**
* D Flip-Flop (edge-triggered, master-slave)
*
* Two D latches in series with inverted enable:
* - Master latch captures D when CLK=0
* - Slave latch outputs when CLK=1
* This creates rising-edge triggered behavior.
*/
function dFlipFlop() {
const gates = [
// Inputs
{ id: 1, type: 'INPUT', x: 30, y: 100, value: 0, label: 'D' },
{ id: 2, type: 'CLOCK', x: 30, y: 350, value: 0, label: 'CLK' },
// CLK inverter (for master latch)
{ id: 3, type: 'NOT', x: 170, y: 350, value: 0 },
// === MASTER LATCH (enabled when CLK=0, i.e. NOT CLK=1) ===
{ id: 4, type: 'NOT', x: 170, y: 200, value: 0 }, // NOT D for master
{ id: 5, type: 'AND', x: 300, y: 60, value: 0 }, // D AND !CLK
{ id: 6, type: 'AND', x: 300, y: 240, value: 0 }, // !D AND !CLK
{ id: 7, type: 'NOR', x: 450, y: 60, value: 0 }, // Master Q
{ id: 8, type: 'NOR', x: 450, y: 240, value: 0 }, // Master Q̅
// === SLAVE LATCH (enabled when CLK=1) ===
{ id: 9, type: 'NOT', x: 570, y: 200, value: 0 }, // NOT master Q for slave
{ id: 10, type: 'AND', x: 680, y: 60, value: 0 }, // masterQ AND CLK
{ id: 11, type: 'AND', x: 680, y: 240, value: 0 }, // !masterQ AND CLK
{ id: 12, type: 'NOR', x: 830, y: 60, value: 0 }, // Slave Q
{ id: 13, type: 'NOR', x: 830, y: 240, value: 0 }, // Slave Q̅
// Outputs
{ id: 14, type: 'OUTPUT', x: 1010, y: 60, value: 0, label: 'Q' },
{ id: 15, type: 'OUTPUT', x: 1010, y: 240, value: 0, label: 'Q̅' }
];
const connections = [
// D → master AND, D → master NOT
{ from: 1, fromPort: 0, to: 5, toPort: 0 },
{ from: 1, fromPort: 0, to: 4, toPort: 0 },
// CLK → NOT (invert for master)
{ from: 2, fromPort: 0, to: 3, toPort: 0 },
// !CLK → master AND gates (enable)
{ from: 3, fromPort: 0, to: 5, toPort: 1 },
{ from: 3, fromPort: 0, to: 6, toPort: 1 },
// !D → master bottom AND
{ from: 4, fromPort: 0, to: 6, toPort: 0 },
// Master AND outputs → Master NOR (SR latch)
{ from: 5, fromPort: 0, to: 7, toPort: 0 },
{ from: 6, fromPort: 0, to: 8, toPort: 1 },
// Master cross-coupling
{ from: 7, fromPort: 0, to: 8, toPort: 0 },
{ from: 8, fromPort: 0, to: 7, toPort: 1 },
// Master Q → slave AND, Master Q → slave NOT
{ from: 7, fromPort: 0, to: 10, toPort: 0 },
{ from: 7, fromPort: 0, to: 9, toPort: 0 },
// CLK → slave AND gates (enable, direct CLK)
{ from: 2, fromPort: 0, to: 10, toPort: 1 },
{ from: 2, fromPort: 0, to: 11, toPort: 1 },
// !masterQ → slave bottom AND
{ from: 9, fromPort: 0, to: 11, toPort: 0 },
// Slave AND outputs → Slave NOR (SR latch)
{ from: 10, fromPort: 0, to: 12, toPort: 0 },
{ from: 11, fromPort: 0, to: 13, toPort: 1 },
// Slave cross-coupling
{ from: 12, fromPort: 0, to: 13, toPort: 0 },
{ from: 13, fromPort: 0, to: 12, toPort: 1 },
// Outputs
{ from: 12, fromPort: 0, to: 14, toPort: 0 },
{ from: 13, fromPort: 0, to: 15, toPort: 0 }
];
return {
name: 'D Flip-Flop (Master-Slave)',
description: 'Edge-triggered D flip-flop built from two D latches. Captures D on the rising edge of CLK. Use with the clock simulation to see it in action.',
gates,
connections,
nextId: 16,
camera: { camX: 0, camY: -50, zoom: 0.9 }
};
}
// Export all examples as a list
export const examples = [
srFlipFlop,
srFlipFlopNand,
dLatch,
dFlipFlop
];
export function getExampleList() {
return examples.map((fn, i) => {
const ex = fn();
return { id: i, name: ex.name, description: ex.description };
});
}
export function loadExample(index) {
if (index < 0 || index >= examples.length) return null;
const ex = examples[index]();
return {
circuit: {
gates: JSON.parse(JSON.stringify(ex.gates)),
connections: JSON.parse(JSON.stringify(ex.connections)),
nextId: ex.nextId
},
camera: ex.camera
};
}