This commit is contained in:
33333-33333 2026-07-29 20:52:11 +09:00
commit 091afc2b5c
77 changed files with 4405 additions and 8223 deletions

View file

@ -395,13 +395,23 @@
result.boardId = board.id || "";
result.direction = port.direction;
result.signalActive = port.direction === "output" ? Boolean(board._circuitOutputState?.[port.id]) : Boolean(board._circuitInputState?.[port.id]);
result._signalDetectorSources = port.direction === "output"
? [...(board._circuitOutputDetectorSources?.[port.id] || [])]
: [...(board._circuitInputDetectorSources?.[port.id] || [])];
return result;
}
function evaluate(board, inputValues = null) {
function evaluate(board, inputValues = null, inputProvenance = null) {
const config = ensureConfig(board);
const inputs = Object.create(null);
for (const port of config.inputs) inputs[port.id] = Boolean(inputValues ? inputValues[port.id] : board?._circuitInputState?.[port.id]);
const inputDetectorSources = Object.create(null);
for (const port of config.inputs) {
inputs[port.id] = Boolean(inputValues ? inputValues[port.id] : board?._circuitInputState?.[port.id]);
const rawSources = inputProvenance ? inputProvenance[port.id] : board?._circuitInputDetectorSources?.[port.id];
inputDetectorSources[port.id] = inputs[port.id]
? [...new Set(Array.isArray(rawSources) ? rawSources.filter(Boolean) : [])]
: [];
}
const parent = new Map();
const rank = new Map();
@ -511,18 +521,74 @@
return Boolean(value);
}
const netDetectorSources = new Map();
const nodeDetectorSources = Object.create(null);
const visitingSourceNodes = new Set();
const visitingSourceNets = new Set();
const unionSources = (...lists) => [...new Set(lists.flatMap(list => Array.isArray(list) ? list : []))];
function driverDetectorSources(token) {
if (token.startsWith("i:")) {
const id = token.slice(2);
return inputs[id] ? inputDetectorSources[id] || [] : [];
}
const match = /^n:([^:]+):o$/.exec(token);
return match ? nodeSourceValue(match[1]) : [];
}
function netSourceValue(token) {
const root = find(token);
if (netDetectorSources.has(root)) return netDetectorSources.get(root);
if (visitingSourceNets.has(root)) return [];
visitingSourceNets.add(root);
let sources = [];
for (const member of members.get(root) || [token]) {
if ((member.startsWith("i:") || /^n:[^:]+:o$/.test(member)) && driverValue(member)) {
sources = unionSources(sources, driverDetectorSources(member));
}
}
visitingSourceNets.delete(root);
netDetectorSources.set(root, sources);
return sources;
}
function nodeSourceValue(id) {
if (Object.prototype.hasOwnProperty.call(nodeDetectorSources, id)) return nodeDetectorSources[id];
if (visitingSourceNodes.has(id) || !nodeValue(id)) return [];
const node = nodeMap.get(id);
if (!node || unstableNodes.has(id) || node.type === "on" || node.type === "not") {
nodeDetectorSources[id] = [];
return [];
}
visitingSourceNodes.add(id);
let sources = netSourceValue(`n:${id}:a`);
if (node.type === "and") sources = unionSources(sources, netSourceValue(`n:${id}:b`));
visitingSourceNodes.delete(id);
nodeDetectorSources[id] = sources;
return sources;
}
const outputs = Object.create(null);
for (const port of config.outputs) outputs[port.id] = netValue(`o:${port.id}`);
const outputDetectorSources = Object.create(null);
for (const port of config.outputs) {
outputs[port.id] = netValue(`o:${port.id}`);
outputDetectorSources[port.id] = outputs[port.id] ? netSourceValue(`o:${port.id}`) : [];
}
if (board) {
board._circuitInputState = inputs;
board._circuitInputDetectorSources = inputDetectorSources;
board._circuitNodeState = nodeValues;
board._circuitOutputState = outputs;
board._circuitOutputDetectorSources = outputDetectorSources;
for (const port of config.outputs) {
const term = terminal(board, `o:${port.id}`);
if (term) term.signalActive = Boolean(outputs[port.id]);
if (term) {
term.signalActive = Boolean(outputs[port.id]);
term._signalDetectorSources = [...(outputDetectorSources[port.id] || [])];
}
}
}
return { inputs, nodes: nodeValues, outputs };
return { inputs, nodes: nodeValues, outputs, inputDetectorSources, outputDetectorSources };
}
function nearestPin(config, x, y, maxDistance = 17) {
@ -698,7 +764,11 @@
dialog = document.createElement("div");
dialog.id = "circuitBoardEditor";
dialog.className = "rotator-editor circuit-grid-editor hidden";
dialog.innerHTML = `<div class="rotator-editor-panel circuit-grid-panel" role="dialog" aria-modal="true" aria-labelledby="circuitBoardTitle">
dialog.setAttribute("role", "dialog");
dialog.setAttribute("aria-modal", "true");
dialog.setAttribute("aria-labelledby", "circuitBoardTitle");
dialog.setAttribute("aria-hidden", "true");
dialog.innerHTML = `<div class="rotator-editor-panel circuit-grid-panel">
<div class="rotator-editor-titlebar"><h2 id="circuitBoardTitle">\u57fa\u76e4</h2><button class="rotator-editor-close" type="button" data-action="close" aria-label="\u9589\u3058\u308b">\u00d7</button></div>
<div class="circuit-port-count-controls">
<label>\u6a2a\u5e45 <button type="button" data-grid-delta="cols:-1">\u2212</button><input id="circuitGridCols" type="number" min="${MIN_COLS}" max="${MAX_COLS}" step="1"><button type="button" data-grid-delta="cols:1">\uff0b</button></label>
@ -1089,12 +1159,16 @@
dialog.querySelector("[data-action=clear]").onclick = () => { snapshot(); draft.nodes = []; draft.wires = []; draw(); };
dialog.querySelector("[data-action=reset]").onclick = () => { snapshot(); draft = defaultConfig(); syncControls(); resizeCanvas(); draw(); };
const close = () => {
dialog.classList.add("hidden");
if (global.TarinaiManagedDialogs?.close) global.TarinaiManagedDialogs.close(dialog);
else { dialog.classList.add("hidden"); dialog.setAttribute("aria-hidden", "true"); }
document.removeEventListener("keydown", handleEditorKeydown, true);
canvas.onpointerdown = null; canvas.onpointermove = null; canvas.onpointerup = null; canvas.onpointercancel = null; canvas.onpointerleave = null; canvas.oncontextmenu = null;
};
dialog.querySelector("[data-action=close]").onclick = close;
dialog.querySelector("[data-action=cancel]").onclick = close;
if (dialog._tarinaiCloseRequestHandler) dialog.removeEventListener("tarinai:dialog-close-request", dialog._tarinaiCloseRequestHandler);
dialog._tarinaiCloseRequestHandler = close;
dialog.addEventListener("tarinai:dialog-close-request", close);
dialog.querySelector("[data-action=save]").onclick = () => {
applySerializedConfig(board, draft); evaluate(board); remapAttachedWireEndpoints(board, worldRef);
if (worldRef) { worldRef.drawListDirty = true; worldRef.markSpatialDirty?.("circuit-board-edited"); }
@ -1102,7 +1176,10 @@
global.render?.(); close();
};
dialog.onclick = event => { if (event.target === dialog) close(); };
syncControls(); resizeCanvas(); setTool("wire"); dialog.classList.remove("hidden"); draw();
syncControls(); resizeCanvas(); setTool("wire");
if (global.TarinaiManagedDialogs?.open) global.TarinaiManagedDialogs.open(dialog, document.activeElement);
else { dialog.classList.remove("hidden"); dialog.setAttribute("aria-hidden", "false"); }
draw();
return true;
}