mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Волна 3 эпика #674. ARCHITECTURE.md 2330 → 653 строки: система координат прототипа 1489×1053, разделы Hidden Isometric Stage 2/4 (текущее — ISOMETRIC.md, история — ADR 122/570), хроники «Additions v1.28–v1.41» и «Audit follow-ups» сняты; подсистемы — короткое описание со ссылкой на канонический документ. Устаревшее исправлено по коду: дерево Layout (store.py, frontend_registration, весь список модулей), хранилища (config, layout, virtual_lights, trails), ленивые локали de и fr, таблица WS API (31 + 3 команды, #256-проекция, space/delete, files/cleanup без keep, контент через /api/houseplan/content), DevItem без несуществующих полей, отказ help/feedback по support_api. Всё ещё верное и не записанное в другом месте перенесено в канонические документы: DEVICE-PRESENTATION (заметки реализации, Action authority, черновик диалога), RADAR (карта реализации), VACUUM (владение кодом), CANVAS (icon_size, --hp-cell-visual-scale, барьер записи координат), FILTERING (#44, каталог), DECOR-EDITOR §7 (инварианты бэкенда ассетов), WALL-THICKNESS (§1 идентичность и нулевые стены, §2 кэши, §4 hover/туннели/острова, §6 удаление комнаты, §9 failed-core, §11 завершение цепочки и комната по грани), ISOMETRIC (isoPlaneMatrix, iso-overlays, створки, служебные атрибуты), LIGHT (Glow над заливкой #55, формула и screen-смешение), CONFIG-COMPATIBILITY (манифест схемы #33, Masonry-слоты #561, квадратный холст v1.48, устаревшие URL контента, layout/set #356), SCOPE (таблица владельцев при сборке файлов), WARM-REMOUNT §5 (холодная загрузка и визуальная непрерывность), UX-MODES (порядок стартового пространства), PDF-EXPORT (граница реализации), FURNITURE (adopt, BOOT_MAX_MS). Раздел TESTING «Backend quality gates (#42)» и строка DEVELOPMENT о bundle-freshness — из того же разбора, в предыдущем коммите. Issue: #680 User-Visible: no Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
170 lines
9.1 KiB
Markdown
170 lines
9.1 KiB
Markdown
# Furniture library
|
|
|
|
Status: **implemented and expanded by issues #159 and #383**. The Background editor
|
|
stores furniture as ordinary decor:
|
|
|
|
```text
|
|
{ kind: "furniture", symbol, x, y, w, h, color, opacity, width_cm, angle?, flip_h?, flip_v? }
|
|
```
|
|
|
|
`w` and `h` are always positive physical extents. Optional boolean `flip_h`
|
|
and `flip_v` mirror the drawing inside that unchanged box; absent flags are
|
|
the original orientation. Existing furniture therefore keeps its position,
|
|
size, rotation, orientation and styling after an update.
|
|
|
|
## Library and source
|
|
|
|
The public library contains **60** top-view symbols, all of them designer
|
|
artwork from the corrected `assets/furniture/houseplan-0.4.1/svg/plan` (#593,
|
|
#606). The previous split — 44 designer symbols plus 12 built-ins drawn from
|
|
unit-box primitives — is gone: pack 0.4.0 redrew those 12 ids (refrigerator,
|
|
dishwasher, washer, dryer, air conditioner, water heater, shower, sink, stairs,
|
|
fireplace, plant and rug) and added `computer`, `hood`, `oven` and `cactus`.
|
|
Pack 0.4.1 corrects that last public id to `exercise` while retaining the
|
|
original drawing and a read-only alias for saved `cactus` objects.
|
|
|
|
The designer pack also contains 33 front-view category illustrations in
|
|
`svg/menu`. All 33 categories are populated. Pack 0.4.0 mistakenly labeled
|
|
an exercise-machine drawing as `cactus` and put it under `plant`; 0.4.1 keeps
|
|
that drawing unchanged under public id `exercise` in the `exercise` category.
|
|
The `bookshelf` and `shelf_floor` plan drawings were also exchanged in 0.4.1
|
|
to match their names. Menu artwork is unchanged. A saved `cactus` continues to
|
|
render and edit as an exercise machine without rewriting the stored id; an
|
|
explicit new selection writes `exercise`.
|
|
|
|
`pack.json` is the source of truth for stable ids, categories, names,
|
|
default centimetre dimensions and SVG paths. Run:
|
|
|
|
```powershell
|
|
npm run furniture:generate
|
|
npm run furniture:check
|
|
```
|
|
|
|
The generator validates a deliberately small, inert SVG subset and produces:
|
|
|
|
- `src/furniture-plan-catalog.generated.ts` — ids, groups, categories and
|
|
default sizes; the only part of the pack in the initial View graph;
|
|
- `src/furniture-plan-art.generated.ts` — the 60 top-view drawings, a lazy
|
|
chunk (#474) loaded by `src/furniture-art-runtime.ts` when a plan draws
|
|
furniture and imported statically by the editor;
|
|
- `src/furniture-menu-art.generated.ts` for the lazy editor graph only.
|
|
|
|
Generated files are never edited manually. The 93 drawings were created by
|
|
Sergey Matyunin (`Matysh`) and granted to the project under its MIT License in
|
|
[issue #593](https://github.com/Matysh/houseplan-card/issues/593#issuecomment-5739841899).
|
|
No separate attribution is required in the interface. The 0.4.1 pack's own
|
|
`README.md` records the reviewed 0.4.0 source archive, its SHA-256 and the
|
|
precise corrections; the historical 0.4.0 README remains unchanged.
|
|
|
|
## Palette interaction
|
|
|
|
Furniture is a two-level non-modal palette in the editor context tray:
|
|
|
|
1. The first level shows front-view **categories**, grouped as Furniture,
|
|
Appliances, Plumbing and Other.
|
|
2. Every category opens a second level, including categories that have only
|
|
one variant.
|
|
3. The second level shows the real top-view drawings. Back returns to the
|
|
category list and clears any armed symbol.
|
|
4. Picking a variant arms one placement and reveals editable Width and Depth
|
|
in the Home Assistant length unit. With a mouse, moving over the plan shows
|
|
the real top-view symbol at its exact future size, wall magnet, rotation and
|
|
canvas clamp. Editing Width or Depth updates that ghost immediately without
|
|
requiring another pointer move.
|
|
5. Clicking the plan places exactly the previewed object and returns to Select.
|
|
Shift keeps the established free-placement behaviour. The ghost is a
|
|
transient, 55%-opaque rendering aid: it is never saved and never enters
|
|
Undo history.
|
|
6. Pointer leave, Escape, palette/tool/editor/space changes and remount clear
|
|
the ghost. Unknown or stale symbol ids show and save nothing.
|
|
|
|
Touch and pen do not emulate hover or show a placement ghost. Their best-effort
|
|
editor path saves one object only after a clean tap; movement, pointer cancel
|
|
or a second contact cancels that pending stamp so pinch/cancel cannot create
|
|
furniture accidentally. View and kiosk touch guarantees remain unchanged.
|
|
|
|
The properties dialog remains a flat native select, grouped by category. Its
|
|
optgroup label includes both the parent group and category because HTML selects
|
|
cannot nest optgroups.
|
|
|
|
## Transform interaction
|
|
|
|
Selected furniture has four corner and four middle-edge resize handles. Corner
|
|
resize is continuous and preserves the original aspect ratio; `Shift` lets
|
|
width and depth change independently. A middle handle changes only its local
|
|
axis, including on a rotated object. Dragging any handle across the fixed
|
|
opposite edge keeps the gesture alive and mirrors the corresponding axis.
|
|
|
|
Rotation is continuous normally and snaps to the nearest multiple of 45° while
|
|
`Shift` is held. The rotation handle uses a circular-arrow cursor. These rules
|
|
are furniture-only: other decor and the backdrop keep their existing grid and
|
|
modifier contracts.
|
|
|
|
Properties show signed width/depth. A negative width is horizontal mirror and
|
|
a negative depth is vertical mirror; the two adjacent checkboxes are the same
|
|
state expressed explicitly. Save stores the absolute extents plus optional
|
|
flags. Zero is not a valid saved dimension.
|
|
|
|
## Rendering contract
|
|
|
|
Each object is still one visible `<path>` and one erase hit path. In Select,
|
|
an additional invisible path follows the same artwork and extends the target
|
|
10 physical centimetres beyond each visible stroke edge; empty areas of the
|
|
bounding box remain non-interactive. Designer artwork
|
|
keeps its native SVG `viewBox`; the renderer applies the user's stored width
|
|
and depth with a non-uniform transform. `vector-effect="non-scaling-stroke"`
|
|
rejects only that local width/depth distortion in the visible result; the
|
|
renderer separately applies the outer plan viewBox scale to the stroke width.
|
|
Consequently:
|
|
|
|
- resizing changes the physical object box without distorting line weight;
|
|
- camera zoom changes the visible line weight exactly like other physical
|
|
decor with the same `width_cm`;
|
|
- the user's decor colour, opacity and physical line width remain authoritative;
|
|
- `data-hp="decor"`, `data-kind="furniture"`, `data-id` and `data-symbol`
|
|
remain stable for card-mod;
|
|
- an id unknown to an older card remains valid data and simply renders
|
|
nothing instead of breaking the plan.
|
|
|
|
The top edge of every drawing is BACK (`y = 0`). Placement and dragging put
|
|
BACK on a **physical surface** of a wall, including the local atomic
|
|
half-thickness; the invisible centreline is not the contact surface. An outer
|
|
wall exposes both its room-facing and exterior surfaces, so raw pointer intent
|
|
keeps furniture inside or outside instead of pulling it through the masonry.
|
|
On a shared wall the raw pointer side selects the room. A new placement exactly
|
|
on an outer-wall axis defaults inside, while an exact-axis drag keeps the
|
|
piece's current side. The magnet reach is measured from the selected physical
|
|
surface. `Shift` keeps free placement and bypasses the wall magnet. Preview,
|
|
commit and drag share the same surface resolver. Furniture is decor:
|
|
it has no entity, state, room aggregation, collision model or automatic
|
|
binding to later wall edits, and already saved coordinates are never migrated.
|
|
|
|
## Compatibility and performance
|
|
|
|
The backend schema additively accepts optional boolean furniture `flip_h` and
|
|
`flip_v`; no configuration version or migration is needed. Every public id that
|
|
existed before #593 still exists and keeps its default centimetre dimensions to
|
|
the number: pack 0.4.0 changes the drawing inside the box, never the box. An
|
|
already saved object's `w` and `h` are never rewritten, and an id unknown to an
|
|
older card remains valid data that simply renders nothing.
|
|
|
|
Pack 0.4.1 reads legacy `symbol: cactus` as `exercise` in the regular plan,
|
|
editor preview/properties and PDF path. The alias is read-only: ordinary saves
|
|
leave the stored id and all physical/style fields alone. An explicit variant
|
|
change stores the new id. Downgrading to a pre-0.4.1 card does not draw a newly
|
|
placed `exercise`, but does not discard its saved record.
|
|
|
|
Plan artwork is available in View and kiosk: a plan with furniture requests the
|
|
artwork chunk once per page before its first frame, and the first-open veil
|
|
waits for it; a plan without furniture never requests it. If the chunk cannot
|
|
be loaded, **nothing** is drawn until the page reloads, and a toast says so
|
|
once. Before #593 the 12 primitive symbols drew regardless; that promise was
|
|
withdrawn deliberately when they became designer artwork, so the failure now
|
|
behaves the same way for all 60 pieces instead of for 48 of them. Front-view
|
|
menu art is imported only after the editor runtime is requested. The editor
|
|
hands its statically imported drawings over synchronously (`adopt`), a chunk
|
|
from another build counts as a failed load, and the boot veil waits for the
|
|
artwork only up to the card's `BOOT_MAX_MS` cap. Touch View/kiosk support is
|
|
blocking; editor ergonomics on touch remain best effort under
|
|
`docs/TOUCH-SUPPORT.md`.
|