fix: complete rewrite of component evaluation system
Major fixes for custom components when used in the main circuit: - Add outputValues[] array for multi-output component gates, so each output port carries its own independent value - readSourcePort() reads the correct port value from source gates instead of always reading gate.value - evaluateComponent() now uses iterative fixed-point evaluation (matching main evaluateAll) instead of a simple 10-pass loop - Store inputIds/outputIds in component definition for consistent port-to-gate mapping across save/load - Renderer reads per-port values for connection color and port glow - Added debug logs for component save and evaluation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
32
js/gates.js
32
js/gates.js
@@ -66,9 +66,22 @@ export function getOutputPorts(gate) {
|
||||
return ports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value from a source gate at a specific output port.
|
||||
* For component gates with multiple outputs, reads from outputValues[].
|
||||
* For normal gates (single output), reads gate.value.
|
||||
*/
|
||||
function readSourcePort(srcGate, fromPort) {
|
||||
if (srcGate.outputValues && fromPort < srcGate.outputValues.length) {
|
||||
return srcGate.outputValues[fromPort];
|
||||
}
|
||||
return srcGate.value || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the output of a single gate given its current input values.
|
||||
* Does NOT recurse — just reads source gate .value directly.
|
||||
* For COMPONENT gates, evaluates internal circuit and stores all outputs.
|
||||
*/
|
||||
function computeGate(gate) {
|
||||
if (gate.type === 'INPUT' || gate.type === 'CLOCK') return gate.value;
|
||||
@@ -79,7 +92,7 @@ function computeGate(gate) {
|
||||
const conn = state.connections.find(c => c.to === gate.id && c.toPort === i);
|
||||
if (conn) {
|
||||
const srcGate = state.gates.find(g => g.id === conn.from);
|
||||
inputs.push(srcGate ? (srcGate.value || 0) : 0);
|
||||
inputs.push(srcGate ? readSourcePort(srcGate, conn.fromPort) : 0);
|
||||
} else {
|
||||
inputs.push(0);
|
||||
}
|
||||
@@ -87,6 +100,8 @@ function computeGate(gate) {
|
||||
|
||||
if (gate.type.startsWith('COMPONENT:')) {
|
||||
const outputs = evaluateComponent(gate, inputs);
|
||||
// Store all output values for multi-output components
|
||||
gate.outputValues = outputs;
|
||||
return outputs[0] || 0;
|
||||
}
|
||||
|
||||
@@ -115,14 +130,25 @@ export function evaluateAll(recordWave = false) {
|
||||
let changed = false;
|
||||
for (const gate of state.gates) {
|
||||
if (gate.type === 'INPUT' || gate.type === 'CLOCK') continue;
|
||||
const oldVal = gate.value;
|
||||
const oldOutputs = gate.outputValues ? [...gate.outputValues] : null;
|
||||
const newVal = computeGate(gate);
|
||||
if (newVal !== gate.value) {
|
||||
if (newVal !== oldVal) {
|
||||
gate.value = newVal;
|
||||
changed = true;
|
||||
}
|
||||
// Also check if outputValues changed (for multi-output components)
|
||||
if (gate.outputValues && oldOutputs) {
|
||||
for (let i = 0; i < gate.outputValues.length; i++) {
|
||||
if (gate.outputValues[i] !== oldOutputs[i]) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!changed) {
|
||||
console.log(`[eval] stable after ${iter + 1} iteration(s)`);
|
||||
if (iter > 0) console.log(`[eval] stable after ${iter + 1} iteration(s)`);
|
||||
break;
|
||||
}
|
||||
if (iter === MAX_ITERATIONS - 1) {
|
||||
|
||||
Reference in New Issue
Block a user