Fix camera perspective and other polish for openscad viewer #238
@@ -17,7 +17,7 @@ module.exports.runScad = async ({
|
|||||||
const { x: rx, y: ry, z: rz } = rotation
|
const { x: rx, y: ry, z: rz } = rotation
|
||||||
const { x: px, y: py, z: pz } = position
|
const { x: px, y: py, z: pz } = position
|
||||||
const cameraArg = `--camera=${px},${py},${pz},${rx},${ry},${rz},300`
|
const cameraArg = `--camera=${px},${py},${pz},${rx},${ry},${rz},300`
|
||||||
const command = `xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" openscad -o /tmp/${tempFile}/output.png ${cameraArg} --imgsize=${x},${y} /tmp/${tempFile}/main.scad`
|
const command = `xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" openscad -o /tmp/${tempFile}/output.png ${cameraArg} --imgsize=${x},${y} --colorscheme DeepOcean /tmp/${tempFile}/main.scad`
|
||||||
console.log('command', command)
|
console.log('command', command)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,49 +1,64 @@
|
|||||||
import { IdeContext } from 'src/components/IdeToolbarNew'
|
import { IdeContext } from 'src/components/IdeToolbarNew'
|
||||||
import { useRef, useState, useEffect, useContext, useMemo } from 'react'
|
import { useRef, useState, useEffect, useContext } from 'react'
|
||||||
import { Canvas, extend, useFrame, useThree } from 'react-three-fiber'
|
import { Canvas, extend, useFrame, useThree } from 'react-three-fiber'
|
||||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||||
|
|
||||||
extend({ OrbitControls })
|
extend({ OrbitControls })
|
||||||
|
|
||||||
function Controls({ onCameraChange }) {
|
let debounceTimeoutId
|
||||||
|
function Controls({ onCameraChange, onDragStart }) {
|
||||||
const controls = useRef()
|
const controls = useRef()
|
||||||
const { scene, camera, gl } = useThree()
|
const { scene, camera, gl } = useThree()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// init camera position
|
// init camera position
|
||||||
camera.position.x = 12
|
camera.position.x = 16
|
||||||
camera.position.y = 12
|
camera.position.y = 12
|
||||||
// Euler rotation can be problematic since order matters
|
camera.fov = 22.5 // matches default openscad fov
|
||||||
// We want it to rotate around the z or vertical axis first then the x axis
|
|
||||||
// in Three Y is the vertical axis (Z for openscad)
|
// 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'
|
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 = () => {
|
||||||
|
const { x: scadX, y: scadZ, z: negScadY } = camera.position
|
||||||
|
const scale = 10
|
||||||
|
const scadY = -negScadY
|
||||||
|
return [scadX * scale, scadY * scale, scadZ * scale]
|
||||||
|
}
|
||||||
|
|
||||||
if (controls.current) {
|
if (controls.current) {
|
||||||
const callback = ({ target }) => {
|
const dragCallback = () => {
|
||||||
const getRotations = () => {
|
clearTimeout(debounceTimeoutId)
|
||||||
const { x, y, z } = target.object.rotation
|
debounceTimeoutId = setTimeout(() => {
|
||||||
const rad2Deg = 180 / Math.PI
|
const [rx, ry, rz] = getRotations()
|
||||||
const scadX = (x + Math.PI / 2) * rad2Deg
|
const [x, y, z] = getPositions()
|
||||||
const scadZ = y * rad2Deg
|
|
||||||
const scadY = z * rad2Deg
|
|
||||||
return [scadX, scadY, scadZ]
|
|
||||||
}
|
|
||||||
const getPositions = () => {
|
|
||||||
const { x: scadX, y: scadZ, z: negScadY } = target.object.position
|
|
||||||
const scale = 10 // I'm not sure why this is exactly 10
|
|
||||||
const scadY = -negScadY
|
|
||||||
return [scadX * scale, scadY * scale, scadZ * scale]
|
|
||||||
}
|
|
||||||
const [rx, ry, rz] = getRotations()
|
|
||||||
const [x, y, z] = getPositions()
|
|
||||||
|
|
||||||
onCameraChange({
|
onCameraChange({
|
||||||
position: { x, y, z },
|
position: { x, y, z },
|
||||||
rotation: { x: rx, y: ry, z: rz },
|
rotation: { x: rx, y: ry, z: rz },
|
||||||
})
|
})
|
||||||
|
}, 400)
|
||||||
}
|
}
|
||||||
controls.current.addEventListener('end', callback)
|
const dragStart = () => {
|
||||||
|
onDragStart()
|
||||||
|
clearTimeout(debounceTimeoutId)
|
||||||
|
}
|
||||||
|
controls.current.addEventListener('end', dragCallback)
|
||||||
|
controls.current.addEventListener('start', dragStart)
|
||||||
const oldCurrent = controls.current
|
const oldCurrent = controls.current
|
||||||
return () => oldCurrent.removeEventListener('end', callback)
|
dragCallback()
|
||||||
|
return () => {
|
||||||
|
oldCurrent.removeEventListener('end', dragCallback)
|
||||||
|
oldCurrent.removeEventListener('start', dragStart)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -52,8 +67,6 @@ function Controls({ onCameraChange }) {
|
|||||||
<orbitControls
|
<orbitControls
|
||||||
ref={controls}
|
ref={controls}
|
||||||
args={[camera, gl.domElement]}
|
args={[camera, gl.domElement]}
|
||||||
enableDamping
|
|
||||||
dampingFactor={0.1}
|
|
||||||
rotateSpeed={0.5}
|
rotateSpeed={0.5}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -65,7 +78,7 @@ function Box(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<mesh {...props} ref={mesh} scale={[1, 1, 1]}>
|
<mesh {...props} ref={mesh} scale={[1, 1, 1]}>
|
||||||
<boxBufferGeometry args={[props.size, props.size, props.size]} />
|
<boxBufferGeometry args={props.size} />
|
||||||
<meshStandardMaterial color={props.color} />
|
<meshStandardMaterial color={props.color} />
|
||||||
</mesh>
|
</mesh>
|
||||||
)
|
)
|
||||||
@@ -74,43 +87,43 @@ let currentCode // I have no idea why this works and using state.code is the dis
|
|||||||
const IdeViewer = () => {
|
const IdeViewer = () => {
|
||||||
const { state, dispatch } = useContext(IdeContext)
|
const { state, dispatch } = useContext(IdeContext)
|
||||||
const [isDragging, setIsDragging] = useState(false)
|
const [isDragging, setIsDragging] = useState(false)
|
||||||
|
const [image, setImage] = useState()
|
||||||
|
|
||||||
const image = useMemo(
|
useEffect(() => {
|
||||||
() =>
|
setImage(
|
||||||
state.objectData?.type === 'png' &&
|
state.objectData?.type === 'png' &&
|
||||||
state.objectData?.data &&
|
state.objectData?.data &&
|
||||||
window.URL.createObjectURL(state.objectData?.data),
|
window.URL.createObjectURL(state.objectData?.data)
|
||||||
[state.objectData]
|
)
|
||||||
)
|
setIsDragging(false)
|
||||||
|
}, [state.objectData])
|
||||||
currentCode = state.code
|
currentCode = state.code
|
||||||
return (
|
return (
|
||||||
<div className="p-8 border-2 m-2">
|
<div className="p-8 border-2 m-2">
|
||||||
<div className="pb-4">
|
|
||||||
Rotating the Three.js Cube should than update the openscad camera to
|
|
||||||
view the CAD object from the same angle. But having trouble translating
|
|
||||||
between the two coordinate systems. OpenScad's camera only changes X and
|
|
||||||
Z angles and Y is always 0. Where as rotation values from Three seem to
|
|
||||||
change all x, y and z. I probably need to learn a bit more about 3d
|
|
||||||
math.
|
|
||||||
</div>
|
|
||||||
<div className="relative" style={{ height: '500px', width: '500px' }}>
|
<div className="relative" style={{ height: '500px', width: '500px' }}>
|
||||||
|
{state.isLoading && (
|
||||||
|
<div className="inset-0 absolute flex items-center justify-center">
|
||||||
|
<div className="h-16 w-16 bg-pink-600 rounded-full animate-ping"></div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{image && (
|
{image && (
|
||||||
<div
|
<div
|
||||||
className="absolute inset-0"
|
className={`absolute inset-0 transition-opacity duration-500 ${
|
||||||
style={{ opacity: isDragging ? '0%' : '100%' }}
|
isDragging ? 'opacity-25' : 'opacity-100'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<img src={image} className="" />
|
<img src={image} className="" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className={`opacity-0 absolute inset-0 ${
|
className={`opacity-0 absolute inset-0 transition-opacity duration-500 ${
|
||||||
isDragging ? 'opacity-100' : 'hover:opacity-50'
|
isDragging ? 'opacity-100' : 'hover:opacity-50'
|
||||||
}`}
|
}`}
|
||||||
onMouseDown={() => setIsDragging(true)}
|
onMouseDown={() => setIsDragging(true)}
|
||||||
onMouseUp={() => setIsDragging(false)}
|
|
||||||
>
|
>
|
||||||
<Canvas>
|
<Canvas>
|
||||||
<Controls
|
<Controls
|
||||||
|
onDragStart={() => setIsDragging(true)}
|
||||||
onCameraChange={({ position, rotation }) => {
|
onCameraChange={({ position, rotation }) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'render',
|
type: 'render',
|
||||||
@@ -126,10 +139,17 @@ const IdeViewer = () => {
|
|||||||
/>
|
/>
|
||||||
<ambientLight />
|
<ambientLight />
|
||||||
<pointLight position={[15, 5, 10]} />
|
<pointLight position={[15, 5, 10]} />
|
||||||
<Box position={[0, 0, 0]} size={10} color="orange" />
|
<Box position={[0, 4.95, 0]} size={[0.1, 10, 0.1]} color="cyan" />
|
||||||
<Box position={[0, 5, 0]} size={1} color="cyan" />
|
<Box
|
||||||
<Box position={[0, 0, 5]} size={1} color="magenta" />
|
position={[0, 0, -5.05]}
|
||||||
<Box position={[5, 0, 0]} size={1} color="hotpink" />
|
size={[0.1, 0.1, 10]}
|
||||||
|
color="orange"
|
||||||
|
/>
|
||||||
|
<Box
|
||||||
|
position={[5.05, 0, 0]}
|
||||||
|
size={[10, 0.1, 0.1]}
|
||||||
|
color="hotpink"
|
||||||
|
/>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,11 +4,14 @@ import { cadPackages } from 'src/helpers/cadPackages'
|
|||||||
export const useIdeState = () => {
|
export const useIdeState = () => {
|
||||||
const initialState = {
|
const initialState = {
|
||||||
ideType: 'openScad',
|
ideType: 'openScad',
|
||||||
consoleMessages: [
|
consoleMessages: [{ type: 'message', message: 'Initialising OpenSCAD' }],
|
||||||
{ type: 'error', message: 'line 15 is being very naughty' },
|
code: `difference(){
|
||||||
{ type: 'message', message: '5 bodies produced' },
|
union(){
|
||||||
],
|
cube(60);
|
||||||
code: 'cube(60);sphere(25);',
|
sphere(25);
|
||||||
|
}
|
||||||
|
translate([30,30,30])cylinder(r=25,h=100);
|
||||||
|
}`,
|
||||||
objectData: {
|
objectData: {
|
||||||
type: 'stl',
|
type: 'stl',
|
||||||
data: 'some binary',
|
data: 'some binary',
|
||||||
@@ -23,6 +26,7 @@ export const useIdeState = () => {
|
|||||||
splitPercentage: 70,
|
splitPercentage: 70,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
isLoading: false,
|
||||||
}
|
}
|
||||||
const reducer = (state, { type, payload }) => {
|
const reducer = (state, { type, payload }) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -38,6 +42,7 @@ export const useIdeState = () => {
|
|||||||
consoleMessages: payload.message
|
consoleMessages: payload.message
|
||||||
? [...state.consoleMessages, payload.message]
|
? [...state.consoleMessages, payload.message]
|
||||||
: payload.message,
|
: payload.message,
|
||||||
|
isLoading: false,
|
||||||
}
|
}
|
||||||
case 'errorRender':
|
case 'errorRender':
|
||||||
return {
|
return {
|
||||||
@@ -45,6 +50,7 @@ export const useIdeState = () => {
|
|||||||
consoleMessages: payload.message
|
consoleMessages: payload.message
|
||||||
? [...state.consoleMessages, payload.message]
|
? [...state.consoleMessages, payload.message]
|
||||||
: payload.message,
|
: payload.message,
|
||||||
|
isLoading: false,
|
||||||
}
|
}
|
||||||
case 'setIdeType':
|
case 'setIdeType':
|
||||||
return {
|
return {
|
||||||
@@ -56,6 +62,11 @@ export const useIdeState = () => {
|
|||||||
...state,
|
...state,
|
||||||
layout: payload.message,
|
layout: payload.message,
|
||||||
}
|
}
|
||||||
|
case 'setLoading':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isLoading: true,
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
@@ -83,6 +94,7 @@ export const useIdeState = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
dispatch({ type: 'setLoading' })
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user