diff --git a/app/api/src/docker/cadquery/Dockerfile b/app/api/src/docker/cadquery/Dockerfile index 861c222..e4465fa 100644 --- a/app/api/src/docker/cadquery/Dockerfile +++ b/app/api/src/docker/cadquery/Dockerfile @@ -43,6 +43,7 @@ RUN wget https://github.com/CadQuery/cq-cli/releases/download/v2.2-beta.2/cq-cli RUN unzip cq-cli-Linux-x86_64.zip RUN chmod +x cq-cli/cq-cli +RUN echo "cadhub-concat-split" > /var/task/cadhub-concat-split COPY cadquery/*.js /var/task/ COPY common/*.js /var/common/ diff --git a/app/api/src/docker/cadquery/cadquery.js b/app/api/src/docker/cadquery/cadquery.js index bd7f372..c4a4b32 100644 --- a/app/api/src/docker/cadquery/cadquery.js +++ b/app/api/src/docker/cadquery/cadquery.js @@ -31,7 +31,6 @@ const stl = async (req, _context, callback) => { statusCode: 200, body: JSON.stringify({ url: getObjectUrl(params, s3, tk), - consoleMessage: previousAsset.consoleMessage, }), } callback(null, response) diff --git a/app/api/src/docker/cadquery/runCQ.js b/app/api/src/docker/cadquery/runCQ.js index 8bf1867..65c13d0 100644 --- a/app/api/src/docker/cadquery/runCQ.js +++ b/app/api/src/docker/cadquery/runCQ.js @@ -1,24 +1,42 @@ -const { makeFile, runCommand } = require('../common/utils') +const { writeFiles, runCommand } = require('../common/utils') const { nanoid } = require('nanoid') module.exports.runCQ = async ({ file, settings: { deflection = 0.3 } = {}, } = {}) => { - const tempFile = await makeFile(file, '.py', nanoid) - const fullPath = `/tmp/${tempFile}/output.stl` + const tempFile = await writeFiles( + [{ file, fileName: 'main.py' }], + 'a' + nanoid() // 'a' ensure nothing funny happens if it start with a bad character like "-", maybe I should pick a safer id generator :shrug: + ) + const fullPath = `/tmp/${tempFile}/output.gz` + const stlPath = `/tmp/${tempFile}/output.stl` const command = [ `cq-cli/cq-cli`, `--codec stl`, `--infile /tmp/${tempFile}/main.py`, - `--outfile ${fullPath}`, + `--outfile ${stlPath}`, `--outputopts "deflection:${deflection};angularDeflection:${deflection};"`, - `&& gzip ${fullPath}`, ].join(' ') console.log('command', command) try { const consoleMessage = await runCommand(command, 30000) + await writeFiles( + [ + { + file: JSON.stringify({ + consoleMessage, + }), + fileName: 'metadata.json', + }, + ], + tempFile + ) + await runCommand( + `cat ${stlPath} /var/task/cadhub-concat-split /tmp/${tempFile}/metadata.json | gzip > ${fullPath}`, + 15000 + ) return { consoleMessage, fullPath } } catch (error) { return { error, fullPath } diff --git a/app/api/src/docker/common/utils.js b/app/api/src/docker/common/utils.js index 0fab7ea..6ee7a9b 100644 --- a/app/api/src/docker/common/utils.js +++ b/app/api/src/docker/common/utils.js @@ -3,24 +3,19 @@ const { promises } = require('fs') const { writeFile } = promises const { createHash } = require('crypto') -const CONSOLE_MESSAGE_KEY = 'console-message-b64' -function putConsoleMessageInMetadata(consoleMessage) { - return { - [CONSOLE_MESSAGE_KEY]: Buffer.from(consoleMessage, 'utf-8').toString( - 'base64' - ), +async function writeFiles(files = [], tempFile) { + console.log(`file to write: ${files.length}`) + + try { + await runCommand(`mkdir /tmp/${tempFile}`) + } catch (e) { + // } -} -function getConsoleMessageFromMetadata(metadata) { - return Buffer.from(metadata[CONSOLE_MESSAGE_KEY], 'base64').toString('utf-8') -} - -async function makeFile(file, extension = '.scad', makeHash) { - const tempFile = 'a' + makeHash() // 'a' ensure nothing funny happens if it start with a bad character like "-", maybe I should pick a safer id generator :shrug: - console.log(`file to write: ${file}`) - - await runCommand(`mkdir /tmp/${tempFile}`) - await writeFile(`/tmp/${tempFile}/main${extension}`, file) + await Promise.all( + files.map(({ file, fileName }) => + writeFile(`/tmp/${tempFile}/${fileName}`, file) + ) + ) return tempFile } @@ -54,10 +49,8 @@ function makeHash(script) { async function checkIfAlreadyExists(params, s3) { try { - const objectHead = await s3.headObject(params).promise() - const consoleMessage = getConsoleMessageFromMetadata(objectHead.Metadata) - console.log('consoleMessage', consoleMessage) - return { isAlreadyInBucket: true, consoleMessage } + await s3.headObject(params).promise() + return { isAlreadyInBucket: true } } catch (e) { console.log("couldn't find it", e) return { isAlreadyInBucket: false } @@ -117,7 +110,7 @@ async function storeAssetAndReturnUrl({ let buffer try { - buffer = await readFile(`${fullPath}.gz`) + buffer = await readFile(fullPath) } catch (e) { console.log('read file error', e) const response = { @@ -136,7 +129,6 @@ async function storeAssetAndReturnUrl({ CacheControl: `max-age=${FiveDays}`, // browser caching to stop downloads of the same part ContentType: 'text/stl', ContentEncoding: 'gzip', - Metadata: putConsoleMessageInMetadata(consoleMessage), }) .promise() console.log('stored object', storedRender) @@ -146,7 +138,6 @@ async function storeAssetAndReturnUrl({ statusCode: 200, body: JSON.stringify({ url, - consoleMessage, }), } callback(null, response) @@ -156,7 +147,7 @@ async function storeAssetAndReturnUrl({ module.exports = { runCommand, - makeFile, + writeFiles, makeHash, checkIfAlreadyExists, getObjectUrl, diff --git a/app/api/src/docker/openscad/Dockerfile b/app/api/src/docker/openscad/Dockerfile index e5580e3..4c7aea7 100644 --- a/app/api/src/docker/openscad/Dockerfile +++ b/app/api/src/docker/openscad/Dockerfile @@ -6,10 +6,12 @@ ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -qq # double check this below, I'm not sure we need inkscape etc RUN apt-get -y -qq install software-properties-common dirmngr apt-transport-https lsb-release ca-certificates xvfb imagemagick unzip inkscape -RUN add-apt-repository ppa:openscad/releases -RUN apt-get update -qq -RUN apt-get install -y -qq openscad RUN apt-get install -y curl wget +RUN touch /etc/apt/sources.list.d/openscad.list +RUN echo "deb https://download.opensuse.org/repositories/home:/t-paul/xUbuntu_20.04/ ./" >> /etc/apt/sources.list.d/openscad.list +RUN wget -qO - https://files.openscad.org/OBS-Repository-Key.pub | apt-key add - +RUN apt-get update -qq +RUN apt-get install -y openscad-nightly # install node14, see comment at the to of node14source_setup.sh ADD common/node14source_setup.sh /nodesource_setup.sh @@ -44,7 +46,9 @@ RUN echo "OPENSCADPATH=/var/task/openscad" >>/etc/profile && \ wget -P /var/task/openscad/ https://github.com/Irev-Dev/Round-Anything/archive/refs/tags/1.0.4.zip && \ unzip /var/task/openscad/1.0.4 # Add our own theming (based on DeepOcean with a different "background" and "opencsg-face-back") -COPY openscad/cadhubtheme.json /usr/share/openscad/color-schemes/render/ +COPY openscad/cadhubtheme.json /usr/share/openscad-nightly/color-schemes/render/ + +RUN echo "cadhub-concat-split" > /var/task/cadhub-concat-split COPY openscad/*.js /var/task/ COPY common/*.js /var/common/ diff --git a/app/api/src/docker/openscad/openscad.js b/app/api/src/docker/openscad/openscad.js index 1f37469..bced701 100644 --- a/app/api/src/docker/openscad/openscad.js +++ b/app/api/src/docker/openscad/openscad.js @@ -57,8 +57,6 @@ const preview = async (req, _context, callback) => { s3, tk ), - consoleMessage: - previousAsset.consoleMessage || previousAssetPng.consoleMessage, type, }), } @@ -67,7 +65,10 @@ const preview = async (req, _context, callback) => { } const { file, settings } = JSON.parse(eventBody) - const { error, consoleMessage, fullPath } = await runScad({ file, settings }) + const { error, consoleMessage, fullPath, customizerPath } = await runScad({ + file, + settings, + }) await storeAssetAndReturnUrl({ error, callback, @@ -100,14 +101,16 @@ const stl = async (req, _context, callback) => { statusCode: 200, body: JSON.stringify({ url: getObjectUrl({ ...params }, s3, tk), - consoleMessage: previousAsset.consoleMessage, }), } callback(null, response) return } - const { file } = JSON.parse(eventBody) - const { error, consoleMessage, fullPath } = await stlExport({ file }) + const { file, settings } = JSON.parse(eventBody) + const { error, consoleMessage, fullPath, customizerPath } = await stlExport({ + file, + settings, + }) await storeAssetAndReturnUrl({ error, callback, @@ -121,6 +124,6 @@ const stl = async (req, _context, callback) => { } module.exports = { - stl: middy(stl).use(cors()), + stl: middy(loggerWrap(stl)).use(cors()), preview: middy(loggerWrap(preview)).use(cors()), } diff --git a/app/api/src/docker/openscad/runScad.js b/app/api/src/docker/openscad/runScad.js index 6e69122..ac27deb 100644 --- a/app/api/src/docker/openscad/runScad.js +++ b/app/api/src/docker/openscad/runScad.js @@ -1,7 +1,8 @@ -const { makeFile, runCommand } = require('../common/utils') +const { writeFiles, runCommand } = require('../common/utils') const { nanoid } = require('nanoid') +const { readFile } = require('fs/promises') -const OPENSCAD_COMMON = `xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" openscad` +const OPENSCAD_COMMON = `xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" openscad-nightly` /** Removes our generated/hash filename with just "main.scad", so that it's a nice message in the IDE */ const cleanOpenScadError = (error) => @@ -11,6 +12,7 @@ module.exports.runScad = async ({ file, settings: { size: { x = 500, y = 500 } = {}, + parameters, camera: { position = { x: 40, y: 40, z: 40 }, rotation = { x: 55, y: 0, z: 25 }, @@ -18,46 +20,113 @@ module.exports.runScad = async ({ } = {}, } = {}, // TODO add view settings } = {}) => { - const tempFile = await makeFile(file, '.scad', nanoid) + const tempFile = await writeFiles( + [ + { file, fileName: 'main.scad' }, + { + file: JSON.stringify({ + parameterSets: { default: parameters }, + fileFormatVersion: '1', + }), + fileName: 'params.json', + }, + ], + 'a' + nanoid() // 'a' ensure nothing funny happens if it start with a bad character like "-", maybe I should pick a safer id generator :shrug: + ) const { x: rx, y: ry, z: rz } = rotation const { x: px, y: py, z: pz } = position const cameraArg = `--camera=${px},${py},${pz},${rx},${ry},${rz},${dist}` - const fullPath = `/tmp/${tempFile}/output.png` + const fullPath = `/tmp/${tempFile}/output.gz` + const imPath = `/tmp/${tempFile}/output.png` + const customizerPath = `/tmp/${tempFile}/customizer.param` const command = [ OPENSCAD_COMMON, - `-o ${fullPath}`, + `-o ${customizerPath}`, + `-o ${imPath}`, + `-p /tmp/${tempFile}/params.json -P default`, cameraArg, `--imgsize=${x},${y}`, `--colorscheme CadHub`, `/tmp/${tempFile}/main.scad`, - `&& gzip ${fullPath}`, ].join(' ') console.log('command', command) try { const consoleMessage = await runCommand(command, 15000) - return { consoleMessage, fullPath } + const params = JSON.parse( + await readFile(customizerPath, { encoding: 'ascii' }) + ).parameters + await writeFiles( + [ + { + file: JSON.stringify({ + customizerParams: params, + consoleMessage, + }), + fileName: 'metadata.json', + }, + ], + tempFile + ) + await runCommand( + `cat ${imPath} /var/task/cadhub-concat-split /tmp/${tempFile}/metadata.json | gzip > ${fullPath}`, + 15000 + ) + return { consoleMessage, fullPath, customizerPath } } catch (dirtyError) { - const error = cleanOpenScadError(dirtyError) - return { error } + return { error: cleanOpenScadError(dirtyError) } } } -module.exports.stlExport = async ({ file } = {}) => { - const tempFile = await makeFile(file, '.scad', nanoid) - const fullPath = `/tmp/${tempFile}/output.stl` +module.exports.stlExport = async ({ file, settings: { parameters } } = {}) => { + const tempFile = await writeFiles( + [ + { file, fileName: 'main.scad' }, + { + file: JSON.stringify({ + parameterSets: { default: parameters }, + fileFormatVersion: '1', + }), + fileName: 'params.json', + }, + ], + 'a' + nanoid() // 'a' ensure nothing funny happens if it start with a bad character like "-", maybe I should pick a safer id generator :shrug: + ) + const fullPath = `/tmp/${tempFile}/output.gz` + const stlPath = `/tmp/${tempFile}/output.stl` + const customizerPath = `/tmp/${tempFile}/customizer.param` const command = [ OPENSCAD_COMMON, - `--export-format=binstl`, - `-o ${fullPath}`, + // `--export-format=binstl`, + `-o ${customizerPath}`, + `-o ${stlPath}`, + `-p /tmp/${tempFile}/params.json -P default`, `/tmp/${tempFile}/main.scad`, - `&& gzip ${fullPath}`, ].join(' ') try { // lambda will time out before this, we might need to look at background jobs if we do git integration stl generation const consoleMessage = await runCommand(command, 60000) - return { consoleMessage, fullPath } + const params = JSON.parse( + await readFile(customizerPath, { encoding: 'ascii' }) + ).parameters + await writeFiles( + [ + { + file: JSON.stringify({ + customizerParams: params, + consoleMessage, + }), + fileName: 'metadata.json', + }, + ], + tempFile + ) + await runCommand( + `cat ${stlPath} /var/task/cadhub-concat-split /tmp/${tempFile}/metadata.json | gzip > ${fullPath}`, + 15000 + ) + return { consoleMessage, fullPath, customizerPath } } catch (error) { return { error, fullPath } } diff --git a/app/web/src/components/Customizer/Customizer.tsx b/app/web/src/components/Customizer/Customizer.tsx index 4928640..657faa2 100644 --- a/app/web/src/components/Customizer/Customizer.tsx +++ b/app/web/src/components/Customizer/Customizer.tsx @@ -8,7 +8,6 @@ import { CadhubNumberParam, CadhubStringChoiceParam, CadhubNumberChoiceParam, - CadhubChoiceParam, } from './customizerConverter' const Customizer = () => { @@ -82,17 +81,22 @@ const Customizer = () => { {customizerParams.map((param, index) => { const otherProps = { value: currentParameters[param.name], - onChange: (value) => updateCustomizerParam(param.name, param.type == 'number' ? Number(value) : value), + onChange: (value) => + updateCustomizerParam( + param.name, + param.type == 'number' ? Number(value) : value + ), } - if(param.input === 'choice-string' || param.input === 'choice-number'){ - return - // }else if(param.input === 'choice-number'){ - // return - }else if (param.type === 'string') { - return - } else if (param.type === 'number') { - return - } else if (param.type === 'boolean') { + if ( + param.input === 'choice-string' || + param.input === 'choice-number' + ) { + return + } else if (param.input === 'default-string') { + return + } else if (param.input === 'default-number') { + return + } else if (param.input === 'default-boolean') { return } return
{JSON.stringify(param)}
@@ -180,12 +184,12 @@ function StringParam({ ) } -function StringChoiceParam({ +function ChoiceParam({ param, value, onChange, }: { - param: CadhubChoiceParam + param: CadhubStringChoiceParam | CadhubNumberChoiceParam value: any onChange: Function }) { @@ -196,7 +200,11 @@ function StringChoiceParam({ value={value} onChange={({ target }) => onChange(target?.value)} > - {param.options.map(opt=>)} + {param.options.map((opt) => ( + + ))} ) diff --git a/app/web/src/components/Customizer/customizerConverter.ts b/app/web/src/components/Customizer/customizerConverter.ts index 3972248..0b93d1c 100644 --- a/app/web/src/components/Customizer/customizerConverter.ts +++ b/app/web/src/components/Customizer/customizerConverter.ts @@ -1,7 +1,12 @@ // CadHub type CadhubTypeNames = 'number' | 'string' | 'boolean' -type CadhubInputNames = 'default-number' | 'default-string' | 'default-boolean' | 'choice-string' | 'choice-number' +type CadhubInputNames = + | 'default-number' + | 'default-string' + | 'default-boolean' + | 'choice-string' + | 'choice-number' export interface CadhubStringOption { name: string @@ -29,6 +34,7 @@ export interface CadhubStringParam extends CadhubParamBase { } export interface CadhubBooleanParam extends CadhubParamBase { type: 'boolean' + input: 'default-boolean' initial?: boolean } export interface CadhubNumberParam extends CadhubParamBase { @@ -45,13 +51,13 @@ export interface CadhubStringChoiceParam extends CadhubParamBase { type: 'string' input: 'choice-string' initial: string - options: Array + options: Array } export interface CadhubNumberChoiceParam extends CadhubParamBase { type: 'number' input: 'choice-number' initial: number - options: Array + options: Array } export type CadhubParams = @@ -60,91 +66,3 @@ export type CadhubParams = | CadhubNumberParam | CadhubStringChoiceParam | CadhubNumberChoiceParam - -// OpenSCAD -const openscadValues = ` -// slider widget for number with max. value -sliderWithMax =34; // [50] - -// slider widget for number in range -sliderWithRange =34; // [10:100] - -//step slider for number -stepSlider=2; //[0:5:100] - -// slider widget for number in range -sliderCentered =0; // [-10:0.1:10] - -// spinbox with step size 1 -Spinbox= 5; - -// Text box for string -String="hello"; - -// Text box for string with length 8 -String2="length"; //8 - -//description -Variable = true; -` - -const openscadConverted: CadhubParams[] = [ - { - type: 'number', - name: 'sliderWithMax', - caption: 'slider widget for number with max. value', - initial: 34, - step: 1, - max: 50, - }, - { - type: 'number', - name: 'sliderWithRange', - caption: 'slider widget for number in range', - initial: 34, - step: 1, - min: 10, - max: 100, - }, - { - type: 'number', - name: 'stepSlider', - caption: 'step slider for number', - initial: 2, - step: 5, - min: 0, - max: 100, - }, - { - type: 'number', - name: 'sliderCentered', - caption: 'slider widget for number in range', - initial: 0, - step: 0.1, - min: -10, - max: 10, - }, - { - type: 'number', - name: 'Spinbox', - caption: 'spinbox with step size 1', - initial: 5, - step: 1, - }, - - { - type: 'string', - name: 'String', - caption: 'Text box for string', - initial: 'hello', - }, - { - type: 'string', - name: 'String2', - caption: 'Text box for string with length 8', - initial: 'length', - maxLength: 8, - }, - - { type: 'boolean', name: 'Variable', caption: 'description', initial: true }, -] diff --git a/app/web/src/components/IdeConsole/IdeConsole.tsx b/app/web/src/components/IdeConsole/IdeConsole.tsx index 24ea63d..a85ce5e 100644 --- a/app/web/src/components/IdeConsole/IdeConsole.tsx +++ b/app/web/src/components/IdeConsole/IdeConsole.tsx @@ -22,7 +22,7 @@ const IdeConsole = () => { {time?.toLocaleString()}
- {message.split('\n').map((line, index) => { + {(message || '').split('\n').map((line, index) => { return (
{line.startsWith('ECHO:') ? ( diff --git a/app/web/src/helpers/cadPackages/cadQueryController.ts b/app/web/src/helpers/cadPackages/cadQueryController.ts index b2c4a81..6524ad8 100644 --- a/app/web/src/helpers/cadPackages/cadQueryController.ts +++ b/app/web/src/helpers/cadPackages/cadQueryController.ts @@ -6,6 +6,7 @@ import { timeoutErrorMessage, RenderArgs, DefaultKernelExport, + splitGziped, } from './common' export const render: DefaultKernelExport['render'] = async ({ @@ -41,11 +42,19 @@ export const render: DefaultKernelExport['render'] = async ({ return createUnhealthyResponse(new Date(), timeoutErrorMessage) } const data = await response.json() - const geometry = await stlToGeometry(data.url) + const newData = await fetch(data.url).then(async (a) => { + const blob = await a.blob() + const text = await new Response(blob).text() + const { consoleMessage } = splitGziped(text) + return { + data: await stlToGeometry(window.URL.createObjectURL(blob)), + consoleMessage, + } + }) return createHealthyResponse({ type: 'geometry', - data: geometry, - consoleMessage: data.consoleMessage, + data: newData.data, + consoleMessage: newData.consoleMessage, date: new Date(), }) } catch (e) { diff --git a/app/web/src/helpers/cadPackages/common.ts b/app/web/src/helpers/cadPackages/common.ts index ca91004..73b76dc 100644 --- a/app/web/src/helpers/cadPackages/common.ts +++ b/app/web/src/helpers/cadPackages/common.ts @@ -13,8 +13,8 @@ export const stlToGeometry = (url) => export interface RenderArgs { code: State['code'] - parameters?: RawCustomizerParams settings: { + parameters?: RawCustomizerParams camera: State['camera'] viewerSize: State['viewerSize'] quality: State['objectData']['quality'] @@ -103,3 +103,16 @@ export type RenderResponse = HealthyResponse | ErrorResponse export interface DefaultKernelExport { render: (arg: RenderArgs) => Promise } + +export const splitGziped = (text: string) => { + const concatSplitStr = 'cadhub-concat-split' + const splitIndex = text.indexOf(concatSplitStr) + const json = text.slice(splitIndex + concatSplitStr.length) + + try { + return JSON.parse(json) + } catch (e) { + console.log(json, e) + return {} + } +} diff --git a/app/web/src/helpers/cadPackages/index.ts b/app/web/src/helpers/cadPackages/index.ts index 568e9b9..90223a0 100644 --- a/app/web/src/helpers/cadPackages/index.ts +++ b/app/web/src/helpers/cadPackages/index.ts @@ -1,7 +1,7 @@ import { DefaultKernelExport } from './common' import type { CadPackage } from 'src/helpers/hooks/useIdeState' -import openscad from './openScadController' +import openscad from './openScad/openScadController' import cadquery from './cadQueryController' import jscad from './jsCad/jsCadController' diff --git a/app/web/src/helpers/cadPackages/jsCad/jsCadController.tsx b/app/web/src/helpers/cadPackages/jsCad/jsCadController.tsx index 56e1458..4f8c2f2 100644 --- a/app/web/src/helpers/cadPackages/jsCad/jsCadController.tsx +++ b/app/web/src/helpers/cadPackages/jsCad/jsCadController.tsx @@ -130,7 +130,6 @@ const workerHelper = new WorkerHelper() export const render: DefaultKernelExport['render'] = async ({ code, - parameters, settings, }: RenderArgs) => { if (!scriptWorker) { @@ -169,11 +168,11 @@ export const render: DefaultKernelExport['render'] = async ({ } }) - workerHelper.resolver() + workerHelper.resolver(null) scriptWorker.postMessage({ action: 'init', baseURI, alias: [] }) } - return workerHelper.render(code, parameters) + return workerHelper.render(code, settings.parameters) } const jsCadController: DefaultKernelExport = { diff --git a/app/web/src/helpers/cadPackages/jsCad/jscadParams.ts b/app/web/src/helpers/cadPackages/jsCad/jscadParams.ts index b541406..c990902 100644 --- a/app/web/src/helpers/cadPackages/jsCad/jscadParams.ts +++ b/app/web/src/helpers/cadPackages/jsCad/jscadParams.ts @@ -1,4 +1,10 @@ -import { CadhubNumberChoiceParam, CadhubNumberOption, CadhubParams, CadhubStringChoiceParam, CadhubStringOption } from 'src/components/Customizer/customizerConverter' +import { + CadhubNumberChoiceParam, + CadhubNumberOption, + CadhubParams, + CadhubStringChoiceParam, + CadhubStringOption, +} from 'src/components/Customizer/customizerConverter' type JscadTypeNames = | 'group' @@ -19,6 +25,7 @@ interface JscadParamBase { type: JscadTypeNames caption: string name: string + initial?: number | string | boolean } interface JscadGroupParam extends JscadParamBase { type: 'group' @@ -81,6 +88,10 @@ type JsCadParams = export function jsCadToCadhubParams(input: JsCadParams[]): CadhubParams[] { return input .map((param): CadhubParams => { + const common: { caption: string; name: string } = { + caption: param.caption, + name: param.name, + } switch (param.type) { case 'slider': case 'number': @@ -88,8 +99,7 @@ export function jsCadToCadhubParams(input: JsCadParams[]): CadhubParams[] { return { type: 'number', input: 'default-number', - caption: param.caption, - name: param.name, + ...common, initial: param.initial, min: param.min, max: param.max, @@ -105,8 +115,7 @@ export function jsCadToCadhubParams(input: JsCadParams[]): CadhubParams[] { return { type: 'string', input: 'default-string', - caption: param.caption, - name: param.name, + ...common, initial: param.initial, placeholder: param.type === 'text' || @@ -123,43 +132,38 @@ export function jsCadToCadhubParams(input: JsCadParams[]): CadhubParams[] { return { type: 'boolean', input: 'default-boolean', - caption: param.caption, - name: param.name, + ...common, initial: !!param.initial, } - case 'choice': - case 'radio': - if(typeof param.values[0] === 'number'){ - let options:Array = [] - let captions = param.captions || param.values - param.values.forEach((value,i)=>{ - options[i] = {name:String(captions[i]), value:Number(value)} - }) - return { - type: 'number', - input: 'choice-number', - caption: param.caption, - name: param.name, - initial: Number(param.initial), - options - } - }else{ - let options:Array = [] - let captions = param.captions || param.values - param.values.forEach((value,i)=>{ - options[i] = {name:String(captions[i]), value:String(value)} - }) - return { - type: 'string', - input: 'choice-string', - caption: param.caption, - name: param.name, - initial: String(param.initial), - options - } + case 'choice': + case 'radio': + if (typeof param.values[0] === 'number') { + const options: Array = [] + const captions = param.captions || param.values + param.values.forEach((value, i) => { + options[i] = { name: String(captions[i]), value: Number(value) } + }) + return { + type: 'number', + input: 'choice-number', + ...common, + initial: Number(param.initial), + options, } - - + } else { + const options: Array = [] + const captions = param.captions || param.values + param.values.forEach((value, i) => { + options[i] = { name: String(captions[i]), value: String(value) } + }) + return { + type: 'string', + input: 'choice-string', + ...common, + initial: String(param.initial), + options, + } + } } }) .filter((a) => a) diff --git a/app/web/src/helpers/cadPackages/openScadController.ts b/app/web/src/helpers/cadPackages/openScad/openScadController.ts similarity index 70% rename from app/web/src/helpers/cadPackages/openScadController.ts rename to app/web/src/helpers/cadPackages/openScad/openScadController.ts index 1986d19..56ebc4c 100644 --- a/app/web/src/helpers/cadPackages/openScadController.ts +++ b/app/web/src/helpers/cadPackages/openScad/openScadController.ts @@ -5,7 +5,9 @@ import { createUnhealthyResponse, timeoutErrorMessage, RenderArgs, -} from './common' + splitGziped, +} from '../common' +import { openScadToCadhubParams } from './openScadParams' export const render = async ({ code, settings }: RenderArgs) => { const pixelRatio = window.devicePixelRatio || 1 @@ -17,6 +19,7 @@ export const render = async ({ code, settings }: RenderArgs) => { const body = JSON.stringify({ settings: { size, + parameters: settings.parameters, camera: { // rounding to give our caching a chance to sometimes work ...settings.camera, @@ -56,15 +59,25 @@ export const render = async ({ code, settings }: RenderArgs) => { } const data = await response.json() const type = data.type !== 'stl' ? 'png' : 'geometry' - const newData = - data.type !== 'stl' - ? fetch(data.url).then((a) => a.blob()) - : stlToGeometry(data.url) + const newData = await fetch(data.url).then(async (a) => { + const blob = await a.blob() + const text = await new Response(blob).text() + const { consoleMessage, customizerParams } = splitGziped(text) + return { + data: + data.type !== 'stl' + ? blob + : await stlToGeometry(window.URL.createObjectURL(blob)), + consoleMessage, + customizerParams, + } + }) return createHealthyResponse({ type, - data: await newData, - consoleMessage: data.consoleMessage, + data: newData.data, + consoleMessage: newData.consoleMessage, date: new Date(), + customizerParams: openScadToCadhubParams(newData.customizerParams || []), }) } catch (e) { return createUnhealthyResponse(new Date()) @@ -93,12 +106,22 @@ export const stl = async ({ code, settings }: RenderArgs) => { return createUnhealthyResponse(new Date(), timeoutErrorMessage) } const data = await response.json() - const geometry = await stlToGeometry(data.url) + const newData = await fetch(data.url).then(async (a) => { + const blob = await a.blob() + const text = await new Response(blob).text() + const { consoleMessage, customizerParams } = splitGziped(text) + return { + data: await stlToGeometry(window.URL.createObjectURL(blob)), + consoleMessage, + customizerParams, + } + }) return createHealthyResponse({ type: 'geometry', - data: geometry, - consoleMessage: data.consoleMessage, + data: newData.data, + consoleMessage: newData.consoleMessage, date: new Date(), + customizerParams: openScadToCadhubParams(newData.customizerParams || []), }) } catch (e) { return createUnhealthyResponse(new Date()) diff --git a/app/web/src/helpers/cadPackages/openScad/openScadParams.ts b/app/web/src/helpers/cadPackages/openScad/openScadParams.ts new file mode 100644 index 0000000..4720a2a --- /dev/null +++ b/app/web/src/helpers/cadPackages/openScad/openScadParams.ts @@ -0,0 +1,102 @@ +import { CadhubParams } from 'src/components/Customizer/customizerConverter' + +interface OpenScadParamsBase { + caption: string + name: string + group: string + initial: number | string | boolean | number[] + type: 'string' | 'number' | 'boolean' +} + +interface OpenScadNumberParam extends OpenScadParamsBase { + type: 'number' + initial: number | number[] + max?: number + min?: number + step?: number + options?: { name: string; value: number }[] +} +interface OpenScadStringParam extends OpenScadParamsBase { + type: 'string' + initial: string + maxLength?: number + options?: { name: string; value: string }[] +} +interface OpenScadBooleanParam extends OpenScadParamsBase { + type: 'boolean' + initial: boolean +} + +export type OpenScadParams = + | OpenScadNumberParam + | OpenScadStringParam + | OpenScadBooleanParam + +export function openScadToCadhubParams( + input: OpenScadParams[] +): CadhubParams[] { + return input + .map((param): CadhubParams => { + const common: { caption: string; name: string } = { + caption: param.caption, + name: param.name, + } + switch (param.type) { + case 'boolean': + return { + type: 'boolean', + input: 'default-boolean', + ...common, + initial: param.initial, + } + case 'string': + if (!Array.isArray(param?.options)) { + return { + type: 'string', + input: 'default-string', + ...common, + initial: param.initial, + maxLength: param.maxLength, + } + } else { + return { + type: 'string', + input: 'choice-string', + ...common, + initial: param.initial, + options: param.options, + } + } + case 'number': + if ( + !Array.isArray(param?.options) && + !Array.isArray(param?.initial) + ) { + return { + type: 'number', + input: 'default-number', + ...common, + initial: param.initial, + min: param.min, + max: param.max, + step: param.step, + } + } else if ( + Array.isArray(param?.options) && + !Array.isArray(param?.initial) + ) { + return { + type: 'number', + input: 'choice-number', + ...common, + initial: param.initial, + options: param.options, + } + } // TODO else vector + break + default: + return + } + }) + .filter((a) => a) +} diff --git a/app/web/src/helpers/hooks/useIdeState.ts b/app/web/src/helpers/hooks/useIdeState.ts index 9846bce..f4a3d2b 100644 --- a/app/web/src/helpers/hooks/useIdeState.ts +++ b/app/web/src/helpers/hooks/useIdeState.ts @@ -51,13 +51,12 @@ show_object(result) const jscad = require('@jscad/modeling') // https://openjscad.xyz/docs/module-modeling_primitives.html -const { circle, rectangle, cube, cuboid, sphere, cylinder } = jscad.primitives +const { cuboid, cylinder } = jscad.primitives -const { rotate, scale, translate } = jscad.transforms +const { rotate, translate } = jscad.transforms const { degToRad } = jscad.utils // because jscad uses radians for rotations -const { colorize } = jscad.colors // https://openjscad.xyz/docs/module-modeling_booleans.html -const { union, intersect, subtract } = jscad.booleans +const { subtract } = jscad.booleans function main({//@jscad-params // Box example @@ -66,6 +65,7 @@ function main({//@jscad-params height=10, // Height hole=3,// Hole for cables diameter (0=no hole) wall=1, // wall {min:0.5, step:0.5} + flip=0, // print orientation {type: 'choice', values: [0, 90, 180]} }){ let wallOffset = wall * 2 @@ -78,7 +78,7 @@ function main({//@jscad-params translate([width/2-wall/2], rotate([0, degToRad(90), 0 ], cylinder({radius:hole/2, height:wall}))) ) } - return rotate([0,0, degToRad(90)], model) + return rotate([degToRad(flip), 0, degToRad(90)], model) } module.exports = {main} @@ -281,8 +281,8 @@ export const requestRender = ({ : cadPackages[state.ideType].render return renderFn({ code, - parameters, settings: { + parameters, camera, viewerSize, quality,