Improve tie-net heuristics and destructive edit safeguards

This commit is contained in:
Rbanh 2026-02-18 20:55:29 -05:00
parent 31d2182258
commit f65e4d9876
4 changed files with 28 additions and 4 deletions

View File

@ -1633,7 +1633,7 @@ async function compileModel(model, opts = {}) {
const m = result.layout_metrics;
setStatus(
`Compiled (${result.errors.length}E, ${result.warnings.length}W | ${m.crossings} crossings, ${m.overlap_edges} overlaps, ${m.total_bends ?? 0} bends, ${(m.detour_ratio ?? 1).toFixed(2)}x detour)`
`Compiled (${result.errors.length}E, ${result.warnings.length}W | ${m.crossings} crossings, ${m.overlap_edges} overlaps, ${m.total_bends ?? 0} bends, ${m.label_tie_routes ?? 0} tie-nets, ${(m.detour_ratio ?? 1).toFixed(2)}x detour)`
);
} catch (err) {
setStatus(`Compile failed: ${err.message}`, false);
@ -2120,7 +2120,7 @@ async function runLayoutAction(path) {
fitView(out.compile.layout);
saveSnapshot();
setStatus(
`Compiled (${out.compile.errors.length}E, ${out.compile.warnings.length}W | ${out.compile.layout_metrics.crossings} crossings, ${out.compile.layout_metrics.overlap_edges} overlaps, ${out.compile.layout_metrics.total_bends ?? 0} bends)`
`Compiled (${out.compile.errors.length}E, ${out.compile.warnings.length}W | ${out.compile.layout_metrics.crossings} crossings, ${out.compile.layout_metrics.overlap_edges} overlaps, ${out.compile.layout_metrics.total_bends ?? 0} bends, ${out.compile.layout_metrics.label_tie_routes ?? 0} tie-nets)`
);
} catch (err) {
setStatus(`Layout action failed: ${err.message}`, false);

View File

@ -260,7 +260,8 @@ export function compile(payload, options = {}) {
total_length: 0,
direct_length: 0,
detour_ratio: 1,
label_tie_fallbacks: 0
label_tie_fallbacks: 0,
label_tie_routes: 0
},
bus_groups: [],
focus_map: {},

View File

@ -1366,6 +1366,15 @@ function detectBusGroups(nets) {
}
function shouldUseLabelTie(net, pinNodes, context) {
if (!pinNodes.length) {
return false;
}
const minX = Math.min(...pinNodes.map((p) => p.exit.x), 0);
const maxX = Math.max(...pinNodes.map((p) => p.exit.x), 0);
const minY = Math.min(...pinNodes.map((p) => p.exit.y), 0);
const maxY = Math.max(...pinNodes.map((p) => p.exit.y), 0);
const span = Math.abs(maxX - minX) + Math.abs(maxY - minY);
if (context.renderMode === "explicit") {
return LABEL_TIE_CLASSES.has(net.class) && pinNodes.length > 2;
}
@ -1378,6 +1387,16 @@ function shouldUseLabelTie(net, pinNodes, context) {
return true;
}
// Prefer readable schematic stubs for dense multi-node nets.
if (pinNodes.length >= 5) {
return true;
}
// Long-distributed analog/signal nets become noisy when fully routed.
if ((net.class === "analog" || net.class === "signal") && pinNodes.length >= 3 && span > GRID * 56) {
return true;
}
return false;
}
@ -1577,6 +1596,7 @@ function computeLayoutMetrics(routed, busGroups) {
(total, rn) => total + (rn.route_stats?.used_label_tie && rn.route_stats?.fallback_reason ? 1 : 0),
0
);
const labelTieRoutes = routed.reduce((total, rn) => total + (rn.route_stats?.used_label_tie ? 1 : 0), 0);
return {
segment_count: segmentCount,
@ -1589,7 +1609,8 @@ function computeLayoutMetrics(routed, busGroups) {
total_length: totalLength,
direct_length: totalDirect,
detour_ratio: totalDirect > 0 ? totalLength / totalDirect : 1,
label_tie_fallbacks: labelTieFallbacks
label_tie_fallbacks: labelTieFallbacks,
label_tie_routes: labelTieRoutes
};
}

View File

@ -15,6 +15,7 @@ test("compile returns svg and topology for valid model", () => {
assert.equal(typeof result.layout_metrics.total_bends, "number");
assert.equal(typeof result.layout_metrics.detour_ratio, "number");
assert.equal(typeof result.layout_metrics.label_tie_fallbacks, "number");
assert.equal(typeof result.layout_metrics.label_tie_routes, "number");
assert.ok(Array.isArray(result.bus_groups));
assert.ok(result.render_mode_used);
});
@ -26,6 +27,7 @@ test("compile fails on invalid model", () => {
assert.ok(result.errors.length > 0);
assert.equal(result.layout_metrics.total_bends, 0);
assert.equal(result.layout_metrics.detour_ratio, 1);
assert.equal(result.layout_metrics.label_tie_routes, 0);
});
test("compile accepts render mode options", () => {