Files
house-plan-maker/apps/client/src/components/editor/tools/FurnitureTool.ts
T
alexei.dolgolyov af8b9fe00f feat: complete house plan maker application
Full-featured house/apartment floor plan editor with:

- Turborepo monorepo (React/Vite client, Fastify/Prisma server, shared Zod schemas)
- 2D room editor with walls, doors, windows, furniture, electrical elements
- 3D room preview with Three.js (auto-hide nearest walls, bird's eye default)
- Wall projection views with interactive drag (elevation, position)
- Apartment floor plan view with room positioning
- Copy/paste, alignment tools, measurement tool, annotations
- Item-attached annotations with leader lines (visible on projections)
- Door open direction (LEFT/RIGHT/INWARD/OUTWARD) with swing arc
- Floor type textures (wood, tile, concrete, laminate, herringbone)
- Wall color picker for 3D view
- Furniture: bed, desk, wardrobe, sofa, table, chair, shelf, nightstand, dresser, bookcase, TV (with stand toggle), AC unit
- Furniture elevation support (wall-mounted items)
- Auto-save with dirty state tracking, batch save API
- Rotation-aware collision detection (SAT/OBB) with 3D elevation check
- Rotation-aware hit testing
- i18n (English/Russian) with locale-aware number formatting
- Dark mode with system preference detection
- Undo/redo, keyboard shortcuts, scale bar
- PDF/PNG/JSON export and JSON import
- Focus trap modal, toast notifications, tooltips
- Responsive layout with overlay palettes
2026-04-05 22:34:03 +03:00

74 lines
1.9 KiB
TypeScript

import type { Point, FurnitureItem } from '@house-plan-maker/shared';
import { generateLocalId } from '../utils/geometry';
import type { FurnitureDef } from '../symbols/furniture';
export interface FurniturePlacementPreview {
readonly x: number;
readonly y: number;
readonly width: number;
readonly depth: number;
readonly rotation: number;
readonly isValid: boolean;
}
/**
* Compute furniture placement preview.
* The x,y represents the top-left corner of the furniture piece.
* The cursor world point is treated as the desired center, so we offset
* by half-width and half-depth to get the top-left corner.
*/
export function computeFurniturePreview(
worldPoint: Point,
furnitureDef: FurnitureDef,
rotation: number = 0,
): FurniturePlacementPreview {
return {
x: worldPoint.x - furnitureDef.width / 2,
y: worldPoint.y - furnitureDef.depth / 2,
width: furnitureDef.width,
depth: furnitureDef.depth,
rotation,
isValid: true,
};
}
/**
* Create a FurnitureItem from placement data.
*/
export function createFurnitureItemFromPlacement(
roomId: string,
preview: FurniturePlacementPreview,
furnitureDef: FurnitureDef,
): FurnitureItem {
return {
id: generateLocalId(),
roomId,
type: furnitureDef.type,
x: preview.x,
y: preview.y,
width: preview.width,
depth: preview.depth,
height: furnitureDef.height,
rotation: preview.rotation,
elevationFromFloor: furnitureDef.type === 'AC_UNIT' ? 2.2 : furnitureDef.height <= 0.05 ? 1.2 : 0,
label: furnitureDef.label,
};
}
/** Rotate a furniture item by the given delta in degrees. */
export function rotateFurniture(item: FurnitureItem, deltaDeg: number): FurnitureItem {
return {
...item,
rotation: (item.rotation + deltaDeg) % 360,
};
}
/** Move a furniture item to a new position. */
export function moveFurniture(item: FurnitureItem, newPos: Point): FurnitureItem {
return {
...item,
x: newPos.x,
y: newPos.y,
};
}