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:
198
lib/glass-patterns.ts
Normal file
198
lib/glass-patterns.ts
Normal 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;
|
||||
10
lib/store.ts
10
lib/store.ts
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user