feat: добавить лестницы между этажами (#663)

Прямые и винтовые лестницы получили отдельную модель, инструменты редактора, безопасную межэтажную навигацию, вычитание из чистой площади и плоское отображение в 2.5D.

Issue: #663
User-Visible: yes
This commit is contained in:
Sergey Matyunin
2026-09-26 21:00:43 +03:00
parent 781902b3a8
commit d03a68b88a
70 changed files with 2635 additions and 107 deletions
+29 -2
View File
@@ -26,6 +26,11 @@ houseplan-card/
│ ├─ houseplan-panel.ts # HA sidebar/app-bar host for the same full card
│ ├─ editor-runtime-loader.ts # lazy loader: dedupe, retry and build handshake
│ ├─ houseplan-editor-runtime.ts # Plan/Devices/Background composition root
│ ├─ stairs-view.ts # eager read-only stair symbols, links and gesture guards
│ ├─ stairs-editor.ts # lazy Plan stair tools, transforms and properties
│ ├─ stairs-editor-model.ts # pure editor transforms, snapping and target-state helpers
│ ├─ stairs.ts # stair model, render geometry and area math
│ ├─ clean-floor.ts # shared room-floor subtraction including stair footprints
│ ├─ decor-image-editor.ts # lazy Background/Furniture image palette, upload and properties controller
│ ├─ houseplan-onboarding-runtime.ts # first-space/import dialogs, independent of editor
│ ├─ iso-scene-render.ts # lazy 2.5D scene/runtime boundary (settings.volumetric_view, #649)
@@ -656,7 +661,7 @@ not be added to an individual sink.
"rooms":[{"id","name","area","poly|x/y/w/h","wall_ids":[…],"settings"}],
"wall_segments":[{"id","a","b","cm","owners":[…]}],
"partitions":[…], "wall_columns":[…],
"openings":[…], "decor":[…], "settings":{…} }],
"openings":[…], "decor":[…], "stairs":[…], "settings":{…} }],
"markers": [{ "id","binding":"device:<id>|entity:<eid>|virtual","hidden","removed",
"name","icon","display","controls","is_light","glow_color","tap_action",
"room_id","pdfs",… }],
@@ -940,7 +945,7 @@ designer handoff are attached to issue #570.
## Markup editor (v1.4.0+)
State inside the card: `_markup` (mode), `_tool` (draw/column/merge/split/resize/opening/
wallthick/delroom), `_path` (the current outline,
stairs/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
rectangles are rendered uniformly (hit-test: point-in-polygon / rect).
@@ -1125,6 +1130,28 @@ While drawing, the length of the current segment follows the cursor (`_fmtLen`
per-space `cell_cm` — canonical centimetres represented by one grid cell; new
spaces use 1 cm or 2.54 cm/1 inch, while missing legacy values fall back to 5 cm.
### Stairs (#663)
Stairs are a separate Plan entity, not decor. The lazy `StairEditorRuntime`
owns the straight/spiral tool group, selection, continuous
move/resize/rotation, wall/stair magnet and properties. The eager
`StairViewRuntime` owns only read-only symbols, guarded navigation and gesture
suppression, so opening a plan does not load the editor graph. The root card
owns lifecycle composition, shared Plan history/persistence and stage pointer
terminals. `stairs.ts` is the pure boundary for the discriminated model, exact
30 cm tread geometry, cached render projection and footprint containment;
`stairs-editor-model.ts` contains the pure transform, target-state and
footprint-to-footprint snapping helpers used by the editor.
`spaces[].stairs[]` is optional and capped at 250 records. A record exists on
one space only; `target_space_id` is a one-way navigation reference and never
creates target geometry. Straight footprints are rotated rectangles; spiral
footprints are circles. `clean-floor.ts` subtracts their geometric overlap
from the same clean-area result used by room cards and summary metrics, without
cutting the painted floor or changing walls/light/vacuum. PDF and full/static
renderers consume the same stair geometry. The 2.5D scene keeps the SVG object
on its floor plane with no height or shadow. See `docs/STAIRS.md`.
## Editor chrome and contextual controls
Every editor uses one stable primary `.editbar`. Its `.editbar-tools` contains
+21 -9
View File
@@ -16,7 +16,7 @@ coordinate system.
## Principle
1. **Coordinates keep their meaning.** Rooms, openings, decor and
1. **Coordinates keep their meaning.** Rooms, openings, decor, stairs and
device positions are still stored normalised. `1.0` is still the
same distance it always was; `cell_cm` still ties a grid cell to
real centimetres. **No data migration.** An existing plan opens as
@@ -56,8 +56,10 @@ Negative zero becomes positive zero.
The lattice allow-list covers room outlines/extents, exact wall endpoints,
opening `x/y`, decor origins/sizes/endpoints, drafts, partitions, columns, open
spans and layout `x/y`. Angles, opening length/host `t`, decor scale and
backdrop transforms retain the nine-decimal scalar contract. The traversal
spans and layout `x/y`. Angles, opening length/host `t`, decor scale, backdrop
transforms and every stair transform field retain the nine-decimal scalar
contract. Stairs are deliberately excluded even when a value is near a grid
node: their continuous wall-face magnet position is authored data. The traversal
deliberately excludes `cell_cm`, `plan_aspect`, `view_box`,
physical centimetre fields, colours/opacities/live values, presentation scales
and vacuum affine calibration. Unknown/future numeric fields round-trip
@@ -113,6 +115,7 @@ the exact target atomically without exposing a default-fit frame.
| `_GEOM` (room x/y, poly points, opening x/y, `view_box` origin) | `-4 .. 4` | `-5000 .. 5000` | coordinate |
| `_EXTENT` (room w/h, `view_box` w/h) | `0.001 .. 4` | `0.001 .. 5000` | size — strictly positive |
| `_NORM` (decor x/y/w/h) | `-1 .. 2` | `-5000 .. 5000` | coordinate |
| stair `x/y`, `length/width/radius` | — | `-5000 .. 5000`; sizes positive | continuous transform |
| opening `length` | `0.001 .. 1` | `0.001 .. 5000` | size — strictly positive |
`+/-5000` is **garbage insurance, not a frame**. At the historical compatibility
@@ -132,6 +135,7 @@ tested. Input is a list of **items**, one per drawn/placed object:
* the backdrop image rectangle, when the space has one;
* every opening (door/window/gate) end-to-end segment;
* every decor shape;
* every stair, using its complete rotated rectangle or circular footprint;
* every device the layout actually places in this space **and that the card
actually draws** — a HIDDEN device (docs/FILTERING.md) is not content: the
frame is presentation, and an object nobody can see must not decide what the
@@ -276,7 +280,7 @@ produce a zero-sized SVG `viewBox`.
in the decision — `_clampView` alone says how far you may walk.
* **Who owns the pointer.** A drag pans only when it starts on empty
scene: the room-resize handles, device badges, openings, room labels
and the decor shapes take the pointer first (`_stagePointerDown`
stairs and the decor shapes take the pointer first (`_stagePointerDown`
bails out on them), and a drawing tool that consumes the press —
decor line/rect/ellipse/text — bails out too. Two fingers are always
a pinch, never a pan. On a **kiosk** screen at swipe zoom (`≤ 1`,
@@ -526,10 +530,18 @@ exception to positional quantisation: it is continuous in both modes, with
Shift selecting independent axes, while Shift on its rotation handle snaps to
45°. Furniture placement and movement remain grid-bound.
Stairs use the continuous branch of this contract for their complete
transform. Move and resize stay off-grid when authored there; Shift snaps only
rotation to 45°. Wall magnet resolves the visible physical face (including
half-thickness) and stair magnet resolves outer footprint-to-footprint contact.
Neither save/load nor Optimize may replace that contact with a nearby lattice
node.
### 9.5 «Оптимизировать планы» — explicit whole-plan maintenance
Existing and imported plans may still hold coordinates between the
nodes. New editor operations cannot create more. General settings contain
Existing and imported plans may still hold grid-bound coordinates between the
nodes. Ordinary grid-bound editor operations do not create more; explicitly
continuous objects are exempt. General settings contain
a **Plan maintenance** group whose action previews and then repairs old
data through all current passes: model upgrades, mandatory grid
alignment, exact open-span canonicalisation and wall-interval compaction.
@@ -585,9 +597,9 @@ either store changes, commits both revisions, and retains one snapshot.
has changed since the optimization. A crash between store writes is
completed from the intent on the next integration setup.
The grid pass deliberately excludes the complete transform of `furniture` and
uploaded `image` decor. Their position, size and rotation are continuously
authored values (#383), so changing even one of those fields would make
The grid pass deliberately excludes the complete transform of `furniture`,
uploaded `image` decor and `spaces[].stairs[]`. Their position, size and
rotation are continuously authored values (#383, #663), so changing even one of those fields would make
Optimize create debt from a normal editor operation. Other decor kinds and
storage-level numeric canonicalization keep their existing grid contract
(#477).
+10 -1
View File
@@ -2,6 +2,16 @@
## Unreleased
- The Plan editor can now place straight and one-turn spiral stair objects with
physical sizes, automatic 30 cm treads and an ascent arrow. Stairs move,
resize, rotate and snap continuously to wall faces or each other; their
footprint is deducted from clean room area without cutting the visible
floor. An optional one-way target turns a stair into safe floor navigation
in View, while gestures, broken targets and fixed-floor cards remain inert.
The target floor is never changed automatically, and the first 2.5D version
stays flat on the floor plane
([#663](https://github.com/Matysh/houseplan-card/issues/663)).
## v1.78.0-beta.5 — 2026-09-26
- In the 2.5D plan the metrics row under a room name (temperature, humidity,
@@ -22,7 +32,6 @@
footer button as the only clickable control. The regression came with the
overlapping-marker fix in v1.76.0-beta.2
([#664](https://github.com/Matysh/houseplan-card/issues/664)).
## v1.78.0-beta.4 — 2026-09-26
- The main toolbar is steadier and more compact: its editor close **×** now
+11 -1
View File
@@ -8,6 +8,17 @@
## Не выпущено
- В редакторе плана появились отдельные прямые и винтовые лестницы с физическими
размерами, автоматическим шагом ступеней 30 см и стрелкой подъёма. Лестницу
можно плавно перемещать, менять её размер, поворачивать и магнитить к грани
стены или другой лестнице; её габарит вычитается из чистой площади комнаты,
но не вырезает видимый пол. Необязательная односторонняя ссылка безопасно
переводит на выбранный этаж в Просмотре, а после жеста, при битой ссылке и в
карточке с закреплённым этажом переход не срабатывает. Целевой этаж
автоматически не меняется; в первой версии 2.5D лестница остаётся плоской
на полу
([#663](https://github.com/Matysh/houseplan-card/issues/663)).
## v1.78.0-beta.5 — 2026-09-26
- На объёмном плане (2.5D) строка показателей под названием комнаты
@@ -28,7 +39,6 @@
единственный кликабельный элемент — кнопка в подвале. Регрессия пришла с
исправлением перекрытий маркеров в v1.76.0-beta.2
([#664](https://github.com/Matysh/houseplan-card/issues/664)).
## v1.78.0-beta.4 — 2026-09-26
- Основная панель стала компактнее и перестала дёргаться: крестик **×**
+22
View File
@@ -112,6 +112,28 @@ An older frontend ignores the field and shows Flat. An older backend preserves
it through the unknown-settings policy. Full backup/import carries `settings`
whole, so the value survives.
## Stairs (#663)
`spaces[].stairs[]` is an optional bounded (250 records per space)
discriminated collection. `kind: "straight"` stores positive `length` and
`width` plus `direction: "forward" | "backward"`; `kind: "spiral"` stores a
positive `radius` plus `direction: "clockwise" | "counterclockwise"`. Both
variants carry stable `id`, continuous normalized centre `x/y`, scalar
`angle`, and an optional nullable `target_space_id`.
Stair transforms follow furniture's continuous contract. Config writes apply
only the nine-decimal scalar cleanup to position, size and angle; they do not
use near-lattice snapping, and Optimize does not move them. Full backup,
support data and diagnostics retain the bounded records. Full import remaps a
known target space id and clears a target that was not imported; single-space
transfer cannot invent a link to an external floor. Deleting a target space
clears incoming links without deleting the source stair.
The collection is additive and requires no model/store version migration. A
current frontend renders and edits it; a legacy frontend ignores the objects.
The current backend preserves valid records through unrelated writes, while a
backend predating the field must not be used to edit a newer configuration.
## Sun-ray window face (#577)
`settings.sun_ray_origin` is an optional global enum: `inner` or `outer`.
+2 -2
View File
@@ -92,7 +92,7 @@ Composition remains SVG-first:
3. screen-facing HTML overlays.
The floor keeps the same nodes and order for paper/backdrop, room fills/hover,
Glow/spill, sun, decor/furniture, opening symbols and vacuum path/outline. Stage
Glow/spill, sun, decor/furniture, flat stair symbols, opening symbols and vacuum path/outline. Stage
1 does not add a second light source/layer. Markers and room cards intentionally
remain above walls without geometric occlusion.
@@ -248,7 +248,7 @@ search. There is no painted plate, long tether, ground dot or per-marker
shadow. The original screen-facing HTML root remains the only hit, focus,
tooltip and action target, and selection/hover cannot invalidate the placement
cache. Vacuum, Glow/spill, SUN, room fills/hover, arbitrary decor,
furniture/backdrop and every persisted coordinate remain on `z=0`.
furniture/backdrop, stairs and every persisted coordinate remain on `z=0`.
Room names remain screen-facing and lose stroke, text shadow, drop shadow and
halo. Iso uses `#303936` on a light presentation and `#f2f0e8` on a dark one;
+1
View File
@@ -11,6 +11,7 @@
| [Полное руководство пользователя](USER-GUIDE.ru.md) | Пользователи и администраторы Home Assistant | Установка, первая настройка, пространства, комнаты, стены, проёмы, устройства, визуальные состояния, заливки, подложка, солнце, пылесосы, киоск, обслуживание и диагностика |
| [Радары присутствия](RADAR.md) | Пользователи, администраторы и интеграторы | Поддерживаемые профили, привязка источников, установка и калибровка, статусы данных, приватность и диагностика |
| [Редактор подложки и декора](DECOR-EDITOR.md) | Пользователи, тестировщики и разработчики | Инструменты, единое выделение и трансформации, физические размеры, магнит, Undo/Redo, поведение картинки-подложки и совместимость старых полей |
| [Лестницы и переходы между этажами](STAIRS.md) | Пользователи, тестировщики и разработчики | Прямые и винтовые лестницы, размеры, направление подъёма, магнит, связь этажей, площадь и ограничения 2.5D |
| [GitHub Issues](https://github.com/Matysh/houseplan-card/issues) и [Project v2](https://github.com/users/Matysh/projects/1) | Владелец, разработчики и контрибьюторы | Единственный актуальный backlog: задачи, приоритеты, решения и статусы выполнения |
Старые тематические документы в этом каталоге остаются инженерными спецификациями и историей решений. При расхождении пользовательского описания с интерфейсом текущей версии приоритет имеет новое руководство, а при расхождении с фактическим поведением — код текущей версии.
+93
View File
@@ -0,0 +1,93 @@
# Stairs
Stairs are plan-level navigation objects. They show the physical place and
direction of an ascent on one space and can link that space to one other House
Plan space. The link is deliberately one-way: adding or editing a stair never
creates or changes anything on the target floor.
## User contract
The Plan editor has one **Stairs** group with **Straight** and **Spiral** tools.
A click places the chosen default; the object remains selected. Drag moves it,
the visible handles resize it, and the upper handle rotates it. Straight stairs
have corner and one-axis edge handles. A spiral stair remains a circle and has
one radial resize handle. Rotation is continuous; holding `Shift` snaps to the
nearest multiple of 45 degrees. `Esc` cancels an active transform or clears the
selection; Delete/Backspace removes the selected stair; the ordinary Plan
Undo/Redo history covers create, edit, transform and delete.
Double click opens properties. A straight stair stores positive length and
width; a spiral stair stores a positive radius. The dialog also selects the
rise direction, rotation and an optional target space. A target cannot be the
current space. A missing, self or deleted target leaves the stair visible and
editable but shows a repair warning and makes View activation a no-op.
In an ordinary multi-space card, a clean click/tap or keyboard activation on a
valid stair switches to the target tab and restores that floor's remembered
camera. Pan, pinch, long press, swipe and pointer cancellation do not navigate.
In a card configured with `floor`, stairs are visible but inert. The target
floor receives no automatic stair, highlight or camera centring.
## Geometry and drawing
Both variants use the same continuous transform contract as furniture: their
authored position, size and angle are not grid-quantised on save or by
**Optimize plans**. A stair near a wall snaps to the visible physical wall face;
a straight stair also becomes parallel to that face. Any straight/circular pair
of stairs can snap footprint-to-footprint. The other stair is never modified or
linked by the magnet.
Straight tread lines are perpendicular to the rise axis and start at the lower
edge every 30 cm. A remainder shorter than 30 cm stays at the upper edge.
Spiral stairs make one full turn; radial treads use the same 30 cm physical step
measured on the travel line at two thirds of the radius. Their direction is
clockwise or counter-clockwise when viewed from above. The arrow always means
physical ascent, not the direction of navigation between named tabs.
The footprint overlap is removed from clean room floor area exactly once. It
does not cut the visible floor, change room or wall geometry, create a light
occluder, affect Glow/sun/vacuum, or participate in Optimize. In 2.5D the first
version remains a flat symbol projected with the floor/decor plane; it has no
height, risers, railings or shadows.
## Persisted model
Each space may contain `stairs: []`, bounded to 250 valid records:
```json
{
"id": "stair-main",
"kind": "straight",
"x": 0.42,
"y": 0.55,
"angle": 90,
"length": 0.24,
"width": 0.10,
"direction": "forward",
"target_space_id": "floor-2"
}
```
`kind:"spiral"` replaces `length`/`width` with `radius` and uses direction
`clockwise` or `counterclockwise`. Coordinates and sizes use the normalized
plan coordinate system; physical labels are derived through `cell_cm`.
`target_space_id` is nullable. Unknown sibling fields survive validation and
round trips for forward compatibility. Full backup/import/export, one-space
transfer, plan-only transfer, diagnostics and support packages preserve the
same records. When a complete backup removes a target space its incoming stair
links are cleared; a one-space transfer cannot invent an external target.
## Implementation boundary
- `src/stairs.ts` owns validation, drawing geometry and area-subtraction
primitives.
- `src/stairs-view.ts` is the eager read-only boundary for symbols, guarded
navigation and touch/pointer gesture suppression.
- `src/stairs-editor-model.ts` owns pure Plan transforms, target state and
stair-to-stair magnet math; `src/stairs-editor.ts` owns the lazy Plan UI and
properties.
- `src/clean-floor.ts` and `src/summary-panel-metrics.ts` consume the same
footprint subtraction for room cards and summary totals; PDF rendering uses
the same stair outline/tread geometry without changing the room floor paint.
- Backend schemas and transfer/support surfaces treat `stairs` as a bounded,
additive space collection. Old configs without it remain unchanged.
+1
View File
@@ -98,6 +98,7 @@ Everything in this table is **public API**.
| Wall body (thickness) | `path` (SVG) | `wall` | `data-id` = segment key, `data-kind` = `shared` \| `outer` | `.wallbody` |
| Independent partition | `path` (SVG editor hit target) | `partition` | `data-id` = partition id, `data-kind` = `partition` | `.physical-hit` |
| Wall column | `path` / `circle` (SVG editor hit target) | `wall-column` | `data-id` = column id, `data-kind` = `square` \| `circle` | `.physical-hit` |
| Stair | `g` (SVG) | `stair` | `data-id` = stair id, `data-kind` = `straight` \| `spiral`, `data-target-state` = `active` \| `missing` \| `self` \| `deleted` \| `fixed` | `.hp-stair` |
| Decor shape | `line` / `rect` / `ellipse` / `text` (SVG) | `decor` | `data-id` = shape id, `data-kind` = `line` \| `rect` \| `ellipse` \| `text` | `.dshape` (`.dtext` on text); persisted colour/alpha are inline SVG attributes and therefore win over weak CSS selectors |
| Furniture | `path` (SVG) | `decor` | `data-id` = shape id, `data-kind` = `furniture`, `data-symbol` = the symbol id (`sofa`, `toilet`, …) | `.dshape .dfurn` |
| Floor / space tab | `button` (HTML, header) | `space-tab` | `data-id` = space id | `.tab` |
+7
View File
@@ -37,6 +37,8 @@ On phones, tablets, wall panels and HA Companion apps, the ordinary View must:
tabs, zoom and one gear whose menu holds every other header action, each
item a 44 px target (#616);
- support convenient pan, pinch zoom and space switching;
- treat a linked stair as a floor-navigation target only after a clean tap;
pan, pinch, long press, swipe and cancellation tails must stay inert;
- fit the whole plan after two clean taps on free scene background, while one
tap on a room keeps the immediate room-fit action;
- provide a touch path for essential information that desktop exposes through
@@ -158,6 +160,11 @@ Desktop `Shift` bypasses that offer and constrains drawing to an exact 45° ray;
touch has no separate modifier gesture. A pan, pinch, cancellation or second
pointer never accepts a face, applies a small-gap repair or creates a room.
Stair placement and transforms remain best-effort editor interactions on
touch, but the safety floor is strict: cancellation and navigation gestures do
not create, move, save or follow a stair. A long press in View does not activate
its target floor, and the next deliberate tap is re-armed immediately.
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
+11
View File
@@ -594,6 +594,7 @@ shape change it is retained only while it remains inside the room.
| Walls | Continuous wall chain; offers rooms when it closes faces and finishes open chains as independent walls | Only a confirmed room has area | Positive thickness blocks light; zero thickness follows the space's dashed/solid policy | Partial room overlap is rejected; there is no separate Partition or Boundary 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 |
| 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 |
| Stairs | Straight flight or one-turn spiral with automatic 30 cm treads and an ascent arrow | Subtracts only its footprint overlap from clean area | No effect on Glow, sun or walls | Lives on this floor only; a valid optional target makes it a View link |
Other operations edit existing geometry:
@@ -605,6 +606,16 @@ Other operations edit existing geometry:
| Thickness | Changes one span or every wall of a room, including zero-thickness walls |
| Delete room | Deletes the room after choosing whether its exclusive physical walls remain; shared walls always remain |
Choose **Stairs → Straight** or **Stairs → Spiral**, then click the plan. A
selected stair moves and resizes smoothly like furniture; `Shift` snaps its
rotation to 45°. It snaps to the visible physical face of a wall or flush to
another stair, and **Optimize plans** preserves that exact continuous
transform. Double click opens size, direction, angle and target-floor
properties. In View a clean activation switches to the valid target and
restores that floor's saved view; gesture tails, broken links and fixed-floor
cards do nothing. The target floor is never changed automatically. In 2.5D the
symbol is flat on the floor. See [Stairs](STAIRS.md).
![Selected partition and its Plan context tray](images/05-plan-context-tray.png)
Deleting a room also clears that exact room from direct device assignments and
+12
View File
@@ -779,6 +779,7 @@ Undo оптимизации.
| Стены | Непрерывную цепочку стен; при замыкании предлагает комнаты, при выходе сохраняет независимые стены | Площадь появляется только у подтверждённой комнаты | Положительная толщина блокирует свет; нулевая следует настройке пространства | Частичное перекрытие комнат запрещено; отдельных инструментов «Перегородка» и «Граница» нет |
| Колонна | Квадратную или круглую опору | Не меняет площадь комнаты | Блокирует свет внутри своей формы | Имеет одну форму/размер/поворот, но не является стеной или комнатой |
| Проём | Дверь, окно, открытый проём или ворота на стене комнаты либо законченном независимом отрезке | Не меняет площадь | Дверь/ворота пропускают свет между полами, открытый проём — всегда; только наружное окно комнаты может давать солнечный луч | Должен целиком помещаться на одном подходящем отрезке стены |
| Лестница | Прямой марш или один оборот винтовой лестницы с автоматическим шагом ступеней 30 см и стрелкой подъёма | Вычитает из чистой площади только пересечение своего габарита | Не влияет на Glow, солнце и стены | Существует только на этом этаже; необязательная корректная цель превращает её в ссылку в Просмотре |
Остальные операции меняют уже созданную геометрию:
@@ -790,6 +791,17 @@ Undo оптимизации.
| Толщина | Задаёт толщину 0–100 см выбранному участку или всем стенам комнаты |
| Удалить комнату | Открывает выбор: удалить комнату и оставить её физические стены либо удалить комнату вместе с ними; общие стены остаются всегда |
Выберите **Лестница → Прямая** или **Лестница → Винтовая**, затем кликните по
плану. Выбранная лестница перемещается и меняет размер плавно, как мебель;
`Shift` привязывает поворот к 45°. Магнит ставит её к видимой физической грани
стены или вплотную к другой лестнице, а **Оптимизировать планы** сохраняет этот
continuous-transform без привязки к сетке. Двойной клик открывает размеры,
направление подъёма, угол и целевой этаж. В Просмотре чистый клик переходит на
корректную цель и восстанавливает сохранённый вид этажа; хвост жеста, битая
ссылка и карточка с закреплённым этажом ничего не делают. На целевом этаже
объект автоматически не создаётся. В 2.5D символ остаётся плоским на полу.
Подробный контракт — в [документе о лестницах](STAIRS.md).
При варианте **«Удалить комнату, оставить стены»** эксклюзивные участки
положительной толщины становятся обычными независимыми перегородками с той же
толщиной, а их двери/окна перепривязываются к ним. Нулевые участки сохраняются
+5
View File
@@ -269,6 +269,11 @@ layer you cannot see is a layer you cannot edit.
- Door, window, gate and passage may be hosted by one finished independent wall
segment. Drafts and columns are never opening targets. Missing hosts fail
dark and expose a rebind action only in Plan.
- **Stairs** places a straight flight or a one-turn spiral as a separate Plan
object. It uses furniture-like continuous move/resize/rotation and physical
wall/stair magnet rather than the grid-bound wall contract. Its optional
one-way target is edited in properties; in View only a clean activation of a
valid target switches floors. The target floor is never modified.
- **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
View File
@@ -43,6 +43,7 @@
"rules",
"settings",
"space",
"stairs",
"summary",
"support",
"vacuum"
@@ -156,6 +157,11 @@
"since": "1.59.0-beta.3",
"audience": ["styling", "test"]
},
"stair": {
"elements": ["g"],
"since": "1.78.0-beta.5",
"audience": ["styling", "test"]
},
"support": {
"elements": ["button"],
"since": "1.73.0-beta.7",
@@ -193,6 +199,7 @@
"resize",
"select",
"split",
"stairs",
"text",
"wall-thickness"
]
+12 -12
View File
@@ -3,7 +3,7 @@
"fixture": "synthetic-only",
"chromium": "151.0.7922.34",
"oxipng": null,
"sourceFingerprint": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceFingerprint": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"captureScriptSha256": "e2d14d89de793011615662af7c83c433768d4761f6cec7f0b3860c797494299e",
"command": "npm run build && node demo/docs/capture.mjs",
"scenarios": {
@@ -15,7 +15,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "1bde1bff8d4ab5f197320b54ef57186b3c07cf2121d5414ada63ce357826bfa8"
},
"view-touch": {
@@ -26,7 +26,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "f9dd5b54f86b814c00bc84149af84485f052b103cdcc9263b5c50bf5d32c69d6"
},
"space-create": {
@@ -37,7 +37,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "aab4811bba3afa6cc849cb9d3989d126b8c327b69a2f8f853e55fc077e0e9a1b"
},
"room-contour-close": {
@@ -48,7 +48,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "e106ff9db6c96adb08beba12cc299a88ac5fa47c079be316f185e5657bc656f0"
},
"plan-context-tray": {
@@ -59,7 +59,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "9b0db923fa7c8aff783b5c7050ee5b410b1b04df81a9e48bbc8111a7d0d47e76"
},
"device-editor": {
@@ -70,7 +70,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "198c084c0a45aa3afdf79181b80d8bb0347c95f3248b4fac9e05d69eb8a35f8f"
},
"device-display-preview": {
@@ -81,7 +81,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "e9b88d65dca74133646aa849d47bc09ad85eb8b16a168c4df6930e8f5856b5f5"
},
"background-editor": {
@@ -92,7 +92,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "44beadd61365ac1730c1efde846e528bfe92d8935fd167f1b6bf3bbad6af8f33"
},
"room-card": {
@@ -103,7 +103,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "d47988b03e6a078508b27b5612b6eafe8ecc33c891b589ddda1cc7c71f43b7c8"
},
"device-info": {
@@ -114,7 +114,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "4ec8c6df671742b90ca55222917076c47c5035a6df0587282e60873c0ce5f64e"
},
"pdf-export": {
@@ -125,7 +125,7 @@
},
"theme": "light",
"language": "en",
"sourceSha256": "25eae6908d0b7e5b154910505e483bc772e2f0406fed852c21b6ee910808a383",
"sourceSha256": "fcc2617295be1d37facbd00dea5df81fc5d3d11605b97f23da563a25df22425e",
"imageSha256": "ad3d735135b180ce015c9cda07fdcec08d7c8707abe0665734ef7b450a8a7093"
}
},