"use client" import { Canvas, useFrame } from '@react-three/fiber' import { Sphere, Line } from '@react-three/drei' import * as THREE from 'three' import { Leva, useControls, button } from 'leva' import { StrictMode, useRef, useEffect, memo } from 'react' // Import our new modular architecture import { useDrawingState, useSnapToGrid, useCoordinateTransform, useCursorIndicator, useKeyboardEvents } from './hooks.js' import { DrawableObject } from './DrawableObjects.jsx' import { ToolPreview } from './ToolPreview.jsx' export default function App() { return ( <StrictMode> <Leva flat /> <Canvas orthographic camera={{ zoom: 50, near: 0.1, far: 200, position: [0, 0, 15] }}> <Experience /> </Canvas> </StrictMode> ) } export function Experience() { // Use our custom hooks for state management const { drawnObjects, currentTool, isDrawing, drawingState, cursorPosition, handleToolChange, handleCanvasClick, handleMouseMove, handleKeyPress, handleObjectDelete, clearAll, toolOptions, toolSpecificData } = useDrawingState() const { snapEnabled, snapValue, setSnapEnabled, setSnapValue } = useSnapToGrid(0.5) const { transformPoint } = useCoordinateTransform() // Leva controls const { tool, snap, snapVal, showLengths } = useControls('Drawing Tools (Refactored)', { tool: { value: 'line', options: toolOptions, onChange: handleToolChange }, snap: { value: true, onChange: setSnapEnabled }, snapVal: { value: 0.5, min: 0.1, max: 2, step: 0.1, onChange: setSnapValue }, showLengths: false, clear: button(clearAll) }) // Set up keyboard event handling useKeyboardEvents(handleKeyPress) return ( <> <directionalLight position={[1, 2, 3]} intensity={1.5} /> <ambientLight intensity={0.5} /> <DrawingCanvas onCanvasClick={handleCanvasClick} onMouseMove={handleMouseMove} snapEnabled={snapEnabled} snapValue={snapValue} transformPoint={transformPoint} /> {/* Render completed objects */} {drawnObjects.map(obj => ( <DrawableObject key={obj.id} object={obj} isDeleteMode={currentTool === 'delete'} showLengths={showLengths} onDelete={() => handleObjectDelete(obj.id)} /> ))} {/* Render current drawing preview */} {isDrawing && drawingState && ( <ToolPreview tool={currentTool} state={drawingState} cursorPosition={cursorPosition} showLengths={showLengths} onShapeComplete={() => handleKeyPress('Escape')} /> )} <GridHelper snapValue={snapValue} visible={snapEnabled} /> </> ) } // Drawing canvas component export function DrawingCanvas({ onCanvasClick, onMouseMove, snapEnabled, snapValue, transformPoint }) { const { cursorRef, showCursor, hideCursor } = useCursorIndicator() const planeRef = useRef() return ( <mesh ref={planeRef} position={[0, 0, 0]} onPointerMove={(e) => { if (!planeRef.current) return const localPoint = planeRef.current.worldToLocal(e.point.clone()) const transformedPoint = transformPoint(localPoint, snapEnabled, snapValue) showCursor(transformedPoint) onMouseMove(transformedPoint, snapEnabled, snapValue) }} onPointerOut={hideCursor} onClick={(e) => { if (!planeRef.current) return const localPoint = planeRef.current.worldToLocal(e.point.clone()) const transformedPoint = transformPoint(localPoint, snapEnabled, snapValue) onCanvasClick(transformedPoint, snapEnabled, snapValue) }} > <planeGeometry args={[20, 20]} /> <meshBasicMaterial transparent opacity={0.1} color="lightblue" /> <CursorIndicator ref={cursorRef} /> </mesh> ) } // Cursor indicator component export const CursorIndicator = ({ color = "orange", size = 0.1, ...props }) => { const ref = useRef() useFrame(() => { if (ref.current) { ref.current.rotation.x += 0.01 ref.current.rotation.y += 0.01 } }) return ( <mesh ref={ref} raycast={() => null} visible={false} {...props}> <sphereGeometry args={[size]} /> <meshBasicMaterial color={color} /> </mesh> ) } // Grid helper component with React.memo to prevent unnecessary re-renders export const GridHelper = memo(function GridHelper({ snapValue, visible }) { if (!visible) return null const lines = [] const size = 20 const divisions = size / snapValue for (let i = -divisions; i <= divisions; i++) { const pos = i * snapValue lines.push( <Line key={`h${i}`} points={[[-size/2, pos, 0], [size/2, pos, 0]]} color="gray" lineWidth={0.5} />, <Line key={`v${i}`} points={[[pos, -size/2, 0], [pos, size/2, 0]]} color="gray" lineWidth={0.5} /> ) } return <group>{lines}</group> })
Comments (0)
Loading comments...