Preserve placement during drag compile updates
Some checks are pending
CI / test (push) Waiting to run

This commit is contained in:
Rbanh 2026-02-19 18:55:48 -05:00
parent cd007ba431
commit 0d53bdaf9a
2 changed files with 54 additions and 6 deletions

View File

@ -364,11 +364,12 @@ function defaultProject() {
}; };
} }
function compileOptions() { function compileOptions(extra = {}) {
return { return {
render_mode: state.renderMode, render_mode: state.renderMode,
show_labels: state.showLabels, show_labels: state.showLabels,
generic_symbols: true generic_symbols: true,
...extra
}; };
} }
@ -1807,12 +1808,21 @@ async function compileModel(model, opts = {}) {
const source = opts.source ?? "manual"; const source = opts.source ?? "manual";
const fit = opts.fit ?? false; const fit = opts.fit ?? false;
const keepView = opts.keepView ?? false; const keepView = opts.keepView ?? false;
const preservePlacement = opts.preservePlacement ?? false;
setStatus(source === "drag" ? "Compiling after drag..." : "Compiling..."); setStatus(source === "drag" ? "Compiling after drag..." : "Compiling...");
try { try {
const result = await apiPost("/compile", { const result = await apiPost("/compile", {
payload: model, payload: model,
options: compileOptions() options: compileOptions(
preservePlacement
? {
preserve_placement: true,
auto_rotate: false,
respect_locks: true
}
: {}
)
}); });
state.model = applyCompileLayoutToModel(model, result); state.model = applyCompileLayoutToModel(model, result);
@ -3316,7 +3326,7 @@ function setupEvents() {
inst.placement.locked = true; inst.placement.locked = true;
} }
} }
await compileModel(state.model, { source: "drag", keepView: true }); await compileModel(state.model, { source: "drag", keepView: true, preservePlacement: true });
} }
}); });

View File

@ -118,6 +118,41 @@ function rotateSide(side, rotation) {
return order[(idx + steps) % 4]; return order[(idx + steps) % 4];
} }
function hasValidPlacement(inst) {
return Number.isFinite(inst?.placement?.x) && Number.isFinite(inst?.placement?.y);
}
function preservePlacedInstances(model, options = {}) {
const respectLocks = options.respectLocks ?? true;
const autoRotate = options.autoRotate ?? false;
const fallback = placeInstances(model, { respectLocks, autoRotate });
const fallbackByRef = new Map((fallback?.placed ?? []).map((inst) => [inst.ref, inst]));
const instances = [...model.instances].sort((a, b) => a.ref.localeCompare(b.ref));
return instances.map((inst) => {
if (hasValidPlacement(inst)) {
return {
...inst,
placement: {
x: toGrid(Number(inst.placement.x)),
y: toGrid(Number(inst.placement.y)),
rotation: normalizeRotation(inst.placement.rotation ?? 0),
locked: respectLocks ? Boolean(inst.placement.locked) : false
}
};
}
return fallbackByRef.get(inst.ref) ?? {
...inst,
placement: {
x: toGrid(MARGIN_X),
y: toGrid(MARGIN_Y),
rotation: normalizeRotation(inst.placement?.rotation ?? 0),
locked: respectLocks ? Boolean(inst.placement?.locked) : false
}
};
});
}
function getNodePin(model, placedMap, node) { function getNodePin(model, placedMap, node) {
const inst = placedMap.get(node.ref); const inst = placedMap.get(node.ref);
if (!inst) { if (!inst) {
@ -2358,8 +2393,11 @@ export function layoutAndRoute(model, options = {}) {
const renderMode = options.render_mode === "explicit" ? "explicit" : DEFAULT_RENDER_MODE; const renderMode = options.render_mode === "explicit" ? "explicit" : DEFAULT_RENDER_MODE;
const respectLocks = options.respect_locks ?? true; const respectLocks = options.respect_locks ?? true;
const autoRotate = options.auto_rotate ?? true; const autoRotate = options.auto_rotate ?? true;
const preservePlacement = options.preserve_placement === true;
const { placed, placedMap } = placeInstances(model, { respectLocks, autoRotate }); const placed = preservePlacement
? preservePlacedInstances(model, { respectLocks, autoRotate: false })
: placeInstances(model, { respectLocks, autoRotate }).placed;
const placedMap = new Map(placed.map((inst) => [inst.ref, inst]));
const bounds = buildBounds(model, placed); const bounds = buildBounds(model, placed);
const { routed, busGroups } = routeAllNets(model, placed, placedMap, bounds, { const { routed, busGroups } = routeAllNets(model, placed, placedMap, bounds, {
renderMode renderMode