fix: address zero-wall review findings

Issue: #306
User-Visible: yes
This commit is contained in:
Matysh
2026-08-26 14:01:58 +03:00
parent db4e0ef999
commit f15bdc678b
10 changed files with 104 additions and 37 deletions
+34 -14
View File
@@ -7347,6 +7347,25 @@ class HouseplanCard extends LitElement {
return this._t(`wall_model.reason.${reason}`);
}
/** A legacy virtual-wall projection is still present only until its first
* structural v9 write. Keep its atomic-failure copy distinct from generic
* wall-model validation without persisting provenance on the new atoms. */
private _hasLegacyZeroWallFields(config: any = this._serverCfg): boolean {
return (config?.spaces || []).some((space: any) => (
(Array.isArray(space?.open_spans) && space.open_spans.length > 0)
|| (space?.rooms || []).some((room: any) => (
Array.isArray(room?.open_to) && room.open_to.length > 0
))
));
}
private _showWallModelMigrationBlocked(error: unknown): void {
const key = this._hasLegacyZeroWallFields()
? 'toast.zero_wall_migration_blocked'
: 'toast.wall_model_migration_blocked';
this._showToast(this._t(key, { reason: this._wallModelBlockerLabel(error) }));
}
private _commitPhysicalGeometry(
name: string,
before: SpaceGeometryState | null,
@@ -7417,9 +7436,7 @@ class HouseplanCard extends LitElement {
} catch (error) {
this._clearGeometryGesture();
this._restoreGeometryStateLocal(before);
this._showToast(this._t('toast.wall_model_migration_blocked', {
reason: this._wallModelBlockerLabel(error),
}));
this._showWallModelMigrationBlocked(error);
return false;
}
let safe = false;
@@ -7551,9 +7568,7 @@ class HouseplanCard extends LitElement {
committedCandidate = commitWallSegmentModel(restoredCandidate).config;
} catch (error) {
this._restoreGeometryStateLocal(before);
this._showToast(this._t('toast.wall_model_migration_blocked', {
reason: this._wallModelBlockerLabel(error),
}));
this._showWallModelMigrationBlocked(error);
return false;
}
if (physicalChanged) {
@@ -8124,7 +8139,9 @@ class HouseplanCard extends LitElement {
});
if (repair.kind === 'ambiguous') {
this._wallRepairDiagnostic = repair.proposals[0] || null;
this._showToast(this._t('toast.wall_repair_ambiguous'));
this._showToast(this._t(
this._drawWallCm === 0 ? 'toast.zero_wall_ambiguous' : 'toast.wall_repair_ambiguous',
));
return;
}
if (repair.kind === 'repair' && !this._overlapRoom(repair.face.ring)) {
@@ -8200,7 +8217,9 @@ class HouseplanCard extends LitElement {
});
if (result.kind === 'ambiguous') {
this._wallRepairDiagnostic = result.proposals[0] || null;
this._showToast(this._t('toast.wall_repair_ambiguous'));
this._showToast(this._t(
this._drawWallCm === 0 ? 'toast.zero_wall_ambiguous' : 'toast.wall_repair_ambiguous',
));
return true;
}
if (result.kind === 'repair') {
@@ -8327,7 +8346,7 @@ class HouseplanCard extends LitElement {
if (p && cmRaw === 0 && zeroWallHasOpening(sp.openings, {
kind: 'partition', id: d.id,
})) {
this._showToast(this._t('toast.zero_wall_opening'));
this._showToast(this._t('toast.zero_wall_opening_conflict'));
return;
}
if (p) p.cm = cmRaw;
@@ -11699,7 +11718,7 @@ class HouseplanCard extends LitElement {
if (cmRaw === 0 && zeroWallHasOpening(sp.openings, {
kind: 'partition', id: partition.id,
})) {
this._showToast(this._t('toast.zero_wall_opening'));
this._showToast(this._t('toast.zero_wall_opening_conflict'));
return;
}
partition.cm = cmRaw;
@@ -11739,7 +11758,7 @@ class HouseplanCard extends LitElement {
return point.every(Number.isFinite) && distToSegment(point, target) <= eps;
});
if (blocked) {
this._showToast(this._t('toast.zero_wall_opening'));
this._showToast(this._t('toast.zero_wall_opening_conflict'));
return;
}
}
@@ -15657,9 +15676,7 @@ class HouseplanCard extends LitElement {
this._optimizeReferenceContext(removeLiveMissingPositions),
);
} catch (error) {
this._showToast(this._t('toast.wall_model_migration_blocked', {
reason: this._wallModelBlockerLabel(error),
}));
this._showWallModelMigrationBlocked(error);
return;
}
const preflight = r.changed ? this._checkOptimizeGeometry(r.config) : null;
@@ -16917,6 +16934,9 @@ class HouseplanCard extends LitElement {
${r.wallSegmentsMigrated ? html`<p class="alignmsg">${this._t(
'gs.wall_segments_migrated', { n: String(r.wallSegmentsMigrated) },
)}</p>` : nothing}
${r.legacyZeroWallsMigrated ? html`<p class="alignmsg">${this._t(
'gs.zero_walls_migrated', { n: String(r.legacyZeroWallsMigrated) },
)}</p>` : nothing}
${modelMaintenance ? html`<p class="alignmsg">${this._t('gs.optimize_changes', {
m: String(r.migrated), c: String(r.canonicalized),
p: String(r.coordsCanonicalized), w: String(r.wallsMerged),
+5 -2
View File
@@ -534,7 +534,9 @@
"toast.wallthick_set": "Wall thickness set",
"toast.wallthick_cleared": "Wall thickness removed",
"toast.physical_range": "Enter a value from {min} to {max} {unit}",
"toast.zero_wall_opening": "Remove the opening on this wall stretch first",
"toast.zero_wall_opening_conflict": "Remove the opening on this wall segment first.",
"toast.zero_wall_ambiguous": "The wall segment is ambiguous. Simplify or adjust the junction.",
"toast.zero_wall_migration_blocked": "The space was not converted: {reason}. No data was changed.",
"toast.physical_angle": "Enter a rotation angle from 0° up to, but not including, 90°",
"toast.physical_limit": "The space has reached the limit for this type of geometry",
"toast.geometry_unsafe": "Change canceled: wall geometry could not be built safely.",
@@ -815,7 +817,8 @@
"gs.preflight_update_hint": "The card and integration versions differ — update House Plan and retry.",
"gs.align_preflight_space": "Space {n}",
"gs.align_preflight_more": ", and {n} more",
"gs.optimize_changes": "Model migrations: {m}; spaces updated: {c}; noisy coordinate values removed: {p}; merged real-wall fragments: {w}; virtual fragments: {s}; independent walls: {i}.",
"gs.optimize_changes": "Model migrations: {m}; spaces updated: {c}; noisy coordinate values removed: {p}; merged real-wall fragments: {w}; merged zero-thickness wall fragments: {s}; independent walls: {i}.",
"gs.zero_walls_migrated": "Virtual wall spans converted: {n}.",
"gs.wall_segments_migrated": "Wall segments stabilised: {n}.",
"gs.optimize_lattice_summary": "Noisy coordinate values canonicalized: {n}; maximum movement: {cm} cm.",
"gs.optimize_lattice_space": "{space}: coordinate values canonicalized: {n}; off-grid values left unchanged: {far}.",
+5 -2
View File
@@ -534,7 +534,9 @@
"toast.wallthick_set": "Толщина стены задана",
"toast.wallthick_cleared": "Толщина стены убрана",
"toast.physical_range": "Введите значение от {min} до {max} {unit}",
"toast.zero_wall_opening": "Сначала удалите проём на этом участке стены",
"toast.zero_wall_opening_conflict": "Сначала удалите проём на этом участке стены.",
"toast.zero_wall_ambiguous": "Не удалось однозначно выбрать участок стены. Уточните геометрию узла.",
"toast.zero_wall_migration_blocked": "Пространство не преобразовано: {reason}. Данные не изменены.",
"toast.physical_angle": "Введите угол поворота от 0° до 90°, не включая 90°",
"toast.physical_limit": "В пространстве достигнут лимит объектов этого типа",
"toast.geometry_unsafe": "Изменение отменено: геометрию стен нельзя безопасно построить.",
@@ -815,7 +817,8 @@
"gs.preflight_update_hint": "Версии карточки и интеграции различаются — обновите House Plan и повторите.",
"gs.align_preflight_space": "Пространство {n}",
"gs.align_preflight_more": " и ещё {n}",
"gs.optimize_changes": "Миграций модели: {m}; обновлено пространств: {c}; устранён шум координат: {p}; объединено отрезков реальных стен: {w}; виртуальных: {s}; независимых: {i}.",
"gs.optimize_changes": "Миграций модели: {m}; обновлено пространств: {c}; устранён шум координат: {p}; объединено отрезков реальных стен: {w}; объединено отрезков стен нулевой толщины: {s}; независимых: {i}.",
"gs.zero_walls_migrated": "Преобразовано виртуальных участков: {n}.",
"gs.wall_segments_migrated": "Стабилизировано сегментов стен: {n}.",
"gs.optimize_lattice_summary": "Канонизировано шумовых значений координат: {n}; максимальный сдвиг: {cm} см.",
"gs.optimize_lattice_space": "{space}: канонизировано значений координат: {n}; оставлено значений вне сетки: {far}.",
+8 -2
View File
@@ -57,9 +57,11 @@ export interface OptimizeReport extends AlignReport, SpaceReferenceReport {
canonicalized: number;
/** Contour-wall atoms materialised into the stable v8 catalogue. */
wallSegmentsMigrated: number;
/** Read-compatible legacy virtual spans converted into zero-wall atoms. */
legacyZeroWallsMigrated: number;
/** Redundant equal-thickness wall entries removed by canonicalisation. */
wallsMerged: number;
/** Touching/overlapping virtual pieces merged on the same room pair. */
/** Touching/overlapping zero-wall atoms merged on the same room pair. */
spansMerged: number;
/** Collinear independent-wall records collapsed into one (#229). */
partitionsMerged: number;
@@ -480,6 +482,7 @@ export function optimizePlans(
} : { ...aligned.report };
let wallsMerged = 0;
let legacyZeroWallsMigrated = 0;
let spansMerged = 0;
let partitionsMerged = 0;
let partitionsReconciled = 0;
@@ -513,7 +516,9 @@ export function optimizePlans(
// the final identity barrier assign stable catalogue IDs. No production
// maintenance pass writes open_spans/open_to back into the candidate.
const knownZeroKeys = new Set(sourceZeroWalls.map((wall) => wall.key));
for (const line of legacyZeroContourLines(before, oldModel.rooms, NORM_W, eps)) {
const legacyZeroLines = legacyZeroContourLines(before, oldModel.rooms, NORM_W, eps);
legacyZeroWallsMigrated += legacyZeroLines.length;
for (const line of legacyZeroLines) {
const a = [line[0] / NORM_W, line[1] / NORM_W];
const b = [line[2] / NORM_W, line[3] / NORM_W];
const key = wallKey(a, b, GRID_STEP_N);
@@ -693,6 +698,7 @@ export function optimizePlans(
glowRoomsMigrated: changed ? migration.glowRooms : 0,
canonicalized: changed ? canonicalized : 0,
wallSegmentsMigrated: changed ? wallSegmentsMigrated : 0,
legacyZeroWallsMigrated: changed ? legacyZeroWallsMigrated : 0,
wallsMerged: changed ? wallsMerged : 0,
spansMerged: changed ? spansMerged : 0,
partitionsMerged: changed ? partitionsMerged : 0,