mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-01 12:18:51 +00:00
docs: spec #329 — wall junction limits and an honest sharp apex
Issue: #329 User-Visible: no
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
# Issue #329 — ограничения стыков стен и честная острая вершина
|
||||
|
||||
Статус: ревизия 1. Решения владельца (чат, 2026-08-27, зафиксированы в issue):
|
||||
пять ограничений приняты; проверки действуют на записи, легаси читается как
|
||||
есть; для остаточных острых вершин легаси-планов рендер закрывает остриё
|
||||
честной фаской вместо «трезубца».
|
||||
|
||||
## 1. Сценарий и пользовательский результат
|
||||
|
||||
Сегодня комната-треугольник с вершиной ≈9.9° (стены 15 см) рендерится
|
||||
«трезубцем»: три пика и два V-выреза (репродукция в issue, фикстура
|
||||
`houseplan-space-test2-2026-08-27_16-14-48.json`, комната `rmtbq3k5e-0`).
|
||||
Причина класса: при остром угле внутренние грани стен смыкаются на
|
||||
`полутолщина/tan(θ/2)` от вершины (86 см при 10°/15 см) — весь участок выше
|
||||
физически перекрыт телами обеих стен, и объединение тел с вершинными
|
||||
правилами #309/#310 распадается.
|
||||
|
||||
После задачи: (а) нарисовать такое больше нельзя — запись отклоняется с
|
||||
точным тостом; (б) уже существующие такие планы рендерятся прилично — остриё
|
||||
закрыто одной фаской, без пиков и вырезов.
|
||||
|
||||
## 2. Нормативные ограничения (решения владельца)
|
||||
|
||||
Все проверки выполняются над кандидатом структурной записи (фронт — в общем
|
||||
барьере перед записью; бэкенд — семантическая дельта-валидация §5). Нарушение
|
||||
= отказ всей записи, план не изменён, тост называет конкретное правило.
|
||||
|
||||
- **П1. Минимальный угол:** угол между СОСЕДНИМИ по азимуту стенами одного
|
||||
узла ≥ **15°**. Считается по осям сегментов, для каждого узла кандидата.
|
||||
- **П2. Валентность узла:** в одном узле сходятся ≤ **6** стен (контурные
|
||||
атомы, независимые стены и перегородки считаются вместе).
|
||||
- **П3. Минимальная длина сегмента:** длина ≥ **max(20 см, толщина
|
||||
сегмента)**. Для нулевых стен (#306) — ≥ 20 см.
|
||||
- **П4. Минимальная дистанция:** несмежные узлы — не ближе **5 см** (1
|
||||
клетка); узел — не ближе 5 см к стене, которой он не принадлежит
|
||||
(касание конца о середину чужой стены остаётся T-стыком и валидно —
|
||||
правило про «почти-касание» без инцидентности).
|
||||
- **П5. Просвет комнаты:** после вычета тел стен внутренняя область комнаты
|
||||
непуста и её площадь ≥ **25 см²** (1 клетка 5×5).
|
||||
|
||||
## 3. Граница применения
|
||||
|
||||
- Проверяются только записи, ИЗМЕНЯЮЩИЕ соответствующую геометрию: рисование
|
||||
и завершение контура, черновики, независимые стены/перегородки, Resize,
|
||||
инструмент «Толщина» (толщина участвует в П3/П5), merge/split.
|
||||
- Существующие данные наследуются: запись, не трогающая нарушающий элемент,
|
||||
проходит; миграция (#306/#316), импорт и восстановление из бэкапа не
|
||||
блокируются никогда.
|
||||
- Optimize (выравнивание/канонизация) не обязан чинить легаси-нарушения и не
|
||||
имеет права создавать новые (пост-условие прогона).
|
||||
|
||||
## 4. Рендер остаточных острых вершин (легаси)
|
||||
|
||||
Для пары стен вершины, чьи внутренние грани смыкаются на расстоянии `d` от
|
||||
вершины, меньшем длины обеих стен (эквивалентно: угол мал настолько, что
|
||||
существует зона полного перекрытия тел):
|
||||
|
||||
- остриё обрезается **одной плоской фаской**, перпендикулярной биссектрисе,
|
||||
проходящей через точку смыкания внутренних граней;
|
||||
- выше фаски тел нет вовсе (никаких пиков), ниже — обычная кладка; V-вырезы
|
||||
запрещены; штриховка непрерывна;
|
||||
- правило дополняет #309/#310 и включается только в вырожденной зоне
|
||||
(`d` внутри длины стен); обычные острые пары (#310, остриё вне зоны
|
||||
перекрытия) не меняются.
|
||||
|
||||
## 5. Бэкенд
|
||||
|
||||
Семантическая дельта-валидация по образцу существующих (например, jamb
|
||||
margin): правила П1–П5 применяются к элементам, которых касается дельта
|
||||
записи (новые/изменённые сегменты, узлы, комнаты); унаследованные нарушения
|
||||
нетронутых элементов проходят. Ошибки — стабильные коды
|
||||
`junction_limit_<rule>` в общем формате `vol.Invalid`/семантических ошибок.
|
||||
Питоновское зеркало обязано давать решения, идентичные фронту.
|
||||
|
||||
## 6. Затронутые поверхности и артефакты
|
||||
|
||||
- `src/wall-thickness.ts` / `src/wall-segment-model.ts` — геометрия проверок
|
||||
П1–П5 (чистые функции) и фаска §4 в построении тел/вершинных правил.
|
||||
- `src/houseplan-card.ts` — вызовы проверок в общем барьере записи, тосты.
|
||||
- `src/i18n/en.json`, `src/i18n/ru.json` — по ключу на правило:
|
||||
`junction.limit_angle`, `junction.limit_valence`, `junction.limit_length`,
|
||||
`junction.limit_distance`, `junction.limit_clearance` (с параметрами
|
||||
значений) + заголовок отказа.
|
||||
- `custom_components/houseplan/validation.py` — дельта-правила §5;
|
||||
`tests_backend` — кейсы принятия унаследованного и отказа нового.
|
||||
- Тесты фронта: юниты на каждую формулу П1–П5 (границы: 14.9°/15°, 6/7 стен,
|
||||
19/20 см, 4/5 см, пустой/непустой просвет), мутанты в гейте на П1 и §4.
|
||||
- Смок: репро-фикстура #329 — новая попытка нарисовать «шпиль» отклоняется;
|
||||
легаси-конфиг рендерится по §4.
|
||||
- Golden: новая сцена `sharp-apex-legacy-dark` по фикстуре issue (рендер §4);
|
||||
существующие сцены не должны измениться — `golden:verify` обязан
|
||||
подтвердить (в матрице нет вершин острее 15° — проверить прогоном).
|
||||
- `docs/USER-GUIDE.md`/`.ru.md` — раздел об ограничениях рисования (таблица
|
||||
П1–П5); CHANGELOG RU+EN (`User-Visible: yes`).
|
||||
- `docs/CONFIG-COMPATIBILITY.md` — абзац: ограничения действуют на записи,
|
||||
унаследованные данные валидны на чтение/миграцию.
|
||||
- Новых полей конфига нет — `config-field-registry` не меняется.
|
||||
|
||||
## 7. Производительность и touch
|
||||
|
||||
Проверки П1–П4 — O(узлы+сегменты) на запись, вне горячего пути рендера. П5
|
||||
использует уже вычисляемые физические тела (кэш) на кандидате записи —
|
||||
допускается только на commit, не на каждый жест курсора. Touch-контракт не
|
||||
затрагивается (правки — не жесты).
|
||||
|
||||
## 8. AC
|
||||
|
||||
- **AC1 (П1).** Завершение контура с вершиной 14° отклоняется, тост называет
|
||||
минимум 15° и узел; 16° — проходит. Юнит на границе 15.0°.
|
||||
- **AC2 (П2).** Седьмая стена в узел — отказ; шесть — проходит.
|
||||
- **AC3 (П3).** Сегмент 19 см — отказ; 20 см — проходит; сегмент 25 см при
|
||||
толщине 30 см — отказ (длина < толщины).
|
||||
- **AC4 (П4).** Узел в 4 см от несмежного узла или чужой стены — отказ; 5 см
|
||||
— проходит; T-стык (конец на чужой стене) — проходит.
|
||||
- **AC5 (П5).** Повторение «шпиля» из фикстуры issue рисованием — отказ по
|
||||
просвету (даже если каждый угол ≥ 15°, но толщины съедают комнату).
|
||||
- **AC6 (§4).** Легаси-фикстура issue рендерится без «трезубца»: одна фаска,
|
||||
ноль V-вырезов; golden-сцена + пиксельная проверка смоком (нет разрывов
|
||||
тела выше фаски).
|
||||
- **AC7.** Resize/«Толщина», приводящие к нарушению любого из П1–П5, — отказ
|
||||
fail-closed, план байт-неизменен.
|
||||
- **AC8.** Миграция/импорт/restore фикстуры с нарушениями проходят.
|
||||
- **AC9 (§5).** Бэкенд: эхо-запись унаследованного нарушения проходит;
|
||||
запись, добавляющая новое нарушение, отклоняется стабильным кодом.
|
||||
- **AC10.** Идемпотентность: повторный commit валидного конфига байт-иден-
|
||||
тичен; Optimize на легаси-фикстуре не создаёт новых нарушений (AC-пост-
|
||||
условие §3).
|
||||
|
||||
## 9. Принято предположительно (поменять свободно)
|
||||
|
||||
- Угол П1 считается между соседними по азимуту осями в узле (не все пары).
|
||||
- Порог просвета 25 см² и способ его вычисления по кэшу физических тел.
|
||||
- Дельта-семантика §5: «элемент дельты» = сегмент/узел/комната, чьи данные
|
||||
изменились относительно previous.
|
||||
- Имена i18n-ключей и коды ошибок бэкенда.
|
||||
- Фаска §4 перпендикулярна биссектрисе в точке смыкания внутренних граней.
|
||||
|
||||
## 10. Риски
|
||||
|
||||
- Ложные отказы на живых планах у границы правил (особенно П4 при плотной
|
||||
геометрии) — смягчается точными тостами и тем, что унаследованное не
|
||||
блокируется.
|
||||
- П5 на больших пространствах — стоимость union; смягчается кэшем тел и
|
||||
проверкой только на commit.
|
||||
- §4 меняет пиксели вырожденных вершин — по построению матрицы golden таких
|
||||
сцен нет (verify обязан подтвердить), новая сцена фиксирует контракт.
|
||||
|
||||
## 11. Откат
|
||||
|
||||
Реверт ветки: ограничения исчезают, рендер возвращается к текущему
|
||||
поведению. Данные фикс не переписывает (валидация и рендер, не миграция).
|
||||
Reference in New Issue
Block a user