import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
import { useRef, useState, useEffect } from 'react'
import {
Canvas,
extend,
useFrame,
useThree,
useUpdate,
} from 'react-three-fiber'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { Vector3 } from 'three'
import { requestRender } from 'src/helpers/hooks/useIdeState'
import texture from './dullFrontLitMetal.png'
import { TextureLoader } from 'three/src/loaders/TextureLoader'
const loader = new TextureLoader
const colorMap = loader.load(texture)
extend({ OrbitControls })
function Asset({ geometry: incomingGeo }) {
const mesh = useRef()
const ref = useUpdate((geometry) => {
geometry.attributes = incomingGeo.attributes
})
if (!incomingGeo) return null
return (
)
}
let debounceTimeoutId
function Controls({ onCameraChange, onDragStart }) {
const controls = useRef()
const { camera, gl } = useThree()
useEffect(() => {
// init camera position
camera.position.x = 200
camera.position.y = 140
camera.position.z = 20
camera.far = 10000
camera.fov = 22.5 // matches default openscad fov
// Order matters with Euler rotations
// We want it to rotate around the z or vertical axis first then the x axis to match openscad
// in Three.js Y is the vertical axis (Z for openscad)
camera.rotation._order = 'YXZ'
const getRotations = () => {
const { x, y, z } = camera.rotation
const rad2Deg = 180 / Math.PI
const scadX = (x + Math.PI / 2) * rad2Deg
const scadZ = y * rad2Deg
const scadY = z * rad2Deg
return [scadX, scadY, scadZ]
}
const getPositions = () => {
// Difficult to make this clean since I'm not sure why it works
// The OpenSCAD camera seems hard to work with but maybe it's just me
// this gives us a vector the same length as the camera.position
const cameraViewVector = new Vector3(0, 0, 1)
.applyQuaternion(camera.quaternion) // make unit vector of the camera
.multiplyScalar(camera.position.length()) // make it the same length as the position vector
// make a vector from the position vector to the cameraView vector
const head2Head = new Vector3().subVectors(
camera.position,
cameraViewVector
)
return {
// I can't seem to get normal vector addition to work
// but this works
position: {
x: camera.position.x + head2Head.x,
y: -camera.position.z - head2Head.z,
z: camera.position.y + head2Head.y,
},
dist: camera.position.length(),
}
}
if (controls.current) {
const dragCallback = () => {
clearTimeout(debounceTimeoutId)
debounceTimeoutId = setTimeout(() => {
const [x, y, z] = getRotations()
const { position, dist } = getPositions()
onCameraChange({
position,
rotation: { x, y, z },
dist,
})
}, 400)
}
const dragStart = () => {
onDragStart()
clearTimeout(debounceTimeoutId)
}
controls.current.addEventListener('end', dragCallback)
controls.current.addEventListener('start', dragStart)
const oldCurrent = controls.current
dragCallback()
return () => {
oldCurrent.removeEventListener('end', dragCallback)
oldCurrent.removeEventListener('start', dragStart)
}
}
}, [])
useFrame(() => controls.current?.update())
return (
)
}
function Box(props) {
// This reference will give us direct access to the mesh
const mesh = useRef()
return (
)
}
function Sphere(props) {
const mesh = useRef()
return (
)
}
const IdeViewer = ({ Loading }) => {
const { state, thunkDispatch } = useIdeContext()
const [isDragging, setIsDragging] = useState(false)
const [image, setImage] = useState()
useEffect(() => {
setImage(state.objectData?.type === 'png' && state.objectData?.data)
setIsDragging(false)
}, [state.objectData?.type, state.objectData?.data])
// the following are tailwind colors in hex, can't use these classes to color three.js meshes.
const pink400 = '#F472B6'
const indigo300 = '#A5B4FC'
const indigo900 = '#312E81'
return (
{state.isLoading &&
}
{image && (
)}
setIsDragging(true)}
>
{state.isLoading && (
)}
)
}
export default IdeViewer