59 lines
8.4 KiB
Markdown
59 lines
8.4 KiB
Markdown
# Floor planner
|
||
|
||
A single-page flooring and skirting planner built with Vite, React and TypeScript. All measurements use millimetres; areas are displayed in square metres. Run it locally or host `dist/` on any static web host.
|
||
|
||
```sh
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
Use a supported Node.js release (22.12+ or 24+). Open the local URL printed by Vite.
|
||
|
||
```sh
|
||
npm test # Pure geometry, planning, URL and CSV tests
|
||
npx playwright install chromium
|
||
npm run test:e2e # Desktop Chromium and mobile-sized Chromium
|
||
npm run build # Strict TypeScript check and production build
|
||
npm run check # Build, unit tests and browser tests
|
||
```
|
||
|
||
## Planning workflow
|
||
|
||
1. Start with a rectangle, fireplace recess, or custom outline. Enter wall lengths in order. The first wall runs east; the turn **after** each wall is positive for right and negative for left. For example, a clockwise rectangle has 90° turns. The final corner is derived from the closed outline.
|
||
2. Add door openings with a width and an offset from the wall's start. Include architrave/trim in the width where skirting must be omitted. Doors terminate skirting; laminate ends at the room outline with a transition and expansion gap. Rooms are planned independently.
|
||
3. For a custom outline, the closing-wall suggestion gives the required length and turn. Add it, then replace the suggested length with the actual measured length. An outline must close within the chosen tolerance and have no intersections or invalid doors before a cut plan can be produced.
|
||
4. Set material dimensions, spacer, kerf and minimum piece sizes alongside the measurements. Kerf means material removed by the blade: a 3 mm cut leaves 600 mm + 397 mm from a 1,000 mm board. Both kerf fields have keyboard and touch tooltips. Skirting stock lengths are purchase options, not a fixed inventory. Materials are shared across rooms; direction is per room.
|
||
5. The drawing and cut plan update on the same page. Flooring and skirting are visible together, with the whole drawing fitting the page without an internal scrollbar. Warnings follow their room, even when rooms share a name. Every installed board is drawn, including full lengths without labels. Only cuts and reusable offcuts receive sequential codes starting at F1 for flooring and A1 for the first room’s skirting. Identical flooring cuts share a batch row; each piece has a small shape preview that opens exact dimensions in a popup. Skirting previews use equal scales on both axes, with close-ups of each end to show the actual mitre angle. Source allocations identify the stock used by each piece. Export CSV for both materials, or print the plan.
|
||
6. Copy the link to save or share the complete project. Measurements, materials, selected room and selected piece live in a compressed, versioned URL fragment. Old links containing steps, layers and zoom still load; those obsolete view choices are ignored. Reloading or opening that link reproduces the plan. Browser back/forward restores room changes. There is no local storage, account, API, database or analytics. Fonts are system fonts; the app makes no external font requests.
|
||
|
||
## Calculation rules and practical limits
|
||
|
||
- Geometry follows measured vectors, checks final-wall length/direction, detects crossings and validates opening bounds and overlaps. A small accepted closure residual snaps only the final wall to the origin, with a visible warning. A mismatch can indicate measurement error or an irregular wall; lengths and corner angles alone cannot identify or reconstruct a bow. Remeasure and represent known changes of direction as additional segments.
|
||
- Polygon offsets and clipping use `clipper-lib` at 0.001 mm internal precision. Displayed dimensions are rounded to 0.1 mm. The supplied cut templates contain board-relative polygon vertices and edge lengths for angled and notched pieces.
|
||
- Laminate is inset from every wall, including fireplace recesses. Interior rows retain full factory width. When the final row would be narrower than the minimum, the first and last rows share the width equally. A bounded search also compares other feasible edge-row arrangements.
|
||
- Brick uses a half-board stagger. Random is deterministic, prefers usable offcuts and avoids a repeating two-row sequence when feasible. The offset is the nearest end-joint distance on the adjacent row's grid, not a distance from a wall. Half a board is the largest possible nearest-joint offset. Settings that cannot be met produce an error instead of a partial layout.
|
||
- Minimum laminate sizes apply to each clipped piece's bounding length/width. Irregular shapes can still have narrow tips or necks; their exact templates are available in the cut popups. Product-specific click systems and assembly sequences are not simulated.
|
||
- Offcut reuse preserves the needed left/right factory ends. Only full-width end remnants are available for reuse; ripped side strips and shaped scraps are conservatively waste. Crosscuts include kerf. The stock count is actual allocated boards, not an area-only estimate. Waste includes kerf and unused material, including still-usable stock remainders.
|
||
- Skirting internal corners use butt joints: the previous wall runs through and the next is shortened by thickness, adjusted for non-square corners. Coping is excluded. External corners use equal mitres. Minimum sizes apply after corner allowances and door subtraction. An unavoidable undersized run is an error.
|
||
- Skirting cut lengths are the longest-point envelope. Cut square blanks first, then bevel their ends as specified; this keeps reusable stock remainders square. Back lengths and diagram positions locate the wall-facing edge. The stock allocation includes crosscut kerf and counts the additional bevel passes.
|
||
- Flooring uses a bounded beam search, minimizing purchased boards, then saw passes. Skirting compares full-stock, balanced and offcut-aware splits and packs them across offered lengths, preferring less waste, then fewer cuts. Available offcuts can move the next wall’s joints. Minimum lengths are reserved on both front and back edges of angled pieces. These are deterministic, auditable heuristics rather than guarantees of a global minimum. Room order is an input and can affect shared-offcut allocation.
|
||
- Board counts exclude an extra allowance for damage or mistakes. Spacer, minimum size and staggering defaults are editable starting values; follow the exact product's installation instructions. For example, [Quick-Step's installation guidance](https://www.quick-step.co.uk/en-gb/laminate/installation) provides product-specific manuals, and its [room-transition guidance](https://www.quick-step.co.uk/en-gb/frequently-asked-questions/laminate/preparation-and-installation/do-i-really-need-expansion-gaps-between-every-room) explains separate expansion joints.
|
||
- Links are bounded to 20 rooms, 100 walls per room and 30 doors per wall. Flooring calculations are bounded to 2,000 purchased boards and 500 rows per room. Large plans may take longer; very large projects should be split into separate links. Long links may exceed the limits of a messaging service.
|
||
|
||
## Code map
|
||
|
||
| File | Responsibility |
|
||
| ------------------------ | ----------------------------------------------------------- |
|
||
| `src/lib/types.ts` | Inputs and derived result contracts |
|
||
| `src/lib/defaults.ts` | Starter measurements and deterministic IDs |
|
||
| `src/lib/geometry.ts` | Vector tracing, closure, validation and polygon offsets |
|
||
| `src/lib/flooring.ts` | Row search, clipping, factory-end-aware stock allocation |
|
||
| `src/lib/skirting.ts` | Corner allowances, run splitting and stock allocation |
|
||
| `src/lib/url-state.ts` | Canonical schema, compression and untrusted-link validation |
|
||
| `src/lib/use-project.ts` | URL and browser-history synchronization |
|
||
| `src/lib/export.ts` | CSV generation, including formula-injection protection |
|
||
| `src/components/` | Editors, SVG drawing and linked cut tables |
|
||
| `tests/` | Pure calculation and state tests |
|
||
| `e2e/` | Actual browser workflows at desktop and mobile sizes |
|
||
|
||
Only the inputs and view selections are persisted. Geometry, plans, quantities, warning messages and drawings are derived. There are no timestamps or random-number calls in the planners. `npm run build` also checks unused variables and strict TypeScript types.
|