feat: unify zero-thickness walls

Issue: #306
User-Visible: yes
This commit is contained in:
Matysh
2026-08-26 13:39:52 +03:00
parent 5e35612bdd
commit a66fb7ef53
88 changed files with 3046 additions and 2990 deletions
+34 -6
View File
@@ -203,6 +203,20 @@ export const CONFIG_FIELD_REGISTRY = Object.freeze([
migration: 'remove the fill token and materialize effective room glow on room edit or explicit optimization',
compatibility: 'backend and frontend accept the legacy token indefinitely',
},
{
id: 'spaces[].open_spans',
selector: { path: ['spaces', '*', 'open_spans'] },
storage: 'House Plan server config',
type: 'Array<{ a: Point; b: Point }>',
default: 'unset',
level: 'space',
ui: 'removed',
runtime: 'read-only compatibility projection to cm:0 contour atoms',
introduced: 'legacy virtual-boundary model',
status: 'migrate-on-write',
migration: 'convert covered wall_segments to cm:0 and remove after a structural write, Optimize or import',
compatibility: 'accepted only below model v9; canonical v9 rejects it',
},
{
id: 'spaces[].rooms[].open_to',
selector: { path: ['spaces', '*', 'rooms', '*', 'open_to'] },
@@ -210,22 +224,36 @@ export const CONFIG_FIELD_REGISTRY = Object.freeze([
type: 'string[] | null',
default: 'unset',
level: 'room',
ui: 'replaced by boundary spans',
runtime: 'read fallback when explicit open_spans are absent',
ui: 'removed',
runtime: 'read-only fallback projected to cm:0 when explicit open_spans are absent',
introduced: 'legacy virtual-boundary model',
status: 'deprecated-read',
migration: 'materialize exact open_spans before geometry changes',
compatibility: 'keep reading until every supported config can represent the same geometry losslessly',
migration: 'convert the proven shared wall_segments to cm:0 and remove after a structural write, Optimize or import',
compatibility: 'accepted only below model v9; canonical v9 rejects it',
},
{
id: 'spaces[].zero_wall_style',
selector: { path: ['spaces', '*', 'zero_wall_style'] },
storage: 'House Plan server config',
type: "'dashed' | 'solid'",
default: 'dashed',
level: 'space',
ui: 'space settings: zero-thickness walls',
runtime: 'dashed transmits Glow/sun; solid blocks both without adding a wall body',
introduced: 'model v9 / issue #306',
status: 'current',
migration: 'never materialized implicitly; missing and unknown values read as dashed',
compatibility: 'older clients ignore the field and do not understand the unified cm:0 light contract',
},
{
id: 'spaces[].wall_segments',
selector: { path: ['spaces', '*', 'wall_segments'] },
storage: 'House Plan server config',
type: 'Array<{ id: string; a: Point; b: Point; cm: number }>',
default: '[] after the first v8 structural write',
default: '[] after the first v8 structural write; required in model v9',
level: 'space',
ui: 'internal canonical wall identity',
runtime: 'authoritative contour-wall identity and thickness in model v8',
runtime: 'authoritative contour-wall identity and thickness; cm:0 is a zero-thickness wall in model v9',
introduced: 'model v8 / issue #282',
status: 'current',
migration: 'materialize atomically on a structural write, Optimize or import',
+2 -2
View File
@@ -14,8 +14,8 @@ const assign = (zone, tokens) => { for (const t of tokens.split(/\s+/)) T[t] = z
assign('base', ':host .sr-only ha-card .bootveil .toast .empty .spacer');
assign('plan', `.stage .plan-svg .hp-paper .hp-static-stage .hp-day-cycle-bg .hp-day-cycle-sun .hp-day-cycle-env
.seg .vertex .pathline .preview .active-axis .active-vertex .physical-hit .physical-chrome .physical-drag
.drawwall .drawwall-preview .drawwall-preview-fill .openwall .openwall-preview .wall-repair-preview
.wallbody .wallbody-fill .wallthick-hover .griddot .boundary-point
.drawwall .drawwall-preview .drawwall-preview-fill .zero-wall .wall-repair-preview
.wallbody .wallbody-fill .wallthick-hover .griddot
.plan-snap-node .plan-snap-line .plan-snap-overlay .hidden-wall-line .hidden-wall-node .hidden-wall-diagnostic
.room .room-outline .room-hover-fill .room-hover-halo .room-hover-outline .roomlabel
.rlhandle .rlgear .rlgearbtn .rlname .rlgo
+3 -18
View File
@@ -1494,21 +1494,6 @@ export const MUTANTS = [
+ ' if not _known_optimize_partition(old_partition):',
}],
},
{
id: 'hidden-diagnostic-under-virtual-walls',
guard: 'node demo/smoke_plan_snap_overlay.mjs',
because: 'hidden wall axes and source endpoints must remain visible above every real and '
+ 'virtual wall body while staying below transient tool previews (#296)',
patches: [{
file: 'src/houseplan-card.ts',
find: ' ${this._editing ? this._renderOpenWalls(disp) : nothing}\n'
+ ' ${this._markup ? svg`<g class="hp-editor-only-layer"\n'
+ ' opacity="${modeVisual?.editorWeight ?? 1}">${this._renderHiddenWallDiagnosticOverlay()}</g>` : nothing}',
replace: ' ${this._markup ? svg`<g class="hp-editor-only-layer"\n'
+ ' opacity="${modeVisual?.editorWeight ?? 1}">${this._renderHiddenWallDiagnosticOverlay()}</g>` : nothing}\n'
+ ' ${this._editing ? this._renderOpenWalls(disp) : nothing}',
}],
},
{
id: 'atomic-child-thickness-parent-fallback',
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '
@@ -1640,10 +1625,10 @@ export const MUTANTS = [
},
{
id: 'barrier-cache-never-invalidated',
guard: 'node demo/smoke_openwall.mjs',
guard: 'node demo/smoke_zero_walls.mjs',
because: 'кэш барьеров, который не инвалидируется по содержимому, — это свет '
+ 'сквозь стену, которая уже существует; смок переключает виртуальную границу '
+ 'и обязан увидеть смену освещённости соседней комнаты',
+ 'сквозь стену, которая уже существует; смок переключает пунктирную нулевую '
+ 'стену в сплошную и обязан увидеть смену освещённости соседней комнаты',
patches: [{
file: 'src/houseplan-card.ts',
find: 'if (this._lightBarrierCache?.key === cacheKey) return this._lightBarrierCache.value;',