import React, { useRef } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls, Grid, OrthographicCamera, Text, Box, Environment, RoundedBox, Cylinder, Plane } from '@react-three/drei';
import * as THREE from 'three';
import { RobotTarget, IOPoint } from '../types';
interface Machine3DProps {
target: RobotTarget;
ioState: IOPoint[];
doorStates: {
front: boolean;
right: boolean;
left: boolean;
back: boolean;
};
}
// Reusable Aluminum Profile Component
const Profile = ({ position, size, rotation = [0, 0, 0] }: { position: [number, number, number], size: [number, number, number], rotation?: [number, number, number] }) => (
);
// Industrial Linear Actuator Rail (Black body + Silver guides)
const RobotRail = ({
length,
orientation = 'horizontal',
profile = [0.25, 0.25]
}: {
length: number,
orientation?: 'horizontal' | 'vertical',
profile?: [number, number]
}) => {
const isHoriz = orientation === 'horizontal';
const [dim1, dim2] = profile;
const size = isHoriz ? [length, dim1, dim2] : [dim1, length, dim2];
const railW = isHoriz ? length : dim1 * 0.4;
const railH = isHoriz ? dim1 * 0.4 : length;
const railD = 0.02;
const guideZ = dim2 / 2 + 0.005;
return (
);
};
// Detailed Industrial Camera Component
const IndustrialCamera = ({ position, rotation = [0, 0, 0], isActive = false }: { position: [number, number, number], rotation?: [number, number, number], isActive?: boolean }) => {
return (
MA-D200B
DC 12V
{isActive && (
)}
);
};
// SATO CL4NX Plus Printer Component
const SatoPrinter = ({ position, rotation = [0, 0, 0] }: { position: [number, number, number], rotation?: [number, number, number] }) => {
return (
SATO
CL4NX Plus
);
};
// Industrial Cart Component
const Cart = ({ position, label }: { position: [number, number, number], label?: string }) => {
return (
{Array.from({ length: 15 }).map((_, i) => (
))}
{[0, 120, 240].map((angle, idx) => {
const rad = 0.65;
const x = Math.cos(angle * Math.PI / 180) * rad;
const z = Math.sin(angle * Math.PI / 180) * rad;
return (
);
})}
{label && (
{label}
)}
);
};
// Glass Door Component
const Door = ({ position, size, isOpen, hingeDirection = 1 }: { position: [number, number, number], size: [number, number, number], isOpen: boolean, hingeDirection?: number }) => {
const meshRef = useRef(null);
useFrame(() => {
if (meshRef.current) {
const targetRotation = isOpen ? Math.PI / 2 * hingeDirection : 0;
meshRef.current.rotation.y = THREE.MathUtils.lerp(meshRef.current.rotation.y, targetRotation, 0.1);
}
});
return (
);
};
// Reusable Labeler Unit Component
const LabelerUnit = ({ side, posY, posZ }: { side: 'left' | 'right', posY: number, posZ: number }) => {
const isLeft = side === 'left';
const wallOffsetX = isLeft ? -1.4 : 1.4;
const printerOffset = isLeft ? 0.3 : -0.3;
return (
);
};
// Tower Lamp Component
const TowerLamp = ({ ioState }: { ioState: IOPoint[] }) => {
const redOn = ioState.find(io => io.id === 0 && io.type === 'output')?.state;
const yellowOn = ioState.find(io => io.id === 1 && io.type === 'output')?.state;
const greenOn = ioState.find(io => io.id === 2 && io.type === 'output')?.state;
const redMat = useRef(null);
const yelMat = useRef(null);
const grnMat = useRef(null);
useFrame((state) => {
const time = state.clock.elapsedTime;
const pulse = (Math.sin(time * 6) * 0.5 + 0.5);
const intensity = 0.5 + (pulse * 3.0);
if (redMat.current) {
redMat.current.emissiveIntensity = redOn ? intensity : 0;
redMat.current.opacity = redOn ? 1.0 : 0.3;
redMat.current.color.setHex(redOn ? 0xff0000 : 0x550000);
}
if (yelMat.current) {
yelMat.current.emissiveIntensity = yellowOn ? intensity : 0;
yelMat.current.opacity = yellowOn ? 1.0 : 0.3;
yelMat.current.color.setHex(yellowOn ? 0xffff00 : 0x555500);
}
if (grnMat.current) {
grnMat.current.emissiveIntensity = greenOn ? intensity : 0;
grnMat.current.opacity = greenOn ? 1.0 : 0.3;
grnMat.current.color.setHex(greenOn ? 0x00ff00 : 0x005500);
}
});
return (
);
};
// Main Machine Model
const MachineModel = ({ target, doorStates }: { target: RobotTarget, doorStates: Machine3DProps['doorStates'] }) => {
const FRAME_WIDTH = 10;
const TOTAL_HEIGHT = 6.5;
const DOCK_HEIGHT = 2.5;
const OPERATIONAL_HEIGHT = TOTAL_HEIGHT - DOCK_HEIGHT;
const FRAME_DEPTH = 3.0;
const CONVEYOR_Y = DOCK_HEIGHT + 0.2;
const LABELER_BASE_Y = CONVEYOR_Y;
const PICKER_Y = 4.8;
const minZLength = 0.6;
const zRailLength = Math.max(minZLength, target.z + 0.4);
const Z_XRAIL = -0.6;
const Z_CARRIAGE = -0.45;
const Z_ZRAIL = -0.3;
const Z_HEAD = 0;
// Convert target coordinates
const pickerX = target.x;
const pickerZ = target.z;
const leftLabelY = target.y;
const leftLabelZ = 0.5;
const rightLabelY = target.y;
const rightLabelZ = 0.5;
return (
{/* --- STRUCTURAL FRAME --- */}
{/* --- DOCKING AREA (LOWER LEVEL) --- */}
{[-3.5, 0, 3.5].map((xPos, idx) => (
{idx === 0 ? "SPARE L" : idx === 1 ? "LOADING" : "SPARE R"}
))}
{/* --- DOORS (UPPER LEVEL ONLY) --- */}
{/* --- TOP COVER (ROOF) --- */}
{/* --- CONVEYORS (Left & Right - UPPER LEVEL) --- */}
Left Conveyor
PLACE REEL
Right Conveyor
PLACE REEL
{/* --- MAIN PICKER (Robot Design) --- */}
{Array.from({ length: 8 }).map((_, i) => {
const angle = (i / 8) * Math.PI * 2;
const radius = 0.4;
const x = Math.cos(angle) * radius;
const z = Math.sin(angle) * radius;
return (
);
})}
{/* --- LABELER UNITS --- */}
{/* --- CAMERAS (Roof Mounted) --- */}
{/* --- REAR DISCHARGE CHUTES --- */}
{/* Grid Floor */}
);
};
export const Machine3D: React.FC = ({ target, ioState, doorStates }) => {
return (
);
};