mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
docs: spec #331 revision 2 — canonical rounding, honest incidence threshold, edge-walk instead of DFS
r1-H1: node keys quantise with the repository's canonicalisation formula (sign·floor(|v|·1e7+0.5)/1e7) — native Math.round and Python round() part ways on .5 ticks, the exact parity lesson coordinate-canonicalization already encodes. r1-M1: the incidence threshold becomes 2e-7 over raw coordinates, which the spec's own example (1.02e-7) actually satisfies; the known valence undercount on neighbouring quanta is stated in §3. r1-M2: the narrow except applies to the candidate side only — a previous-side migration bug stays a "no baseline" fallback, symmetric with §2.5. r1-M3: the branch walk is an O(E) edge traversal of the collinear component, not a combinatorial DFS; AC3 gains a 100-fork case. r1-M4: the USER-GUIDE limits section documents the new refusal toast. L1: §1 opens with the user sentence. Issue: #331 User-Visible: no
This commit is contained in:
@@ -1,11 +1,19 @@
|
||||
# Issue #331 — пограничная точность ограничений стыков (#329)
|
||||
|
||||
Статус: ревизия 1. Родители: #329 (правила), #330 (производительность —
|
||||
Статус: ревизия 2 (spec-ревью r1: H1 — квантование по формуле канонизации
|
||||
вместо нативного round, M1 — порог инцидентности 2e-7 и исправленный пример,
|
||||
M2 — узкий except только для кандидата, M3 — обход по рёбрам вместо DFS,
|
||||
M4 — USER-GUIDE, L1 — пользовательская фраза §1). Родители: #329 (правила), #330 (производительность —
|
||||
слита, код проверок актуален по её итогу). Сёстры вне скоупа: #333
|
||||
(optimize/import-лазейка), #339 (смешанные толщины у острия).
|
||||
|
||||
## 1. Сценарий и пользовательский результат
|
||||
|
||||
Пользователь заканчивает ресайз или рисование на обычном плане — и получает
|
||||
отказ «стены слишком близко: 0.00001 см», хотя ничего не нарушал; а сосед,
|
||||
случайно нарисовавший стену дважды по одной линии, не получает отказа
|
||||
вовсе. Задача делает пограничные вердикты честными.
|
||||
|
||||
Класс симптома «легитимная запись отклонена / нелегитимная пропущена» уже
|
||||
дважды бил по бетам (#316, #319). Репро на `dev` после #330 (все —
|
||||
исполнением):
|
||||
@@ -32,16 +40,22 @@
|
||||
|
||||
### 2.1 Квантованный ключ узла и инцидентность
|
||||
|
||||
Ключ узла — координаты, квантованные к **1e-7** (`round(v·1e7)/1e7`) с
|
||||
нормализацией `−0 → 0`; формат строки ключа одинаков в TS и python. Порог
|
||||
выбран на два порядка грубее канонической сетки хранения (1e-9) и на
|
||||
порядки тоньше любого осмысленного зазора плана (минимальный порог правил —
|
||||
5 см ≈ 4e-4 в норм. координатах): плавающий мусор сливается, реальная
|
||||
геометрия неразличимой не становится.
|
||||
Ключ узла — координаты, квантованные к **1e-7** по формуле канонизации
|
||||
этого репозитория: `sign(v) · floor(|v|·1e7 + 0.5) / 1e7` (r1-H1: нативные
|
||||
`Math.round`/`round()` расходятся на .5-тиках — banker's rounding в python
|
||||
против round-half-away в JS; `coordinate-canonicalization.ts/.py` уже
|
||||
используют ровно эту формулу с комментарием о паритете, и ключи обязаны ей
|
||||
следовать). `−0` нормализуется в `0`; формат строки ключа одинаков в TS и
|
||||
python. Порог выбран на два порядка грубее канонической сетки хранения
|
||||
(1e-9) и на порядки тоньше любого осмысленного зазора плана (минимальный
|
||||
порог правил — 5 см ≈ 4e-4 в норм. координатах).
|
||||
|
||||
В П4 пары узлов с евклидовой дистанцией ≤ **1e-7** считаются ОДНИМ узлом
|
||||
(инцидентность, не нарушение) — закрывает случай «через границу кванта»
|
||||
(например `−5.1e-8` и `5.1e-8`).
|
||||
В П4 пары узлов с евклидовой дистанцией ≤ **2e-7** (по сырым координатам)
|
||||
считаются ОДНИМ узлом — инцидентность, не нарушение (r1-M1: порог покрывает
|
||||
пару, севшую на соседние кванты, например `−5.1e-8` и `5.1e-8` с сырой
|
||||
дистанцией 1.02e-7). Известное следствие для П2: мусорная пара на соседних
|
||||
квантах остаётся двумя записями в карте валентности — недосчёт не хуже
|
||||
текущего поведения и валентность никогда не ЗАВЫШАЕТСЯ; помечено в §3.
|
||||
|
||||
### 2.2 П1: ноль градусов — честное нарушение
|
||||
|
||||
@@ -55,9 +69,14 @@
|
||||
|
||||
`collinearRunLengthUnits` / `collinear_run_length_units` переписываются
|
||||
итеративно (явный стек, без рекурсии — глубина входа больше не
|
||||
ограничивает). При развилке из нескольких коллинеарных продолжений одной
|
||||
толщины берётся **максимальная** ветвь (DFS по вариантам, visited на рёбрах)
|
||||
— вторая ветвь больше не теряется молча.
|
||||
ограничивает). При развилке коллинеарных продолжений одной толщины ветвь
|
||||
больше не теряется: обход идёт **по рёбрам с visited-набором** — каждый
|
||||
атом участвует в прогоне не более одного раза, суммарная работа O(E) на
|
||||
вызов независимо от числа развилок (r1-M3: полный DFS по вариантам дал бы
|
||||
комбинаторику; вместо «максимальной из всех путей» прогоном считается
|
||||
суммарная длина коллинеарной КОМПОНЕНТЫ связности — это консервативнее к
|
||||
пользователю, чем текущий `.find`, монотонно и дёшево). Бюджеты бенча #330
|
||||
остаются зелёными без правок — закреплено AC7.
|
||||
|
||||
### 2.4 Коллинеарность — к базе цепочки
|
||||
|
||||
@@ -76,9 +95,14 @@
|
||||
|
||||
### 2.6 Узкий except в зеркале
|
||||
|
||||
`_migrated_spaces`: `except Exception` → `except (WallSegmentMigrationError,
|
||||
ValueError)` + `_LOGGER.debug` с причиной. TypeError/RecursionError и прочие
|
||||
баги миграции всплывают в общий обработчик WS честной ошибкой.
|
||||
`_migrated_spaces` получает флаг стороны (r1-M2 — симметрия с §2.5):
|
||||
|
||||
- для **кандидата** — `except (WallSegmentMigrationError, ValueError)` +
|
||||
`_LOGGER.debug`; TypeError/RecursionError и прочие баги миграции всплывают
|
||||
в общий обработчик WS честной ошибкой (fail-closed, как §2.5);
|
||||
- для **previous** — прежний широкий фолбэк «нет базы для наследования» +
|
||||
`_LOGGER.debug`: недоказуемое наследование не повод блокировать
|
||||
несвязанную запись (тот самый симптом П1, который задача чинит).
|
||||
|
||||
## 3. Границы
|
||||
|
||||
@@ -88,22 +112,29 @@ ValueError)` + `_LOGGER.debug` с причиной. TypeError/RecursionError и
|
||||
(было: одна «стена»).
|
||||
- Спека #329 §2 дополняется абзацем о квантовании ключей и нуле градусов
|
||||
(той же правкой, отдельной ревизией её файла).
|
||||
- Известное следствие §2.1: валентность узла (П2) на мусорной паре соседних
|
||||
квантов может недосчитать единицу — не хуже текущего поведения и не
|
||||
завышает (r1-M1).
|
||||
- Вне скоупа: #333, #339, любые изменения производительности (#330 —
|
||||
бюджеты бенча обязаны остаться зелёными).
|
||||
|
||||
## 4. Acceptance criteria
|
||||
|
||||
- **AC1 (ключи/инцидентность).** Пара `−1e-8`/`0` — ноль нарушений П4;
|
||||
пара «через границу кванта» (`−5.1e-8`/`5.1e-8`) — ноль; узлы на
|
||||
дистанции 4 см — по-прежнему нарушение; ровно 5 см — нет. Оба зеркала,
|
||||
через паритет-набор.
|
||||
пара «через границу кванта» `−5.1e-8`/`5.1e-8` (сырая дистанция 1.02e-7 ≤
|
||||
порога 2e-7) — ноль; узлы на дистанции 4 см — по-прежнему нарушение;
|
||||
ровно 5 см — нет. Квантование на .5-тике (`v = 2.5e-7`) даёт ОДИН и тот
|
||||
же ключ в TS и python (формула канонизации, r1-H1) — прямой юнит в
|
||||
паритет-наборе. Оба зеркала.
|
||||
- **AC2 (0°).** Точный дубль стены — нарушение П1 с `actual < 0.001°`;
|
||||
прямая стена из двух атомов через узел (180°-пара) — по-прежнему чиста;
|
||||
T-стык — чист. Оба зеркала.
|
||||
- **AC3 (итеративность/ветви).** Цепочка 10 000 атомов — ответ без
|
||||
переполнения (обе реализации); развилка двух коллинеарных ветвей — длина
|
||||
= максимальной ветви (юниты с точным ожиданием), короткий доборный атом
|
||||
перепада толщин остаётся законным (существующий AC3b #329 зелёный).
|
||||
переполнения (обе реализации); развилка коллинеарных ветвей — длина =
|
||||
суммарной длине компоненты (юнит с точным ожиданием, обе ветви учтены);
|
||||
цепочка со 100 развилками подряд — линейное время (входит в бенч-сетку
|
||||
через юнит-таймаут, r1-M3); короткий доборный атом перепада толщин
|
||||
остаётся законным (существующий AC3b #329 зелёный).
|
||||
- **AC4 (дуга).** Дуга 30×0.9° не считается одной стеной (первый атом
|
||||
короче 20 см в сумме с коллинеарной БАЗЕ частью — отказ П3); прямая с
|
||||
одним изломом 0.5° — считается.
|
||||
@@ -137,6 +168,9 @@ ValueError)` + `_LOGGER.debug` с причиной. TypeError/RecursionError и
|
||||
занижения, фиксируется юнитом с точным ожиданием; (3) 0°-нарушение
|
||||
может всплыть на легаси-планах с дублями — наследование #329 §3
|
||||
по-прежнему пропускает несвязанные правки (инвариант не трогается).
|
||||
- **Release**: обычная бета; CHANGELOG (en+ru) одной строкой user-visible
|
||||
- **Release**: обычная бета; `docs/USER-GUIDE.ru.md` и `.md` — раздел
|
||||
«Ограничения стыков стен» дополняется предложением о тосте
|
||||
`junction.limit_check_failed` (отказ при невозможности проверить, r1-M4);
|
||||
CHANGELOG (en+ru) одной строкой user-visible
|
||||
(«точность проверок стыков на границах: ложные отказы устранены, дубли
|
||||
стен видимы»); откат — чистый revert.
|
||||
|
||||
Reference in New Issue
Block a user