Add customizer support for OpenSCAD
This also includes sending metadata and part of the concatenated gzip, not in the s3 metadata as that has a 2kb limit. Resolves #320
This commit is contained in:
@@ -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 unzip cq-cli-Linux-x86_64.zip
|
||||||
|
|
||||||
RUN chmod +x cq-cli/cq-cli
|
RUN chmod +x cq-cli/cq-cli
|
||||||
|
RUN echo "cadhub-concat-split" > /var/task/cadhub-concat-split
|
||||||
|
|
||||||
COPY cadquery/*.js /var/task/
|
COPY cadquery/*.js /var/task/
|
||||||
COPY common/*.js /var/common/
|
COPY common/*.js /var/common/
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ const stl = async (req, _context, callback) => {
|
|||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
url: getObjectUrl(params, s3, tk),
|
url: getObjectUrl(params, s3, tk),
|
||||||
consoleMessage: previousAsset.consoleMessage,
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
callback(null, response)
|
callback(null, response)
|
||||||
|
|||||||
@@ -1,24 +1,42 @@
|
|||||||
const { makeFile, runCommand } = require('../common/utils')
|
const { writeFiles, runCommand } = require('../common/utils')
|
||||||
const { nanoid } = require('nanoid')
|
const { nanoid } = require('nanoid')
|
||||||
|
|
||||||
module.exports.runCQ = async ({
|
module.exports.runCQ = async ({
|
||||||
file,
|
file,
|
||||||
settings: { deflection = 0.3 } = {},
|
settings: { deflection = 0.3 } = {},
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
const tempFile = await makeFile(file, '.py', nanoid)
|
const tempFile = await writeFiles(
|
||||||
const fullPath = `/tmp/${tempFile}/output.stl`
|
[{ 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 = [
|
const command = [
|
||||||
`cq-cli/cq-cli`,
|
`cq-cli/cq-cli`,
|
||||||
`--codec stl`,
|
`--codec stl`,
|
||||||
`--infile /tmp/${tempFile}/main.py`,
|
`--infile /tmp/${tempFile}/main.py`,
|
||||||
`--outfile ${fullPath}`,
|
`--outfile ${stlPath}`,
|
||||||
`--outputopts "deflection:${deflection};angularDeflection:${deflection};"`,
|
`--outputopts "deflection:${deflection};angularDeflection:${deflection};"`,
|
||||||
`&& gzip ${fullPath}`,
|
|
||||||
].join(' ')
|
].join(' ')
|
||||||
console.log('command', command)
|
console.log('command', command)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const consoleMessage = await runCommand(command, 30000)
|
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 }
|
return { consoleMessage, fullPath }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { error, fullPath }
|
return { error, fullPath }
|
||||||
|
|||||||
@@ -3,24 +3,19 @@ const { promises } = require('fs')
|
|||||||
const { writeFile } = promises
|
const { writeFile } = promises
|
||||||
const { createHash } = require('crypto')
|
const { createHash } = require('crypto')
|
||||||
|
|
||||||
const CONSOLE_MESSAGE_KEY = 'console-message-b64'
|
async function writeFiles(files = [], tempFile) {
|
||||||
function putConsoleMessageInMetadata(consoleMessage) {
|
console.log(`file to write: ${files.length}`)
|
||||||
return {
|
|
||||||
[CONSOLE_MESSAGE_KEY]: Buffer.from(consoleMessage, 'utf-8').toString(
|
|
||||||
'base64'
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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}`)
|
|
||||||
|
|
||||||
|
try {
|
||||||
await runCommand(`mkdir /tmp/${tempFile}`)
|
await runCommand(`mkdir /tmp/${tempFile}`)
|
||||||
await writeFile(`/tmp/${tempFile}/main${extension}`, file)
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
await Promise.all(
|
||||||
|
files.map(({ file, fileName }) =>
|
||||||
|
writeFile(`/tmp/${tempFile}/${fileName}`, file)
|
||||||
|
)
|
||||||
|
)
|
||||||
return tempFile
|
return tempFile
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,10 +49,8 @@ function makeHash(script) {
|
|||||||
|
|
||||||
async function checkIfAlreadyExists(params, s3) {
|
async function checkIfAlreadyExists(params, s3) {
|
||||||
try {
|
try {
|
||||||
const objectHead = await s3.headObject(params).promise()
|
await s3.headObject(params).promise()
|
||||||
const consoleMessage = getConsoleMessageFromMetadata(objectHead.Metadata)
|
return { isAlreadyInBucket: true }
|
||||||
console.log('consoleMessage', consoleMessage)
|
|
||||||
return { isAlreadyInBucket: true, consoleMessage }
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("couldn't find it", e)
|
console.log("couldn't find it", e)
|
||||||
return { isAlreadyInBucket: false }
|
return { isAlreadyInBucket: false }
|
||||||
@@ -117,7 +110,7 @@ async function storeAssetAndReturnUrl({
|
|||||||
let buffer
|
let buffer
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buffer = await readFile(`${fullPath}.gz`)
|
buffer = await readFile(fullPath)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('read file error', e)
|
console.log('read file error', e)
|
||||||
const response = {
|
const response = {
|
||||||
@@ -136,7 +129,6 @@ async function storeAssetAndReturnUrl({
|
|||||||
CacheControl: `max-age=${FiveDays}`, // browser caching to stop downloads of the same part
|
CacheControl: `max-age=${FiveDays}`, // browser caching to stop downloads of the same part
|
||||||
ContentType: 'text/stl',
|
ContentType: 'text/stl',
|
||||||
ContentEncoding: 'gzip',
|
ContentEncoding: 'gzip',
|
||||||
Metadata: putConsoleMessageInMetadata(consoleMessage),
|
|
||||||
})
|
})
|
||||||
.promise()
|
.promise()
|
||||||
console.log('stored object', storedRender)
|
console.log('stored object', storedRender)
|
||||||
@@ -146,7 +138,6 @@ async function storeAssetAndReturnUrl({
|
|||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
url,
|
url,
|
||||||
consoleMessage,
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
callback(null, response)
|
callback(null, response)
|
||||||
@@ -156,7 +147,7 @@ async function storeAssetAndReturnUrl({
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
runCommand,
|
runCommand,
|
||||||
makeFile,
|
writeFiles,
|
||||||
makeHash,
|
makeHash,
|
||||||
checkIfAlreadyExists,
|
checkIfAlreadyExists,
|
||||||
getObjectUrl,
|
getObjectUrl,
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ ARG DEBIAN_FRONTEND=noninteractive
|
|||||||
RUN apt-get update -qq
|
RUN apt-get update -qq
|
||||||
# double check this below, I'm not sure we need inkscape etc
|
# 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 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 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
|
# install node14, see comment at the to of node14source_setup.sh
|
||||||
ADD common/node14source_setup.sh /nodesource_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 && \
|
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
|
unzip /var/task/openscad/1.0.4
|
||||||
# Add our own theming (based on DeepOcean with a different "background" and "opencsg-face-back")
|
# 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 openscad/*.js /var/task/
|
||||||
COPY common/*.js /var/common/
|
COPY common/*.js /var/common/
|
||||||
|
|||||||
@@ -57,8 +57,6 @@ const preview = async (req, _context, callback) => {
|
|||||||
s3,
|
s3,
|
||||||
tk
|
tk
|
||||||
),
|
),
|
||||||
consoleMessage:
|
|
||||||
previousAsset.consoleMessage || previousAssetPng.consoleMessage,
|
|
||||||
type,
|
type,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@@ -67,7 +65,10 @@ const preview = async (req, _context, callback) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { file, settings } = JSON.parse(eventBody)
|
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({
|
await storeAssetAndReturnUrl({
|
||||||
error,
|
error,
|
||||||
callback,
|
callback,
|
||||||
@@ -100,14 +101,16 @@ const stl = async (req, _context, callback) => {
|
|||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
url: getObjectUrl({ ...params }, s3, tk),
|
url: getObjectUrl({ ...params }, s3, tk),
|
||||||
consoleMessage: previousAsset.consoleMessage,
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
callback(null, response)
|
callback(null, response)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const { file } = JSON.parse(eventBody)
|
const { file, settings } = JSON.parse(eventBody)
|
||||||
const { error, consoleMessage, fullPath } = await stlExport({ file })
|
const { error, consoleMessage, fullPath, customizerPath } = await stlExport({
|
||||||
|
file,
|
||||||
|
settings,
|
||||||
|
})
|
||||||
await storeAssetAndReturnUrl({
|
await storeAssetAndReturnUrl({
|
||||||
error,
|
error,
|
||||||
callback,
|
callback,
|
||||||
@@ -121,6 +124,6 @@ const stl = async (req, _context, callback) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
stl: middy(stl).use(cors()),
|
stl: middy(loggerWrap(stl)).use(cors()),
|
||||||
preview: middy(loggerWrap(preview)).use(cors()),
|
preview: middy(loggerWrap(preview)).use(cors()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
const { makeFile, runCommand } = require('../common/utils')
|
const { writeFiles, runCommand } = require('../common/utils')
|
||||||
const { nanoid } = require('nanoid')
|
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 */
|
/** Removes our generated/hash filename with just "main.scad", so that it's a nice message in the IDE */
|
||||||
const cleanOpenScadError = (error) =>
|
const cleanOpenScadError = (error) =>
|
||||||
@@ -11,6 +12,7 @@ module.exports.runScad = async ({
|
|||||||
file,
|
file,
|
||||||
settings: {
|
settings: {
|
||||||
size: { x = 500, y = 500 } = {},
|
size: { x = 500, y = 500 } = {},
|
||||||
|
parameters,
|
||||||
camera: {
|
camera: {
|
||||||
position = { x: 40, y: 40, z: 40 },
|
position = { x: 40, y: 40, z: 40 },
|
||||||
rotation = { x: 55, y: 0, z: 25 },
|
rotation = { x: 55, y: 0, z: 25 },
|
||||||
@@ -18,46 +20,113 @@ module.exports.runScad = async ({
|
|||||||
} = {},
|
} = {},
|
||||||
} = {}, // TODO add view settings
|
} = {}, // 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: rx, y: ry, z: rz } = rotation
|
||||||
const { x: px, y: py, z: pz } = position
|
const { x: px, y: py, z: pz } = position
|
||||||
const cameraArg = `--camera=${px},${py},${pz},${rx},${ry},${rz},${dist}`
|
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 = [
|
const command = [
|
||||||
OPENSCAD_COMMON,
|
OPENSCAD_COMMON,
|
||||||
`-o ${fullPath}`,
|
`-o ${customizerPath}`,
|
||||||
|
`-o ${imPath}`,
|
||||||
|
`-p /tmp/${tempFile}/params.json -P default`,
|
||||||
cameraArg,
|
cameraArg,
|
||||||
`--imgsize=${x},${y}`,
|
`--imgsize=${x},${y}`,
|
||||||
`--colorscheme CadHub`,
|
`--colorscheme CadHub`,
|
||||||
`/tmp/${tempFile}/main.scad`,
|
`/tmp/${tempFile}/main.scad`,
|
||||||
`&& gzip ${fullPath}`,
|
|
||||||
].join(' ')
|
].join(' ')
|
||||||
console.log('command', command)
|
console.log('command', command)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const consoleMessage = await runCommand(command, 15000)
|
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) {
|
} catch (dirtyError) {
|
||||||
const error = cleanOpenScadError(dirtyError)
|
return { error: cleanOpenScadError(dirtyError) }
|
||||||
return { error }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.stlExport = async ({ file } = {}) => {
|
module.exports.stlExport = async ({ file, settings: { parameters } } = {}) => {
|
||||||
const tempFile = await makeFile(file, '.scad', nanoid)
|
const tempFile = await writeFiles(
|
||||||
const fullPath = `/tmp/${tempFile}/output.stl`
|
[
|
||||||
|
{ 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 = [
|
const command = [
|
||||||
OPENSCAD_COMMON,
|
OPENSCAD_COMMON,
|
||||||
`--export-format=binstl`,
|
// `--export-format=binstl`,
|
||||||
`-o ${fullPath}`,
|
`-o ${customizerPath}`,
|
||||||
|
`-o ${stlPath}`,
|
||||||
|
`-p /tmp/${tempFile}/params.json -P default`,
|
||||||
`/tmp/${tempFile}/main.scad`,
|
`/tmp/${tempFile}/main.scad`,
|
||||||
`&& gzip ${fullPath}`,
|
|
||||||
].join(' ')
|
].join(' ')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// lambda will time out before this, we might need to look at background jobs if we do git integration stl generation
|
// 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)
|
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) {
|
} catch (error) {
|
||||||
return { error, fullPath }
|
return { error, fullPath }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
CadhubNumberParam,
|
CadhubNumberParam,
|
||||||
CadhubStringChoiceParam,
|
CadhubStringChoiceParam,
|
||||||
CadhubNumberChoiceParam,
|
CadhubNumberChoiceParam,
|
||||||
CadhubChoiceParam,
|
|
||||||
} from './customizerConverter'
|
} from './customizerConverter'
|
||||||
|
|
||||||
const Customizer = () => {
|
const Customizer = () => {
|
||||||
@@ -82,17 +81,22 @@ const Customizer = () => {
|
|||||||
{customizerParams.map((param, index) => {
|
{customizerParams.map((param, index) => {
|
||||||
const otherProps = {
|
const otherProps = {
|
||||||
value: currentParameters[param.name],
|
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'){
|
if (
|
||||||
return <StringChoiceParam key={index} param={param} {...otherProps} />
|
param.input === 'choice-string' ||
|
||||||
// }else if(param.input === 'choice-number'){
|
param.input === 'choice-number'
|
||||||
// return <StringChoiceParam key={index} param={param} {...otherProps} />
|
) {
|
||||||
}else if (param.type === 'string') {
|
return <ChoiceParam key={index} param={param} {...otherProps} />
|
||||||
|
} else if (param.input === 'default-string') {
|
||||||
return <StringParam key={index} param={param} {...otherProps} />
|
return <StringParam key={index} param={param} {...otherProps} />
|
||||||
} else if (param.type === 'number') {
|
} else if (param.input === 'default-number') {
|
||||||
return <NumberParam key={index} param={param} {...otherProps} />
|
return <NumberParam key={index} param={param} {...otherProps} />
|
||||||
} else if (param.type === 'boolean') {
|
} else if (param.input === 'default-boolean') {
|
||||||
return <BooleanParam key={index} param={param} {...otherProps} />
|
return <BooleanParam key={index} param={param} {...otherProps} />
|
||||||
}
|
}
|
||||||
return <div key={index}>{JSON.stringify(param)}</div>
|
return <div key={index}>{JSON.stringify(param)}</div>
|
||||||
@@ -180,12 +184,12 @@ function StringParam({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function StringChoiceParam({
|
function ChoiceParam({
|
||||||
param,
|
param,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
}: {
|
}: {
|
||||||
param: CadhubChoiceParam
|
param: CadhubStringChoiceParam | CadhubNumberChoiceParam
|
||||||
value: any
|
value: any
|
||||||
onChange: Function
|
onChange: Function
|
||||||
}) {
|
}) {
|
||||||
@@ -196,7 +200,11 @@ function StringChoiceParam({
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={({ target }) => onChange(target?.value)}
|
onChange={({ target }) => onChange(target?.value)}
|
||||||
>
|
>
|
||||||
{param.options.map(opt=><option value={opt.value} key={opt.name}>{opt.name}</option>)}
|
{param.options.map((opt) => (
|
||||||
|
<option value={opt.value} key={opt.name}>
|
||||||
|
{opt.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
</select>
|
</select>
|
||||||
</CustomizerParamBase>
|
</CustomizerParamBase>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
// CadHub
|
// CadHub
|
||||||
|
|
||||||
type CadhubTypeNames = 'number' | 'string' | 'boolean'
|
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 {
|
export interface CadhubStringOption {
|
||||||
name: string
|
name: string
|
||||||
@@ -29,6 +34,7 @@ export interface CadhubStringParam extends CadhubParamBase {
|
|||||||
}
|
}
|
||||||
export interface CadhubBooleanParam extends CadhubParamBase {
|
export interface CadhubBooleanParam extends CadhubParamBase {
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
|
input: 'default-boolean'
|
||||||
initial?: boolean
|
initial?: boolean
|
||||||
}
|
}
|
||||||
export interface CadhubNumberParam extends CadhubParamBase {
|
export interface CadhubNumberParam extends CadhubParamBase {
|
||||||
@@ -60,91 +66,3 @@ export type CadhubParams =
|
|||||||
| CadhubNumberParam
|
| CadhubNumberParam
|
||||||
| CadhubStringChoiceParam
|
| CadhubStringChoiceParam
|
||||||
| CadhubNumberChoiceParam
|
| 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 },
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const IdeConsole = () => {
|
|||||||
{time?.toLocaleString()}
|
{time?.toLocaleString()}
|
||||||
</div>
|
</div>
|
||||||
<div className={(type === 'error' ? 'text-red-400' : '') + ' pl-4'}>
|
<div className={(type === 'error' ? 'text-red-400' : '') + ' pl-4'}>
|
||||||
{message.split('\n').map((line, index) => {
|
{(message || '').split('\n').map((line, index) => {
|
||||||
return (
|
return (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
{line.startsWith('ECHO:') ? (
|
{line.startsWith('ECHO:') ? (
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
timeoutErrorMessage,
|
timeoutErrorMessage,
|
||||||
RenderArgs,
|
RenderArgs,
|
||||||
DefaultKernelExport,
|
DefaultKernelExport,
|
||||||
|
splitGziped,
|
||||||
} from './common'
|
} from './common'
|
||||||
|
|
||||||
export const render: DefaultKernelExport['render'] = async ({
|
export const render: DefaultKernelExport['render'] = async ({
|
||||||
@@ -41,11 +42,19 @@ export const render: DefaultKernelExport['render'] = async ({
|
|||||||
return createUnhealthyResponse(new Date(), timeoutErrorMessage)
|
return createUnhealthyResponse(new Date(), timeoutErrorMessage)
|
||||||
}
|
}
|
||||||
const data = await response.json()
|
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({
|
return createHealthyResponse({
|
||||||
type: 'geometry',
|
type: 'geometry',
|
||||||
data: geometry,
|
data: newData.data,
|
||||||
consoleMessage: data.consoleMessage,
|
consoleMessage: newData.consoleMessage,
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ export const stlToGeometry = (url) =>
|
|||||||
|
|
||||||
export interface RenderArgs {
|
export interface RenderArgs {
|
||||||
code: State['code']
|
code: State['code']
|
||||||
parameters?: RawCustomizerParams
|
|
||||||
settings: {
|
settings: {
|
||||||
|
parameters?: RawCustomizerParams
|
||||||
camera: State['camera']
|
camera: State['camera']
|
||||||
viewerSize: State['viewerSize']
|
viewerSize: State['viewerSize']
|
||||||
quality: State['objectData']['quality']
|
quality: State['objectData']['quality']
|
||||||
@@ -103,3 +103,16 @@ export type RenderResponse = HealthyResponse | ErrorResponse
|
|||||||
export interface DefaultKernelExport {
|
export interface DefaultKernelExport {
|
||||||
render: (arg: RenderArgs) => Promise<RenderResponse>
|
render: (arg: RenderArgs) => Promise<RenderResponse>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DefaultKernelExport } from './common'
|
import { DefaultKernelExport } from './common'
|
||||||
import type { CadPackage } from 'src/helpers/hooks/useIdeState'
|
import type { CadPackage } from 'src/helpers/hooks/useIdeState'
|
||||||
|
|
||||||
import openscad from './openScadController'
|
import openscad from './openScad/openScadController'
|
||||||
import cadquery from './cadQueryController'
|
import cadquery from './cadQueryController'
|
||||||
import jscad from './jsCad/jsCadController'
|
import jscad from './jsCad/jsCadController'
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,6 @@ const workerHelper = new WorkerHelper()
|
|||||||
|
|
||||||
export const render: DefaultKernelExport['render'] = async ({
|
export const render: DefaultKernelExport['render'] = async ({
|
||||||
code,
|
code,
|
||||||
parameters,
|
|
||||||
settings,
|
settings,
|
||||||
}: RenderArgs) => {
|
}: RenderArgs) => {
|
||||||
if (!scriptWorker) {
|
if (!scriptWorker) {
|
||||||
@@ -169,11 +168,11 @@ export const render: DefaultKernelExport['render'] = async ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
workerHelper.resolver()
|
workerHelper.resolver(null)
|
||||||
scriptWorker.postMessage({ action: 'init', baseURI, alias: [] })
|
scriptWorker.postMessage({ action: 'init', baseURI, alias: [] })
|
||||||
}
|
}
|
||||||
|
|
||||||
return workerHelper.render(code, parameters)
|
return workerHelper.render(code, settings.parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
const jsCadController: DefaultKernelExport = {
|
const jsCadController: DefaultKernelExport = {
|
||||||
|
|||||||
@@ -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 =
|
type JscadTypeNames =
|
||||||
| 'group'
|
| 'group'
|
||||||
@@ -19,6 +25,7 @@ interface JscadParamBase {
|
|||||||
type: JscadTypeNames
|
type: JscadTypeNames
|
||||||
caption: string
|
caption: string
|
||||||
name: string
|
name: string
|
||||||
|
initial?: number | string | boolean
|
||||||
}
|
}
|
||||||
interface JscadGroupParam extends JscadParamBase {
|
interface JscadGroupParam extends JscadParamBase {
|
||||||
type: 'group'
|
type: 'group'
|
||||||
@@ -81,6 +88,10 @@ type JsCadParams =
|
|||||||
export function jsCadToCadhubParams(input: JsCadParams[]): CadhubParams[] {
|
export function jsCadToCadhubParams(input: JsCadParams[]): CadhubParams[] {
|
||||||
return input
|
return input
|
||||||
.map((param): CadhubParams => {
|
.map((param): CadhubParams => {
|
||||||
|
const common: { caption: string; name: string } = {
|
||||||
|
caption: param.caption,
|
||||||
|
name: param.name,
|
||||||
|
}
|
||||||
switch (param.type) {
|
switch (param.type) {
|
||||||
case 'slider':
|
case 'slider':
|
||||||
case 'number':
|
case 'number':
|
||||||
@@ -88,8 +99,7 @@ export function jsCadToCadhubParams(input: JsCadParams[]): CadhubParams[] {
|
|||||||
return {
|
return {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
input: 'default-number',
|
input: 'default-number',
|
||||||
caption: param.caption,
|
...common,
|
||||||
name: param.name,
|
|
||||||
initial: param.initial,
|
initial: param.initial,
|
||||||
min: param.min,
|
min: param.min,
|
||||||
max: param.max,
|
max: param.max,
|
||||||
@@ -105,8 +115,7 @@ export function jsCadToCadhubParams(input: JsCadParams[]): CadhubParams[] {
|
|||||||
return {
|
return {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
input: 'default-string',
|
input: 'default-string',
|
||||||
caption: param.caption,
|
...common,
|
||||||
name: param.name,
|
|
||||||
initial: param.initial,
|
initial: param.initial,
|
||||||
placeholder:
|
placeholder:
|
||||||
param.type === 'text' ||
|
param.type === 'text' ||
|
||||||
@@ -123,43 +132,38 @@ export function jsCadToCadhubParams(input: JsCadParams[]): CadhubParams[] {
|
|||||||
return {
|
return {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
input: 'default-boolean',
|
input: 'default-boolean',
|
||||||
caption: param.caption,
|
...common,
|
||||||
name: param.name,
|
|
||||||
initial: !!param.initial,
|
initial: !!param.initial,
|
||||||
}
|
}
|
||||||
case 'choice':
|
case 'choice':
|
||||||
case 'radio':
|
case 'radio':
|
||||||
if(typeof param.values[0] === 'number'){
|
if (typeof param.values[0] === 'number') {
|
||||||
let options:Array<CadhubNumberOption> = []
|
const options: Array<CadhubNumberOption> = []
|
||||||
let captions = param.captions || param.values
|
const captions = param.captions || param.values
|
||||||
param.values.forEach((value,i)=>{
|
param.values.forEach((value, i) => {
|
||||||
options[i] = {name:String(captions[i]), value:Number(value)}
|
options[i] = { name: String(captions[i]), value: Number(value) }
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
input: 'choice-number',
|
input: 'choice-number',
|
||||||
caption: param.caption,
|
...common,
|
||||||
name: param.name,
|
|
||||||
initial: Number(param.initial),
|
initial: Number(param.initial),
|
||||||
options
|
options,
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
let options:Array<CadhubStringOption> = []
|
const options: Array<CadhubStringOption> = []
|
||||||
let captions = param.captions || param.values
|
const captions = param.captions || param.values
|
||||||
param.values.forEach((value,i)=>{
|
param.values.forEach((value, i) => {
|
||||||
options[i] = {name:String(captions[i]), value:String(value)}
|
options[i] = { name: String(captions[i]), value: String(value) }
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
input: 'choice-string',
|
input: 'choice-string',
|
||||||
caption: param.caption,
|
...common,
|
||||||
name: param.name,
|
|
||||||
initial: String(param.initial),
|
initial: String(param.initial),
|
||||||
options
|
options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter((a) => a)
|
.filter((a) => a)
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import {
|
|||||||
createUnhealthyResponse,
|
createUnhealthyResponse,
|
||||||
timeoutErrorMessage,
|
timeoutErrorMessage,
|
||||||
RenderArgs,
|
RenderArgs,
|
||||||
} from './common'
|
splitGziped,
|
||||||
|
} from '../common'
|
||||||
|
import { openScadToCadhubParams } from './openScadParams'
|
||||||
|
|
||||||
export const render = async ({ code, settings }: RenderArgs) => {
|
export const render = async ({ code, settings }: RenderArgs) => {
|
||||||
const pixelRatio = window.devicePixelRatio || 1
|
const pixelRatio = window.devicePixelRatio || 1
|
||||||
@@ -17,6 +19,7 @@ export const render = async ({ code, settings }: RenderArgs) => {
|
|||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
settings: {
|
settings: {
|
||||||
size,
|
size,
|
||||||
|
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
|
||||||
...settings.camera,
|
...settings.camera,
|
||||||
@@ -56,15 +59,25 @@ export const render = async ({ code, settings }: RenderArgs) => {
|
|||||||
}
|
}
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
const type = data.type !== 'stl' ? 'png' : 'geometry'
|
const type = data.type !== 'stl' ? 'png' : 'geometry'
|
||||||
const newData =
|
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'
|
data.type !== 'stl'
|
||||||
? fetch(data.url).then((a) => a.blob())
|
? blob
|
||||||
: stlToGeometry(data.url)
|
: await stlToGeometry(window.URL.createObjectURL(blob)),
|
||||||
|
consoleMessage,
|
||||||
|
customizerParams,
|
||||||
|
}
|
||||||
|
})
|
||||||
return createHealthyResponse({
|
return createHealthyResponse({
|
||||||
type,
|
type,
|
||||||
data: await newData,
|
data: newData.data,
|
||||||
consoleMessage: data.consoleMessage,
|
consoleMessage: newData.consoleMessage,
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
|
customizerParams: openScadToCadhubParams(newData.customizerParams || []),
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return createUnhealthyResponse(new Date())
|
return createUnhealthyResponse(new Date())
|
||||||
@@ -93,12 +106,22 @@ export const stl = async ({ code, settings }: RenderArgs) => {
|
|||||||
return createUnhealthyResponse(new Date(), timeoutErrorMessage)
|
return createUnhealthyResponse(new Date(), timeoutErrorMessage)
|
||||||
}
|
}
|
||||||
const data = await response.json()
|
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({
|
return createHealthyResponse({
|
||||||
type: 'geometry',
|
type: 'geometry',
|
||||||
data: geometry,
|
data: newData.data,
|
||||||
consoleMessage: data.consoleMessage,
|
consoleMessage: newData.consoleMessage,
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
|
customizerParams: openScadToCadhubParams(newData.customizerParams || []),
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return createUnhealthyResponse(new Date())
|
return createUnhealthyResponse(new Date())
|
||||||
102
app/web/src/helpers/cadPackages/openScad/openScadParams.ts
Normal file
102
app/web/src/helpers/cadPackages/openScad/openScadParams.ts
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -51,13 +51,12 @@ show_object(result)
|
|||||||
|
|
||||||
const jscad = require('@jscad/modeling')
|
const jscad = require('@jscad/modeling')
|
||||||
// https://openjscad.xyz/docs/module-modeling_primitives.html
|
// 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 { degToRad } = jscad.utils // because jscad uses radians for rotations
|
||||||
const { colorize } = jscad.colors
|
|
||||||
// https://openjscad.xyz/docs/module-modeling_booleans.html
|
// https://openjscad.xyz/docs/module-modeling_booleans.html
|
||||||
const { union, intersect, subtract } = jscad.booleans
|
const { subtract } = jscad.booleans
|
||||||
|
|
||||||
function main({//@jscad-params
|
function main({//@jscad-params
|
||||||
// Box example
|
// Box example
|
||||||
@@ -66,6 +65,7 @@ function main({//@jscad-params
|
|||||||
height=10, // Height
|
height=10, // Height
|
||||||
hole=3,// Hole for cables diameter (0=no hole)
|
hole=3,// Hole for cables diameter (0=no hole)
|
||||||
wall=1, // wall {min:0.5, step:0.5}
|
wall=1, // wall {min:0.5, step:0.5}
|
||||||
|
flip=0, // print orientation {type: 'choice', values: [0, 90, 180]}
|
||||||
}){
|
}){
|
||||||
|
|
||||||
let wallOffset = wall * 2
|
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})))
|
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}
|
module.exports = {main}
|
||||||
@@ -281,8 +281,8 @@ export const requestRender = ({
|
|||||||
: cadPackages[state.ideType].render
|
: cadPackages[state.ideType].render
|
||||||
return renderFn({
|
return renderFn({
|
||||||
code,
|
code,
|
||||||
parameters,
|
|
||||||
settings: {
|
settings: {
|
||||||
|
parameters,
|
||||||
camera,
|
camera,
|
||||||
viewerSize,
|
viewerSize,
|
||||||
quality,
|
quality,
|
||||||
|
|||||||
Reference in New Issue
Block a user