feat: unify Plan wall drawing

Issue: #173
User-Visible: yes
This commit is contained in:
Sergey Matyunin
2026-08-18 20:33:44 +03:00
parent e206e8761c
commit fc1e7951e4
28 changed files with 3302 additions and 1924 deletions
+28 -22
View File
@@ -329,8 +329,8 @@ quotas, nothing is ever deleted for being old (docs/SCOPE.md).
Room-boundary walls remain *derived* from room outlines (`roomEdges`, deduped by
`segKey`), so deleting a room keeps the boundaries its neighbours still
contribute. Three explicitly typed exceptions are stored per space:
`room_drafts` for persisted unfinished room outlines, `partitions` for
one-segment independent walls and `wall_columns` for square/circular columns.
`room_drafts` for crash-safe unfinished Walls chains, `partitions` for finished
independent wall segments and `wall_columns` for square/circular columns.
They do not create a room or HA area and never split a room implicitly. Their
physical bodies are unioned with room walls for rendering and light occlusion,
and subtracted from clean room floor area. Openings (doors, windows and gates)
@@ -405,7 +405,7 @@ uses the Stage 1 latched Flat fallback. Details and fixed ratios are recorded in
## Markup editor (v1.4.0+)
State inside the card: `_markup` (mode), `_tool` (draw/partition/column/merge/split/resize/opening/
State inside the card: `_markup` (mode), `_tool` (draw/column/merge/split/resize/opening/
boundary/wallthick/delroom), `_path` (the current outline,
vertices on the GRID_N=240 grid). Clicks on the stage → `_svgPoint`→`_snap`. The outline is closed
= a click on the first vertex → area select (hass.areas) + name → room {poly}. Polygon rooms and
@@ -428,26 +428,32 @@ viewBox, so zoom never changes the effective target. The first point is a
transient gesture only: Esc, Undo/Redo, navigation, external config adoption,
pointer cancellation and multi-touch discard it without touching history.
Every completed segment of an unfinished room contour is persisted in
`room_drafts`, including the thickness selected when that segment was placed.
Switching tools keeps it; the same editing session resumes automatically, and
after reload either endpoint can be selected to continue. Closing converts the
draft into a room, while the room dialog's secondary action converts its edges
to independent partitions. `partition` creates exactly one wall per gesture;
`column` creates a square object whose size comes from the current Thickness
field. Double click edits physical-object properties, pointer drag moves the
whole object on the grid, and Delete removes only the selected object. The
legacy root `space.segments` array is still stripped on every save.
Every completed Walls segment is persisted in `room_drafts`, including the
thickness selected when that segment was placed. Changing Plan tool, editor or
floor explicitly finishes an open chain by converting it to ordinary
`partitions` in one history/save transaction; re-selecting Walls is a no-op.
Pan, pinch, pointer cancellation and suppressed clicks never finish a chain or
append a segment. A finished open chain is ordinary masonry and is not resumed
as a draft. Reload recovery may resume only a still-active persisted draft.
Adjacent-room auto-close reuses the immutable architectural snap snapshot and
requires the first and prospective terminal points to belong to one completed
room's same solid segment after opening/open-span cuts. It runs only after the
draft has two edges and after explicit first-point/Ctrl closure handling. The
prospective polygon is checked for range, self-intersection and room overlap
before `_path` or `room_drafts` changes; success persists the terminal segment
and then opens the ordinary room dialog. Cancel therefore returns to the open
draft at that terminal point, while Save lets the normal room/wall
normalization preserve an existing shared wall's thickness.
`src/wall-face-graph.ts` derives an immutable planar graph from solid room edges
after opening cuts, partitions, inactive drafts and the active chain. A sweep
broadphase atomizes endpoint, T, X and collinear intersections; deterministic
half-edge traversal extracts bounded canonical faces. The click handler diffs
the graph before/after the latest segment and offers only newly created faces
that contain an atom of that segment, ordered by area and canonical key. Exact
or partial overlap with a room is rejected while legal nesting is preserved.
A clean single-room divider reuses `splitRoomPath`, offering only the smaller
child while the larger child retains the original room identity and metadata.
Room answers are buffered in `_wallFaceBatch`. Create/Keep-as-walls advance the
queue without mutating geometry; Cancel/Esc restores the terminal draft. The
last answer revalidates every face and capacity limit, then commits all accepted
rooms, the split result and every unconsumed active atom in one Undo/Redo and
config transaction. Existing saved source geometry is never atomized or
rewritten merely because it participated in a face. `column` still creates a
physical object whose size comes from the current Thickness field. The legacy
root `space.segments` array is stripped on every save.
While drawing, the length of the current segment follows the cursor (`_fmtLen` → `segmentCm`/
`formatLength`): metres, or feet+inches when `hass.config.unit_system` is imperial. The scale is
+28 -15
View File
@@ -545,11 +545,11 @@ pixels, therefore selection remains usable at every zoom.
## Architectural connection overlay
When **Room outline** or **Partition** is active in the Plan editor, a derived
When **Walls** is active in the Plan editor, a derived
pointer-transparent SVG layer exposes the centre axes of completed room walls,
saved inactive outlines and independent partitions. It is painted after their
physical wall bodies, but before interactive editor chrome. Columns, decor,
devices, the active outline and its live preview are not candidates. Door,
devices, the active wall chain and its live preview are not candidates. Door,
window, gate and intentionally open-span intervals are cut from room axes; a
cut boundary does not become a new endpoint.
@@ -565,17 +565,30 @@ runs again on click, so hover is only a preview and never authoritative.
Endpoint and line candidates override the normal grid and Shift/45° result.
Outside the hit zone, §9.3–9.4 remain unchanged. A line connection adds only the
new segment endpoint; it does not split or rewrite the existing wall. The
current anchor is excluded to prevent zero-length segments, while the first
point of a valid room outline remains an explicit closure target. The static
geometry is cached by structural editor state; pointer movement changes at
most the single active candidate and never writes config, layout or storage.
current anchor is excluded to prevent zero-length segments. The static geometry
is cached by structural editor state; pointer movement changes at most the
single active candidate and never writes config, layout or storage.
For **Room outline**, the same completed-room provenance also enables adjacent
auto-close. Once the draft already has two edges, a click that places its next
point on the same uninterrupted solid room-wall segment as the first point is
validated as the prospective polygon before any mutation. A valid click
persists that terminal draft segment and opens the standard room dialog with
the existing wall as the closing edge. A cut, a different edge, a saved draft
or an independent partition is ineligible; an eligible but invalid polygon is
reported without appending a point or segment. Explicit first-point and
Ctrl/Cmd closure retain priority.
## Planar wall faces
Every completed Walls segment is first persisted in the active `room_drafts`
chain. On the click path only, an immutable planar graph is built from solid
room edges after opening cuts, independent partitions, inactive drafts and the
active chain both before and after the latest segment. Endpoint, T, X and
collinear-overlap junctions atomize that computed graph without rewriting any
saved wall. A deterministic half-edge walk extracts bounded faces; canonical
identity ignores winding, cyclic start and derived collinear subdivision.
Only faces added by the latest segment and containing one of its atoms are
offered. They are ordered by area and then canonical key. Existing exact or
partially overlapping rooms are excluded, nested rooms remain legal, and any
physical gap — including an opening cut — remains a gap. A clean divider across
one room reuses the Split contract: the larger side keeps the room identity,
metadata and device binding, and only the smaller side is offered.
The active draft remains persisted while the resulting room dialogs are open.
Create/Keep-as-walls answers are buffered; Cancel/Esc discards all answers and
restores the terminal draft. The final answer revalidates the whole batch and
applies accepted rooms plus every unconsumed active atom as one history/config
transaction. Graph construction never runs on pointermove, Home Assistant state
updates or ordinary rendering.
+5
View File
@@ -7,6 +7,11 @@
real Home Assistant device, while Glow, Light fill, room counts and both
markers update together; removing the final link restores the preserved
manual state ([#174](https://github.com/Matysh/houseplan-card/issues/174)).
- The Plan editor now has one **Walls** tool instead of separate Room outline
and Partition tools. An open chain becomes ordinary walls when the tool,
editor or floor changes; closing endpoint/T/X geometry offers every newly
formed room in stable area order, with clean room splits and atomic
Create/Keep/Cancel decisions ([#173](https://github.com/Matysh/houseplan-card/issues/173)).
- Collinear exterior walls now change thickness exactly at their saved
breakpoint. After splitting a room, a 10 cm wall therefore keeps its full
depth up to the divider without leaking onto an adjacent zero-thickness
+6
View File
@@ -14,6 +14,12 @@
заливка «Свет», статистика комнаты и оба маркера обновляются вместе; после
снятия последней связи возвращается сохранённое ручное состояние
([#174](https://github.com/Matysh/houseplan-card/issues/174)).
- В редакторе Плана теперь один инструмент **«Стены»** вместо отдельных
«Контура комнаты» и «Перегородки». Незамкнутая цепочка становится обычными
стенами при смене инструмента, редактора или этажа; замыкание через конечные,
T- и X-точки предлагает все новые комнаты по возрастанию площади, а
Create/«Оставить стенами»/Cancel применяются атомарно
([#173](https://github.com/Matysh/houseplan-card/issues/173)).
- Коллинеарные внешние стены теперь меняют толщину точно в сохранённой точке
перехода. После разделения комнаты стена 10 см сохраняет полную толщину до
перегородки и не продолжается по соседнему фасаду с нулевой толщиной; План,
+2 -2
View File
@@ -17,12 +17,12 @@ change must pass through a published beta/RC before stable. Stable release
commits are promotion-only (versions, generated bundles and release/changelog
metadata). Only an explicit owner-approved emergency hotfix may skip this gate.
## Snapshot (2026-08-17)
## Snapshot (2026-08-18)
| Item | State |
|---|---|
| Version | **v1.65.0-beta.1** everywhere (manifest, const.py, package.json, CARD_VERSION) — prerelease candidate for the current `S8-merged` queue |
| Current local cycle | v1.65.0-beta.1 carries #164, #166 and #167: confirmed appliance cycles receive the working marker, rotated real north produces physically correct window sunlight, and current-space export can create a plan-only template without devices or Home Assistant bindings. It also carries the #165 first-push process-gate correction. Publication waits for the exact-SHA Validate and fail-closed prerelease preflight; stable v1.64.0 remains unchanged. |
| Current local cycle | v1.65.0-beta.1 carries #164, #166 and #167: confirmed appliance cycles receive the working marker, rotated real north produces physically correct window sunlight, and current-space export can create a plan-only template without devices or Home Assistant bindings. It also carries the #165 first-push process-gate correction. #173 is in its issue branch and unshipped: the Plan editor is moving to one continuous Walls tool with deterministic planar-face room creation. Publication waits for the exact-SHA Validate and fail-closed prerelease preflight; stable v1.64.0 remains unchanged. |
| Hidden Labs Stage | #89 Stage 1 ships in v1.63.0-beta.1. #122 Stage 2 ships in v1.64.0 and evolves the same hidden, expiring `iso` experiment with matte walls, a low exterior floor edge, restrained shared shadows and live vertical door/window/gate panels. Flat remains default; editors and `houseplan-space-card` remain flat; live floor effects and HA actions remain unchanged. Public activation remains a separate task. |
| Workflow | Superseded 2026-08-12: the pre-1.62 rule of "local edits without tests or commits" is **dead** — since release 1.62 every product change follows `PROCESS.md` (issue in `S5-ready`+, branch `issue/<NN>-slug`, trailers on every commit, review pipeline; `AGENTS.md` is the summary). Release mechanics below remain current. A requested pre-release gets a production build plus the smallest targeted unit/smoke set covering the changed surfaces, one tested `dev` commit/tag and a GitHub Release with `prerelease=true`; `main` stays untouched. The complete local frontend/backend/smoke gate runs only before a stable release, after which `main` is fast-forwarded to the exact tested `dev` SHA and the GitHub Release uses `prerelease=false`. Release bodies are short and bilingual (Russian first): only significant user changes get individual bullets, while minor/code-only work is grouped as `Мелкие исправления и улучшения` / `Small fixes and improvements`; every body ends with separate links to the Russian and English changelogs. Detailed RU/EN changelog bullets may link the corresponding closed GitHub Issues; open or partially delivered issues are never presented as shipped. Telegram announcements are sent only for stable releases; beta and RC publication is silent. `docs/RELEASE-NOTES.md` is the current canonical body instance; `npm run release:prerelease -- <tag> --issues=… --yes` is the primary local publication path and the manual `Publish prerelease` workflow is its GitHub-only equivalent once present on `main`. Nothing is copied to the home instance by hand |
| GitHub | https://github.com/Matysh/houseplan-card — [Issues](https://github.com/Matysh/houseplan-card/issues) are the canonical task records; their labels carry priority and workflow status (`PROCESS.md` §9). GitHub Projects is no longer used. `main` carries stable releases; pre-release tags may point directly at `dev`. Work lands on `dev` and is merged into `main` for a stable release, so `dev` is normally equal to or ahead of `main`, never behind. Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) |
+26 -22
View File
@@ -357,11 +357,19 @@ separately promised workflows:
## Room markup editor ★
- [ ] Grid appears; dots snap; the outline draws pair-by-pair; shared walls reused
- [ ] The toolbar has one **Walls** tool and no separate Room outline or
Partition drawing button; Split remains available [auto:
smoke_unified_wall_tool]
- [ ] Grid appears; dots snap; the wall chain draws pair-by-pair; shared walls reused
- [ ] Ruler: while drawing, the length of the current segment follows the cursor
(metres, or feet+inches on an imperial HA); scale = space "cm per cell" (default 5)
- [ ] A line cannot exist on its own: start an outline, do NOT close it, leave markup —
no lines are left behind (nothing was written to the config)
- [ ] Every completed segment is crash-safe in `room_drafts`. Changing tool,
editor or floor finishes an open chain as ordinary partitions in one
history/config transaction; the finished chain is not resumed as a draft
[auto: smoke_unified_wall_tool]
- [ ] Re-selecting Walls, Reset, pan, pinch, a second pointer, `pointercancel`
and a suppressed synthetic click never finish a chain or save an extra
segment [auto: smoke_unified_wall_tool]
- [ ] Deleting a room removes its walls, EXCEPT those shared with a neighbouring room
(the neighbour still yields them); deleting the neighbour too removes them as well
- [ ] There is no "Erase" tool in the markup toolbar (removed in v1.19.0)
@@ -377,31 +385,27 @@ separately promised workflows:
(imported/legacy polygons), not only on rooms drawn on the current grid [manual]
- [ ] Split: a click far from any wall (middle of the room) is a miss with a toast —
the wall-snap pull is capped, accidental clicks do not pick a wall [manual]
- [ ] Esc / Ctrl+Z removes the last dot (and its line); Reset clears the path
- [ ] Closing the contour (click the first dot, ≥4 points) opens the room dialog
- [ ] Ctrl/Cmd+click closes the current endpoint back to the first point without
adding another vertex. It requires at least two existing edges and refuses
degenerate or self-intersecting closure [auto: smoke_editor_tabs]
- [ ] Adjacent-room auto-close: after two drawn edges, ending on the same
uninterrupted solid existing-room wall interval as the first point adds
the terminal segment and opens the normal room dialog. Endpoints and
wall-interior snap points both work; Save creates one room without an
independent partition and preserves the shared wall thickness. Cancel
restores the open draft ending at that point [auto: smoke_room_autoclose;
unit: plan-snap-overlay.test.mjs]
- [ ] Adjacent-room auto-close safety: a second point alone stays open; different
room edges, draft/partition-only axes and points separated by an opening
or open-span cut do not trigger it. An eligible self-intersecting contour
shows the existing error and writes neither the terminal point nor segment
[auto: smoke_room_autoclose; unit: plan-snap-overlay.test.mjs]
- [ ] Esc / Ctrl+Z removes the last dot (and its line); Reset clears the active path
- [ ] A latest segment that creates bounded endpoint, T or X faces opens the
room queue in area/key order. Existing exact/partial rooms and physical
gaps, including opening cuts, are excluded; nested rooms remain eligible
[unit: wall-face-graph; auto: smoke_unified_wall_tool +
smoke_room_autoclose]
- [ ] Create and Keep as walls answers are buffered. The last answer applies all
rooms and unconsumed wall atoms as one Undo/Redo transaction; Cancel/Esc
restores the terminal draft without partial rooms [auto:
smoke_unified_wall_tool]
- [ ] A clean divider across one existing room offers only the smaller child;
the larger child keeps the original room id, name, area binding, settings
and device placement [auto: smoke_unified_wall_tool]
- [ ] Room dialog: area list shows only unassigned areas; picking an area prefills the name
- [ ] Room dialog uses the medium width and its body has no horizontal overflow;
long options stay inside it at desktop and narrow widths [auto:
smoke_editor_tabs; manual: narrow viewport]
- [ ] "No area" room (decorative) requires a name; saves with `area: null`
- [ ] Cancel in the dialog reopens the contour (last point undone)
- [ ] Cancel in a Walls face queue restores the persisted terminal draft
- [ ] Saving a room with an area: area devices appear with icons; positions are fixed into the layout [manual]
- [ ] Erase tool removes exactly the clicked line; Delete-room removes the polygon after confirm
- [ ] Delete-room removes the polygon after confirm; there is no Erase tool
- [ ] Device icons hidden during markup; visible again on exit
## Devices on the plan ★
+6
View File
@@ -77,6 +77,12 @@ only to dismiss an explicit group/palette is consumed instead of falling
through to the plan. Pinch/pan outside the tray remains scene-owned. This is a
safety guarantee, not a promise of full touch parity for editor precision work.
The Plan editor's Walls chain follows the same floor. One clean tap may append
one segment; pan, pinch, a second pointer, `pointercancel` and a suppressed
synthetic click never append, finish or convert the chain. Only an explicit
tool/editor/floor change finishes an open chain as ordinary walls. Junction
hover is best effort on coarse pointers; click resolution remains authoritative.
During a View/editor visual transition the moving stage is inert while the
header tabs remain available. A pinch, cancelled pointer or synthetic click
cannot operate stale geometry; leaving the editor is always a single safe
+10 -9
View File
@@ -143,8 +143,8 @@ Use this order for a first working room:
2. Upload SVG/PNG/JPG/WebP, reuse an uploaded plan, or choose no image.
3. Set the real size of a grid cell. It controls wall, opening, furniture and
area measurements.
4. In Plan choose **Room outline**, click the vertices, then click the first
point to close the outline.
4. In Plan choose **Walls**, draw the boundary, then click its first point to
close the first room face.
5. Name the room and bind it to an HA area, or use **No area**.
6. Add wall thickness and openings if needed.
7. In Device, position auto-discovered markers and choose their actions.
@@ -153,7 +153,7 @@ Use this order for a first working room:
![Creating the first space with synthetic data](images/03-space-create.png)
![Closing a new room outline on its first point](images/04-room-contour-close.png)
![Closing a new wall chain on its first point](images/04-room-contour-close.png)
Do all plan creation on desktop. Phone, tablet and wall-panel View remain full
product surfaces after setup.
@@ -237,10 +237,12 @@ enabled for that space.
### Create a room
Select **Room outline** and click grid nodes. The current segment shows its real
length. Clicking the first point closes the outline and opens the room dialog.
Rooms may share boundaries but may not overlap. A closed room has area; an open
wall path does not.
Select **Walls** and draw one continuous chain. Every completed segment is
crash-safe. Changing tool, editor or floor finishes an open chain as ordinary
independent walls. When the latest segment creates bounded endpoint/T/X faces,
House Plan offers them from smallest to largest. Save creates that room, Keep
as walls rejects only that candidate, and Cancel restores the whole draft with
no partial rooms.
Existing segment endpoints and lines appear above walls while drawing. An
endpoint grows when the next click will join it. A point on a line shows where
@@ -250,8 +252,7 @@ the click will create a valid junction.
| Tool | Result | Room area | Light and shadow | Main limit |
|---|---|---|---|---|
| Room outline | Closed room or persisted open wall segments | Only a closed saved outline has area | Physical segments block light; openings pass it | Rooms cannot overlap; close on the first point |
| Partition | Independent wall segment | Does not split or change area | Blocks light and casts a wall shadow | Does not create a room or HA-area binding |
| Walls | Continuous wall chain; offers rooms when it closes faces and finishes open chains as independent walls | Only a confirmed room has area | Physical segments block light; openings pass it | Partial room overlap is rejected; there is no separate Partition drawing tool |
| Column | Square or circular support | Does not change area | Blocks light inside its shape | One shape/size/rotation; not a wall or room |
| Boundary | Virtual span of a shared wall | Geometry and area stay unchanged | Light passes; no physical wall is painted | Only a shared boundary between adjacent rooms |
| Opening | Door, window or gate | Does not change area | Door/gate passage follows state; window may cast sun | Must fit completely on a suitable wall segment |
+17 -20
View File
@@ -279,24 +279,23 @@ desktop: для точного рисования, Resize, модификато
### Создание комнаты
1. Откройте **Редактор плана**.
2. Выберите **Контур комнаты**.
2. Выберите **Стены**.
3. При необходимости укажите толщину новых стен в появившейся над холстом
панели параметров. `0`/пусто создаёт тонкую осевую границу.
4. Поставьте минимум три точки по сетке.
5. Замкните контур кликом по первой точке или `Ctrl` + кликом в любом месте.
Быстрое замыкание доступно после двух нарисованных граней и не сработает,
если замыкающая стена создаёт самопересечение.
Если первая и текущая точки лежат на одном непрерывном сплошном участке
стены существующей комнаты, текущий клик сам добавит последнюю грань,
замкнёт контур общей стеной и откроет тот же диалог. Можно попадать как в
конечный узел, так и в точку на линии. Проём или намеренный разрыв разделяет
участок, разные грани не объединяются, а второй клик после единственной
первой точки остаётся обычным незамкнутым отрезком.
6. В диалоге задайте название, выберите свободную HA-зону либо оставьте в списке «— без зоны —», затем нажмите «Сохранить».
панели параметров.
4. Рисуйте одну непрерывную цепочку. Если оставить её открытой и сменить
инструмент, этаж либо выйти из редактора, сегменты сохранятся обычными
независимыми стенами; отдельной кнопки завершения нет.
5. Когда новый сегмент замкнёт область собственными стенами, существующими
стенами либо вычисляемой конечной, T- или X-точкой, откроется диалог комнаты.
Разрыв проёма стеной не считается. Если возникло несколько областей, диалоги
идут от меньшей площади к большей.
6. Для каждой области задайте название и свободную HA-зону и нажмите
«Сохранить», либо выберите **Оставить стенами**. Cancel/Esc отменяет всю
очередь без частично созданных комнат и оставляет нарисованный draft.
Во время рисования показываются длина сегмента и угол. Углы, кратные 45°, выделяются. Удерживайте `Shift`, чтобы зафиксировать текущий отрезок по ближайшему такому направлению; предпросмотр и поставленная точка останутся на сетке. Новая комната не может частично перекрывать другую, но полностью вложенная «островная» комната поддерживается.
При работе инструментами **Контур комнаты** и **Перегородка** поверх уже
При работе инструментом **Стены** поверх уже
нарисованных стен видны тонкие осевые линии и точки их концов. Увеличенная
точка показывает точное место следующего соединения: конец стены имеет
приоритет, а увеличенная точка посреди линии создаёт T-соединение, не разрезая
@@ -311,10 +310,9 @@ T-соединение входит в проходящую стену без в
карточке. Толщина каждого ранее поставленного отрезка остаётся своей; свободный
конец незамкнутой стены остаётся плоским.
Каждый законченный отрезок незамкнутого контура сохраняется сразу. В той же
сессии инструмент продолжит его автоматически; после перезагрузки выберите
кликом один из концов. Замкнутый контур можно сохранить как комнату или выбрать
в диалоге действие **Оставить замкнутыми стенами**.
Каждый законченный отрезок цепочки сохраняется сразу. Esc/Ctrl+Z удаляет
последнюю точку. Pan, pinch и `pointercancel` ничего не завершают и не добавляют.
После перезагрузки сохранённый draft можно продолжить кликом по его концу.
### Связь с HA-зоной
@@ -326,8 +324,7 @@ T-соединение входит в проходящую стену без в
| Инструмент | Что создаёт | Площадь комнаты | Свет и тени | Главное ограничение |
|---|---|---|---|---|
| Контур комнаты | Замкнутую комнату или сохраняемые отрезки незавершённой стены | Только замкнутый сохранённый контур имеет площадь | Физические сегменты блокируют свет; проёмы пропускают его | Комнаты не могут перекрываться; контур замыкается на первой точке |
| Перегородка | Независимый отрезок стены | Не делит и не меняет площадь | Блокирует свет и отбрасывает тень как стена | Не создаёт комнату и не связывается с HA-зоной |
| Стены | Непрерывную цепочку стен; при замыкании предлагает комнаты, при выходе сохраняет независимые стены | Площадь появляется только у подтверждённой комнаты | Физические сегменты блокируют свет; проёмы пропускают его | Частичное перекрытие комнат запрещено; отдельного инструмента «Перегородка» нет |
| Колонна | Квадратную или круглую опору | Не меняет площадь комнаты | Блокирует свет внутри своей формы | Имеет одну форму/размер/поворот, но не является стеной или комнатой |
| Граница | Виртуальный участок общей стены | Не меняет геометрию и площадь | Свет проходит, физическая стенка не рисуется | Работает только на общей границе соседних комнат |
| Проём | Дверь, окно, открытый проём или ворота на стене | Не меняет площадь | Дверь/ворота пропускают свет по состоянию, открытый проём — всегда между комнатами; окно может давать солнечный луч | Должен целиком помещаться на подходящем отрезке стены |
+15 -11
View File
@@ -80,9 +80,9 @@ Header in View: space tabs, device count, zoom cluster. Nothing else.
## Plan — geometry and appearance of the space
- Toolbar tools: **Walls** / room outline (with its session wall-thickness field
immediately on the right, default 15 cm — docs/WALL-THICKNESS.md §6), Delete
room, Merge, Split, Resize, Partition, Column,
- Toolbar tools: **Walls** (one continuous chain with its session wall-thickness
field, default 15 cm — docs/WALL-THICKNESS.md §6), Delete room, Merge, Split,
Resize, Column,
Opening (place / drag along walls / properties), Boundary, Thickness
(docs/WALL-THICKNESS.md — click a wall, set cm/inches from HA's unit system;
empty/0 clears), Room labels (drag positions — labels are part of the plan).
@@ -94,6 +94,10 @@ Header in View: space tabs, device count, zoom cluster. Nothing else.
- Independent partitions and columns are masonry for hit testing as well as
area/light: room hover stops at their physical bodies just as it stops at a
thick room wall. This does not split the room or change its HA area.
- Changing Plan tool, editor or floor finishes an open Walls chain as ordinary
partitions. Closing one or more planar faces opens the room queue; its
decisions are buffered and applied as one Undo/Redo transaction. Re-selecting
Walls, Reset, pan, pinch and pointer cancellation are not finish actions.
- Boundary is contextual: two points on one solid shared wall make the selected
stretch virtual; one click on an existing dashed stretch restores it whole.
Outer walls and room boundaries hidden under independent masonry are not
@@ -163,14 +167,14 @@ layer you cannot see is a layer you cannot edit.
## Plan — independent physical objects
- **Walls** still draws room contours. An unfinished contour is saved after
every completed segment and can be resumed from either free endpoint. A
click on the endpoint of another saved outline joins both records; branching
from the middle of a segment is intentionally not supported.
- **Partition** draws one physical wall segment; **Column** places a square
column whose side is the current Thickness value. Neither object creates a
room or HA area. A closed partition ring subtracts only its wall body from
the original room floor.
- **Walls** draws one continuous crash-safe chain. Newly closed planar faces
may become rooms; changing tool/editor/floor finishes an open chain as
independent wall objects. A click on another saved draft endpoint may join
it, while branching from the middle of a saved draft is unsupported.
- Finished independent walls remain selectable physical objects. **Column**
places a square column whose side is the current Thickness value. Neither an
independent wall nor a column creates a room or HA area by itself. A closed
independent-wall ring subtracts only its wall body from room floor.
- **Select** is the only mode in which these objects intercept input. It offers
rigid grid-bound drag, double-click/tap properties, Delete, and a rotate
handle for square columns (5° steps; Shift is free). Draft Delete removes the
+7 -5
View File
@@ -152,11 +152,13 @@ clears; apply-to-room. Hooks: `data-hp="wall"`. i18n en/ru.
**Draw with thickness.** The Plan toolbar's **Walls** button carries its session
thickness field immediately on the right (default **15 cm**, or inches when HA
is imperial). Closing a new room outline
writes that cm onto every new edge that does not already have one; shared
stretches that already carry a neighbour's thickness are left alone. Empty / 0
leaves the room thin. Live thick preview follows the rubber-band while drawing.
Split does not use this field. The Thickness tool remains for later edits.
is imperial). Every persisted draft segment remembers the value used when it
was placed. Finishing an open chain transfers those values to the resulting
independent walls. If a segment creates rooms, consumed boundary atoms receive
their source value and unconsumed atoms become independent walls with the same
value; existing shared masonry remains authoritative. Empty / 0 leaves the new
wall thin. Live thick preview follows the rubber-band while drawing. Split does
not use this field. The Thickness tool remains for later edits.
## 7. Out of scope
+2 -2
View File
@@ -42,7 +42,7 @@ House Plan предлагает создать комнаты; отказ ост
Никакой отдельной кнопки «Завершить» и клавиши Enter не появляется.
## 3. Подтверждённое текущее состояние
## 3. Проблема и подтверждённое текущее состояние
1. `MarkupTool` содержит разные `draw` и `partition`; toolbar показывает две
кнопки, а warm viewport способен восстановить оба значения.
@@ -519,7 +519,7 @@ RU/EN обязательно обновляют:
- **AC16 (`smoke` + code review; разработчик/ревьюер):** touch safety floor и
View/kiosk не регрессируют; no-hover tap может выполнить тот же commit, но
hover parity редактора не обещается.
- **AC17 (`typecheck` + `unit` + `build` + documentation review`; разработчик):**
- **AC17 (`typecheck` + `unit` + `build` + documentation review; разработчик):**
implementation-loop gates зелёные, три bundle-копии побайтно одинаковы, RU/EN
i18n, оба changelog и перечисленные документы обновлены в одном
user-visible implementation commit.