"use client"; import { Suspense } from "react"; import { RoundedBox, useTexture } from "@react-three/drei"; import * as THREE from "three"; // ============================================ // PHYSICAL CONSTANTS (mm converted to meters) // ============================================ const PROFILE_DEPTH_M = 0.04; // 40mm profile depth const DOOR_FACE_Z = PROFILE_DEPTH_M / 2; // 20mm - front face of door const MOUNT_RADIUS = 0.006; // 6mm radius standoff cylinders const MOUNT_LENGTH = 0.04; // 40mm standoff length const MOUNT_CENTER_Z = DOOR_FACE_Z + MOUNT_LENGTH / 2; // Center of mount const GRIP_CENTER_Z = DOOR_FACE_Z + MOUNT_LENGTH; // Front face of mount = grip center const GRIP_RADIUS = 0.01; // 10mm radius for round grips const GRIP_BAR_SIZE = 0.02; // 20mm for square grip cross-section export interface HandleProps { finish: string; doorWidth: number; doorHeight: number; railDepth: number; stileWidth: number; } // ============================================ // MATERIALS // ============================================ /** * Powder-coated steel material matching door frame finish. * Loaded with texture for visual continuity with the frame. */ function HandleMaterialTextured({ color, finish }: { color: string; finish: string }) { try { const texturePath = ({ zwart: "/textures/proinn/proinn-metaalkleur-zwart.jpg", brons: "/textures/proinn/proinn-metaalkleur-brons.jpg", grijs: "/textures/proinn/proinn-metaalkleur-antraciet.jpg", goud: "/textures/proinn/proinn-metaalkleur-goud.jpg", beige: "/textures/proinn/proinn-metaalkleur-beige.jpg", ral: "/textures/proinn/proinn-metaalkleur-ral-keuze.jpg", } as Record)[finish] || "/textures/proinn/proinn-metaalkleur-zwart.jpg"; const texture = useTexture(texturePath); texture.wrapS = texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(0.2, 1); texture.colorSpace = THREE.SRGBColorSpace; return ( ); } catch { return ; } } function HandleMaterialFallback({ color }: { color: string }) { return ( ); } /** Wrap textured material in Suspense */ function PowderCoatMaterial({ color, finish }: { color: string; finish: string }) { return ( }> ); } function getColor(finish: string): string { return ({ zwart: "#1a1a1a", brons: "#8B6F47", grijs: "#525252", goud: "#B8860B", beige: "#C8B88A", ral: "#4A6741", } as Record)[finish] || "#1a1a1a"; } // ============================================ // SHARED MOUNT COMPONENT // ============================================ /** * A single cylindrical standoff (pootje) connecting handle to door face. * Rotated 90° on X to point outward from the door surface. */ function MountStandoff({ position, color, finish, }: { position: [number, number, number]; color: string; finish: string; }) { return ( ); } // ============================================ // HANDLE COMPONENTS // ============================================ /** * U-Greep: Proper U-shaped bar handle with two standoff mounts. * The grip sits 40mm off the door face, connected by two cylindrical pootjes. * Mounted on the right stile (vertical frame profile). */ export function UGreep({ finish, doorWidth, doorHeight, stileWidth }: HandleProps) { const color = getColor(finish); const gripLength = Math.min(doorHeight * 0.25, 0.6); // Max 60cm, proportional const mountSpacing = gripLength - GRIP_BAR_SIZE; // Distance between mount centers const xPos = doorWidth / 2 - stileWidth / 2; // Center of right stile return ( {/* Top mount standoff */} {/* Bottom mount standoff */} {/* Vertical grip bar */} ); } /** * Beugelgreep: Vertical bar handle (round) with mounting blocks. * Two rectangular mounting blocks press against the door face, * with a round bar connecting them. * Mounted on the right stile (vertical frame profile). */ export function Beugelgreep({ finish, doorWidth, doorHeight, stileWidth }: HandleProps) { const color = getColor(finish); const gripLength = Math.min(doorHeight * 0.35, 0.8); // Max 80cm const barDiameter = 0.025; // 25mm const mountBlockSize: [number, number, number] = [0.04, 0.05, MOUNT_LENGTH]; const mountSpacing = gripLength * 0.85; const xPos = doorWidth / 2 - stileWidth / 2; // Center of right stile return ( {/* Top mounting block (sits on door face, extends outward) */} {/* Bottom mounting block */} {/* Main vertical round bar */} {/* Mounting screw details */} {[mountSpacing / 2, -mountSpacing / 2].map((y, i) => ( ))} ); } /** * Hoekgreep: L-shaped corner handle with standoff mounts. * Horizontal bar + vertical bar meeting at a rounded corner. */ export function Hoekgreep({ finish, doorWidth, stileWidth }: HandleProps) { const color = getColor(finish); const horizontalLength = 0.15; const verticalLength = 0.12; const barThickness = 0.02; const barWidth = 0.03; // Position on right stile center const xPos = doorWidth / 2 - stileWidth / 2; return ( {/* Top mount standoff */} {/* Bottom mount standoff */} {/* Horizontal bar */} {/* Vertical bar */} {/* Corner radius */} ); } /** * Maangreep: Crescent/moon shaped handle with standoff mounts. * Curved torus section mounted on two pootjes. */ export function Maangreep({ finish, doorWidth, stileWidth }: HandleProps) { const color = getColor(finish); const curveRadius = 0.08; // Position on right stile center const xPos = doorWidth / 2 - stileWidth / 2; return ( {/* Left mount standoff */} {/* Right mount standoff */} {/* Main curved handle body */} {/* Left end cap */} {/* Right end cap */} ); } /** * Ovaalgreep: Oval/elliptical pull handle with standoff mounts. * Extruded ellipse shape mounted on two pootjes. */ export function Ovaalgreep({ finish, doorWidth, stileWidth }: HandleProps) { const color = getColor(finish); // Position on right stile center const xPos = doorWidth / 2 - stileWidth / 2; const shape = new THREE.Shape(); const rx = 0.06; const ry = 0.03; for (let i = 0; i <= 64; i++) { const angle = (i / 64) * Math.PI * 2; const x = Math.cos(angle) * rx; const y = Math.sin(angle) * ry; if (i === 0) shape.moveTo(x, y); else shape.lineTo(x, y); } return ( {/* Top mount standoff */} {/* Bottom mount standoff */} {/* Oval handle */} ); } /** * Klink: Traditional lever handle with rosette, standoff-mounted. * Rosette plate against door, lever extending horizontally. */ export function Klink({ finish, doorWidth, stileWidth }: HandleProps) { const color = getColor(finish); const leverLength = 0.12; // Position on right stile center const xPos = doorWidth / 2 - stileWidth / 2; return ( {/* Mounting rosette (flat against door face) */} {/* Square spindle standoff (connects rosette to lever) */} {/* Lever handle */} {/* Lever end grip */} {/* Lock cylinder below */} ); }