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
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user