Preview and stl mvp working

This commit is contained in:
Kurt Hutten
2021-11-21 17:49:22 +11:00
parent 8bdfec02d9
commit b1ff7796b9
8 changed files with 57 additions and 110 deletions

View File

@@ -1,9 +1,10 @@
{ {
"cSpell.words": [ "cSpell.words": [
"Cadhub", "Cadhub",
"cadquery",
"curv",
"Customizer", "Customizer",
"Hutten", "Hutten",
"cadquery",
"jscad", "jscad",
"openscad", "openscad",
"sendmail" "sendmail"

View File

@@ -34,8 +34,12 @@ const makeRequest = (route, port) => [
app.post(...makeRequest('/openscad/preview', 5052)) app.post(...makeRequest('/openscad/preview', 5052))
app.post(...makeRequest('/openscad/stl', 5053)) app.post(...makeRequest('/openscad/stl', 5053))
app.post(...makeRequest('/cadquery/stl', 5060)) app.post(...makeRequest('/cadquery/stl', 5060))
app.post(...makeRequest('/curv/preview', 5070))
app.post(...makeRequest('/curv/stl', 5071))
app.listen(port, () => { app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`) console.log(`Example app listening at http://localhost:${port}`)
}) })

View File

@@ -2,17 +2,27 @@ FROM public.ecr.aws/lts/ubuntu:20.04_stable
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update --fix-missing -qq RUN apt-get update --fix-missing -qq
RUN apt-get -y -qq install git software-properties-common dirmngr apt-transport-https lsb-release ca-certificates xvfb unzip maim clang cmake git-core libboost-all-dev libopenexr-dev libtbb-dev libglm-dev libpng-dev libeigen3-dev dbus-x11 libxcursor-dev libxinerama-dev libxrandr-dev libglu1-mesa-dev libgles2-mesa-dev libgl1-mesa-dev libxi-dev RUN apt-get update --fix-missing && apt-get -y -qq install software-properties-common dirmngr apt-transport-https lsb-release ca-certificates xvfb
RUN git clone https://github.com/curv3d/curv.git
RUN cd curv RUN apt-get update -qq
RUN git submodule init && git submodule update
RUN mkdir build RUN apt-get -y -qq install git
RUN cd build RUN apt-get update --fix-missing && apt-get -y --fix-broken install python-pycurl python-apt
RUN sed -i 's/set(LEAN_BUILD 0)/set(LEAN_BUILD 1)/' ../CMakeLists.txt RUN apt-get -y -qq --fix-broken install software-properties-common
RUN cmake ..
RUN make RUN apt-get -y -qq install xvfb unzip maim clang cmake
RUN make install RUN apt-get -y -qq install git-core libboost-all-dev
RUN apt-get -y -qq install libopenexr-dev libtbb-dev
RUN apt-get -y -qq install libglm-dev libpng-dev
RUN apt-get -y -qq install libeigen3-dev dbus-x11
RUN apt-get -y -qq install libxcursor-dev libxinerama-dev
RUN apt-get -y -qq install libxrandr-dev libglu1-mesa-dev
RUN apt-get -y -qq install libgles2-mesa-dev libgl1-mesa-dev
RUN apt-get -y -qq install libxi-dev
RUN git clone --recursive https://github.com/curv3d/curv; cd curv; make; make install
# install node14, see comment at the top of node14source_setup.sh # install node14, see comment at the top of node14source_setup.sh
ADD src/docker/common/node14source_setup.sh /nodesource_setup.sh ADD src/docker/common/node14source_setup.sh /nodesource_setup.sh
@@ -46,7 +56,7 @@ RUN echo "cadhub-concat-split" > /var/task/cadhub-concat-split
# using built javascript from dist # using built javascript from dist
# run `yarn rw build` before bulding this image # run `yarn rw build` before bulding this image
COPY dist/docker/openscad/* /var/task/js/ COPY dist/docker/curv/* /var/task/js/
COPY dist/docker/common/* /var/task/common/ COPY dist/docker/common/* /var/task/common/
COPY src/docker/common/entrypoint.sh /entrypoint.sh COPY src/docker/common/entrypoint.sh /entrypoint.sh
RUN ["chmod", "+x", "/entrypoint.sh"] RUN ["chmod", "+x", "/entrypoint.sh"]

View File

@@ -1,21 +1,5 @@
import { writeFiles, runCommand } from '../common/utils' import { writeFiles, runCommand } from '../common/utils'
import { nanoid } from 'nanoid' import { nanoid } from 'nanoid'
const { readFile } = require('fs/promises')
const fs = require('fs');
const { spawn } = require('child_process');
function* getXDisplayNumber() {
const startValue = 99;
let i = startValue;
while(true) {
i -= 1;
// Never hit zero since 0 is usually used by desktop users.
if (i <= 0) i = startValue;
yield ':' + i;
}
}
export const runCurv = async ({ export const runCurv = async ({
file, file,
@@ -54,61 +38,23 @@ export const runCurv = async ({
const fullPath = `/tmp/${tempFile}/output.gz` const fullPath = `/tmp/${tempFile}/output.gz`
const imPath = `/tmp/${tempFile}/output.png` const imPath = `/tmp/${tempFile}/output.png`
const customizerPath = `/tmp/${tempFile}/customizer.param` const customizerPath = `/tmp/${tempFile}/customizer.param`
const summaryPath = `/tmp/${tempFile}/summary.json` // contains camera info
const code = file;
const DISPLAY = getXDisplayNumber().next().value;
const xvfbProcess = spawn(
'Xvfb',
[DISPLAY, '-ac', '-nocursor', '-screen', '0', '480x500x24'],
);
const curvProcess = spawn(
'curv',
['-' ],
{ env: Object.assign({}, process.env, { DISPLAY, PATH: '/usr/local/bin' }) }
);
curvProcess.stdin.write(code);
curvProcess.stdin.end();
let statusSet = false;
let contentTypeSet = false;
curvProcess.stderr.on('data', (buf) => {
const data = buf.toString('utf8');
if (data.indexOf('shape') >= 0) {
setTimeout(() => {
const screenshotProcess = spawn('maim', ['--hidecursor', imPath], { env: { DISPLAY } });
screenshotProcess.on('close', () => {
curvProcess.kill();
xvfbProcess.kill();
});
}, 1000);
}
if (data.indexOf('ERROR') < 0) { return; }
curvProcess.kill();
xvfbProcess.kill();
});
const command = [
'xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" curv',
`-o ${imPath}`,
`-O xsize=${x}`,
`-O ysize=${y}`,
`-O bg=webRGB[26,26,29]`, // #1A1A1D
`/tmp/${tempFile}/main.curv`,
].join(' ')
console.log('command', command)
try { try {
const files: string[] = await Promise.all( const consoleMessage = await runCommand(command, 15000)
[customizerPath, summaryPath].map((path) =>
readFile(path, { encoding: 'ascii' })
)
)
const [params, cameraInfo] = files.map((fileStr: string) =>
JSON.parse(fileStr)
)
await writeFiles( await writeFiles(
[ [
{ {
file: JSON.stringify({ file: JSON.stringify({
cameraInfo: viewAll ? cameraInfo.camera : undefined,
customizerParams: params.parameters,
consoleMessage, consoleMessage,
type: 'png', type: 'png',
}), }),
@@ -143,7 +89,6 @@ export const stlExport = async ({ file, settings: { parameters } } = {}) => {
) )
const fullPath = `/tmp/${tempFile}/output.gz` const fullPath = `/tmp/${tempFile}/output.gz`
const stlPath = `/tmp/${tempFile}/output.stl` const stlPath = `/tmp/${tempFile}/output.stl`
const customizerPath = `/tmp/${tempFile}/customizer.param`
const command = [ const command = [
'curv', 'curv',
'-o', stlPath, '-o', stlPath,
@@ -155,14 +100,10 @@ export const stlExport = async ({ file, settings: { parameters } } = {}) => {
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)
const params = JSON.parse(
await readFile(customizerPath, { encoding: 'ascii' })
).parameters
await writeFiles( await writeFiles(
[ [
{ {
file: JSON.stringify({ file: JSON.stringify({
customizerParams: params,
consoleMessage, consoleMessage,
type: 'stl', type: 'stl',
}), }),
@@ -175,7 +116,7 @@ export const stlExport = async ({ file, settings: { parameters } } = {}) => {
`cat ${stlPath} /var/task/cadhub-concat-split /tmp/${tempFile}/metadata.json | gzip > ${fullPath}`, `cat ${stlPath} /var/task/cadhub-concat-split /tmp/${tempFile}/metadata.json | gzip > ${fullPath}`,
15000 15000
) )
return { consoleMessage, fullPath, customizerPath } return { consoleMessage, fullPath }
} catch (error) { } catch (error) {
return { error, fullPath } return { error, fullPath }
} }

View File

@@ -53,11 +53,20 @@ services:
command: js/curv.preview command: js/curv.preview
# Adding volumes so that the containers can be restarted for js only changes in local dev # Adding volumes so that the containers can be restarted for js only changes in local dev
volumes: volumes:
- ../../dist/docker/openscad:/var/task/js/ - ../../dist/docker/curv:/var/task/js/
- ../../dist/docker/common:/var/task/common/ - ../../dist/docker/common:/var/task/common/
ports: ports:
- "5052:8080" - "5070:8080"
environment: curv-stl:
AWS_SECRET_ACCESS_KEY: "${DEV_AWS_SECRET_ACCESS_KEY}" build:
AWS_ACCESS_KEY_ID: "${DEV_AWS_ACCESS_KEY_ID}" context: ../../
BUCKET: "${DEV_BUCKET}" dockerfile: ./src/docker/curv/Dockerfile
image: curv
command: js/curv.stl
# Adding volumes so that the containers can be restarted for js only changes in local dev
volumes:
- ../../dist/docker/curv:/var/task/js/
- ../../dist/docker/common:/var/task/common/
ports:
- "5071:8080"

View File

@@ -6,7 +6,7 @@ module.exports = (config, { env }) => {
} }
}) })
config.module.rules.push({ config.module.rules.push({
test: /\.(md|jscad\.js|py|scad)$/i, test: /\.(md|jscad\.js|py|scad|curv)$/i,
use: 'raw-loader', use: 'raw-loader',
}); });
return config return config

View File

@@ -18,7 +18,7 @@ const IdeEditor = ({ Loading }) => {
cadquery: 'python', cadquery: 'python',
openscad: 'cpp', openscad: 'cpp',
jscad: 'javascript', jscad: 'javascript',
curv: 'javascript', curv: 'python',
INIT: '', INIT: '',
} }
const monaco = useMonaco() const monaco = useMonaco()

View File

@@ -7,8 +7,6 @@ import {
RenderArgs, RenderArgs,
splitGziped, splitGziped,
} from '../common' } from '../common'
import { CurvToCadhubParams } from './openScadParams'
import type { XYZ, Camera } from 'src/helpers/hooks/useIdeState'
export const render = async ({ code, settings }: RenderArgs) => { export const render = async ({ code, settings }: RenderArgs) => {
const pixelRatio = window.devicePixelRatio || 1 const pixelRatio = window.devicePixelRatio || 1
@@ -61,21 +59,8 @@ export const render = async ({ code, settings }: RenderArgs) => {
} }
const blob = await response.blob() const blob = await response.blob()
const text = await new Response(blob).text() const text = await new Response(blob).text()
const { consoleMessage, customizerParams, type, cameraInfo } = const { consoleMessage, type } =
splitGziped(text) splitGziped(text)
const vecArray2Obj = (arr: number[]): XYZ => ({
x: arr[0],
y: arr[1],
z: arr[2],
})
const camera: Camera = cameraInfo
? {
dist: cameraInfo?.distance,
position: vecArray2Obj(cameraInfo?.translation),
rotation: vecArray2Obj(cameraInfo?.rotation),
isScadUpdate: true,
}
: undefined
return createHealthyResponse({ return createHealthyResponse({
type: type !== 'stl' ? 'png' : 'geometry', type: type !== 'stl' ? 'png' : 'geometry',
data: data:
@@ -83,9 +68,7 @@ export const render = async ({ code, settings }: RenderArgs) => {
? blob ? blob
: await stlToGeometry(window.URL.createObjectURL(blob)), : await stlToGeometry(window.URL.createObjectURL(blob)),
consoleMessage, consoleMessage,
camera,
date: new Date(), date: new Date(),
customizerParams: curvToCadhubParams(customizerParams || []),
}) })
} catch (e) { } catch (e) {
return createUnhealthyResponse(new Date()) return createUnhealthyResponse(new Date())
@@ -115,7 +98,7 @@ export const stl = async ({ code /*settings*/ }: RenderArgs) => {
} }
const blob = await response.blob() const blob = await response.blob()
const text = await new Response(blob).text() const text = await new Response(blob).text()
const { consoleMessage, customizerParams, type } = splitGziped(text) const { consoleMessage, type } = splitGziped(text)
return createHealthyResponse({ return createHealthyResponse({
type: type !== 'stl' ? 'png' : 'geometry', type: type !== 'stl' ? 'png' : 'geometry',
data: data:
@@ -124,7 +107,6 @@ export const stl = async ({ code /*settings*/ }: RenderArgs) => {
: await stlToGeometry(window.URL.createObjectURL(blob)), : await stlToGeometry(window.URL.createObjectURL(blob)),
consoleMessage, consoleMessage,
date: new Date(), date: new Date(),
customizerParams: openScadToCadhubParams(customizerParams || []),
}) })
} catch (e) { } catch (e) {
return createUnhealthyResponse(new Date()) return createUnhealthyResponse(new Date())