feat: Add professional 3D handles, glass patterns, and living room scene

 New Features:
- 6 procedural 3D handles (Beugelgreep, Hoekgreep, Maangreep, Ovaalgreep, Klink, U-greep)
- Glass pattern generator (Standard, DT9 rounded corners, DT10 U-shapes)
- Dynamic living room scene with adaptive doorway
- Enhanced camera controls (zoomed out, more freedom)
- Texture loading system (prepared for future enhancement)

🎨 Visual Improvements:
- Professional handle details (screws, mounting blocks, rosettes)
- Realistic materials (metalness 0.95, proper roughness)
- Living room context (wood floor, white walls, baseboards)
- Better lighting (sunlight simulation, fill lights)
- Apartment environment preset

🏗️ Technical:
- Parametric glass shapes with THREE.Shape
- Dynamic doorway sizing based on door dimensions
- Store updates for handle and glass pattern types
- UI components for all new options

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ubuntu
2026-02-10 18:23:52 +00:00
parent bd9c6545da
commit b30e8d18d4
7 changed files with 968 additions and 112 deletions

198
lib/glass-patterns.ts Normal file
View File

@@ -0,0 +1,198 @@
/**
* Glass Pattern Generators
* Creates custom THREE.Shape objects for decorative glass panels
* Based on reference drawings: dt9 (rounded corners), dt10 (U-shapes)
*/
import * as THREE from "three";
export type GlassPattern = "standard" | "dt9-rounded" | "dt10-ushape";
/**
* Standard rectangular glass panel
*/
export function createStandardGlass(width: number, height: number): THREE.Shape {
const shape = new THREE.Shape();
const hw = width / 2;
const hh = height / 2;
shape.moveTo(-hw, -hh);
shape.lineTo(hw, -hh);
shape.lineTo(hw, hh);
shape.lineTo(-hw, hh);
shape.lineTo(-hw, -hh);
return shape;
}
/**
* DT9: Rounded corners glass panel
* Creates elegant rounded corners on glass sections
*/
export function createRoundedCornerGlass(
width: number,
height: number,
cornerRadius: number = 0.08
): THREE.Shape {
const shape = new THREE.Shape();
const hw = width / 2;
const hh = height / 2;
const r = Math.min(cornerRadius, width / 4, height / 4);
// Start from bottom-left corner (after radius)
shape.moveTo(-hw + r, -hh);
// Bottom edge
shape.lineTo(hw - r, -hh);
// Bottom-right rounded corner
shape.quadraticCurveTo(hw, -hh, hw, -hh + r);
// Right edge
shape.lineTo(hw, hh - r);
// Top-right rounded corner
shape.quadraticCurveTo(hw, hh, hw - r, hh);
// Top edge
shape.lineTo(-hw + r, hh);
// Top-left rounded corner
shape.quadraticCurveTo(-hw, hh, -hw, hh - r);
// Left edge
shape.lineTo(-hw, -hh + r);
// Bottom-left rounded corner
shape.quadraticCurveTo(-hw, -hh, -hw + r, -hh);
return shape;
}
/**
* DT10: U-shaped glass panel (top section - inverted U)
* Creates an upside-down U shape for the upper glass section
*/
export function createInvertedUGlass(width: number, height: number): THREE.Shape {
const shape = new THREE.Shape();
const hw = width / 2;
const hh = height / 2;
const uRadius = hw * 0.85; // U curve radius
// Start at top-left
shape.moveTo(-hw, hh);
// Top edge
shape.lineTo(hw, hh);
// Right edge down
shape.lineTo(hw, -hh + uRadius);
// Bottom U curve (inverted)
shape.absarc(0, -hh + uRadius, uRadius, 0, Math.PI, false);
// Left edge up
shape.lineTo(-hw, hh);
return shape;
}
/**
* DT10: U-shaped glass panel (bottom section - normal U)
* Creates a normal U shape for the lower glass section
*/
export function createNormalUGlass(width: number, height: number): THREE.Shape {
const shape = new THREE.Shape();
const hw = width / 2;
const hh = height / 2;
const uRadius = hw * 0.85; // U curve radius
// Start at bottom-left
shape.moveTo(-hw, -hh);
// Bottom edge
shape.lineTo(hw, -hh);
// Right edge up
shape.lineTo(hw, hh - uRadius);
// Top U curve
shape.absarc(0, hh - uRadius, uRadius, 0, Math.PI, true);
// Left edge down
shape.lineTo(-hw, -hh);
return shape;
}
/**
* DT9 Asymmetric: Create multiple panels with different rounded corners
* For complex DT9 layouts with side panels
*/
export function createDT9Panels(
mainWidth: number,
mainHeight: number,
sideWidth: number,
position: "top-right" | "bottom-left" | "top-left" | "bottom-right"
): {
mainPanel: THREE.Shape;
roundedPanel: THREE.Shape;
dividerHeight: number;
} {
const cornerRadius = 0.12;
// Main large panel with one rounded corner
const mainPanel = new THREE.Shape();
const mw = mainWidth / 2;
const mh = mainHeight / 2;
if (position === "top-right") {
// Main panel with rounded top-right corner
mainPanel.moveTo(-mw, -mh);
mainPanel.lineTo(mw - cornerRadius, -mh);
mainPanel.quadraticCurveTo(mw, -mh, mw, -mh + cornerRadius);
mainPanel.lineTo(mw, mh - cornerRadius);
mainPanel.quadraticCurveTo(mw, mh, mw - cornerRadius, mh);
mainPanel.lineTo(-mw, mh);
mainPanel.lineTo(-mw, -mh);
} else if (position === "bottom-left") {
// Main panel with rounded bottom-left corner
mainPanel.moveTo(-mw + cornerRadius, -mh);
mainPanel.lineTo(mw, -mh);
mainPanel.lineTo(mw, mh);
mainPanel.lineTo(-mw + cornerRadius, mh);
mainPanel.quadraticCurveTo(-mw, mh, -mw, mh - cornerRadius);
mainPanel.lineTo(-mw, -mh + cornerRadius);
mainPanel.quadraticCurveTo(-mw, -mh, -mw + cornerRadius, -mh);
}
// Small rounded panel
const roundedPanel = createRoundedCornerGlass(sideWidth, mainHeight / 3, cornerRadius * 0.6);
return {
mainPanel,
roundedPanel,
dividerHeight: mainHeight / 3,
};
}
/**
* Pattern metadata for UI selection
*/
export const glassPatternOptions = [
{
value: "standard" as GlassPattern,
label: "Standaard",
description: "Rechthoekige glaspanelen",
},
{
value: "dt9-rounded" as GlassPattern,
label: "DT9 - Afgeronde hoeken",
description: "Elegante ronde hoeken",
},
{
value: "dt10-ushape" as GlassPattern,
label: "DT10 - U-vormen",
description: "Decoratieve U-vormige panelen",
},
] as const;

