massive refactor toDrop cascadeStudio and add CadQuery + OpenSCAD

resolves #400
This commit is contained in:
Kurt Hutten
2021-07-08 21:17:07 +10:00
parent 477a557eb8
commit 8e558d2342
158 changed files with 2335 additions and 2300 deletions

View File

@@ -52,9 +52,9 @@ export const render = async ({
}
}
const openScad = {
const openscad = {
render,
// more functions to come
}
export default openScad
export default openscad

View File

@@ -1,7 +1,7 @@
import openScad from './openScadController'
import cadQuery from './cadQueryController'
import openscad from './openScadController'
import cadquery from './cadQueryController'
export const cadPackages = {
openScad,
cadQuery,
openscad,
cadquery,
}

View File

@@ -56,7 +56,10 @@ export const render = async ({ code, settings }: RenderArgs) => {
}
const data = await response.json()
const type = data.type !== 'stl' ? 'png' : 'geometry'
const newData = data.type !== 'stl' ? data.url : stlToGeometry(data.url)
const newData =
data.type !== 'stl'
? fetch(data.url).then((a) => a.blob())
: stlToGeometry(data.url)
return createHealthyResponse({
type,
data: await newData,
@@ -102,12 +105,12 @@ export const stl = async ({ code, settings }: RenderArgs) => {
}
}
const openScad = {
const openscad = {
render,
stl,
}
export default openScad
export default openscad
function cleanError(error) {
return error.replace(/["|']\/tmp\/.+\/main.scad["|']/g, "'main.scad'")

View File

@@ -1,55 +0,0 @@
import { initialize } from 'src/cascade/js/MainPage/CascadeMain'
import { monacoEditor } from 'src/cascade/js/MainPage/CascadeState'
class CascadeController {
_hasInitialised = false
incomingOnCodeChang = () => {}
controllerOnCodeChange = (code) => {
this.incomingOnCodeChang(code)
}
initialise(onCodeChange, code) {
const onInit = () => {
const editor = monacoEditor
editor.setValue(code)
editor.evaluateCode(false)
}
// only inits on first call, after that it just updates the editor and revaluates code, maybe should rename?
this.incomingOnCodeChang = onCodeChange
if (!this._hasInitialised) {
initialize(this.controllerOnCodeChange, code, onInit)
this._hasInitialised = true
return
}
onInit()
}
capture(environment, width = 512, height = 384) {
environment.camera.aspect = width / height
environment.camera.updateProjectionMatrix()
environment.renderer.setSize(width, height)
environment.renderer.render(
environment.scene,
environment.camera,
null,
false
)
let imgBlob = new Promise((resolve, reject) => {
environment.renderer.domElement.toBlob(
(blob) => {
blob.name = `part_capture-${Date.now()}`
resolve(blob)
},
'image/jpeg',
1
)
})
// Return to original dimensions
environment.onWindowResize()
return imgBlob
}
}
export default new CascadeController()

View File

@@ -1,7 +1,5 @@
// TODO: create a tidy util for uploading to Cloudinary and returning the public ID
import axios from 'axios'
import { threejsViewport } from 'src/cascade/js/MainPage/CascadeState'
import CascadeController from 'src/helpers/cascadeController'
const CLOUDINARY_UPLOAD_PRESET = 'CadHub_project_images'
const CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/irevdev/upload'
@@ -21,12 +19,3 @@ export async function uploadToCloudinary(imgBlob) {
console.error('ERROR', e)
}
}
export const captureAndSaveViewport = async () => {
// Get the canvas image as a Data URL
const imgBlob = await CascadeController.capture(threejsViewport.environment)
// Upload the image to Cloudinary
const { public_id: publicId } = await uploadToCloudinary(imgBlob)
return { publicId, imgBlob }
}

View File

@@ -0,0 +1,51 @@
import { useRef, useEffect } from 'react'
import { useIdeContext } from './useIdeContext'
import { requestRender } from './useIdeState'
export const use3dViewerResize = () => {
const viewerDomRef = useRef(null)
const debounceTimeoutId = useRef<number>()
const { thunkDispatch } = useIdeContext()
useEffect(handleViewerSizeUpdate, [viewerDomRef])
function handleViewerSizeUpdate() {
if (viewerDomRef !== null && viewerDomRef.current) {
const { width, height } = viewerDomRef.current.getBoundingClientRect()
thunkDispatch({
type: 'updateViewerSize',
payload: { viewerSize: { width, height } },
})
thunkDispatch((dispatch, getState) => {
const state = getState()
if (['png', 'INIT'].includes(state.objectData?.type)) {
dispatch({ type: 'setLoading' })
requestRender({
state,
dispatch,
code: state.code,
viewerSize: { width, height },
camera: state.camera,
})
}
})
}
}
const debouncedViewerSizeUpdate = () => {
clearTimeout(debounceTimeoutId.current)
debounceTimeoutId.current = setTimeout(() => {
handleViewerSizeUpdate()
}, 1000) as unknown as number
}
useEffect(() => {
window.addEventListener('resize', debouncedViewerSizeUpdate)
return () => {
window.removeEventListener('resize', debouncedViewerSizeUpdate)
}
}, [])
return {
viewerDomRef,
handleViewerSizeUpdate,
}
}

View File

@@ -1,11 +1,24 @@
import { IdeContext } from 'src/pages/DevIdePage/DevIdePage'
import { useContext } from 'react'
import { createContext, useContext } from 'react'
import { State, initialState } from 'src/helpers/hooks/useIdeState'
import type { Project } from 'src/components/IdeProjectCell/IdeProjectCell'
interface IdeContextType {
state: State
thunkDispatch: (actionOrThunk: any) => any
project: null | Project
}
export const IdeContext = createContext<IdeContextType>({
state: initialState,
thunkDispatch: () => {},
project: null,
})
export function useIdeContext() {
return useContext(IdeContext)
}
export const ideTypeNameMap = {
openScad: 'OpenSCAD',
cadQuery: 'CadQuery',
openscad: 'OpenSCAD',
cadquery: 'CadQuery',
}

View File

@@ -1,5 +1,6 @@
import { useReducer } from 'react'
import { cadPackages } from 'src/helpers/cadPackages'
import type { RootState } from '@react-three/fiber'
function withThunk(dispatch, getState) {
return (actionOrThunk) =>
@@ -9,7 +10,7 @@ function withThunk(dispatch, getState) {
}
const initCodeMap = {
openScad: `// involute donut
openscad: `// involute donut
// ^ first comment is used for download title (i.e "involute-donut.stl")
@@ -21,7 +22,7 @@ color(c="hotpink")rotate_extrude()translate([20,0])offset(radius)offset(-radius)
circle(d=34);
translate([-200,-500])square([500,500]);
}`,
cadQuery: `# demo shaft coupler
cadquery: `# demo shaft coupler
# ^ first comment is used for download title (i.e. "demo-shaft-coupler.stl")
@@ -52,7 +53,7 @@ interface XYZ {
}
export interface State {
ideType: 'INIT' | 'openScad' | 'cadQuery'
ideType: 'INIT' | 'openscad' | 'cadquery'
consoleMessages: { type: 'message' | 'error'; message: string; time: Date }[]
code: string
objectData: {
@@ -68,6 +69,7 @@ export interface State {
}
viewerSize: { width: number; height: number }
isLoading: boolean
threeInstance: RootState
}
const code = ''
@@ -97,6 +99,7 @@ export const initialState: State = {
camera: {},
viewerSize: { width: 0, height: 0 },
isLoading: false,
threeInstance: null,
}
export const useIdeState = (): [State, (actionOrThunk: any) => any] => {
@@ -106,7 +109,8 @@ export const useIdeState = (): [State, (actionOrThunk: any) => any] => {
return {
...state,
code:
localStorage.getItem(makeCodeStoreKey(payload.cadPackage)) ||
payload.code ||
// localStorage.getItem(makeCodeStoreKey(payload.cadPackage)) ||
initCodeMap[payload.cadPackage] ||
'',
ideType: payload.cadPackage,
@@ -164,6 +168,11 @@ export const useIdeState = (): [State, (actionOrThunk: any) => any] => {
...state,
layout: initialLayout,
}
case 'setThreeInstance':
return {
...state,
threeInstance: payload,
}
default:
return state
}
@@ -182,7 +191,7 @@ interface RequestRenderArgs {
code: State['code']
camera: State['camera']
viewerSize: State['viewerSize']
quality: State['objectData']['quality']
quality?: State['objectData']['quality']
specialCadProcess?: string
}
@@ -192,7 +201,7 @@ export const requestRender = ({
code,
camera,
viewerSize,
quality,
quality = 'low',
specialCadProcess = null,
}: RequestRenderArgs) => {
if (

View File

@@ -0,0 +1,21 @@
import { useMutation } from '@redwoodjs/web'
const UPDATE_PROJECT_MUTATION_HOOK = gql`
mutation UpdateProjectMutationHook(
$id: String!
$input: UpdateProjectInput!
) {
updateProject: updateProject(id: $id, input: $input) {
id
}
}
`
export const useUpdateProject = ({ onCompleted }) => {
const [updateProject, { loading, error }] = useMutation(
UPDATE_PROJECT_MUTATION_HOOK,
{ onCompleted }
)
return { updateProject, loading, error }
}