import React, { useRef } from 'react'; import { TransformControls, Text } from '@react-three/drei'; import * as THREE from 'three'; import { useFrame } from '@react-three/fiber'; import { SimObject, ObjectType, AxisObject, CylinderObject, LedObject, SwitchObject, AxisData } from '../types'; interface ObjectProps { data: SimObject; isSelected?: boolean; isPlaying?: boolean; onSelect?: (id: string) => void; onUpdate?: (id: string, updates: Partial) => void; onInteract?: (id: string) => void; axes?: AxisData[]; } // -- Helper Material -- const selectedMaterial = new THREE.MeshBasicMaterial({ color: '#4ade80', wireframe: true, transparent: true, opacity: 0.5 }); // -- Axis Visualizer Component -- const AxisVisualizer = ({ object, isPlaying, axes, isSelected }: { object: AxisObject, isPlaying: boolean, axes?: AxisData[], isSelected?: boolean }) => { const carriageRef = useRef(null); const groupRef = useRef(null); // Resolve current value from Global Axis Channel const axisValue = axes && object.axisIndex !== undefined && axes[object.axisIndex] ? axes[object.axisIndex].value : 0; // Fallback to 0 if axes not provided or index invalid useFrame(() => { if (object.type === ObjectType.AXIS_LINEAR && carriageRef.current) { const railLength = 5; const normalizedPos = ((axisValue - object.min) / (object.max - object.min)) * railLength; const safePos = isNaN(normalizedPos) ? 0 : Math.max(0, Math.min(railLength, normalizedPos)); carriageRef.current.position.x = safePos; } else if (object.type === ObjectType.AXIS_ROTARY && groupRef.current) { const rotationAngle = (axisValue % 360) * (Math.PI / 180); // The inner group for rotary axis is rotated, not the carriage directly const innerRotaryGroup = groupRef.current.children.find(child => child.name === 'rotary-inner-group'); if (innerRotaryGroup) { innerRotaryGroup.rotation.y = -rotationAngle; } } }); if (object.type === ObjectType.AXIS_LINEAR) { const railLength = 5; const normalizedPos = ((axisValue - object.min) / (object.max - object.min)) * railLength; const safePos = isNaN(normalizedPos) ? 0 : Math.max(0, Math.min(railLength, normalizedPos)); return ( {object.name} ({axisValue.toFixed(1)}) {isSelected && !isPlaying && ( )} ); } else if (object.type === ObjectType.AXIS_ROTARY) { const rotationAngle = (axisValue % 360) * (Math.PI / 180); return ( {object.name} ({axisValue.toFixed(0)}°) {isSelected && !isPlaying && ( )} ); } return null; }; // -- Cylinder Visualizer Component -- const CylinderVisualizer = ({ object, isPlaying, isSelected }: { object: CylinderObject, isPlaying: boolean, isSelected?: boolean }) => { const housingLen = 2; const extension = Math.min(object.stroke, Math.max(0, object.currentPosition)); return ( {object.name} {isSelected && !isPlaying && ( )} ); }; // -- Switch Component -- export const Switch: React.FC = ({ data, isSelected, isPlaying, onSelect, onInteract }) => { const sw = data as SwitchObject; return ( { e.stopPropagation(); if (!isPlaying) onSelect?.(sw.id); if (onInteract) onInteract(sw.id); }} > {sw.name} {isSelected && !isPlaying && ( )} ); }; // -- LED Component -- export const Led: React.FC = ({ data, isSelected, isPlaying, onSelect }) => { const led = data as LedObject; return ( { e.stopPropagation(); if (!isPlaying) onSelect?.(led.id); }} > {led.name} {isSelected && !isPlaying && ( )} ); }; export const EditableObject: React.FC = (props) => { const { data, enableTransform, isPlaying, onUpdate, snap, axes } = props; const handleTransformChange = (e: any) => { if (e.target.object) { const o = e.target.object; onUpdate?.(data.id, { position: { x: o.position.x, y: o.position.y, z: o.position.z }, rotation: { x: o.rotation.x, y: o.rotation.y, z: o.rotation.z } }); } }; let ComponentToRender; switch (data.type) { case ObjectType.AXIS_LINEAR: case ObjectType.AXIS_ROTARY: ComponentToRender = ; break; case ObjectType.CYLINDER: ComponentToRender = ; break; case ObjectType.SWITCH: ComponentToRender = ; break; case ObjectType.LED: ComponentToRender = ; break; default: ComponentToRender = null; } return ( { e.stopPropagation(); if (!isPlaying) props.onSelect?.(data.id); }} > {ComponentToRender} {props.isSelected && enableTransform && !isPlaying && ( )} ); };