mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 21:01:21 +00:00
@@ -1,8 +1,8 @@
|
||||
# Issue #150 — Точная геометрия коллинеарного перепада толщины стен
|
||||
|
||||
- **Issue:** https://github.com/Matysh/houseplan-card/issues/150
|
||||
- **Редакция:** первая редакция для независимого ревью; статус задачи определяется
|
||||
только метками issue
|
||||
- **Редакция:** r2 после `SPEC-REVIEW-150-r1`; статус задачи определяется только
|
||||
метками issue
|
||||
- **Тип / приоритет:** bug / P2
|
||||
- **Оценка:** пользовательская ценность 8/10; ценность для разработки 8/10;
|
||||
сложность 7/10, риск 8/10
|
||||
@@ -21,15 +21,24 @@ Editor.
|
||||
пользователь выбирает внешнюю стену одной дочерней комнаты, задаёт `10 см` и
|
||||
нажимает «Применить ко всем стенам комнаты».
|
||||
|
||||
**До → после, без терминов реализации:** сейчас стены выбранной комнаты рядом
|
||||
с разделителем выглядят примерно вдвое тоньше указанного значения; после
|
||||
исправления они имеют полные 10 см до самой точки разделителя, соседняя комната
|
||||
остаётся без толщины, а переход между участками виден как точная ступень.
|
||||
**До → после, без терминов реализации:** сейчас после Split толщина выбранной
|
||||
комнаты сохраняется правильно, но у точки разделителя не заканчивается и
|
||||
визуально продолжается по внешним стенам соседней комнаты, для которых задано
|
||||
`0`. После исправления выбранные участки имеют полные 10 см до точки
|
||||
разделителя, соседняя комната остаётся без толщины, а граница видна как точная
|
||||
ступень.
|
||||
|
||||
Первоначальное описание «примерно 5 вместо 10» уточнено после ревью. На
|
||||
скриншоте внутренняя половина внешней стены действительно занимает 5 см, потому
|
||||
что действующий контракт стены — `centreline ±½`; внешняя половина лежит на
|
||||
белом поле и читается по штриховке. Полная глубина выбранного участка равна
|
||||
10 см. Реальный дефект скриншота — отсутствие границы `10 → 0`: штриховка
|
||||
10-сантиметрового body продолжается снаружи вдоль нулевого фасада соседа.
|
||||
|
||||
Задача поддерживает J4 и J6 из `docs/SCOPE.md`: штатное редактирование должно
|
||||
создавать измеримо правдивую и стабильно читаемую архитектуру.
|
||||
|
||||
## 2. Воспроизведение и подтверждённая причина
|
||||
## 2. Красное воспроизведение на `dev` и подтверждённая причина
|
||||
|
||||
На конфигурационном уровне команда уже работает правильно:
|
||||
|
||||
@@ -37,15 +46,40 @@ Editor.
|
||||
- общий разделитель получает `10 см` как одна физическая стена;
|
||||
- внешние стены соседней комнаты остаются `0`.
|
||||
|
||||
Ошибка появляется при построении канонической геометрии. После Split общий
|
||||
внешний фасад является одной collinear boundary, но на ней встречаются соседние
|
||||
atomic intervals `10 → 0`. Boolean union комнат схлопывает child-room vertex, а
|
||||
последующая variable-offset exterior envelope не сохраняет локальный перепад
|
||||
как жёсткую поперечную ступень. В результате wall shell у endpoint разделителя
|
||||
сужается/интерполируется и выглядит как половина заданной толщины.
|
||||
Повторная проверка после `SPEC-REVIEW-150-r1` выполнена на текущем `dev`
|
||||
`a05aa5d` двумя путями: через фактические обработчики Plan Editor
|
||||
`_splitClick()` → `_wallThickClick()` → `_wallThickApply(true)` и через
|
||||
исполняемые `setWallThicknessForRoom()` → `wallIntervals()` →
|
||||
`wallBodiesGeometry()` с production-параметром `coordScale = 1000`.
|
||||
|
||||
Это не дефект сохранения команды «Применить ко всем» и не исправляется
|
||||
дублированием wall key, CSS stroke или визуальной накладкой только в Plan.
|
||||
Красный fixture: прямоугольник `[100,100]–[900,900]`, вертикальный Split в
|
||||
`x = 500`, слева `10 см`, справа `0`, разделитель `10 см`. Данные дают:
|
||||
|
||||
- левый верхний interval: `cm = 10`;
|
||||
- правый верхний interval: `cm = 0`;
|
||||
- divider: `cm = 10`;
|
||||
- ожидаемая полная глубина: `8.333333` render units.
|
||||
|
||||
Но текущий masonry path содержит верхнюю грань
|
||||
`M 95.833… 95.833… L 900 95.833…`: глубина 10 см протянута через весь правый
|
||||
нулевой interval до `x = 900`. Поэтому probe на правом участке, например
|
||||
`[700, 96]`, ошибочно находится внутри masonry, хотя обязан быть вне его.
|
||||
Сечение выбранного левого участка при этом полное — `8.333333` units; тест,
|
||||
проверяющий только толстую сторону, дефект не обнаруживает.
|
||||
|
||||
Подтверждённая причина находится в `exteriorBoundaryProfile()`. Коэффициент
|
||||
`t` точки разбиения нормирован на диапазон `0…1`, но сравнивается с `eps` в
|
||||
render units. При обычных `pitch = 1/240` и `coordScale = 1000` используемый
|
||||
`eps` равен примерно `0.6667`; проверки `t > eps && t < 1 - eps` становятся
|
||||
невыполнимыми, а дедупликация `t` тем же допуском также размерностно неверна.
|
||||
Breakpoint `x = 500` после boolean union не материализуется, и весь
|
||||
коллинеарный edge получает depth того interval, на который попал его midpoint.
|
||||
Воспроизведение обязано использовать production scale; проверка с
|
||||
`coordScale = 1` не ловит ошибку.
|
||||
|
||||
Это не дефект сохранения команды «Применить ко всем», не изменение семантики
|
||||
`centreline ±½` и не исправляется дублированием wall key, CSS stroke или
|
||||
визуальной накладкой только в Plan.
|
||||
|
||||
## 3. Решения владельца
|
||||
|
||||
@@ -269,46 +303,56 @@ interface WallEntry {
|
||||
Controls, dialogs, tool sequence, labels и focus не меняются. Новые locale keys
|
||||
не требуются.
|
||||
|
||||
Plan Editor остаётся desktop-first. Touch editor — best effort, но safety floor
|
||||
обязателен: тот же сохранённый config на touch не может получить другую
|
||||
физическую геометрию из-за pointer type. View и kiosk fully supported и обязаны
|
||||
показывать тот же полный stepped body.
|
||||
Plan Editor остаётся desktop-first. **Touch editor: best effort / intentionally
|
||||
degraded.** Safety floor обязателен: тот же сохранённый config на touch не может
|
||||
получить другую физическую геометрию из-за pointer type. View и kiosk fully
|
||||
supported и обязаны показывать тот же полный stepped body.
|
||||
|
||||
`prefers-reduced-motion` не затрагивается. Исправление geometry не добавляет
|
||||
animation и не использует цвет как единственное доказательство толщины.
|
||||
|
||||
## 12. Acceptance criteria
|
||||
|
||||
1. Точный сценарий issue сохраняет data profile `10 см` на всех внешних стенах
|
||||
выбранной комнаты и shared divider, `0` на остальных внешних стенах соседа.
|
||||
2. Нормальное сечение каждого 10-сантиметрового участка до endpoint имеет
|
||||
полные 10 см: 5 см наружу и 5 см внутрь, в численном пересчёте render units.
|
||||
3. На endpoint разделителя фасад меняется `10 → 0` точной поперечной ступенью;
|
||||
нет taper, half-depth strip, переноса 10 см на соседа, зуба или щели.
|
||||
4. Матрица `0 ↔ 10`, `10 ↔ 20`, `1 ↔ 100` и равных толщин корректна в обоих
|
||||
направлениях, при обратном winding и перестановке/переименовании комнат.
|
||||
5. Shared divider сохраняет свою полную толщину, соединяется внутри и не
|
||||
1. **[unit]** Production-scale fixture из §2 сохраняет data profile `10 см` на
|
||||
внешних стенах выбранной комнаты и shared divider, `0` на внешних стенах
|
||||
соседа; тест явно передаёт `coordScale = 1000`.
|
||||
2. **[unit]** Нормальное сечение каждого 10-сантиметрового участка до endpoint
|
||||
имеет полные 10 см: 5 см наружу и 5 см внутрь, в численном пересчёте render
|
||||
units.
|
||||
3. **[unit + golden]** На endpoint разделителя фасад меняется `10 → 0` точной
|
||||
поперечной ступенью; probes на нулевой стороне находятся вне masonry, нет
|
||||
taper, переноса 10 см на соседа, зуба или щели.
|
||||
4. **[unit]** Матрица `0 ↔ 10`, `10 ↔ 20`, `1 ↔ 100` и равных толщин корректна
|
||||
в обоих направлениях, при обратном winding и перестановке/переименовании
|
||||
комнат.
|
||||
5. **[unit]** Shared divider сохраняет полную толщину, соединяется внутри и не
|
||||
расширяет фасад наружу.
|
||||
6. Paper, clean floor и displayed area следуют локальным depths и точной
|
||||
ступени без клина/утечки пола.
|
||||
7. Plan, View/kiosk, static и hidden Iso имеют одинаковый wall/floor outline;
|
||||
Glow и солнце блокируются ровно нарисованным body.
|
||||
8. Openings около/через transition используют локальные depths и не создают
|
||||
ложных cuts или двойной fill.
|
||||
9. Регрессии #123 (corner Split facade) и #141 (independent junctions) остаются
|
||||
закрытыми; nested/courtyard/open-span fixtures не меняются ошибочно.
|
||||
10. Старый config исправляется вычисляемо без save/migration и остаётся
|
||||
совместимым с rollback.
|
||||
11. Structural geometry кешируется; ordinary HA tick не повторяет topology.
|
||||
12. Boolean failure остаётся fail-closed, не показывает известную ошибочную
|
||||
half-depth геометрию.
|
||||
6. **[unit]** Paper, clean floor и displayed area следуют локальным depths и
|
||||
точной ступени без клина/утечки пола.
|
||||
7. **[smoke + golden]** Plan, View/kiosk, static и hidden Iso имеют одинаковый
|
||||
wall/floor outline; Glow и солнце блокируются ровно нарисованным body.
|
||||
8. **[unit + smoke]** Openings около/через transition используют локальные
|
||||
depths и не создают ложных cuts или двойной fill.
|
||||
9. **[unit + smoke]** Регрессии #123 (corner Split facade) и #141 (independent
|
||||
junctions) остаются закрытыми; nested/courtyard/open-span fixtures не
|
||||
меняются ошибочно.
|
||||
10. **[unit + ревью кода]** Старый config исправляется вычисляемо без
|
||||
save/migration и остаётся совместимым с rollback.
|
||||
11. **[unit + ревью кода]** Structural geometry кешируется; ordinary HA tick не
|
||||
повторяет topology.
|
||||
12. **[unit + ревью кода]** Boolean failure остаётся fail-closed и не
|
||||
воскрешает raw per-room rings.
|
||||
|
||||
## 13. Проверки и доказательства
|
||||
|
||||
### Unit — данные и точная геометрия
|
||||
|
||||
- fixture из issue: Split rectangle, all walls `0`, all-room `10` для одной
|
||||
child room; assert effective intervals и shared wall ownership;
|
||||
- красный production-scale fixture из §2: Split rectangle, all walls `0`,
|
||||
all-room `10` для одной child room; assert effective intervals, shared wall
|
||||
ownership, полную толстую сторону и отсутствие masonry probes на нулевой
|
||||
стороне;
|
||||
- отдельная нормализованная вариация с `coordScale = 1` не заменяет fixture с
|
||||
`coordScale = 1000`;
|
||||
- cross-section/point containment на interior участках доказывает exact
|
||||
`±h_i`, а не только сравнивает bbox/скриншот;
|
||||
- transition faces совпадают с divider endpoint и перпендикулярны centreline;
|
||||
|
||||
Reference in New Issue
Block a user