"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...