massive refactor toDrop cascadeStudio and add CadQuery + OpenSCAD
resolves #400
This commit is contained in:
@@ -52,9 +52,9 @@ export const render = async ({
|
||||
}
|
||||
}
|
||||
|
||||
const openScad = {
|
||||
const openscad = {
|
||||
render,
|
||||
// more functions to come
|
||||
}
|
||||
|
||||
export default openScad
|
||||
export default openscad
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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'")
|
||||
|
||||
@@ -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()
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
51
app/web/src/helpers/hooks/use3dViewerResize.ts
Normal file
51
app/web/src/helpers/hooks/use3dViewerResize.ts
Normal 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,
|
||||
}
|
||||
}
|
||||
@@ -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',
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
|
||||
21
app/web/src/helpers/hooks/useUpdateProject.ts
Normal file
21
app/web/src/helpers/hooks/useUpdateProject.ts
Normal 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 }
|
||||
}
|
||||
Reference in New Issue
Block a user