/** * Door Manufacturing Specifications * Based on "Metalworks" market analysis * All dimensions in millimeters (mm) */ // ============================================ // MANUFACTURING CONSTANTS // ============================================ export const PROFILE_WIDTH = 40; // mm - Face width export const PROFILE_DEPTH = 40; // mm - Tube depth export const PROFILE_CORNER_RADIUS = 2; // mm - Rounded corners for welding export const STILE_WIDTH = 40; // mm - Vertical profiles export const RAIL_WIDTH = 20; // mm - Horizontal slim-line profiles export const GLASS_THICKNESS = 7; // mm - Standard 33.1 Safety Glass export const GLASS_OFFSET = 15; // mm - Center glass in 40mm profile export const RAIL_HEIGHT_SLIM = 20; // mm - Slim horizontal rails export const RAIL_HEIGHT_ROBUST = 40; // mm - Standard robust rails export const TAATS_PIVOT_OFFSET = 60; // mm export const STELRUIMTE = 10; // mm export const HANGNAAD = 3; // mm export const WALL_THICKNESS = 150; // mm export function calculateMountingDimensions(sparingsmaatWidth: number, sparingsmaatHeight: number) { const frameOuterWidth = sparingsmaatWidth - STELRUIMTE; const frameOuterHeight = sparingsmaatHeight - STELRUIMTE / 2; const doorLeafWidth = frameOuterWidth - (2 * HANGNAAD); const doorLeafHeight = frameOuterHeight - (2 * HANGNAAD); return { sparingsmaatWidth, sparingsmaatHeight, frameOuterWidth, frameOuterHeight, doorLeafWidth, doorLeafHeight, stelruimtePerSide: STELRUIMTE / 2, hangnaadPerSide: HANGNAAD, }; } // ============================================ // PHYSICAL PART TYPES // ============================================ export type PartType = 'stile' | 'rail' | 'glass' | 'divider'; export type DoorModel = 'taats' | 'scharnier' | 'paneel'; export type GridLayout = | 'geen' | '2-vlak' | '3-vlak' | '4-vlak' | '6-vlak' | '8-vlak' | 'kruis' | 'ongelijk-3' | 'boerderij' | 'herenhuis'; export interface PhysicalPart { type: PartType; x: number; y: number; z: number; width: number; height: number; depth: number; label?: string; isGlass?: boolean; } export interface DoorAssembly { modelId: DoorModel; gridLayout: GridLayout; doorWidth: number; doorHeight: number; parts: PhysicalPart[]; } // ============================================ // GRID PATTERN DEFINITIONS (DATA-DRIVEN) // ============================================ /** * Grid pattern definition as data. * horizontalPositions: fractional Y positions (0 = bottom, 1 = top) for horizontal dividers * verticalPositions: fractional X positions (0 = left, 1 = right) for vertical dividers */ interface GridPatternDef { horizontalPositions: number[]; verticalPositions: number[]; } const GRID_PATTERNS: Record = { 'geen': { horizontalPositions: [], verticalPositions: [] }, '2-vlak': { horizontalPositions: [0.5], verticalPositions: [] }, '3-vlak': { horizontalPositions: [1 / 3, 2 / 3], verticalPositions: [] }, '4-vlak': { horizontalPositions: [0.25, 0.5, 0.75], verticalPositions: [] }, '6-vlak': { horizontalPositions: [1 / 3, 2 / 3], verticalPositions: [0.5] }, '8-vlak': { horizontalPositions: [0.25, 0.5, 0.75], verticalPositions: [0.5] }, 'kruis': { horizontalPositions: [0.5], verticalPositions: [0.5] }, 'ongelijk-3': { horizontalPositions: [0.35, 0.65], verticalPositions: [] }, 'boerderij': { horizontalPositions: [0.7], verticalPositions: [0.5] }, 'herenhuis': { horizontalPositions: [0.3, 0.7], verticalPositions: [] }, }; // ============================================ // LAYOUT GENERATION // ============================================ export function generateDoorAssembly( modelId: DoorModel, gridLayout: GridLayout, doorWidth: number, doorHeight: number ): DoorAssembly { const parts: PhysicalPart[] = []; const pattern = GRID_PATTERNS[gridLayout] || GRID_PATTERNS['geen']; // ============================================ // PERIMETER FRAME // ============================================ // LEFT STILE parts.push({ type: 'stile', x: -doorWidth / 2 + PROFILE_WIDTH / 2, y: 0, z: 0, width: PROFILE_WIDTH, height: doorHeight, depth: PROFILE_DEPTH, label: 'Left Stile', }); // RIGHT STILE parts.push({ type: 'stile', x: doorWidth / 2 - PROFILE_WIDTH / 2, y: 0, z: 0, width: PROFILE_WIDTH, height: doorHeight, depth: PROFILE_DEPTH, label: 'Right Stile', }); // TOP RAIL const topRailWidth = doorWidth - PROFILE_WIDTH * 2; parts.push({ type: 'rail', x: 0, y: doorHeight / 2 - RAIL_HEIGHT_ROBUST / 2, z: 0, width: topRailWidth, height: RAIL_HEIGHT_ROBUST, depth: PROFILE_DEPTH, label: 'Top Rail', }); // BOTTOM RAIL parts.push({ type: 'rail', x: 0, y: -doorHeight / 2 + RAIL_HEIGHT_ROBUST / 2, z: 0, width: topRailWidth, height: RAIL_HEIGHT_ROBUST, depth: PROFILE_DEPTH, label: 'Bottom Rail', }); // ============================================ // HORIZONTAL DIVIDERS (from pattern data) // ============================================ const innerHeight = doorHeight - RAIL_HEIGHT_ROBUST * 2; const innerBottom = -doorHeight / 2 + RAIL_HEIGHT_ROBUST; for (const fraction of pattern.horizontalPositions) { const dividerY = innerBottom + innerHeight * fraction; // Determine width: if there are vertical dividers, horizontal dividers span full width // (vertical dividers will be handled separately) parts.push({ type: 'divider', x: 0, y: dividerY, z: 0, width: topRailWidth, height: RAIL_HEIGHT_SLIM, depth: PROFILE_DEPTH, label: `H-Divider ${Math.round(fraction * 100)}%`, }); } // ============================================ // VERTICAL DIVIDERS (from pattern data) // ============================================ const innerWidth = doorWidth - PROFILE_WIDTH * 2; const innerLeft = -doorWidth / 2 + PROFILE_WIDTH; for (const fraction of pattern.verticalPositions) { const dividerX = innerLeft + innerWidth * fraction; const verticalDividerHeight = innerHeight; parts.push({ type: 'divider', x: dividerX, y: 0, z: 0, width: PROFILE_WIDTH, height: verticalDividerHeight, depth: PROFILE_DEPTH, label: `V-Divider ${Math.round(fraction * 100)}%`, }); } // ============================================ // PANEEL TYPE CENTER VERTICAL DIVIDER // ============================================ if (modelId === 'paneel' && !pattern.verticalPositions.includes(0.5)) { const verticalDividerHeight = doorHeight - RAIL_HEIGHT_ROBUST * 2; parts.push({ type: 'divider', x: 0, y: 0, z: 0, width: PROFILE_WIDTH, height: verticalDividerHeight, depth: PROFILE_DEPTH, label: 'Center Vertical Divider', }); } // ============================================ // GLASS PANELS // ============================================ const glassWidth = doorWidth - PROFILE_WIDTH * 2 - GLASS_OFFSET * 2; const glassHeight = doorHeight - RAIL_HEIGHT_ROBUST * 2 - GLASS_OFFSET * 2; parts.push({ type: 'glass', x: 0, y: 0, z: 0, width: glassWidth, height: glassHeight, depth: GLASS_THICKNESS, label: 'Main Glass Panel', isGlass: true, }); return { modelId, gridLayout, doorWidth, doorHeight, parts, }; } export function mmToMeters(mm: number): number { return mm / 1000; } export function getDividerPositions( gridLayout: GridLayout, doorHeight: number ): number[] { const pattern = GRID_PATTERNS[gridLayout]; if (!pattern) return []; const doorHeightMeters = mmToMeters(doorHeight); const innerHeight = doorHeightMeters - mmToMeters(RAIL_HEIGHT_ROBUST * 2); const innerBottom = -doorHeightMeters / 2 + mmToMeters(RAIL_HEIGHT_ROBUST); return pattern.horizontalPositions.map( (fraction) => innerBottom + innerHeight * fraction ); } export function validateDoorDimensions( doorWidth: number, doorHeight: number ): { valid: boolean; errors: string[] } { const errors: string[] = []; if (doorWidth < PROFILE_WIDTH * 3) { errors.push(`Door width too small (min: ${PROFILE_WIDTH * 3}mm)`); } if (doorHeight < RAIL_HEIGHT_ROBUST * 3) { errors.push(`Door height too small (min: ${RAIL_HEIGHT_ROBUST * 3}mm)`); } if (doorWidth > 1200) { errors.push('Door width exceeds maximum (1200mm) - structural integrity'); } if (doorHeight > 3000) { errors.push('Door height exceeds maximum (3000mm) - structural integrity'); } return { valid: errors.length === 0, errors, }; }