From 6b864e2a81dfa525b8637808a579d613acc11e2d Mon Sep 17 00:00:00 2001 From: RoyZhao1991 <211295381+RoyZhao1991@users.noreply.github.com> Date: Wed, 20 May 2026 20:21:49 +0800 Subject: [PATCH] Fix schematic trace net hover highlighting --- lib/components/SchematicViewer.tsx | 31 ++++--- ...nnectivity-map-keys-to-schematic-traces.ts | 93 +++++++++++++++++++ ...ivity-map-keys-to-schematic-traces.test.ts | 49 ++++++++++ 3 files changed, 161 insertions(+), 12 deletions(-) create mode 100644 lib/utils/add-subcircuit-connectivity-map-keys-to-schematic-traces.ts create mode 100644 tests/add-subcircuit-connectivity-map-keys-to-schematic-traces.test.ts diff --git a/lib/components/SchematicViewer.tsx b/lib/components/SchematicViewer.tsx index ab4fd20..df043c0 100644 --- a/lib/components/SchematicViewer.tsx +++ b/lib/components/SchematicViewer.tsx @@ -31,6 +31,7 @@ import { getStoredBoolean, setStoredBoolean } from "lib/hooks/useLocalStorage" import { MouseTracker } from "./MouseTracker" import { SchematicComponentMouseTarget } from "./SchematicComponentMouseTarget" import { SchematicPortMouseTarget } from "./SchematicPortMouseTarget" +import { addSubcircuitConnectivityMapKeysToSchematicTraces } from "lib/utils/add-subcircuit-connectivity-map-keys-to-schematic-traces" interface Props { circuitJson: CircuitJson @@ -262,18 +263,24 @@ export const SchematicViewer = ({ const svgString = useMemo(() => { if (!containerWidth || !containerHeight) return "" - return convertCircuitJsonToSchematicSvg(circuitJson as any, { - width: containerWidth, - height: containerHeight || 720, - drawPorts: showSchematicPorts, - grid: !showGrid - ? undefined - : { - cellSize: 1, - labelCells: true, - }, - colorOverrides, - }) + const circuitJsonWithTraceConnectivity = + addSubcircuitConnectivityMapKeysToSchematicTraces(circuitJson) + + return convertCircuitJsonToSchematicSvg( + circuitJsonWithTraceConnectivity as any, + { + width: containerWidth, + height: containerHeight || 720, + drawPorts: showSchematicPorts, + grid: !showGrid + ? undefined + : { + cellSize: 1, + labelCells: true, + }, + colorOverrides, + }, + ) }, [ circuitJsonKey, containerWidth, diff --git a/lib/utils/add-subcircuit-connectivity-map-keys-to-schematic-traces.ts b/lib/utils/add-subcircuit-connectivity-map-keys-to-schematic-traces.ts new file mode 100644 index 0000000..c2f4189 --- /dev/null +++ b/lib/utils/add-subcircuit-connectivity-map-keys-to-schematic-traces.ts @@ -0,0 +1,93 @@ +import type { CircuitJson } from "circuit-json" + +const normalizePinToken = (token: string | number | undefined | null) => { + if (token === undefined || token === null) return "" + return String(token).replace(/^pin/i, "") +} + +export const addSubcircuitConnectivityMapKeysToSchematicTraces = ( + circuitJson: CircuitJson, +): CircuitJson => { + const componentNameById = new Map() + const sourceTraceKeyById = new Map() + const portKeyByComponentAndPin = new Map() + + for (const elm of circuitJson as any[]) { + if (elm.type === "source_component" && elm.source_component_id) { + componentNameById.set(elm.source_component_id, elm.name) + } + } + + for (const elm of circuitJson as any[]) { + if ( + elm.type === "source_trace" && + elm.source_trace_id && + elm.subcircuit_connectivity_map_key + ) { + sourceTraceKeyById.set( + elm.source_trace_id, + elm.subcircuit_connectivity_map_key, + ) + } + + if ( + elm.type === "source_port" && + elm.source_component_id && + elm.subcircuit_connectivity_map_key + ) { + const componentName = componentNameById.get(elm.source_component_id) + if (!componentName) continue + + const pinTokens = new Set() + pinTokens.add(normalizePinToken(elm.pin_number)) + pinTokens.add(normalizePinToken(elm.name)) + for (const hint of elm.port_hints ?? []) { + pinTokens.add(normalizePinToken(hint)) + } + + for (const pinToken of pinTokens) { + if (!pinToken) continue + portKeyByComponentAndPin.set( + `${componentName}.${pinToken}`, + elm.subcircuit_connectivity_map_key, + ) + } + } + } + + const getKeyForSchematicTrace = (schematicTrace: any) => { + if (schematicTrace.subcircuit_connectivity_map_key) { + return schematicTrace.subcircuit_connectivity_map_key + } + + const directSourceTraceKey = sourceTraceKeyById.get( + schematicTrace.source_trace_id, + ) + if (directSourceTraceKey) return directSourceTraceKey + + const solverMatch = + schematicTrace.source_trace_id?.match(/^solver_(.+?)-(.+)$/) + if (!solverMatch) return undefined + + for (const endpoint of [solverMatch[1], solverMatch[2]]) { + const endpointMatch = endpoint.match(/^(.+)\.([^.\s]+)$/) + if (!endpointMatch) continue + const key = portKeyByComponentAndPin.get( + `${endpointMatch[1]}.${normalizePinToken(endpointMatch[2])}`, + ) + if (key) return key + } + + return undefined + } + + return (circuitJson as any[]).map((elm) => { + if (elm.type !== "schematic_trace") return elm + const subcircuitConnectivityMapKey = getKeyForSchematicTrace(elm) + if (!subcircuitConnectivityMapKey) return elm + return { + ...elm, + subcircuit_connectivity_map_key: subcircuitConnectivityMapKey, + } + }) as CircuitJson +} diff --git a/tests/add-subcircuit-connectivity-map-keys-to-schematic-traces.test.ts b/tests/add-subcircuit-connectivity-map-keys-to-schematic-traces.test.ts new file mode 100644 index 0000000..86008a1 --- /dev/null +++ b/tests/add-subcircuit-connectivity-map-keys-to-schematic-traces.test.ts @@ -0,0 +1,49 @@ +import { expect, test } from "bun:test" +import { addSubcircuitConnectivityMapKeysToSchematicTraces } from "lib/utils/add-subcircuit-connectivity-map-keys-to-schematic-traces" + +test("adds subcircuit connectivity keys to solver-generated schematic traces", () => { + const circuitJson = [ + { + type: "source_component", + source_component_id: "source_component_0", + name: "R1", + }, + { + type: "source_component", + source_component_id: "source_component_1", + name: "C1", + }, + { + type: "source_port", + source_port_id: "source_port_0", + source_component_id: "source_component_0", + pin_number: 2, + name: "pin2", + port_hints: ["pin2", "2"], + subcircuit_connectivity_map_key: "net0", + }, + { + type: "source_port", + source_port_id: "source_port_1", + source_component_id: "source_component_1", + pin_number: 1, + name: "pin1", + port_hints: ["pin1", "1"], + subcircuit_connectivity_map_key: "net0", + }, + { + type: "schematic_trace", + schematic_trace_id: "schematic_trace_0", + source_trace_id: "solver_R1.2-C1.1", + edges: [], + junctions: [], + }, + ] as any + + const nextCircuitJson = + addSubcircuitConnectivityMapKeysToSchematicTraces(circuitJson) + + expect((nextCircuitJson as any)[4].subcircuit_connectivity_map_key).toBe( + "net0", + ) +})