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 {
render_mode: state.renderMode,
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 fit = opts.fit ?? false;
const keepView = opts.keepView ?? false;
const preservePlacement = opts.preservePlacement ?? false;
setStatus(source === "drag" ? "Compiling after drag..." : "Compiling...");
try {
const result = await apiPost("/compile", {
payload: model,
options: compileOptions()
options: compileOptions(
preservePlacement
? {
preserve_placement: true,
auto_rotate: false,
respect_locks: true
}
: {}
)
});
state.model = applyCompileLayoutToModel(model, result);
@ -3316,7 +3326,7 @@ function setupEvents() {
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];
}
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) {
const inst = placedMap.get(node.ref);
if (!inst) {
@ -2358,8 +2393,11 @@ export function layoutAndRoute(model, options = {}) {
const renderMode = options.render_mode === "explicit" ? "explicit" : DEFAULT_RENDER_MODE;
const respectLocks = options.respect_locks ?? true;
const autoRotate = options.auto_rotate ?? true;
const { placed, placedMap } = placeInstances(model, { respectLocks, autoRotate });
const preservePlacement = options.preserve_placement === true;
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 { routed, busGroups } = routeAllNets(model, placed, placedMap, bounds, {
renderMode