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,118 @@
|
||||
import type { Point, CreateRoomDto } from '@house-plan-maker/shared';
|
||||
|
||||
export interface RoomTemplate {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
readonly icon: string;
|
||||
readonly defaultWidth: number;
|
||||
readonly defaultHeight: number;
|
||||
readonly wallHeight: number;
|
||||
readonly shape: readonly Point[];
|
||||
/** Suggested door positions (positionAlongWall on wall index) */
|
||||
readonly suggestedDoors: readonly { readonly wallIndex: number; readonly position: number }[];
|
||||
/** Suggested window positions */
|
||||
readonly suggestedWindows: readonly { readonly wallIndex: number; readonly position: number }[];
|
||||
}
|
||||
|
||||
function rectangularShape(width: number, height: number): readonly Point[] {
|
||||
return [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: width, y: 0 },
|
||||
{ x: width, y: height },
|
||||
{ x: 0, y: height },
|
||||
];
|
||||
}
|
||||
|
||||
export const ROOM_TEMPLATES: readonly RoomTemplate[] = [
|
||||
{
|
||||
id: 'bedroom',
|
||||
name: 'Bedroom',
|
||||
description: 'Standard bedroom (4m x 3.5m) with door and window',
|
||||
icon: '\u{1F6CF}',
|
||||
defaultWidth: 4,
|
||||
defaultHeight: 3.5,
|
||||
wallHeight: 2.7,
|
||||
shape: rectangularShape(4, 3.5),
|
||||
suggestedDoors: [{ wallIndex: 3, position: 1 }],
|
||||
suggestedWindows: [{ wallIndex: 1, position: 2 }],
|
||||
},
|
||||
{
|
||||
id: 'kitchen',
|
||||
name: 'Kitchen',
|
||||
description: 'Kitchen (3.5m x 3m) with door',
|
||||
icon: '\u{1F373}',
|
||||
defaultWidth: 3.5,
|
||||
defaultHeight: 3,
|
||||
wallHeight: 2.7,
|
||||
shape: rectangularShape(3.5, 3),
|
||||
suggestedDoors: [{ wallIndex: 3, position: 1.5 }],
|
||||
suggestedWindows: [{ wallIndex: 1, position: 1.5 }],
|
||||
},
|
||||
{
|
||||
id: 'bathroom',
|
||||
name: 'Bathroom',
|
||||
description: 'Bathroom (2.5m x 2m)',
|
||||
icon: '\u{1F6C1}',
|
||||
defaultWidth: 2.5,
|
||||
defaultHeight: 2,
|
||||
wallHeight: 2.7,
|
||||
shape: rectangularShape(2.5, 2),
|
||||
suggestedDoors: [{ wallIndex: 3, position: 1 }],
|
||||
suggestedWindows: [],
|
||||
},
|
||||
{
|
||||
id: 'living-room',
|
||||
name: 'Living Room',
|
||||
description: 'Spacious living room (5m x 4m) with window',
|
||||
icon: '\u{1F6CB}',
|
||||
defaultWidth: 5,
|
||||
defaultHeight: 4,
|
||||
wallHeight: 2.7,
|
||||
shape: rectangularShape(5, 4),
|
||||
suggestedDoors: [{ wallIndex: 3, position: 1 }],
|
||||
suggestedWindows: [{ wallIndex: 1, position: 2 }, { wallIndex: 2, position: 2.5 }],
|
||||
},
|
||||
{
|
||||
id: 'office',
|
||||
name: 'Office',
|
||||
description: 'Home office (3m x 2.5m)',
|
||||
icon: '\u{1F4BB}',
|
||||
defaultWidth: 3,
|
||||
defaultHeight: 2.5,
|
||||
wallHeight: 2.7,
|
||||
shape: rectangularShape(3, 2.5),
|
||||
suggestedDoors: [{ wallIndex: 3, position: 1.25 }],
|
||||
suggestedWindows: [{ wallIndex: 1, position: 1.25 }],
|
||||
},
|
||||
{
|
||||
id: 'empty',
|
||||
name: 'Empty Room',
|
||||
description: 'Custom empty room (3m x 3m)',
|
||||
icon: '\u25A1',
|
||||
defaultWidth: 3,
|
||||
defaultHeight: 3,
|
||||
wallHeight: 2.7,
|
||||
shape: rectangularShape(3, 3),
|
||||
suggestedDoors: [],
|
||||
suggestedWindows: [],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Convert a room template into a CreateRoomDto.
|
||||
*/
|
||||
export function templateToCreateRoomDto(
|
||||
template: RoomTemplate,
|
||||
name?: string,
|
||||
order?: number,
|
||||
): CreateRoomDto {
|
||||
return {
|
||||
name: name ?? template.name,
|
||||
shape: [...template.shape],
|
||||
width: template.defaultWidth,
|
||||
height: template.defaultHeight,
|
||||
wallHeight: template.wallHeight,
|
||||
order: order ?? 0,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user