mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
docs(spec): keep a passage full length, read thickness atomically
Spec review r1 returned two blocking findings and both were right. A measured side that is itself a passage would have been shortened by its neighbouring walls, while insetContour — the very function the area label already uses — treats that joint as a flat cap and shortens nothing. Length and area would have diverged again, at a different boundary, which is the defect this task exists to remove. The zero rule now comes first and returns the full centreline length for an open side. The thickness source was wrong as a matter of fact, not of taste: an existing test shows thicknessCmAt returns 0 for a whole-edge query against a partially set thickness, so a split-thickness edge would have silently stopped shortening. Half-depths now come from roomWallProfile, the atomic profile that innerContourForRoom already uses for the area, so one edge is resolved by one mechanism. Two acceptance criteria and two mutation guards added for the closed findings. Issue: #233 User-Visible: no
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
- Тип: bug · приоритет P2 · ценность 7/10 · сложность 3/10 · риск 4/10
|
||||
- Issue: [#233](https://github.com/Matysh/houseplan-card/issues/233)
|
||||
- Ветка: `issue/233-resize-inner-dimensions`
|
||||
- Статус ТЗ: на ревью
|
||||
- Статус ТЗ: на ревью (заход r2)
|
||||
|
||||
Канонические документы: `docs/SCOPE.md`, `docs/WALL-THICKNESS.md`,
|
||||
`docs/CANVAS.md`, `docs/USER-GUIDE.ru.md`, `docs/TOUCH-SUPPORT.md`,
|
||||
@@ -101,7 +101,19 @@ innerEdgeSpan(prev, a, b, next, oPrev, oSelf, oNext) -> number
|
||||
2. внутренняя линия ребра `a→b` пересекается с внутренними линиями соседей;
|
||||
3. результат — расстояние между двумя точками пересечения.
|
||||
|
||||
**Отступления, каждое обязано быть явным:**
|
||||
**Правило нуля идёт первым и перекрывает всё остальное (находка H1 ревью r1):**
|
||||
если **сама измеряемая сторона** имеет нулевую толщину — это проём или сторона,
|
||||
открытая в соседнюю комнату, — возвращается осевая длина `|b−a|` целиком, без
|
||||
сокращения на соседей.
|
||||
|
||||
Причина не в удобстве, а в согласованности с площадью. У проёма нет внутренней
|
||||
грани, от которой измеряют, поэтому подпись показывает полную протяжённость
|
||||
открытой стороны. Ровно так же ведёт себя `insetContour` на стыке нулевой и
|
||||
ненулевой толщины: там flat cap (`#172`), и открытая сторона не укорачивается.
|
||||
Без этого правила длина и площадь снова разошлись бы — только на другой границе,
|
||||
и это был бы тот же дефект, ради которого затевалась задача.
|
||||
|
||||
**Остальные отступления, каждое обязано быть явным:**
|
||||
|
||||
- сосед параллелен (пересечения нет) либо `oSelf` и оба соседа равны нулю →
|
||||
возвращается осевая длина `|b−a|`;
|
||||
@@ -116,14 +128,33 @@ innerEdgeSpan(prev, a, b, next, oPrev, oSelf, oNext) -> number
|
||||
|
||||
## 7. Источник толщин для подписи
|
||||
|
||||
Половинные глубины берутся существующим `thicknessCmAt(walls, a, b, pitch,
|
||||
coordScale)` — тем же способом, которым толщину узнаёт остальной редактор, — и
|
||||
переводятся в единицы через `wallCmToUnits(cm, cellCm, gridPitch) / 2`.
|
||||
**`thicknessCmAt` по ребру комнаты не подходит, и это установлено тестом**
|
||||
(находка H2 ревью r1). `test/wall-thickness.test.mjs` «thicknessCmAt exact-parent
|
||||
fallback does not leak from partial or unrelated spans» показывает: запрос по
|
||||
полному ребру против частично заданной толщины возвращает **0**, а не толщину
|
||||
участка. То есть на ребре со сплит-толщиной подписи молча перестали бы
|
||||
сокращаться, и мы получили бы новый вид того же расхождения.
|
||||
|
||||
Если одно ребро комнаты разрезано на атомарные участки с разной толщиной,
|
||||
`thicknessCmAt` вернёт толщину участка по середине ребра. Это принятое
|
||||
упрощение: подпись — одно число на ребро, и «одно число» для стены переменной
|
||||
толщины не существует. Записано здесь, чтобы ревью не считало это недосмотром.
|
||||
Источник половинных глубин — **атомарный профиль**
|
||||
`roomWallProfile(rooms, roomId, walls, openCuts, pitch, cellCm, gridPitch,
|
||||
coordScale)`, тот самый, из которого `innerContourForRoom` строит контур для
|
||||
площади. Он возвращает атомарный полигон, `cms` и уже готовые `offsets`
|
||||
(половинные глубины в единицах, с учётом `kinds`: у виртуальных и открытых
|
||||
участков там ноль). Длина и площадь одного ребра резолвятся одним механизмом —
|
||||
это и есть смысл задачи.
|
||||
|
||||
Отображение «ребро комнаты → атомарные участки»: атомарный полигон получен
|
||||
разрезанием того же контура, поэтому участки ребра `a→b` — это последовательные
|
||||
атомарные рёбра, лежащие на отрезке `a→b`. Из них берутся:
|
||||
|
||||
- **сокращение у конца `a`** — по атомарному соседу, входящему в вершину `a`;
|
||||
- **сокращение у конца `b`** — по атомарному соседу, выходящему из вершины `b`;
|
||||
- **`oSelf`** — по атомарному участку, содержащему середину `a→b`.
|
||||
|
||||
Ребро со участками разной толщины даёт одно число: расстояние между внутренними
|
||||
гранями стен на его концах. Средний участок другой толщины на это расстояние не
|
||||
влияет — и это верно физически, а не упрощение. Если весь участок середины
|
||||
нулевой, срабатывает правило нуля из §6.
|
||||
|
||||
## 8. Поверхности
|
||||
|
||||
@@ -161,6 +192,8 @@ coordScale)` — тем же способом, которым толщину у
|
||||
| AC4 | Ребро с нулевой толщиной соседа (проём, открытая сторона) не сокращается с этого конца | unit |
|
||||
| AC5 | Диагональное ребро: внутренняя длина равна расстоянию между точками пересечения внутренних линий, а не `|b−a| − o − o` | unit |
|
||||
| AC6 | Стены толще комнаты: возвращается 0, подпись не показывает отрицательное число | unit |
|
||||
| AC6a | Измеряемая сторона — проём (`oSelf = 0`) при соседях-стенах 15 см: подпись показывает полную осевую длину, а не сокращённую (H1) | unit |
|
||||
| AC6b | Ребро со сплит-толщиной (часть 20 см, часть без записи) резолвится через атомарный профиль: сокращение на концах есть, нуля из `thicknessCmAt` не возникает (H2) | unit |
|
||||
| AC7 | Все три подписи `_rszEdgeLabels` внутренние; габарит `_rszScaleLabels` — bbox внутреннего контура | smoke |
|
||||
| AC8 | Площадь при ресайзе **не изменилась** этой задачей — то же значение, что до правки | smoke |
|
||||
| AC9 | release-артефакты: оба changelog и оба USER-GUIDE в том же коммите | ревью кода |
|
||||
@@ -171,6 +204,8 @@ coordScale)` — тем же способом, которым толщину у
|
||||
|---|---|---|
|
||||
| `resize-labels-show-centreline` | подписи снова считают осевую длину | AC1, AC7 |
|
||||
| `inner-span-ignores-neighbour-thickness` | сокращение считается только по своей толщине, соседи игнорируются | AC2 |
|
||||
| `inner-span-shortens-a-passage` | правило нуля из §6 снято: проём начинает сокращаться на соседей | AC6a |
|
||||
| `inner-span-reads-whole-edge-thickness` | источник толщин подменяется на `thicknessCmAt` по ребру комнаты | AC6b |
|
||||
|
||||
## 12. План автотестов
|
||||
|
||||
@@ -198,8 +233,9 @@ coordScale)` — тем же способом, которым толщину у
|
||||
вычитает колонны и перегородки внутри комнаты. Риск в том, что пользователь
|
||||
прочитает это как ошибку. Митигация: расхождение теперь объясняется мебелью в
|
||||
комнате, а не системой измерения; в changelog это сказано словами.
|
||||
2. **Атомарные участки разной толщины** дают одно число на ребро (§7) —
|
||||
упрощение, названное явно.
|
||||
2. **Атомарные участки разной толщины** дают одно число на ребро — расстояние
|
||||
между гранями стен на его концах (§7). Это физически верно, а не упрощение:
|
||||
участок другой толщины в середине на это расстояние не влияет.
|
||||
3. **`insetContour` не сохраняет вершины** — именно поэтому контракт §6 не
|
||||
использует индексы внутреннего контура. Мутант
|
||||
`inner-span-ignores-neighbour-thickness` стережёт, что сокращение считается по
|
||||
|
||||
Reference in New Issue
Block a user