mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-30 11:49:16 +00:00
fix: preserve authored wall paths across identity barrier
Issue: #282 User-Visible: yes
This commit is contained in:
+4
-1
@@ -19,7 +19,10 @@
|
||||
atomically on their first structural edit or through **Optimize plans**;
|
||||
Resize, Undo/Redo and repeated edits preserve the same identities without
|
||||
accumulating off-grid coordinates. The backend Optimize transaction also
|
||||
enforces this upgrade when invoked directly. Ambiguous legacy geometry is
|
||||
enforces this upgrade when invoked directly. An authored wall chain remains
|
||||
valid when it becomes a room or independent walls, while a rejected
|
||||
transaction stores no partial result and no longer reports false success.
|
||||
Ambiguous legacy geometry is
|
||||
left unchanged with a recovery prompt. No Plan editor controls or
|
||||
zero-thickness-wall behaviour changed
|
||||
([#282](https://github.com/Matysh/houseplan-card/issues/282)).
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
изменении или через **«Оптимизировать планы»**; Resize, отмена/повтор и
|
||||
последующие правки сохраняют те же ID без накопления координат вне сетки;
|
||||
серверная транзакция Optimize также гарантирует это обновление при прямом
|
||||
вызове.
|
||||
вызове. Принятая пользователем цепочка остаётся допустимой при преобразовании
|
||||
в комнату или независимые стены, а отклонённая транзакция не записывает
|
||||
частичный результат и не показывает ложное сообщение об успехе.
|
||||
Неоднозначная старая геометрия остаётся без изменений с подсказкой по
|
||||
восстановлению. Кнопки редактора плана и поведение стен нулевой толщины не
|
||||
менялись
|
||||
|
||||
+10
-2
@@ -7353,7 +7353,15 @@ class HouseplanCard extends LitElement {
|
||||
let safe = false;
|
||||
try {
|
||||
const afterSpace = committedCandidate.spaces.find((space: any) => space.id === before.spaceId);
|
||||
safe = wallModelOffGridValueCount(afterSpace) <= wallModelOffGridValueCount(historyBefore)
|
||||
// A completed point of the active, grid-snapped chain is authored input,
|
||||
// even before its first draft record exists in `before`. Include that
|
||||
// transient carrier in the growth baseline; later conversion from draft
|
||||
// to room/partition must not count the same coordinate as newly derived.
|
||||
const authoredPoints = this._path.length >= 2
|
||||
? this._path.map((point) => [point[0] / NORM_W, point[1] / NORM_W])
|
||||
: [];
|
||||
safe = wallModelOffGridValueCount(afterSpace)
|
||||
<= wallModelOffGridValueCount(historyBefore, authoredPoints)
|
||||
&& this._checkSpacePhysicalGeometry(committedCandidate, before.spaceId).ok;
|
||||
} catch {
|
||||
safe = false;
|
||||
@@ -13692,7 +13700,7 @@ class HouseplanCard extends LitElement {
|
||||
else delete sp.walls;
|
||||
}
|
||||
|
||||
this._commitPhysicalGeometry(this._t('history.wall_face_batch'), before);
|
||||
if (!this._commitPhysicalGeometry(this._t('history.wall_face_batch'), before)) return;
|
||||
delete this._resumeDraftBySpace[this._space];
|
||||
this._path = [];
|
||||
this._activeDraftId = null;
|
||||
|
||||
@@ -80,7 +80,9 @@ const canonicalSpan = (a: number[], b: number[]): [Point, Point] => (
|
||||
const lengthOf = (a: number[], b: number[]): number => Math.hypot(b[0] - a[0], b[1] - a[1]);
|
||||
|
||||
/** Unique authored/derived contour coordinates that are materially off-grid. */
|
||||
export const wallModelOffGridValueCount = (space: any): number => {
|
||||
export const wallModelOffGridValueCount = (
|
||||
space: any, additionalPoints: readonly (readonly number[])[] = [],
|
||||
): number => {
|
||||
const values = new Set<string>();
|
||||
const inspect = (value: unknown): void => {
|
||||
const number = Number(value);
|
||||
@@ -101,6 +103,9 @@ export const wallModelOffGridValueCount = (space: any): number => {
|
||||
inspect(segment?.b?.[0]); inspect(segment?.b?.[1]);
|
||||
}
|
||||
}
|
||||
for (const additionalPoint of additionalPoints) {
|
||||
inspect(additionalPoint?.[0]); inspect(additionalPoint?.[1]);
|
||||
}
|
||||
return values.size;
|
||||
};
|
||||
const projectT = (p: number[], a: number[], b: number[]): number => {
|
||||
|
||||
@@ -144,6 +144,11 @@ test('off-grid contour guard counts values once across compatibility projections
|
||||
wall_segments: [{ id: 'wall', a: [point, 0], b: [point, 1], cm: 20 }],
|
||||
walls: [{ key: 'legacy', a: [point, 0], b: [point, 1], cm: 20 }],
|
||||
}), 1);
|
||||
assert.equal(
|
||||
wallModelOffGridValueCount({ rooms: [] }, [[point, 0], [point, 1]]),
|
||||
1,
|
||||
'a transient authored path contributes its coordinates to the baseline once',
|
||||
);
|
||||
});
|
||||
|
||||
test('room openings acquire a stable wall host while partition hosts stay untouched', () => {
|
||||
|
||||
Reference in New Issue
Block a user