"use client" import { Canvas } from '@react-three/fiber' import { Leva, useControls } from 'leva' import { StrictMode, useLayoutEffect, useMemo, useRef } from 'react' import { Billboard, Box, OrbitControls, Text } from '@react-three/drei' import * as THREE from 'three' export default function App() { return ( <StrictMode> <Leva flat /> <Canvas camera={{ fov: 100, near: 0.1, far: 200, position: [- 50, 50, 50] }}> <Experience /> </Canvas> </StrictMode> ) } export function Experience() { const m = 1 / 3.28; const { B, H, a, D } = useControls('Geometry', { B: { value: 40, min: 1 }, H: { value: 12, min: 0 }, a: { value: 15, min: 0, max: 75 }, D: { value: 50, min: 0 } }) const Hextra = B / 2 * Math.tan(a * Math.PI / 180) const Hpeak = H + Hextra const path = [[B, 0], [B, H], [B / 2, Hpeak], [0, H], [0, 0]] const position = [-B / 2, 0, -D / 2] const L = 10; const vL = L * Math.cos(a * Math.PI / 180) const hL = L * Math.sin(a * Math.PI / 180) const leastDim = Math.min(B, D) const z = Math.max(Math.min(0.1 * leastDim, 0.4 * H), 0.04 * leastDim, 1 * m) const y = Math.max(6 * m, 2 * z) const defaultText = { color: 'black', fontSize: 2, anchorY: 'middle' } return <> <ambientLight intensity={0.5} /> <pointLight position={[10, 10, 10]} /> <OrbitControls /> <Extrusion start={[0, 0]} paths={path} position={position} depth={D} bevelEnabled={false} /> <Arrow start={[-B / 2 - L, H / 2, 0]} end={[-B / 2, H / 2, 0]} text={{ ...defaultText, content: '1' }} /> <Arrow start={[-B / 2 - L, H / 2, D / 2 - y / 2]} end={[-B / 2, H / 2, D / 2 - y / 2]} text={{ ...defaultText, content: '1E' }} /> <Arrow start={[-B / 4 - hL, H + Hextra / 2 + vL, 0]} end={[-B / 4, H + Hextra / 2, 0]} text={{ ...defaultText, content: '2' }} /> <Arrow start={[-B / 4 - hL, H + Hextra / 2 + vL, D / 2 - y / 2]} end={[-B / 4, H + Hextra / 2, D / 2 - y / 2]} text={{ ...defaultText, content: '2E' }} /> <Box args={[0.25, H, y]} position={[-B / 2, H / 2, D / 2 - y / 2]} material-color={"hotpink"} material-transparent={true} material-opacity={0.5} /> <Arrow start={[B / 4 + hL, H + Hextra / 2 + vL, 0]} end={[B / 4, H + Hextra / 2, 0]} text={{ ...defaultText, content: '3' }} /> <Arrow start={[B / 4 + hL, H + Hextra / 2 + vL, D / 2 - y / 2]} end={[B / 4, H + Hextra / 2, D / 2 - y / 2]} text={{ ...defaultText, content: '3E' }} /> <Arrow start={[B / 2 + L, H / 2, 0]} end={[B / 2, H / 2, 0]} text={{ ...defaultText, content: '4' }} /> <Arrow start={[B / 2 + L, H / 2, D / 2 - y / 2]} end={[B / 2, H / 2, D / 2 - y / 2]} text={{ ...defaultText, content: '4E' }} /> <Arrow start={[0, H / 2, D / 2 + L]} end={[0, H / 2, D / 2]} text={{ ...defaultText, content: '5' }} /> <Arrow start={[-B / 2 + z / 2, H / 2, D / 2 + L]} end={[-B / 2 + z / 2, H / 2, D / 2]} text={{ ...defaultText, content: '5E' }} /> <Arrow start={[0, H / 2, -1 * (D / 2 + L)]} end={[0, H / 2, -D / 2]} text={{ ...defaultText, content: '6' }} /> <Arrow start={[-B / 2 + z / 2, H / 2, -1 * (D / 2 + L)]} end={[-B / 2 + z / 2, H / 2, -D / 2]} text={{ ...defaultText, content: '6E' }} /> </> } export function PressureBox({ normal, size }) { return ( <Box args={[0.25, H, y]} position={[-B / 2, H / 2, D / 2 - y / 2]}> <meshNormalMaterial /> </Box> ) } export function Extrusion({ start = [0, 0], paths, position = [0, 0, 0], ...props }) { const shape = useMemo(() => { const shape = new THREE.Shape() shape.moveTo(...start) paths.forEach(path => shape.lineTo(...path)) return shape }, [start, paths]) return ( <mesh position={position}> <extrudeGeometry args={[shape, props]} /> <meshStandardMaterial /> </mesh> ) } export function Arrow({ start = [0, 0, 0], end = [20, 0, 0], color = 'black', thickness = 0.15, text = { content: 'Hello World', color: 'black', fontSize: 2, fontWeight: 'normal', fontStyle: 'normal', anchorY: 'bottom' }, ...props }) { const length = Math.sqrt((start[0] - end[0]) ** 2 + (start[1] - end[1]) ** 2 + (start[2] - end[2]) ** 2) const headLength = 10 * thickness const headWidth = 3 * thickness const end3 = useMemo(() => new THREE.Vector3(...end), [end]) const refgroup = useRef() useLayoutEffect(() => { refgroup.current.lookAt(end3) }, [end3]) return ( <group ref={refgroup} position={start} {...props}> <mesh position={[0, 0, (length - headLength) / 2]} scale={[thickness, length - headLength, thickness]} rotation-x={Math.PI / 2}> <cylinderGeometry args={[1, 1, 1, 12]} /> <meshBasicMaterial color={color} /> </mesh> <mesh position={[0, 0, length - headLength / 2]} scale={[headWidth, headLength, headWidth]} rotation-x={Math.PI / 2}> <coneGeometry args={[1, 1, 12]} /> <meshBasicMaterial color={color} /> </mesh> {text && <Billboard> <Text position={[0.5, 0, 0]} anchorX='left' {...text}>{text.content}</Text> </Billboard>} </group> ) }
Comments (0)
Loading comments...