set up local development for openscad lamdas
and make a start on combining with Three.js IdeViewer Resolves #227 related #231
This commit is contained in:
11
api/src/docker/Dockerfile
Normal file
11
api/src/docker/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM node:14
|
||||||
|
|
||||||
|
COPY package.json ./
|
||||||
|
COPY yarn.lock ./
|
||||||
|
COPY aws-emulator.js ./
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["node", "./aws-emulator.js"]
|
||||||
@@ -9,14 +9,25 @@ But Kurt Hutten credentials for deployment, though if you wanted to set your own
|
|||||||
|
|
||||||
## testing changes locally
|
## testing changes locally
|
||||||
|
|
||||||
Honestly it's a pain in the neck, for a number of reasons.
|
|
||||||
|
|
||||||
You'll need to have Docker installed
|
You'll need to have Docker installed
|
||||||
|
|
||||||
Because of the way the docker containers to be deployed as lambdas on aws are somewhat specialised for the purpose we're using `docker-compose` to spin one up for each function/endpoint. But more work needs to be done to get this to work with the app locally.
|
Because of the way the docker containers to be deployed as lambdas on aws are somewhat specialised for the purpose we're using `docker-compose` to spin one up for each function/endpoint. So we've added a aws-emulation layer
|
||||||
|
|
||||||
|
first cd into this folder `cd api/src/docker`
|
||||||
|
then
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose up --build
|
docker-compose up --build
|
||||||
```
|
```
|
||||||
The first time you run the, when it has to build the main image it will take some time, but launching again will be quicker.
|
The first time you run the, when it has to build the main image it will take some time, but launching again will be quicker.
|
||||||
|
|
||||||
|
After which we'll also spin up a light express server to act as an emulator to transform some the request from the front end into how the lambda's expect them.
|
||||||
|
```
|
||||||
|
yarn install
|
||||||
|
yarn emulate
|
||||||
|
```
|
||||||
|
You can now change the url in `web/src/helpers/cadPackages/openScadController.js` from the aws url to `http://localhost:8080`
|
||||||
|
|
||||||
|
If you change anything in the `api/src/docker/openscad` directory, you will need to stop the docker process and restart it (will be fairly quick if you're only changing the js)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
34
api/src/docker/aws-emulator.js
Normal file
34
api/src/docker/aws-emulator.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
const express = require('express')
|
||||||
|
var cors = require('cors')
|
||||||
|
const axios = require('axios')
|
||||||
|
const stream = require('stream')
|
||||||
|
const app = express()
|
||||||
|
const port = 8080
|
||||||
|
app.use(express.json())
|
||||||
|
app.use(cors())
|
||||||
|
|
||||||
|
const invocationURL = (port) =>
|
||||||
|
`http://localhost:${port}/2015-03-31/functions/function/invocations`
|
||||||
|
|
||||||
|
app.post('/render', async (req, res) => {
|
||||||
|
const { data } = await axios.post(invocationURL(5052), {
|
||||||
|
body: Buffer.from(JSON.stringify(req.body)).toString('base64'),
|
||||||
|
})
|
||||||
|
if (data.statusCode !== 200) {
|
||||||
|
res.status(data.statusCode)
|
||||||
|
res.send(res.body)
|
||||||
|
} else {
|
||||||
|
const fileContents = Buffer.from(data.body, 'base64')
|
||||||
|
|
||||||
|
const readStream = new stream.PassThrough()
|
||||||
|
readStream.end(fileContents)
|
||||||
|
|
||||||
|
res.set('Content-disposition', 'attachment; filename=' + 'output')
|
||||||
|
res.set('Content-Type', 'image/png')
|
||||||
|
readStream.pipe(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Example app listening at http://localhost:${port}`)
|
||||||
|
})
|
||||||
@@ -1,16 +1,27 @@
|
|||||||
services:
|
services:
|
||||||
|
# aws-emulator:
|
||||||
|
# build: .
|
||||||
|
# networks:
|
||||||
|
# - awsland
|
||||||
|
# ports:
|
||||||
|
# - "5050:8080"
|
||||||
|
|
||||||
openscad-health:
|
openscad-health:
|
||||||
build: ./openscad/.
|
build: ./openscad/.
|
||||||
image: openscad
|
image: openscad
|
||||||
command: openscad.health
|
command: openscad.health
|
||||||
ports:
|
ports:
|
||||||
- "5051:8080"
|
- "5051:8080"
|
||||||
|
|
||||||
openscad-render:
|
openscad-render:
|
||||||
image: openscad
|
image: openscad
|
||||||
# build: ./openscad/.
|
# build: ./openscad/.
|
||||||
command: openscad.render
|
command: openscad.render
|
||||||
|
# networks:
|
||||||
|
# - awsland
|
||||||
ports:
|
ports:
|
||||||
- "5052:8080"
|
- "5052:8080"
|
||||||
|
|
||||||
openscad-export:
|
openscad-export:
|
||||||
image: openscad
|
image: openscad
|
||||||
# build: ./openscad/.
|
# build: ./openscad/.
|
||||||
@@ -18,3 +29,6 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5053:8080"
|
- "5053:8080"
|
||||||
|
|
||||||
|
# networks:
|
||||||
|
# awsland:
|
||||||
|
# name: awsland
|
||||||
|
|||||||
@@ -5,15 +5,23 @@ const { nanoid } = require('nanoid')
|
|||||||
|
|
||||||
module.exports.runScad = async ({
|
module.exports.runScad = async ({
|
||||||
file,
|
file,
|
||||||
settings: { size: { x = 500, y = 500 } = {} } = {}, // TODO add view settings
|
settings: {
|
||||||
|
size: { x = 500, y = 500 } = {},
|
||||||
|
camera: {
|
||||||
|
position = { x: 40, y: 40, z: 40 },
|
||||||
|
rotation = { x: 55, y: 0, z: 25 },
|
||||||
|
} = {},
|
||||||
|
} = {}, // TODO add view settings
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
const tempFile = await makeFile(file)
|
const tempFile = await makeFile(file)
|
||||||
|
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},300`
|
||||||
|
const command = `xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" openscad -o /tmp/${tempFile}/output.png ${cameraArg} --imgsize=${x},${y} /tmp/${tempFile}/main.scad`
|
||||||
|
console.log('command', command)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await runCommand(
|
const result = await runCommand(command, 10000)
|
||||||
`xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" openscad -o /tmp/${tempFile}/output.png --imgsize=${x},${y} /tmp/${tempFile}/main.scad`,
|
|
||||||
10000
|
|
||||||
)
|
|
||||||
return { result, tempFile }
|
return { result, tempFile }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { error, tempFile }
|
return { error, tempFile }
|
||||||
|
|||||||
20
api/src/docker/package.json
Normal file
20
api/src/docker/package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "aws-emulator",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "thin layer so that we can use docker lambdas locally",
|
||||||
|
"scripts": {
|
||||||
|
"lambdas": "docker-compose up --build",
|
||||||
|
"emulate": "nodemon ./aws-emulator.js",
|
||||||
|
"watch": "concurrently \"yarn lambdas\" \"yarn emulate\""
|
||||||
|
},
|
||||||
|
"main": "aws-emulator.js",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.17.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"concurrently": "^6.0.0",
|
||||||
|
"nodemon": "^2.0.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,8 @@ service: cad-lambdas
|
|||||||
#app: your-app-name
|
#app: your-app-name
|
||||||
#org: your-org-name
|
#org: your-org-name
|
||||||
|
|
||||||
plugins:
|
# plugins:
|
||||||
- serverless-offline
|
# - serverless-offline
|
||||||
|
|
||||||
# You can pin your service to only deploy with a specific Serverless version
|
# You can pin your service to only deploy with a specific Serverless version
|
||||||
# Check out our docs for more details
|
# Check out our docs for more details
|
||||||
|
|||||||
1479
api/src/docker/yarn.lock
Normal file
1479
api/src/docker/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@
|
|||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-image-crop": "^8.6.6",
|
"react-image-crop": "^8.6.6",
|
||||||
"react-mosaic-component": "^4.1.1",
|
"react-mosaic-component": "^4.1.1",
|
||||||
|
"react-three-fiber": "^5.3.19",
|
||||||
"rich-markdown-editor": "^11.0.2",
|
"rich-markdown-editor": "^11.0.2",
|
||||||
"styled-components": "^5.2.0",
|
"styled-components": "^5.2.0",
|
||||||
"three": "^0.118.3"
|
"three": "^0.118.3"
|
||||||
|
|||||||
@@ -1,25 +1,133 @@
|
|||||||
import { useContext } from 'react'
|
|
||||||
import { IdeContext } from 'src/components/IdeToolbarNew'
|
import { IdeContext } from 'src/components/IdeToolbarNew'
|
||||||
|
import { useRef, useState, useEffect, useContext, useMemo } from 'react'
|
||||||
|
import { Canvas, extend, useFrame, useThree } from 'react-three-fiber'
|
||||||
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||||
|
import { Vector3 } from 'three'
|
||||||
|
|
||||||
|
extend({ OrbitControls })
|
||||||
|
|
||||||
|
function Controls({ onCameraChange }) {
|
||||||
|
const controls = useRef()
|
||||||
|
const { scene, camera, gl } = useThree()
|
||||||
|
useEffect(() => {
|
||||||
|
// init camera position
|
||||||
|
camera.position.x = 12
|
||||||
|
camera.position.y = 12
|
||||||
|
|
||||||
|
if (controls.current) {
|
||||||
|
const callback = ({ target }) => {
|
||||||
|
// TODO figure out Three's rotations to make it match openscad's camera
|
||||||
|
const vector = new Vector3()
|
||||||
|
camera.getWorldDirection(vector)
|
||||||
|
console.log(Object.values(vector).map((val) => (val * 180) / Math.PI))
|
||||||
|
const { _x, _y, _z } = target.object.rotation
|
||||||
|
const rad2Deg = 180 / Math.PI
|
||||||
|
const [x, y, z] = [_x * rad2Deg, -_y * rad2Deg, _z * rad2Deg]
|
||||||
|
console.log({ y, z, x })
|
||||||
|
console.log(target, 'target')
|
||||||
|
onCameraChange({
|
||||||
|
position: target.object.position,
|
||||||
|
rotation: { x, y, z },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
controls.current.addEventListener('end', callback)
|
||||||
|
return () => controls.current.removeEventListener('end', callback)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useFrame(() => controls.current.update())
|
||||||
|
return (
|
||||||
|
<orbitControls
|
||||||
|
ref={controls}
|
||||||
|
args={[camera, gl.domElement]}
|
||||||
|
enableDamping
|
||||||
|
dampingFactor={0.1}
|
||||||
|
rotateSpeed={0.5}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Box(props) {
|
||||||
|
// This reference will give us direct access to the mesh
|
||||||
|
const mesh = useRef()
|
||||||
|
|
||||||
|
// Set up state for the hovered and active state
|
||||||
|
const [hovered, setHover] = useState(false)
|
||||||
|
const [active, setActive] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh
|
||||||
|
{...props}
|
||||||
|
ref={mesh}
|
||||||
|
scale={[1, 1, 1]}
|
||||||
|
onClick={(event) => setActive(!active)}
|
||||||
|
onPointerOver={(event) => setHover(true)}
|
||||||
|
onPointerOut={(event) => setHover(false)}
|
||||||
|
>
|
||||||
|
<boxBufferGeometry args={[10, 10, 10]} />
|
||||||
|
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
|
||||||
|
</mesh>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const IdeViewer = () => {
|
const IdeViewer = () => {
|
||||||
const { state } = useContext(IdeContext)
|
const { state, dispatch } = useContext(IdeContext)
|
||||||
const image =
|
const [isDragging, setIsDragging] = useState(false)
|
||||||
state.objectData?.type === 'png' &&
|
|
||||||
state.objectData?.data &&
|
const image = useMemo(
|
||||||
window.URL.createObjectURL(state.objectData?.data)
|
() =>
|
||||||
|
state.objectData?.type === 'png' &&
|
||||||
|
state.objectData?.data &&
|
||||||
|
window.URL.createObjectURL(state.objectData?.data),
|
||||||
|
[state.objectData]
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
<div className="p-8 border-2 m-2">
|
<div className="p-8 border-2 m-2">
|
||||||
<div className="pb-4">hi I'm viewer</div>
|
<div className="pb-4">
|
||||||
<div>
|
Rotating the Three.js Cube should than update the openscad camera to
|
||||||
I should be showing an{' '}
|
view the CAD object from the same angle. But having trouble translating
|
||||||
<span className="font-mono uppercase">{state.objectData?.type}</span>{' '}
|
between the two coordinate systems. OpenScad's camera only changes X and
|
||||||
right now with the data{' '}
|
Z angles and Y is always 0. Where as rotation values from Three seem to
|
||||||
|
change all x, y and z. I probably need to learn a bit more about 3d
|
||||||
|
math.
|
||||||
</div>
|
</div>
|
||||||
{image && (
|
<div className="relative" style={{ height: '500px', width: '500px' }}>
|
||||||
<div>
|
{image && (
|
||||||
<img src={image} className="" />
|
<div
|
||||||
|
className="absolute inset-0"
|
||||||
|
style={{ opacity: isDragging ? '0%' : '100%' }}
|
||||||
|
>
|
||||||
|
<img src={image} className="" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={`opacity-0 absolute inset-0 ${
|
||||||
|
isDragging ? 'opacity-100' : 'hover:opacity-50'
|
||||||
|
}`}
|
||||||
|
onMouseDown={() => setIsDragging(true)}
|
||||||
|
onMouseUp={() => setIsDragging(false)}
|
||||||
|
>
|
||||||
|
<Canvas>
|
||||||
|
<Controls
|
||||||
|
onCameraChange={({ position, rotation }) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'render',
|
||||||
|
payload: {
|
||||||
|
code: state.code,
|
||||||
|
camera: {
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ambientLight />
|
||||||
|
<pointLight position={[15, 5, 10]} />
|
||||||
|
<Box position={[0, 0, 0]} />
|
||||||
|
</Canvas>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
|
// const openScadBaseURL = 'http://localhost:8080' // for local development
|
||||||
const openScadBaseURL =
|
const openScadBaseURL =
|
||||||
'https://x2wvhihk56.execute-api.us-east-1.amazonaws.com/dev'
|
'https://x2wvhihk56.execute-api.us-east-1.amazonaws.com/dev'
|
||||||
|
|
||||||
export const render = async ({ code, settings }) => {
|
export const render = async ({ code, settings }) => {
|
||||||
|
const body = JSON.stringify({
|
||||||
|
settings: {
|
||||||
|
size: {
|
||||||
|
x: 500,
|
||||||
|
y: 500,
|
||||||
|
},
|
||||||
|
camera: settings.camera,
|
||||||
|
},
|
||||||
|
file: code,
|
||||||
|
})
|
||||||
const response = await fetch(openScadBaseURL + '/render', {
|
const response = await fetch(openScadBaseURL + '/render', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: new Headers().append('Content-Type', 'application/json'),
|
headers: {
|
||||||
body: JSON.stringify({
|
'Content-Type': 'application/json',
|
||||||
settings: {
|
},
|
||||||
size: {
|
body,
|
||||||
x: 700,
|
|
||||||
y: 300,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
file: code,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
if (response.status === 400) {
|
if (response.status === 400) {
|
||||||
const { error } = await response.json()
|
const { error } = await response.json()
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const useIdeState = () => {
|
|||||||
second: 'Console',
|
second: 'Console',
|
||||||
splitPercentage: 70,
|
splitPercentage: 70,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
const reducer = (state, { type, payload }) => {
|
const reducer = (state, { type, payload }) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -56,6 +56,8 @@ export const useIdeState = () => {
|
|||||||
...state,
|
...state,
|
||||||
layout: payload.message,
|
layout: payload.message,
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +66,10 @@ export const useIdeState = () => {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case 'render':
|
case 'render':
|
||||||
cadPackages[state.ideType]
|
cadPackages[state.ideType]
|
||||||
.render({ code: payload.code })
|
.render({
|
||||||
|
code: payload.code,
|
||||||
|
settings: { camera: payload.camera },
|
||||||
|
})
|
||||||
.then(({ objectData, message, isError }) => {
|
.then(({ objectData, message, isError }) => {
|
||||||
if (isError) {
|
if (isError) {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|||||||
Reference in New Issue
Block a user