mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-30 19:58:50 +00:00
fix: keep editor writers at optimizer fixed point
Issue: #477 User-Visible: yes
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
import { spacePhysicalGeometryFingerprint } from './plan-geometry-preflight';
|
||||
import {
|
||||
isSinglePartitionAppend, wallChainLiveCandidateSpace, wallChainLiveSeed,
|
||||
type WallChainLiveSeed,
|
||||
} from './draft-live-preflight';
|
||||
|
||||
const NORM_W = 1000;
|
||||
@@ -34,7 +35,10 @@ export interface WallChainLiveCommitRuntime<
|
||||
Violation,
|
||||
> {
|
||||
host: WallChainLiveHost<State, Geometry>;
|
||||
_commitPhysicalGeometry: (name: string, before: State | null) => boolean;
|
||||
_commitPhysicalGeometry: (
|
||||
name: string, before: State | null,
|
||||
additionalAuthoredPoints?: readonly (readonly number[])[], recordHistory?: boolean,
|
||||
) => boolean;
|
||||
_clearGeometryGesture: () => void;
|
||||
_restoreGeometryStateInConfig: (config: ServerConfig, state: State) => boolean;
|
||||
_restoreGeometryStateLocal: (state: State) => boolean;
|
||||
@@ -48,6 +52,88 @@ export interface WallChainLiveCommitRuntime<
|
||||
_saveConfig: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* #477 finish transaction for a chain which has already been normalised on a
|
||||
* clone. It uses the same production physical/junction proof as a terminal
|
||||
* click, centred on the pre-normalisation seed. Unlike a click it records no
|
||||
* extra history command: all segment commands already exist.
|
||||
*/
|
||||
export function commitWallChainFinishGeometry<
|
||||
State extends { spaceId: string },
|
||||
Geometry extends JunctionSharedGeometry,
|
||||
Violation,
|
||||
>(
|
||||
runtime: WallChainLiveCommitRuntime<State, Geometry, Violation>,
|
||||
name: string,
|
||||
before: State | null,
|
||||
seed: WallChainLiveSeed | null,
|
||||
): boolean {
|
||||
const { host } = runtime;
|
||||
const liveCandidate = host._serverCfg;
|
||||
const fallback = () => runtime._commitPhysicalGeometry(name, before, [], false);
|
||||
if (!before || !liveCandidate || !seed
|
||||
|| Number(liveCandidate.model_version || 0) !== WALL_SEGMENT_MODEL_VERSION) return fallback();
|
||||
|
||||
let committedCandidate: ServerConfig;
|
||||
try {
|
||||
committedCandidate = commitWallSegmentModel(liveCandidate).config;
|
||||
} catch (error) {
|
||||
runtime._clearGeometryGesture();
|
||||
runtime._restoreGeometryStateLocal(before);
|
||||
runtime._showWallModelMigrationBlocked(error);
|
||||
return false;
|
||||
}
|
||||
const committedSpace = committedCandidate.spaces.find((space) => space.id === before.spaceId);
|
||||
const previousConfig = JSON.parse(JSON.stringify(liveCandidate)) as ServerConfig;
|
||||
if (!committedSpace || !runtime._restoreGeometryStateInConfig(previousConfig, before)) {
|
||||
return fallback();
|
||||
}
|
||||
const previousSpace = previousConfig.spaces.find((space) => space.id === before.spaceId);
|
||||
const candidateProjection = wallChainLiveCandidateSpace(committedSpace, seed);
|
||||
const previousProjection = wallChainLiveCandidateSpace(previousSpace, seed);
|
||||
if (!candidateProjection || !previousProjection) return fallback();
|
||||
const localCandidate = {
|
||||
...committedCandidate, spaces: [candidateProjection.space],
|
||||
} as unknown as ServerConfig;
|
||||
const localPrevious = {
|
||||
...previousConfig, spaces: [previousProjection.space],
|
||||
} as unknown as ServerConfig;
|
||||
|
||||
let geometry: Geometry | null = null;
|
||||
let safe = false;
|
||||
try {
|
||||
const authoredPoints = host._path.length >= 2
|
||||
? host._path.map((point) => [point[0] / NORM_W, point[1] / NORM_W]) : [];
|
||||
safe = wallModelOffGridValueCount(committedSpace)
|
||||
<= wallModelOffGridValueCount(before, authoredPoints)
|
||||
&& host._checkSpacePhysicalGeometry(
|
||||
localCandidate, before.spaceId, (value) => { geometry = value; },
|
||||
).ok;
|
||||
} catch {
|
||||
safe = false;
|
||||
}
|
||||
if (!safe) return rejectUnsafe(runtime, before);
|
||||
const introduced = runtime._junctionLimitsIntroduced(
|
||||
localCandidate, localPrevious, before.spaceId, geometry, candidateProjection.roomIds,
|
||||
);
|
||||
if (introduced.length) {
|
||||
runtime._clearGeometryGesture();
|
||||
runtime._restoreGeometryStateLocal(before);
|
||||
host._showToast(runtime._junctionLimitLabel(introduced[0]));
|
||||
return false;
|
||||
}
|
||||
|
||||
adoptWallSegmentModelCandidateInPlace(liveCandidate, committedCandidate);
|
||||
const acceptedSpace = liveCandidate.spaces.find((space) => space.id === before.spaceId);
|
||||
const pending = host._pendingPhysicalWrites.get(before.spaceId);
|
||||
host._pendingPhysicalWrites.set(before.spaceId, {
|
||||
before: pending?.before || before,
|
||||
fingerprint: spacePhysicalGeometryFingerprint(acceptedSpace),
|
||||
});
|
||||
runtime._saveConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
const rejectUnsafe = <State extends { spaceId: string }, Geometry extends JunctionSharedGeometry,
|
||||
Violation>(runtime: WallChainLiveCommitRuntime<State, Geometry, Violation>, before: State): false => {
|
||||
runtime._clearGeometryGesture();
|
||||
|
||||
Reference in New Issue
Block a user