mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
docs: close M1, M2 and M3 from the spec review of #229
M1: "merge across the whole space when a chain ends" quietly overreached the owner's split — drawing fixes its own seam, the optimiser fixes what has piled up, and only the latter comes with a report and an undo. Section 8.6 now scopes it to the connected component the new chain belongs to. M2: the tolerance for "something else meets here" was only defined for a partition-to-partition joint. Room edges, columns and drafts now use the same EPS_JOIN — a gap cannot be a junction in one case and not in another. M3: an opening also carries a materialised x/y/angle projection that CONFIG-COMPATIBILITY (#132) requires to stay in step with its host, and the code already re-materialises it after every host change. Merging is such a change; AC3 now fails if only host.t is recomputed and the projection goes stale. Issue: #229 User-Visible: no
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
|
||||
## 3. Подтверждённая причина
|
||||
|
||||
`_finishWallChain` (`houseplan-card.ts:6558`) режет цепочку по числу поставленных
|
||||
`_finishWallChain` (`houseplan-card.ts:6538`) режет цепочку по числу поставленных
|
||||
точек и добавляет **по одной независимой перегородке на сегмент**:
|
||||
|
||||
```ts
|
||||
@@ -72,9 +72,9 @@ run of one thickness», оптимизатор её вызывает (`plan-opti
|
||||
## 6. Scope
|
||||
|
||||
- Чистый модуль слияния (`src/wall-merge.ts`) — правила без DOM, проверяемые юнитами.
|
||||
- Вызов при завершении цепочки (`_finishWallChain`).
|
||||
- Вызов при завершении цепочки (`_finishWallChain`, `houseplan-card.ts:6538`).
|
||||
- Вызов в «Оптимизировать планы» с новым счётчиком в отчёте.
|
||||
- Пересчёт `host.t` проёмов, висящих на сращиваемых перегородках.
|
||||
- Пересчёт `host.t` и материализованной проекции `x/y/angle` проёмов, висящих на сращиваемых перегородках.
|
||||
- i18n строки счётчика (en + ru), оба changelog.
|
||||
|
||||
## 7. Не входит в задачу
|
||||
@@ -112,6 +112,13 @@ run of one thickness», оптимизатор её вызывает (`plan-opti
|
||||
независимы и пересекаться могут без общей вершины — такой случай не является
|
||||
стыком и в слиянии не участвует.
|
||||
|
||||
**Все четыре причины проверяются одним допуском `EPS_JOIN`** — тем же, которым
|
||||
проверяется совпадение концов самих перегородок (§8.3). Отдельного допуска для
|
||||
ребра комнаты, колонны или черновика нет: иначе один и тот же зазор считался бы
|
||||
стыком в одном случае и не считался в другом, а AC2 перестал бы быть однозначным.
|
||||
Точка стыка берётся у ребра комнаты — ближайшая вершина полигона, у колонны —
|
||||
её центр, у черновика — конец сохранённого контура.
|
||||
|
||||
### 8.3. Допуски
|
||||
|
||||
Один источник истины на обе проверки, объявленный константами в модуле:
|
||||
@@ -138,18 +145,49 @@ run of one thickness», оптимизатор её вызывает (`plan-opti
|
||||
2. пересчитывается в долю новой длины с учётом того, какой конец стал началом;
|
||||
3. если хозяин поглощён — `host.id` переписывается на выжившую запись.
|
||||
|
||||
4. **пересчитывается материализованная проекция** `x/y/angle` того же проёма.
|
||||
|
||||
`docs/CONFIG-COMPATIBILITY.md` (#132) объявляет legacy `x/y/angle` обязательной
|
||||
компаньонкой хоста: «the legacy `x/y/angle` siblings remain a materialized
|
||||
compatibility projection for older readers», и прямо требует, чтобы full export,
|
||||
plan-only export, merge и оптимизация сохраняли согласованность. В коде это уже
|
||||
норма: после любого изменения хозяина вызывается `materializePartitionOpening`
|
||||
(`houseplan-card.ts:7824` — перетаскивание перегородки, `:12007` — правка
|
||||
проёма). Слияние — такое же изменение хозяина и обязано делать то же самое.
|
||||
|
||||
**Инвариант:** координаты центра проёма в единицах плана до и после слияния
|
||||
совпадают с точностью до `EPS_JOIN`. Именно это проверяет AC3 — не «поле
|
||||
пересчитано», а «дверь физически не сдвинулась».
|
||||
совпадают с точностью до `EPS_JOIN` — **и в резолвленном виде, и в
|
||||
материализованной проекции**. Именно это проверяет AC3: не «поле пересчитано», а
|
||||
«дверь физически не сдвинулась», причём для обоих читателей — текущего фронтенда
|
||||
и того, который умеет читать только `x/y/angle`.
|
||||
|
||||
### 8.5. Границы применения
|
||||
|
||||
- при завершении цепочки сращиваются только записи **этого пространства**;
|
||||
- слияние применяется многократно до стабилизации: три отрезка подряд дают одну
|
||||
запись, а не две;
|
||||
- порядок обхода не влияет на результат (проверяется перестановочным тестом,
|
||||
как в #218).
|
||||
|
||||
### 8.6. Что именно сращивается при завершении цепочки
|
||||
|
||||
Решения §4.1 и §4.2 делят работу: рисование чинит **свой** шов, накопленное
|
||||
чинит «Оптимизировать планы» — там для этого есть отчёт и отмена.
|
||||
|
||||
Поэтому при завершении цепочки слияние затрагивает только записи, связанные с
|
||||
этой цепочкой: сегменты самой цепочки и те существующие перегородки, с которыми
|
||||
она имеет общий конец, — далее транзитивно, пока цепочка стыков не оборвётся.
|
||||
Иными словами, компонента связности по общим концам, содержащая хотя бы один
|
||||
новый сегмент.
|
||||
|
||||
Перегородки, не связанные с новой цепочкой, не трогаются, даже если между собой
|
||||
они образуют шов, подлежащий слиянию. Такой шов дождётся оптимизатора.
|
||||
|
||||
**Почему не «всё пространство».** В пространстве владельца из #228 уже лежит
|
||||
коллинеарная пара `#0`/`#4`. Если дорисовать стену в другом углу, «слияние по
|
||||
всему пространству» молча починило бы и эту пару — то есть правка старых данных
|
||||
без отчёта и без выделенной отмены, ровно то, что §4.2 обещает делать только по
|
||||
явной команде.
|
||||
|
||||
## 9. Данные, i18n, a11y, privacy, security
|
||||
|
||||
- **Данные:** формат перегородки не меняется; меняется их количество и `host.id`
|
||||
@@ -182,10 +220,13 @@ run of one thickness», оптимизатор её вызывает (`plan-opti
|
||||
2. **AC2 — узел с причиной остаётся.** Слияния не происходит, если на общем конце
|
||||
есть третья перегородка, ребро комнаты, колонна или конец черновика — четыре
|
||||
отдельных случая. **Доказательство:** `unit`.
|
||||
3. **AC3 — проём не двигается.** Перегородка с дверью посередине сращивается с
|
||||
соседней; координаты центра проёма в единицах плана до и после совпадают,
|
||||
`host.id` указывает на существующую запись, `host.t` в пределах `[0,1]`.
|
||||
Тест красный до реализации §8.4. **Доказательство:** `unit`.
|
||||
3. **AC3 — проём не двигается, в обоих представлениях.** Перегородка с дверью
|
||||
посередине сращивается с соседней; координаты центра, полученные через
|
||||
`resolvePartitionOpening`, до и после совпадают, `host.id` указывает на
|
||||
существующую запись, `host.t` в пределах `[0,1]`. Одновременно материализованные
|
||||
`x/y/angle` того же проёма пересчитаны и согласованы с резолвленной позицией
|
||||
(#132). Тест красный до реализации §8.4 — и остаётся красным, если пересчитан
|
||||
только `host.t`, а проекция устарела. **Доказательство:** `unit`.
|
||||
4. **AC4 — разная толщина не сращивается.** Два коллинеарных отрезка с разными
|
||||
`cm` остаются двумя записями. **Доказательство:** `unit`.
|
||||
5. **AC5 — допуски.** Отрезки, разведённые на расстояние больше `EPS_JOIN`, не
|
||||
@@ -198,8 +239,10 @@ run of one thickness», оптимизатор её вызывает (`plan-opti
|
||||
отчёт показывает их число, повторный запуск ничего не меняет
|
||||
(идемпотентность). **Доказательство:** `unit` (`optimizePlans`).
|
||||
8. **AC8 — ничего лишнего.** Стены комнат, колонны и черновики контуров не
|
||||
изменяются; количество комнат и их геометрия те же.
|
||||
**Доказательство:** `unit` + diff review.
|
||||
изменяются; количество комнат и их геометрия те же. Отдельно: завершение
|
||||
цепочки не трогает перегородки вне её компоненты связности — пространство с
|
||||
готовым швом в стороне после рисования новой стены сохраняет этот шов до
|
||||
запуска оптимизатора (§8.6). **Доказательство:** `unit` + diff review.
|
||||
9. **AC9 — release-артефакты.** Оба changelog, строка счётчика в обоих языках,
|
||||
`dist`/demo/integration бандлы идентичны друг другу.
|
||||
**Доказательство:** diff + сверка копий бандла.
|
||||
@@ -223,6 +266,8 @@ run of one thickness», оптимизатор её вызывает (`plan-opti
|
||||
| `partition-merge-ignores-thickness` | сращивать при разном `cm` | юнит AC4 |
|
||||
| `partition-merge-ignores-junction` | сращивать через примыкание третьей стены | юнит AC2 |
|
||||
| `partition-merge-keeps-relative-t` | не пересчитывать `host.t` | юнит AC3 |
|
||||
| `partition-merge-skips-materialization` | не обновлять `x/y/angle` проёма | юнит AC3 |
|
||||
| `chain-merge-sweeps-whole-space` | сращивать все перегородки пространства, а не компоненту цепочки | юнит AC8 |
|
||||
|
||||
## 15. Release-артефакты
|
||||
|
||||
@@ -248,6 +293,8 @@ run of one thickness», оптимизатор её вызывает (`plan-opti
|
||||
бы оно было детерминированным и не зависело от времени; это — самое простое.
|
||||
3. **Конкретные значения `EPS_ANGLE` и `EPS_JOIN`** подбираются при реализации;
|
||||
ТЗ фиксирует только их природу (доли шага сетки) и границы (§8.3).
|
||||
4. **Слияние при завершении цепочки применяется ко всему пространству**, а не
|
||||
только к новым записям: цепочка могла примкнуть к нарисованному ранее отрезку,
|
||||
и шов на стыке — тот же самый случай.
|
||||
4. **Область слияния при завершении цепочки** описана нормативом §8.6, а не
|
||||
здесь: это не свободное предположение, а граница между двумя решениями
|
||||
владельца (§4.1 и §4.2). Ревизия r2: прежняя редакция говорила «применяется ко
|
||||
всему пространству» — сращивало бы и старые швы в другом углу, обходя обещанный
|
||||
для них отчёт и отмену (находка M1 ревью r1).
|
||||
|
||||
Reference in New Issue
Block a user