Add viewall flag to openscad cli in prep for zoom to fit for scad previews

This commit is contained in:
Kurt Hutten
2021-10-17 10:15:33 +11:00
parent cd90c3ce49
commit 35d55074bd
8 changed files with 115 additions and 26 deletions

View File

@@ -11,6 +11,7 @@ const cleanOpenScadError = (error) =>
export const runScad = async ({ export const runScad = async ({
file, file,
settings: { settings: {
viewAll = false,
size: { x = 500, y = 500 } = {}, size: { x = 500, y = 500 } = {},
parameters, parameters,
camera: { camera: {
@@ -44,10 +45,13 @@ export const runScad = async ({
const fullPath = `/tmp/${tempFile}/output.gz` const fullPath = `/tmp/${tempFile}/output.gz`
const imPath = `/tmp/${tempFile}/output.png` const imPath = `/tmp/${tempFile}/output.png`
const customizerPath = `/tmp/${tempFile}/customizer.param` const customizerPath = `/tmp/${tempFile}/customizer.param`
const summaryPath = `/tmp/${tempFile}/summary.json` // contains camera info
const command = [ const command = [
OPENSCAD_COMMON, OPENSCAD_COMMON,
`-o ${customizerPath}`, `-o ${customizerPath}`,
`-o ${imPath}`, `-o ${imPath}`,
`--summary camera --summary-file ${summaryPath}`,
viewAll ? '--viewall' : '',
`-p /tmp/${tempFile}/params.json -P default`, `-p /tmp/${tempFile}/params.json -P default`,
cameraArg, cameraArg,
`--imgsize=${x},${y}`, `--imgsize=${x},${y}`,
@@ -58,14 +62,20 @@ export const runScad = async ({
try { try {
const consoleMessage = await runCommand(command, 15000) const consoleMessage = await runCommand(command, 15000)
const params = JSON.parse( const files: string[] = await Promise.all(
await readFile(customizerPath, { encoding: 'ascii' }) [customizerPath, summaryPath].map((path) =>
).parameters readFile(path, { encoding: 'ascii' })
)
)
const [params, cameraInfo] = files.map((fileStr: string) =>
JSON.parse(fileStr)
)
await writeFiles( await writeFiles(
[ [
{ {
file: JSON.stringify({ file: JSON.stringify({
customizerParams: params, cameraInfo: viewAll ? cameraInfo.camera : undefined,
customizerParams: params.parameters,
consoleMessage, consoleMessage,
type: 'png', type: 'png',
}), }),

View File

@@ -5,7 +5,7 @@ import { RedwoodProvider } from '@redwoodjs/web'
import FatalErrorBoundary from 'src/components/FatalErrorBoundary/FatalErrorBoundary' import FatalErrorBoundary from 'src/components/FatalErrorBoundary/FatalErrorBoundary'
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo' import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'
import FatalErrorPage from 'src/pages/FatalErrorPage' import FatalErrorPage from 'src/pages/FatalErrorPage'
import { createMuiTheme } from '@material-ui/core/styles' import { createTheme } from '@material-ui/core/styles'
import { ThemeProvider } from '@material-ui/styles' import { ThemeProvider } from '@material-ui/styles'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
@@ -22,7 +22,7 @@ const goTrueClient = new GoTrue({
setCookie: true, setCookie: true,
}) })
const theme = createMuiTheme({ const theme = createTheme({
palette: { palette: {
type: 'dark', type: 'dark',
primary: { primary: {

View File

@@ -16,6 +16,7 @@ import texture from './dullFrontLitMetal.png'
import Customizer from 'src/components/Customizer/Customizer' import Customizer from 'src/components/Customizer/Customizer'
import DelayedPingAnimation from 'src/components/DelayedPingAnimation/DelayedPingAnimation' import DelayedPingAnimation from 'src/components/DelayedPingAnimation/DelayedPingAnimation'
import type { ArtifactTypes } from 'src/helpers/cadPackages/common' import type { ArtifactTypes } from 'src/helpers/cadPackages/common'
import { State } from 'src/helpers/hooks/useIdeState'
const thresholdAngle = 12 const thresholdAngle = 12
@@ -24,9 +25,9 @@ function Asset({
dataType, dataType,
controlsRef, controlsRef,
}: { }: {
geometry: any geometry: any // eslint-disable-line @typescript-eslint/no-explicit-any
dataType: 'INIT' | ArtifactTypes dataType: 'INIT' | ArtifactTypes
controlsRef: React.MutableRefObject<any> controlsRef: React.MutableRefObject<any> // eslint-disable-line @typescript-eslint/no-explicit-any
}) { }) {
const threeInstance = useThree() const threeInstance = useThree()
const [initZoom, setInitZoom] = useState(true) const [initZoom, setInitZoom] = useState(true)
@@ -75,7 +76,7 @@ function Asset({
zoomToFit() zoomToFit()
setInitZoom(false) setInitZoom(false)
} }
}, [incomingGeo, dataType]) }, [incomingGeo, dataType, controlsRef, initZoom, threeInstance])
const PrimitiveArray = React.useMemo( const PrimitiveArray = React.useMemo(
() => () =>
dataType === 'primitive-array' && dataType === 'primitive-array' &&
@@ -112,15 +113,29 @@ function Asset({
} }
let debounceTimeoutId let debounceTimeoutId
function Controls({ onCameraChange, onDragStart, onInit, controlsRef }) { function Controls({
onCameraChange,
onDragStart,
onInit,
controlsRef,
camera: scadCamera,
}: {
onCameraChange: Function
onDragStart: (a: any) => void
onInit: Function
controlsRef: React.MutableRefObject<any>
camera: State['camera']
}) {
const threeInstance = useThree() const threeInstance = useThree()
const { camera, gl } = threeInstance const { camera, gl } = threeInstance
useEffect(() => { useEffect(() => {
// setup three to openscad camera sync
onInit(threeInstance) onInit(threeInstance)
// init camera position // init camera position
camera.position.x = 200 camera.position.x = 80
camera.position.y = 140 camera.position.y = 50
camera.position.z = 20 camera.position.z = 50
camera.far = 10000 camera.far = 10000
camera.fov = 22.5 // matches default openscad fov camera.fov = 22.5 // matches default openscad fov
camera.updateProjectionMatrix() camera.updateProjectionMatrix()
@@ -146,8 +161,8 @@ function Controls({ onCameraChange, onDragStart, onInit, controlsRef }) {
) )
const { x, y, z } = head2Head.add(camera.position) const { x, y, z } = head2Head.add(camera.position)
return { return {
position: { x, y, z }, position: { x: x / 2, y: y / 2, z: z / 2 },
dist: camera.position.length(), dist: camera.position.length() / 2,
} }
} }
@@ -180,6 +195,34 @@ function Controls({ onCameraChange, onDragStart, onInit, controlsRef }) {
} }
}, [camera, controlsRef]) }, [camera, controlsRef])
useEffect(() => {
if (!scadCamera?.isScadUpdate || !scadCamera?.position) {
return
}
// sync Three camera to OpenSCAD
const { x, y, z } = scadCamera.position || {}
const scadCameraPos = new Vector3(x * 2, y * 2, z * 2)
const cameraViewVector = new Vector3(0, 0, 1)
const { x: rx, y: ry, z: rz } = scadCamera.rotation || {}
const scadCameraEuler = new THREE.Euler(
...[rx, ry, rz].map((r) => (r * Math.PI) / 180),
'YZX'
) // I don't know why it seems to like 'YZX' order
cameraViewVector.applyEuler(scadCameraEuler)
cameraViewVector.multiplyScalar(scadCamera.dist * 2)
const scadToThreeCameraPosition = new Vector3().subVectors(
// I have no idea why this works
cameraViewVector.clone().add(scadCameraPos),
cameraViewVector
)
scadToThreeCameraPosition.multiplyScalar(
scadCamera.dist / scadToThreeCameraPosition.length()
)
camera.position.copy(scadToThreeCameraPosition.clone())
camera.updateProjectionMatrix()
}, [scadCamera, camera])
return ( return (
<OrbitControls <OrbitControls
makeDefault makeDefault
@@ -218,6 +261,7 @@ export function PureIdeViewer({
isLoading, isLoading,
isMinimal = false, isMinimal = false,
scadRatio = 1, scadRatio = 1,
camera,
}: { }: {
dataType: 'INIT' | ArtifactTypes dataType: 'INIT' | ArtifactTypes
artifact: any artifact: any
@@ -226,6 +270,7 @@ export function PureIdeViewer({
onCameraChange: Function onCameraChange: Function
isMinimal?: boolean isMinimal?: boolean
scadRatio?: number scadRatio?: number
camera?: State['camera']
}) { }) {
const [isDragging, setIsDragging] = useState(false) const [isDragging, setIsDragging] = useState(false)
const [image, setImage] = useState() const [image, setImage] = useState()
@@ -276,6 +321,7 @@ export function PureIdeViewer({
onInit={onInit} onInit={onInit}
onCameraChange={onCameraChange} onCameraChange={onCameraChange}
controlsRef={controlsRef} controlsRef={controlsRef}
camera={camera}
/> />
<PerspectiveCamera makeDefault up={[0, 0, 1]}> <PerspectiveCamera makeDefault up={[0, 0, 1]}>
<pointLight <pointLight
@@ -347,7 +393,6 @@ const IdeViewer = ({
} }
const onCameraChange = (camera) => { const onCameraChange = (camera) => {
if (handleOwnCamera) { if (handleOwnCamera) {
console.log('yo')
return return
} }
thunkDispatch({ thunkDispatch({
@@ -362,6 +407,7 @@ const IdeViewer = ({
state, state,
dispatch, dispatch,
camera, camera,
viewAll: state?.objectData?.type === 'INIT',
}) })
} }
}) })
@@ -374,6 +420,7 @@ const IdeViewer = ({
onInit={onInit} onInit={onInit}
onCameraChange={onCameraChange} onCameraChange={onCameraChange}
isLoading={state.isLoading} isLoading={state.isLoading}
camera={state?.camera}
/> />
) )
} }

View File

@@ -1,10 +1,11 @@
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader' import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'
import { State } from 'src/helpers/hooks/useIdeState' import { State } from 'src/helpers/hooks/useIdeState'
import { CadhubParams } from 'src/components/Customizer/customizerConverter' import { CadhubParams } from 'src/components/Customizer/customizerConverter'
import type { Camera } from 'src/helpers/hooks/useIdeState'
export const lambdaBaseURL = export const lambdaBaseURL =
process.env.CAD_LAMBDA_BASE_URL || process.env.CAD_LAMBDA_BASE_URL ||
'https://2inlbple1b.execute-api.us-east-1.amazonaws.com/prod2' 'https://oxt2p7ddgj.execute-api.us-east-1.amazonaws.com/prod'
export const stlToGeometry = (url) => export const stlToGeometry = (url) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
@@ -18,6 +19,7 @@ export interface RenderArgs {
camera: State['camera'] camera: State['camera']
viewerSize: State['viewerSize'] viewerSize: State['viewerSize']
quality: State['objectData']['quality'] quality: State['objectData']['quality']
viewAll: boolean
} }
} }
@@ -36,6 +38,7 @@ export interface HealthyResponse {
} }
customizerParams?: any[] customizerParams?: any[]
currentParameters?: RawCustomizerParams currentParameters?: RawCustomizerParams
camera?: Camera
} }
export interface RawCustomizerParams { export interface RawCustomizerParams {
@@ -48,12 +51,14 @@ export function createHealthyResponse({
consoleMessage, consoleMessage,
type, type,
customizerParams, customizerParams,
camera,
}: { }: {
date: Date date: Date
data: any data: any
consoleMessage: string consoleMessage: string
type: HealthyResponse['objectData']['type'] type: HealthyResponse['objectData']['type']
customizerParams?: CadhubParams[] customizerParams?: CadhubParams[]
camera?: Camera
}): HealthyResponse { }): HealthyResponse {
return { return {
status: 'healthy', status: 'healthy',
@@ -66,6 +71,7 @@ export function createHealthyResponse({
message: consoleMessage, message: consoleMessage,
time: date, time: date,
}, },
camera,
customizerParams, customizerParams,
} }
} }

View File

@@ -99,7 +99,7 @@ class WorkerHelper {
} }
render = ( render = (
code: string, code: string,
parameters: { [key: string]: any } parameters: { [key: string]: any } // eslint-disable-line @typescript-eslint/no-explicit-any
): Promise<RenderResponse> => { ): Promise<RenderResponse> => {
const response: Promise<RenderResponse> = new Promise( const response: Promise<RenderResponse> = new Promise(
(resolve: ResolveFn) => { (resolve: ResolveFn) => {

View File

@@ -8,6 +8,7 @@ import {
splitGziped, splitGziped,
} from '../common' } from '../common'
import { openScadToCadhubParams } from './openScadParams' import { openScadToCadhubParams } from './openScadParams'
import type { XYZ, Camera } from 'src/helpers/hooks/useIdeState'
export const render = async ({ code, settings }: RenderArgs) => { export const render = async ({ code, settings }: RenderArgs) => {
const pixelRatio = window.devicePixelRatio || 1 const pixelRatio = window.devicePixelRatio || 1
@@ -19,6 +20,7 @@ export const render = async ({ code, settings }: RenderArgs) => {
const body = JSON.stringify({ const body = JSON.stringify({
settings: { settings: {
size, size,
viewAll: settings.viewAll,
parameters: settings.parameters, parameters: settings.parameters,
camera: { camera: {
// rounding to give our caching a chance to sometimes work // rounding to give our caching a chance to sometimes work
@@ -59,7 +61,21 @@ export const render = async ({ code, settings }: RenderArgs) => {
} }
const blob = await response.blob() const blob = await response.blob()
const text = await new Response(blob).text() const text = await new Response(blob).text()
const { consoleMessage, customizerParams, type } = splitGziped(text) const { consoleMessage, customizerParams, type, cameraInfo } =
splitGziped(text)
const vecArray2Obj = (arr: number[]): XYZ => ({
x: arr[0],
y: arr[1],
z: arr[2],
})
const camera: Camera = cameraInfo
? {
dist: cameraInfo?.distance,
position: vecArray2Obj(cameraInfo?.translation),
rotation: vecArray2Obj(cameraInfo?.rotation),
isScadUpdate: true,
}
: undefined
return createHealthyResponse({ return createHealthyResponse({
type: type !== 'stl' ? 'png' : 'geometry', type: type !== 'stl' ? 'png' : 'geometry',
data: data:
@@ -67,6 +83,7 @@ export const render = async ({ code, settings }: RenderArgs) => {
? blob ? blob
: await stlToGeometry(window.URL.createObjectURL(blob)), : await stlToGeometry(window.URL.createObjectURL(blob)),
consoleMessage, consoleMessage,
camera,
date: new Date(), date: new Date(),
customizerParams: openScadToCadhubParams(customizerParams || []), customizerParams: openScadToCadhubParams(customizerParams || []),
}) })

View File

@@ -18,12 +18,13 @@ export const use3dViewerResize = () => {
}) })
thunkDispatch((dispatch, getState) => { thunkDispatch((dispatch, getState) => {
const state = getState() const state = getState()
if (['png', 'INIT'].includes(state.objectData?.type)) { if (state.objectData?.type === 'png') {
dispatch({ type: 'setLoading' }) dispatch({ type: 'setLoading' })
requestRender({ requestRender({
state, state,
dispatch, dispatch,
viewerSize: { width, height }, viewerSize: { width, height },
viewAll: state.objectData?.type === 'INIT',
}) })
} }
}) })

View File

@@ -19,11 +19,17 @@ const codeStorageKey = 'Last-editor-code'
export const makeCodeStoreKey = (ideType) => `${codeStorageKey}-${ideType}` export const makeCodeStoreKey = (ideType) => `${codeStorageKey}-${ideType}`
let mutableState: State = null let mutableState: State = null
interface XYZ { export interface XYZ {
x: number x: number
y: number y: number
z: number z: number
} }
export interface Camera {
dist?: number
position?: XYZ
rotation?: XYZ
isScadUpdate?: boolean
}
export interface MosaicTree { export interface MosaicTree {
first: string | MosaicTree first: string | MosaicTree
@@ -69,11 +75,7 @@ export interface State {
currentParameters?: RawCustomizerParams currentParameters?: RawCustomizerParams
isCustomizerOpen: boolean isCustomizerOpen: boolean
layout: MosaicTree layout: MosaicTree
camera: { camera: Camera
dist?: number
position?: XYZ
rotation?: XYZ
}
viewerSize: { width: number; height: number } viewerSize: { width: number; height: number }
isLoading: boolean isLoading: boolean
threeInstance: RootState threeInstance: RootState
@@ -165,6 +167,7 @@ const reducer = (state: State, { type, payload }): State => {
? [...state.consoleMessages, payload.message] ? [...state.consoleMessages, payload.message]
: payload.message, : payload.message,
isLoading: false, isLoading: false,
camera: payload.camera || state.camera,
} }
} }
case 'errorRender': case 'errorRender':
@@ -308,6 +311,7 @@ export const useIdeState = (): [State, (actionOrThunk: any) => any] => {
interface RequestRenderArgsStateless { interface RequestRenderArgsStateless {
state: State state: State
camera?: State['camera'] camera?: State['camera']
viewAll?: boolean
viewerSize?: State['viewerSize'] viewerSize?: State['viewerSize']
quality?: State['objectData']['quality'] quality?: State['objectData']['quality']
specialCadProcess?: string specialCadProcess?: string
@@ -317,6 +321,7 @@ interface RequestRenderArgsStateless {
export const requestRenderStateless = ({ export const requestRenderStateless = ({
state, state,
camera, camera,
viewAll,
viewerSize, viewerSize,
quality = 'low', quality = 'low',
specialCadProcess = null, specialCadProcess = null,
@@ -339,6 +344,7 @@ export const requestRenderStateless = ({
parameters: state.isCustomizerOpen parameters: state.isCustomizerOpen
? parameters || state.currentParameters ? parameters || state.currentParameters
: {}, : {},
viewAll,
camera: camera || state.camera, camera: camera || state.camera,
viewerSize: viewerSize || state.viewerSize, viewerSize: viewerSize || state.viewerSize,
quality, quality,
@@ -363,6 +369,7 @@ export const requestRender = ({ dispatch, ...rest }: RequestRenderArgs) => {
status, status,
customizerParams, customizerParams,
currentParameters, currentParameters,
camera,
}) => { }) => {
if (status === 'error') { if (status === 'error') {
dispatch({ dispatch({
@@ -378,6 +385,7 @@ export const requestRender = ({ dispatch, ...rest }: RequestRenderArgs) => {
lastRunCode: code, lastRunCode: code,
customizerParams, customizerParams,
currentParameters, currentParameters,
camera,
}, },
}) })
return objectData return objectData