Phase 3: room features (modes, room size, doors, wall screens, obstacles, import/export)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
A web tool for venues (events, banquets, conferences) to lay out furniture in a room and, eventually, auto-optimize the layout.
|
||||
|
||||
## Current scope: Phase 1 + Phase 2 (UI only)
|
||||
## Current scope: Phases 1-3 (UI only)
|
||||
|
||||
Layout and editing UI. No optimizer yet.
|
||||
|
||||
@@ -22,7 +22,14 @@ Layout and editing UI. No optimizer yet.
|
||||
- Screens: while selected, unlocked and unrotated, left/right end handles change their length (`resizeLength`, min 0.5 m). Thickness is only changed by size wheels, never handles (top-down view).
|
||||
- Custom furniture: "Custom" tile opens a form (name, Seating/Table, width, length, seats, available). Saving adds a config + palette tile. Custom seating draws as a chair, custom tables as a table.
|
||||
|
||||
Out of scope: room features (doors, room screen), room import, placement rules, optimization, scanning. Do not design in a way that blocks the roadmap below.
|
||||
**Room features (Phase 3)**
|
||||
- A Layout / Room toggle sits at the top of the sidebar. Switching closes menus and deselects. In Room mode furniture is visible but can't be moved or selected.
|
||||
- Room panel: width and length wheels (3-60 m, step 0.1; the room grows/shrinks from its top-left). It can't shrink past the bounding box of placed furniture, obstacles and wall items (`minRoomSize`; anything else that would clash is rejected via `tryResizeRoom` and the wheel glides back). Add door / Add screen (max 2) / Add obstacle, Export room, Import room.
|
||||
- Walls are drawn inside the canvas (`WALL_PX`); a door is a gap in the wall plus a quarter-circle swing arc. Doors and wall screens slide along their own wall only (no corner-hopping to adjacent walls). Door panel: width 0.7-4 m, "Loading door" toggle, delete (the last door can't be deleted). Wall-screen panel: width 1-12 m, delete. Obstacles (default 0.6 x 0.6 m, 0.2-10 m) drag anywhere; width/length wheels, delete. Delete/Backspace also deletes the selected room element.
|
||||
- Keep-clear zones: the door swing square and the 0.6 m strip inside the opening, the 0.3 m strip under a wall screen, and obstacles. `getBlockedRects(room)` returns them all and `isValidPlacement` reads it, so furniture can't overlap them (red outline, drop blocked). Doors/screens can't sit on furniture or obstacles or share wall length; obstacles can't overlap furniture, each other, or door/screen zones.
|
||||
- Export downloads `room.json` (width, height, doors, screens, obstacles; no furniture). Import validates with `parseRoom` (`roomSchema.ts`) and shows a clear error; if furniture is placed it asks for confirmation first, and if the placed furniture doesn't fit the imported room it clears all furniture and warns.
|
||||
|
||||
Out of scope: non-rectangular rooms, floor plan image import, phone scanning, placement rules, scoring, optimization. Do not design in a way that blocks the roadmap below.
|
||||
|
||||
## Tech stack
|
||||
|
||||
@@ -48,7 +55,12 @@ interface FurnitureItem {
|
||||
seatsOverride?: number; // tables: overrides config.seatsPerUnit
|
||||
}
|
||||
|
||||
interface Room { width: number; height: number; } // meters
|
||||
type Wall = "top" | "bottom" | "left" | "right";
|
||||
// offset = meters from the wall's start (top/bottom start at the left, left/right at the top) to the near edge
|
||||
interface Door { id: string; wall: Wall; offset: number; width: number; isLoading: boolean; }
|
||||
interface RoomScreen { id: string; wall: Wall; offset: number; width: number; } // spec calls it "Screen"; renamed to avoid clashing with the screen furniture type / DOM Screen
|
||||
interface Obstacle { id: string; x: number; y: number; width: number; height: number; }
|
||||
interface Room { width: number; height: number; doors: Door[]; screens: RoomScreen[]; obstacles: Obstacle[]; } // meters
|
||||
|
||||
interface FurnitureTypeConfig {
|
||||
id: FurnitureType; name: string; shape: Shape;
|
||||
@@ -62,7 +74,7 @@ interface FurnitureTypeConfig {
|
||||
// seats for an item = item.seatsOverride ?? config.seatsPerUnit (effectiveSeats)
|
||||
```
|
||||
|
||||
Store (`store.ts`): `{ room, items, configs: Record<id, config>, typeOrder: id[] }`. Defaults live in the configs (chair 0.5x0.5, table 1.8x0.8 w/ 8 seats, screen 2x0.2). Chairs and table seats are independent.
|
||||
Store (`store.ts`; `setRoom` replaces the room, optionally clearing furniture): `{ room, items, configs: Record<id, config>, typeOrder: id[] }`. Defaults live in the configs (chair 0.5x0.5, table 1.8x0.8 w/ 8 seats, screen 2x0.2). Chairs and table seats are independent.
|
||||
|
||||
## Conventions
|
||||
|
||||
@@ -74,9 +86,9 @@ Store (`store.ts`): `{ room, items, configs: Record<id, config>, typeOrder: id[]
|
||||
|
||||
## File layout
|
||||
|
||||
- `src/types.ts`; `src/geometry.ts` (pure: `boundsOf`, `clampToRoom`, `overlaps`, `isValidPlacement`, `tryUpdate`, `resizeLength`, `fitScale`); `src/inventory.ts` (pure: `placedCount`, `remaining`, `effectiveSeats`); `src/store.ts` (reducer + built-in configs).
|
||||
- `src/types.ts`; `src/geometry.ts` (pure: `boundsOf`, `clampToRoom`, `overlaps`, `isValidPlacement`, `tryUpdate`, `resizeLength`, `fitScale`, plus room rules: `getBlockedRects`, `isValidWallItem`, `isValidObstacle`, `isLayoutValid`, `minRoomSize`, `tryResizeRoom`, `findWallSpot`, `findObstacleSpot`); `src/roomSchema.ts` (pure: `parseRoom`, `exportRoom`); `src/inventory.ts` (pure: `placedCount`, `remaining`, `effectiveSeats`); `src/store.ts` (reducer + built-in configs).
|
||||
- `src/App.tsx`: owns the store, selection, sidebar mode, palette drag, room scale, Delete key.
|
||||
- `src/components/`: `Palette`, `TypeMenu`, `ItemPanel`, `CustomForm`, `Wheel`, `Room` (Konva), `FurnitureShape`, `Icons`, `ScaleBar`, `panelStyles`.
|
||||
- `src/components/`: `Palette`, `TypeMenu`, `ItemPanel`, `CustomForm`, `ModeToggle`, `RoomPanels`, `Wheel`, `Room` (Konva), `FurnitureShape`, `Icons`, `ScaleBar`, `panelStyles`.
|
||||
|
||||
## Roadmap (not built yet, don't block)
|
||||
|
||||
@@ -103,6 +115,6 @@ npx tsc --noEmit # type check
|
||||
|
||||
## Working agreement
|
||||
|
||||
- Ask before expanding scope past Phase 2.
|
||||
- Ask before expanding scope past Phase 3.
|
||||
- After changes, make sure the app builds and type checks.
|
||||
- Hackathon pace: prefer simple and working over clever.
|
||||
Reference in New Issue
Block a user