View File

@@ -8,11 +8,12 @@ import {
type DoorConfig,
type SidePanel,
} from './calculations';
import type { GlassPattern } from './glass-patterns';
export type DoorType = 'taats' | 'scharnier' | 'paneel';
export type GridType = '3-vlak' | '4-vlak' | 'geen';
export type Finish = 'zwart' | 'brons' | 'grijs';
export type Handle = 'u-greep' | 'klink' | 'geen';
export type Handle = 'beugelgreep' | 'hoekgreep' | 'maangreep' | 'ovaalgreep' | 'klink' | 'u-greep' | 'geen';
interface ConfiguratorState {
// Door configuration
@@ -24,6 +25,7 @@ interface ConfiguratorState {
gridType: GridType;
finish: Finish;
handle: Handle;
glassPattern: GlassPattern;
// Dimensions (in mm)
width: number;
@@ -43,6 +45,7 @@ interface ConfiguratorState {
setGridType: (type: GridType) => void;
setFinish: (finish: Finish) => void;
setHandle: (handle: Handle) => void;
setGlassPattern: (pattern: GlassPattern) => void;
setWidth: (width: number) => void;
setHeight: (height: number) => void;
setDimensions: (width: number, height: number) => void;
@@ -68,7 +71,8 @@ export const useConfiguratorStore = create<ConfiguratorState>((set, get) => ({
sidePanel: 'geen',
gridType: '3-vlak',
finish: 'zwart',
handle: 'u-greep',
handle: 'beugelgreep',
glassPattern: 'standard',
width: 1000,
height: 2400,
@@ -101,6 +105,8 @@ export const useConfiguratorStore = create<ConfiguratorState>((set, get) => ({
setHandle: (handle) => set({ handle }),
setGlassPattern: (glassPattern) => set({ glassPattern }),
setWidth: (width) => {
const { doorConfig, sidePanel } = get();
const minWidth = calculateHoleMinWidth(doorConfig, sidePanel);