diff --git a/components/configurator/handles-3d.tsx b/components/configurator/handles-3d.tsx
index 784ee65..11d650a 100644
--- a/components/configurator/handles-3d.tsx
+++ b/components/configurator/handles-3d.tsx
@@ -1,9 +1,25 @@
"use client";
-import { RoundedBox } from "@react-three/drei";
+import { Suspense } from "react";
+import { RoundedBox, useTexture } from "@react-three/drei";
import * as THREE from "three";
-interface HandleProps {
+// ============================================
+// 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;
@@ -11,66 +27,190 @@ interface HandleProps {
stileWidth: number;
}
-// Steel material for handles
-const HandleMaterial = ({ color }: { color: string }) => (
-
-);
+// ============================================
+// MATERIALS
+// ============================================
/**
- * Beugelgreep - Vertical bar handle with mounting blocks
- * Classic industrial style, common on steel pivot doors
+ * Powder-coated steel material matching door frame finish.
+ * Loaded with texture for visual continuity with the frame.
*/
-export function Beugelgreep({ finish, doorHeight, railDepth }: HandleProps) {
- const color = ({
- zwart: "#1a1a1a",
- brons: "#8B6F47",
- grijs: "#525252",
- }[finish] || "#1a1a1a") as string;
+function HandleMaterialTextured({ color, finish }: { color: string; finish: string }) {
+ try {
+ const texturePath = {
+ zwart: "/textures/aluwdoors/aluwdoors-configurator-metaalkleur-zwart.jpg",
+ brons: "/textures/aluwdoors/aluwdoors-configurator-metaalkleur-brons.jpg",
+ grijs: "/textures/aluwdoors/aluwdoors-configurator-metaalkleur-antraciet.jpg",
+ }[finish] || "/textures/aluwdoors/aluwdoors-configurator-metaalkleur-zwart.jpg";
- const handleLength = Math.min(doorHeight * 0.35, 0.8); // Max 80cm
- const barDiameter = 0.025; // 25mm diameter bar
- const mountBlockSize: [number, number, number] = [0.04, 0.06, 0.03]; // Mount block dimensions
+ 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" }[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.
+ */
+export function UGreep({ finish, doorHeight }: 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
return (
-
- {/* Main vertical bar */}
-
-
-
-
+
+ {/* Top mount standoff */}
+
- {/* Top mounting block */}
+ {/* 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.
+ */
+export function Beugelgreep({ finish, doorHeight }: 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;
+
+ return (
+
+ {/* Top mounting block (sits on door face, extends outward) */}
-
+
{/* Bottom mounting block */}
-
+
- {/* Mounting screws (detail) */}
- {[
- [0, handleLength / 2 + mountBlockSize[1] / 2, 0.005],
- [0, -handleLength / 2 - mountBlockSize[1] / 2, 0.005],
- ].map((pos, i) => (
-
-
+ {/* Main vertical round bar */}
+
+
+
+
+
+ {/* Mounting screw details */}
+ {[mountSpacing / 2, -mountSpacing / 2].map((y, i) => (
+
+
))}
@@ -79,32 +219,44 @@ export function Beugelgreep({ finish, doorHeight, railDepth }: HandleProps) {
}
/**
- * Hoekgreep - L-shaped corner handle
- * Minimalist flush-mount design
+ * Hoekgreep: L-shaped corner handle with standoff mounts.
+ * Horizontal bar + vertical bar meeting at a rounded corner.
*/
-export function Hoekgreep({ finish, doorWidth, railDepth, stileWidth }: HandleProps) {
- const color = ({
- zwart: "#1a1a1a",
- brons: "#8B6F47",
- grijs: "#525252",
- }[finish] || "#1a1a1a") as string;
+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;
- const horizontalLength = 0.15; // 15cm horizontal
- const verticalLength = 0.12; // 12cm vertical
- const barThickness = 0.02; // 20mm thick
- const barWidth = 0.03; // 30mm wide
+ // Position near right stile
+ const xPos = doorWidth / 2 - stileWidth - 0.12;
return (
-
+
+ {/* Top mount standoff */}
+
+
+ {/* Bottom mount standoff */}
+
+
{/* Horizontal bar */}
-
+
{/* Vertical bar */}
@@ -112,157 +264,173 @@ export function Hoekgreep({ finish, doorWidth, railDepth, stileWidth }: HandlePr
args={[barWidth, verticalLength, barThickness]}
radius={0.003}
smoothness={4}
- position={[0, 0, 0]}
+ position={[0, 0, GRIP_CENTER_Z]}
castShadow
>
-
+
- {/* Corner radius (decorative) */}
-
-
-
+ {/* Corner radius */}
+
+
+
);
}
/**
- * Maangreep - Crescent/moon shaped recessed handle
- * Elegant curved design for flush doors
+ * Maangreep: Crescent/moon shaped handle with standoff mounts.
+ * Curved torus section mounted on two pootjes.
*/
-export function Maangreep({ finish, doorWidth, railDepth, stileWidth }: HandleProps) {
- const color = ({
- zwart: "#1a1a1a",
- brons: "#8B6F47",
- grijs: "#525252",
- }[finish] || "#1a1a1a") as string;
-
- const curveRadius = 0.08; // 8cm radius
- const handleDepth = 0.025; // 25mm deep recess
+export function Maangreep({ finish, doorWidth, stileWidth }: HandleProps) {
+ const color = getColor(finish);
+ const curveRadius = 0.08;
+ const xPos = doorWidth / 2 - stileWidth - 0.12;
return (
-
+
+ {/* Left mount standoff */}
+
+
+ {/* Right mount standoff */}
+
+
{/* Main curved handle body */}
-
+
-
+
{/* Left end cap */}
-
+
-
+
{/* Right end cap */}
-
+
-
+
-
- {/* Recessed mounting plate */}
-
-
-
);
}
/**
- * Ovaalgreep - Oval/elliptical pull handle
- * Modern minimalist design
+ * Ovaalgreep: Oval/elliptical pull handle with standoff mounts.
+ * Extruded ellipse shape mounted on two pootjes.
*/
-export function Ovaalgreep({ finish, doorWidth, railDepth, stileWidth }: HandleProps) {
- const color = ({
- zwart: "#1a1a1a",
- brons: "#8B6F47",
- grijs: "#525252",
- }[finish] || "#1a1a1a") as string;
+export function Ovaalgreep({ finish, doorWidth, stileWidth }: HandleProps) {
+ const color = getColor(finish);
+ const xPos = doorWidth / 2 - stileWidth - 0.12;
- // Create oval shape using THREE.Shape
const shape = new THREE.Shape();
- const rx = 0.06; // 6cm horizontal radius
- const ry = 0.03; // 3cm vertical radius
-
- // Draw ellipse
+ 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);
- }
+ if (i === 0) shape.moveTo(x, y);
+ else shape.lineTo(x, y);
}
- const extrudeSettings = {
- depth: 0.02,
- bevelEnabled: true,
- bevelThickness: 0.003,
- bevelSize: 0.003,
- bevelSegments: 8,
- };
-
return (
-
- {/* Oval handle ring */}
-
-
-
+
+ {/* Top mount standoff */}
+
+
+ {/* Bottom mount standoff */}
+
+
+ {/* Oval handle */}
+
+
+
);
}
/**
- * Klink - Traditional door handle with lever
- * Classic hinged door handle
+ * Klink: Traditional lever handle with rosette, standoff-mounted.
+ * Rosette plate against door, lever extending horizontally.
*/
-export function Klink({ finish, doorWidth, railDepth, stileWidth }: HandleProps) {
- const color = ({
- zwart: "#1a1a1a",
- brons: "#8B6F47",
- grijs: "#525252",
- }[finish] || "#1a1a1a") as string;
-
- const leverLength = 0.12; // 12cm lever
- const leverThickness = 0.015; // 15mm thick
+export function Klink({ finish, doorWidth, stileWidth }: HandleProps) {
+ const color = getColor(finish);
+ const leverLength = 0.12;
+ const xPos = doorWidth / 2 - stileWidth - 0.1;
return (
-
- {/* Mounting rosette (round plate) */}
-
+
+ {/* Mounting rosette (flat against door face) */}
+
-
+
+
+
+ {/* Square spindle standoff (connects rosette to lever) */}
+
+
+
{/* Lever handle */}
-
+
- {/* Lever end (ergonomic grip) */}
-
+ {/* Lever end grip */}
+
-
+
- {/* Lock cylinder (detail) */}
-
+ {/* Lock cylinder below */}
+
);
}
-
-/**
- * U-Greep - U-shaped bar handle (current simple version)
- * Straight vertical bar for pivot doors
- */
-export function UGreep({ finish, railDepth }: HandleProps) {
- const color = ({
- zwart: "#1a1a1a",
- brons: "#8B6F47",
- grijs: "#525252",
- }[finish] || "#1a1a1a") as string;
-
- return (
-
-
-
- );
-}