CadQuery MVP integration #281
@@ -1,6 +1,7 @@
|
||||
import { useContext, useRef, useEffect } from 'react'
|
||||
import { Mosaic, MosaicWindow } from 'react-mosaic-component'
|
||||
import { IdeContext } from 'src/components/IdeToolbarNew'
|
||||
import { requestRender } from 'src/helpers/hooks/useIdeState'
|
||||
import IdeEditor from 'src/components/IdeEditor'
|
||||
import IdeViewer from 'src/components/IdeViewer'
|
||||
import IdeConsole from 'src/components/IdeConsole'
|
||||
@@ -13,7 +14,7 @@ const ELEMENT_MAP = {
|
||||
}
|
||||
|
||||
const IdeContainer = () => {
|
||||
const { state, dispatch } = useContext(IdeContext)
|
||||
const { state, thunkDispatch } = useContext(IdeContext)
|
||||
const viewerDOM = useRef(null)
|
||||
const debounceTimeoutId = useRef
|
||||
|
||||
@@ -22,12 +23,22 @@ const IdeContainer = () => {
|
||||
function handleViewerSizeUpdate() {
|
||||
if (viewerDOM !== null && viewerDOM.current) {
|
||||
const { width, height } = viewerDOM.current.getBoundingClientRect()
|
||||
dispatch({
|
||||
type: 'render',
|
||||
payload: {
|
||||
code: state.code,
|
||||
viewerSize: { width, height },
|
||||
},
|
||||
thunkDispatch({
|
||||
type: 'updateViewerSize',
|
||||
payload: { viewerSize: { width, height } },
|
||||
})
|
||||
thunkDispatch((dispatch, getState) => {
|
||||
const state = getState()
|
||||
if (state.ideType === 'openScad') {
|
||||
dispatch({ type: 'setLoading' })
|
||||
requestRender({
|
||||
state,
|
||||
dispatch,
|
||||
code: state.code,
|
||||
viewerSize: { width, height },
|
||||
camera: state.camera,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -49,20 +60,27 @@ const IdeContainer = () => {
|
||||
return (
|
||||
<div id="cadhub-ide" className="flex-auto h-full">
|
||||
<Mosaic
|
||||
renderTile={(id, path) => (
|
||||
<MosaicWindow path={path} title={id} className={id.toLowerCase()}>
|
||||
{id === 'Viewer' ? (
|
||||
<div id="view-wrapper" className="h-full" ref={viewerDOM}>
|
||||
{ELEMENT_MAP[id]}
|
||||
</div>
|
||||
) : (
|
||||
ELEMENT_MAP[id]
|
||||
)}
|
||||
</MosaicWindow>
|
||||
)}
|
||||
renderTile={(id, path) => {
|
||||
const title = id === 'Editor' ? `${id} (${state.ideType})` : id
|
||||
return (
|
||||
<MosaicWindow
|
||||
path={path}
|
||||
title={title}
|
||||
className={id.toLowerCase()}
|
||||
>
|
||||
{id === 'Viewer' ? (
|
||||
<div id="view-wrapper" className="h-full" ref={viewerDOM}>
|
||||
{ELEMENT_MAP[id]}
|
||||
</div>
|
||||
) : (
|
||||
ELEMENT_MAP[id]
|
||||
)}
|
||||
</MosaicWindow>
|
||||
)
|
||||
}}
|
||||
value={state.layout}
|
||||
onChange={(newLayout) =>
|
||||
dispatch({ type: 'setLayout', payload: { message: newLayout } })
|
||||
thunkDispatch({ type: 'setLayout', payload: { message: newLayout } })
|
||||
}
|
||||
onRelease={handleViewerSizeUpdate}
|
||||
/>
|
||||
|
||||
@@ -2,10 +2,15 @@ import { useContext, useEffect, Suspense, lazy } from 'react'
|
||||
import { isBrowser } from '@redwoodjs/prerender/browserUtils'
|
||||
import { IdeContext } from 'src/components/IdeToolbarNew'
|
||||
import { codeStorageKey } from 'src/helpers/hooks/useIdeState'
|
||||
import { requestRender } from 'src/helpers/hooks/useIdeState'
|
||||
const Editor = lazy(() => import('@monaco-editor/react'))
|
||||
|
||||
const IdeEditor = () => {
|
||||
const { state, dispatch } = useContext(IdeContext)
|
||||
const { state, thunkDispatch } = useContext(IdeContext)
|
||||
const ideTypeToLanguageMap = {
|
||||
cadQuery: 'python',
|
||||
openScad: 'cpp',
|
||||
}
|
||||
|
||||
const scriptKey = 'encoded_script'
|
||||
useEffect(() => {
|
||||
@@ -17,7 +22,7 @@ const IdeEditor = () => {
|
||||
const [key, scriptBase64] = hash.slice(1).split('=')
|
||||
if (key === scriptKey) {
|
||||
const script = atob(scriptBase64)
|
||||
dispatch({ type: 'updateCode', payload: script })
|
||||
thunkDispatch({ type: 'updateCode', payload: script })
|
||||
}
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
@@ -27,25 +32,38 @@ const IdeEditor = () => {
|
||||
}, [state.code])
|
||||
|
||||
function handleCodeChange(value, _event) {
|
||||
dispatch({ type: 'updateCode', payload: value })
|
||||
thunkDispatch({ type: 'updateCode', payload: value })
|
||||
}
|
||||
function handleSaveHotkey(event) {
|
||||
//ctrl|meta + s is very intuitive for most devs
|
||||
const { key, ctrlKey, metaKey } = event
|
||||
if (key === 's' && (ctrlKey || metaKey)) {
|
||||
event.preventDefault()
|
||||
dispatch({ type: 'render', payload: { code: state.code } })
|
||||
thunkDispatch((dispatch, getState) => {
|
||||
const state = getState()
|
||||
dispatch({ type: 'setLoading' })
|
||||
requestRender({
|
||||
state,
|
||||
dispatch,
|
||||
code: state.code,
|
||||
|
|
||||
viewerSize: state.viewerSize,
|
||||
camera: state.camera,
|
||||
})
|
||||
})
|
||||
localStorage.setItem(codeStorageKey, state.code)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full" onKeyDown={handleSaveHotkey}>
|
||||
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
|
||||
className="h-full"
|
||||
onKeyDown={handleSaveHotkey}
|
||||
>
|
||||
<Suspense fallback={<div>. . . loading</div>}>
|
||||
<Editor
|
||||
defaultValue={state.code}
|
||||
// TODO #247 cpp seems better than js for the time being
|
||||
defaultLanguage="cpp"
|
||||
defaultLanguage={ideTypeToLanguageMap[state.ideType] || 'cpp'}
|
||||
language={ideTypeToLanguageMap[state.ideType] || 'cpp'}
|
||||
onChange={handleCodeChange}
|
||||
/>
|
||||
</Suspense>
|
||||
|
I wanted this loading message to be light text on dark background to match roughly the theme of the editor, but I there must be very specific styles that force it to be white, I didn't bother digging into it too much. I wanted this loading message to be light text on dark background to match roughly the theme of the editor, but I there must be very specific styles that force it to be white, I didn't bother digging into it too much.
Is there somewhere we can gather up these low-priority frontend things for me to go through and clean up for you? I think that might be a good way for me to be useful. Is there somewhere we can gather up these low-priority frontend things for me to go through and clean up for you? I think that might be a good way for me to be useful.
Not real sure, might be worth having a chat about where to put what. my intuition says there's no problem with lots of little issues for things you want to get to. Not real sure, might be worth having a chat about where to put what. my intuition says there's no problem with lots of little issues for things you want to get to.
|
||||
|
||||
@@ -3,15 +3,26 @@ import IdeContainer from 'src/components/IdeContainer'
|
||||
import { isBrowser } from '@redwoodjs/prerender/browserUtils'
|
||||
import { useIdeState, codeStorageKey } from 'src/helpers/hooks/useIdeState'
|
||||
import { copyTextToClipboard } from 'src/helpers/clipboard'
|
||||
import { requestRender } from 'src/helpers/hooks/useIdeState'
|
||||
|
||||
export const IdeContext = createContext()
|
||||
const IdeToolbarNew = () => {
|
||||
const [state, dispatch] = useIdeState()
|
||||
const [state, thunkDispatch] = useIdeState()
|
||||
function setIdeType(ide) {
|
||||
dispatch({ type: 'setIdeType', payload: { message: ide } })
|
||||
thunkDispatch({ type: 'setIdeType', payload: { message: ide } })
|
||||
}
|
||||
function handleRender() {
|
||||
dispatch({ type: 'render', payload: { code: state.code } })
|
||||
thunkDispatch((dispatch, getState) => {
|
||||
const state = getState()
|
||||
dispatch({ type: 'setLoading' })
|
||||
requestRender({
|
||||
state,
|
||||
dispatch,
|
||||
code: state.code,
|
||||
viewerSize: state.viewerSize,
|
||||
camera: state.camera,
|
||||
})
|
||||
})
|
||||
localStorage.setItem(codeStorageKey, state.code)
|
||||
}
|
||||
function handleMakeLink() {
|
||||
@@ -23,21 +34,17 @@ const IdeToolbarNew = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<IdeContext.Provider value={{ state, dispatch }}>
|
||||
<IdeContext.Provider value={{ state, thunkDispatch: thunkDispatch }}>
|
||||
<div className="h-full flex flex-col">
|
||||
<nav className="flex">
|
||||
{/* <button
|
||||
onClick={() => setIdeType('openCascade')}
|
||||
<button
|
||||
onClick={() =>
|
||||
setIdeType(state.ideType === 'openScad' ? 'cadQuery' : 'openScad')
|
||||
}
|
||||
className="p-2 br-2 border-2 m-2 bg-blue-200"
|
||||
>
|
||||
Switch to OpenCascade
|
||||
Switch to {state.ideType === 'openScad' ? 'CadQuery' : 'OpenSCAD'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIdeType('openScad')}
|
||||
className="p-2 br-2 border-2 m-2 bg-indigo-200"
|
||||
>
|
||||
Switch to OpenSCAD
|
||||
</button> */}
|
||||
<button onClick={handleRender} className="p-2 br-2 border-2 m-2">
|
||||
Render
|
||||
</button>
|
||||
|
||||
@@ -1,11 +1,41 @@
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
import { IdeContext } from 'src/components/IdeToolbarNew'
|
||||
import { useRef, useState, useEffect, useContext } from 'react'
|
||||
import { Canvas, extend, useFrame, useThree } from 'react-three-fiber'
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
import {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
Canvas,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
extend,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
useFrame,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
useThree,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
useUpdate,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
} from 'react-three-fiber'
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
import { Vector3 } from 'three'
|
||||
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
import { requestRender } from 'src/helpers/hooks/useIdeState'
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
|
||||
extend({ OrbitControls })
|
||||
|
||||
function Asset({ url }) {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
const [loadedGeometry, setLoadedGeometry] = useState()
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
const mesh = useRef()
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
const ref = useUpdate((geometry) => {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
geometry.attributes = loadedGeometry.attributes
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
})
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
useEffect(() => {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
if (url) {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
const decoded = atob(url)
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
const loader = new STLLoader()
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
setLoadedGeometry(loader.parse(decoded))
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
}, [url])
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
if (!loadedGeometry) return null
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
return (
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<mesh ref={mesh} scale={[1, 1, 1]}>
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<bufferGeometry attach="geometry" ref={ref} />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<meshStandardMaterial color="#F472B6" />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
</mesh>
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
)
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
let debounceTimeoutId
|
||||
function Controls({ onCameraChange, onDragStart }) {
|
||||
const controls = useRef()
|
||||
@@ -85,7 +115,7 @@ function Controls({ onCameraChange, onDragStart }) {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
}
|
||||
}, [])
|
||||
|
||||
useFrame(() => controls.current.update())
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
useFrame(() => controls.current?.update())
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
return (
|
||||
<orbitControls
|
||||
ref={controls}
|
||||
@@ -115,9 +145,8 @@ function Sphere(props) {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
</mesh>
|
||||
)
|
||||
}
|
||||
let currentCode // I have no idea why this works and using state.code is the dispatch doesn't but it was always stale
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
const IdeViewer = () => {
|
||||
const { state, dispatch } = useContext(IdeContext)
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
const { state, thunkDispatch } = useContext(IdeContext)
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [image, setImage] = useState()
|
||||
|
||||
@@ -127,8 +156,7 @@ const IdeViewer = () => {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
'data:image/png;base64,' + state.objectData?.data
|
||||
)
|
||||
setIsDragging(false)
|
||||
}, [state.objectData])
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
currentCode = state.code
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
}, [state.objectData?.type, state.objectData?.data])
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
|
||||
const openSCADDeepOceanThemeBackground = '#323232'
|
||||
// the following are tailwind colors in hex, can't use these classes to color three.js meshes.
|
||||
@@ -151,41 +179,64 @@ const IdeViewer = () => {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
isDragging ? 'opacity-25' : 'opacity-100'
|
||||
}`}
|
||||
>
|
||||
<img src={image} className="h-full w-full" />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<img alt="code-cad preview" src={image} className="h-full w-full" />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
</div>
|
||||
)}
|
||||
{state.isLoading && (
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<div className="inset-0 absolute flex items-center justify-center">
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<div className="h-16 w-16 bg-pink-600 rounded-full animate-ping"></div>
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
</div>
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
)}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<div
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
className={`opacity-0 absolute inset-0 transition-opacity duration-500 ${
|
||||
isDragging ? 'opacity-100' : 'hover:opacity-50'
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
!(isDragging || state.ideType !== 'openScad')
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
? 'hover:opacity-50'
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
: 'opacity-100'
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
}`}
|
||||
onMouseDown={() => setIsDragging(true)}
|
||||
>
|
||||
<Canvas>
|
||||
<Controls
|
||||
onDragStart={() => setIsDragging(true)}
|
||||
onCameraChange={(camera) =>
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
dispatch({
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
type: 'render',
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
payload: {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
code: currentCode,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
camera,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
},
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
onCameraChange={(camera) => {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
thunkDispatch({
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
type: 'updateCamera',
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
payload: { camera },
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
})
|
||||
}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
thunkDispatch((dispatch, getState) => {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
const state = getState()
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
if (state.ideType === 'openScad') {
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
dispatch({ type: 'setLoading' })
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
requestRender({
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
state,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
dispatch,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
code: state.code,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
viewerSize: state.viewerSize,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
camera,
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
})
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
})
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
}}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
/>
|
||||
<ambientLight />
|
||||
<pointLight position={[15, 5, 10]} />
|
||||
<Sphere position={[0, 0, 0]} color={pink400} />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<Box position={[0, 50, 0]} size={[1, 100, 1]} color={indigo900} />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<Box position={[0, 0, -50]} size={[1, 1, 100]} color={indigo300} />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<Box position={[50, 0, 0]} size={[100, 1, 1]} color={pink400} />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
{state.ideType === 'openScad' && (
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<>
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<Sphere position={[0, 0, 0]} color={pink400} />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<Box position={[0, 50, 0]} size={[1, 100, 1]} color={indigo900} />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<Box
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
position={[0, 0, -50]}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
size={[1, 1, 100]}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
color={indigo300}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
/>
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<Box position={[50, 0, 0]} size={[100, 1, 1]} color={pink400} />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
</>
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
)}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
{state.ideType === 'cadQuery' && (
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<Asset url={state.objectData?.data} />
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
)}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
</Canvas>
|
||||
</div>
|
||||
{state.isLoading && (
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<div className="inset-0 absolute flex items-center justify-center">
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
<div className="h-16 w-16 bg-pink-600 rounded-full animate-ping"></div>
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
</div>
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
)}
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
This is where the stl is loaded in for CadQuery. This is where the stl is loaded in for CadQuery.
|
||||
68
web/src/helpers/cadPackages/cadQueryController.js
Normal file
@@ -0,0 +1,68 @@
|
||||
let openScadBaseURL = process.env.CADQUERY_BASE_URL
|
||||
|
||||
export const render = async ({ code }) => {
|
||||
const body = JSON.stringify({
|
||||
settings: {},
|
||||
file: code,
|
||||
})
|
||||
try {
|
||||
const response = await fetch(openScadBaseURL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body,
|
||||
})
|
||||
if (response.status === 400) {
|
||||
// TODO add proper error messages for CadQuery
|
||||
const { error } = await response.json()
|
||||
const cleanedErrorMessage = error.replace(
|
||||
/["|']\/tmp\/.+\/main.scad["|']/g,
|
||||
"'main.scad'"
|
||||
)
|
||||
return {
|
||||
status: 'error',
|
||||
message: {
|
||||
type: 'error',
|
||||
message: addDateToLog(cleanedErrorMessage),
|
||||
},
|
||||
}
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
status: 'healthy',
|
||||
objectData: {
|
||||
type: 'stl',
|
||||
data: data.imageBase64,
|
||||
},
|
||||
message: {
|
||||
type: 'message',
|
||||
message: addDateToLog(data.result),
|
||||
},
|
||||
}
|
||||
} catch (e) {
|
||||
// TODO handle errors better
|
||||
// I think we should display something overlayed on the viewer window something like "network issue try again"
|
||||
// and in future I think we need timeouts differently as they maybe from a user trying to render something too complex
|
||||
// or something with minkowski in it :/ either way something like "render timed out, try again or here are tips to reduce part complexity" with a link talking about $fn and minkowski etc
|
||||
return {
|
||||
status: 'error',
|
||||
message: {
|
||||
type: 'error',
|
||||
message: addDateToLog('network issue'),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const openScad = {
|
||||
render,
|
||||
// more functions to come
|
||||
}
|
||||
|
||||
export default openScad
|
||||
|
||||
function addDateToLog(message) {
|
||||
return `-> ${new Date().toLocaleString()}
|
||||
${message}`
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import openScad from './openScadController'
|
||||
import openCascade from './newCascadeController'
|
||||
import cadQuery from './cadQueryController'
|
||||
|
||||
export const cadPackages = {
|
||||
openScad,
|
||||
openCascade,
|
||||
}
|
||||
cadQuery,
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// Rename this file to remove "new" once Cascade integration is complete
|
||||
|
||||
export const render = async ({ code, settings }) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
const shouldReject = Math.random() < 0.7
|
||||
if (shouldReject) {
|
||||
resolve({
|
||||
objectData: {
|
||||
type: 'stl',
|
||||
data: ((Math.random() * 256 + 1) >>> 0).toString(2), // Randomized 8-bit numbers for funzies
|
||||
},
|
||||
message: {
|
||||
type: 'message',
|
||||
message: `bodies rendered by: ${code}`,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
reject({
|
||||
message: {
|
||||
type: 'error',
|
||||
message: 'unable to parse line: x',
|
||||
},
|
||||
})
|
||||
}
|
||||
}, 700)
|
||||
})
|
||||
}
|
||||
|
||||
const openCascade = {
|
||||
render,
|
||||
// More functions to come
|
||||
}
|
||||
|
||||
export default openCascade
|
||||
@@ -2,33 +2,16 @@ let openScadBaseURL =
|
||||
process.env.OPENSCAD_BASE_URL ||
|
||||
'https://x2wvhihk56.execute-api.us-east-1.amazonaws.com/dev'
|
||||
|
||||
let lastViewPortSize = 'INIT'
|
||||
let lastCameraSettings = 'INIT'
|
||||
|
||||
export const render = async ({ code, settings }) => {
|
||||
const pixelRatio = window.devicePixelRatio || 1
|
||||
const size = settings.viewerSize
|
||||
? {
|
||||
x: Math.round(settings.viewerSize?.width * pixelRatio),
|
||||
y: Math.round(settings.viewerSize?.height * pixelRatio),
|
||||
}
|
||||
: lastViewPortSize
|
||||
const camera = settings.camera || lastCameraSettings
|
||||
if (settings.camera) {
|
||||
lastCameraSettings = settings.camera
|
||||
}
|
||||
if (settings.viewerSize) {
|
||||
lastViewPortSize = size
|
||||
}
|
||||
if ([camera, size].includes('INIT')) {
|
||||
return {
|
||||
status: 'insufficient-preview-info',
|
||||
}
|
||||
const size = {
|
||||
x: Math.round(settings.viewerSize?.width * pixelRatio),
|
||||
y: Math.round(settings.viewerSize?.height * pixelRatio),
|
||||
}
|
||||
const body = JSON.stringify({
|
||||
settings: {
|
||||
size,
|
||||
camera,
|
||||
camera: settings.camera,
|
||||
},
|
||||
file: code,
|
||||
})
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { useReducer } from 'react'
|
||||
import { cadPackages } from 'src/helpers/cadPackages'
|
||||
|
||||
function withThunk(dispatch, getState) {
|
||||
return (actionOrThunk) =>
|
||||
typeof actionOrThunk === 'function'
|
||||
? actionOrThunk(dispatch, getState)
|
||||
: dispatch(actionOrThunk)
|
||||
}
|
||||
|
||||
|
I think redux thunks might be a little more complex than this, but not by much. Basically you can use this like a normal dispatch, but if you want to something async you just pass it a function instead, you would have already scrolled passed examples. I think redux thunks might be a little more complex than this, but not by much. Basically you can use this like a normal dispatch, but if you want to something async you just pass it a function instead, you would have already scrolled passed examples.
|
||||
const donutInitCode = `
|
||||
color(c="DarkGoldenrod")rotate_extrude()translate([20,0])circle(d=30);
|
||||
donut();
|
||||
@@ -17,16 +24,17 @@ module stick(basewid, angl){
|
||||
}`
|
||||
|
||||
export const codeStorageKey = 'Last-openscad-code'
|
||||
let mutableState = null
|
||||
|
||||
export const useIdeState = () => {
|
||||
const code = localStorage.getItem(codeStorageKey) || donutInitCode
|
||||
const initialState = {
|
||||
ideType: 'openScad',
|
||||
ideType: 'cadQuery',
|
||||
consoleMessages: [{ type: 'message', message: 'Initialising OpenSCAD' }],
|
||||
code,
|
||||
objectData: {
|
||||
type: 'stl',
|
||||
data: 'some binary',
|
||||
data: null,
|
||||
},
|
||||
layout: {
|
||||
direction: 'row',
|
||||
@@ -38,6 +46,8 @@ export const useIdeState = () => {
|
||||
splitPercentage: 70,
|
||||
},
|
||||
},
|
||||
camera: {},
|
||||
viewerSize: { width: 0, height: 0 },
|
||||
isLoading: false,
|
||||
}
|
||||
const reducer = (state, { type, payload }) => {
|
||||
@@ -74,51 +84,64 @@ export const useIdeState = () => {
|
||||
...state,
|
||||
layout: payload.message,
|
||||
}
|
||||
case 'updateCamera':
|
||||
return {
|
||||
...state,
|
||||
camera: payload.camera,
|
||||
}
|
||||
case 'updateViewerSize':
|
||||
return {
|
||||
...state,
|
||||
viewerSize: payload.viewerSize,
|
||||
}
|
||||
case 'setLoading':
|
||||
return {
|
||||
...state,
|
||||
isLoading: true,
|
||||
}
|
||||
case 'resetLoading':
|
||||
return {
|
||||
...state,
|
||||
isLoading: false,
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function dispatchMiddleware(dispatch, state) {
|
||||
return ({ type, payload }) => {
|
||||
switch (type) {
|
||||
case 'render':
|
||||
cadPackages[state.ideType]
|
||||
.render({
|
||||
code: payload.code,
|
||||
settings: {
|
||||
camera: payload.camera,
|
||||
viewerSize: payload.viewerSize,
|
||||
},
|
||||
})
|
||||
.then(({ objectData, message, status }) => {
|
||||
if (status === 'insufficient-preview-info') return
|
||||
if (status === 'error') {
|
||||
dispatch({
|
||||
type: 'errorRender',
|
||||
payload: { message },
|
||||
})
|
||||
} else {
|
||||
dispatch({
|
||||
type: 'healthyRender',
|
||||
payload: { objectData, message },
|
||||
})
|
||||
}
|
||||
})
|
||||
dispatch({ type: 'setLoading' })
|
||||
break
|
||||
|
||||
default:
|
||||
return dispatch({ type, payload })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [state, dispatch] = useReducer(reducer, initialState)
|
||||
return [state, dispatchMiddleware(dispatch, state)]
|
||||
mutableState = state
|
||||
const getState = () => mutableState
|
||||
return [state, withThunk(dispatch, getState)]
|
||||
}
|
||||
|
||||
export const requestRender = ({
|
||||
state,
|
||||
dispatch,
|
||||
code,
|
||||
camera,
|
||||
viewerSize,
|
||||
}) => {
|
||||
cadPackages[state.ideType]
|
||||
.render({
|
||||
code,
|
||||
settings: {
|
||||
camera,
|
||||
viewerSize,
|
||||
},
|
||||
})
|
||||
.then(({ objectData, message, status }) => {
|
||||
if (status === 'error') {
|
||||
dispatch({
|
||||
type: 'errorRender',
|
||||
payload: { message },
|
||||
})
|
||||
} else {
|
||||
dispatch({
|
||||
type: 'healthyRender',
|
||||
payload: { objectData, message },
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => dispatch({ type: 'resetLoading' })) // TODO should probably display something to the user here
|
||||
}
|
||||
|
||||
I had missed you added this, what a great addition! And for it to be agnostic of the CAD package; this is the kind of unifying stuff that's gonna make it so nice to switch between packages.