set up local development for openscad lamdas #232

Merged
Irev-Dev merged 1 commits from kurt/227 into main 2021-03-10 09:04:37 +01:00
13 changed files with 4613 additions and 3490 deletions

11
api/src/docker/Dockerfile Normal file
View 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"]

View File

@@ -9,14 +9,25 @@ But Kurt Hutten credentials for deployment, though if you wanted to set your own
## testing changes locally
Honestly it's a pain in the neck, for a number of reasons.
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
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.
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)

View 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}`)
})

View File

@@ -1,16 +1,27 @@
services:
# aws-emulator:
# build: .
# networks:
# - awsland
# ports:
# - "5050:8080"
openscad-health:
build: ./openscad/.
image: openscad
command: openscad.health
ports:
- "5051:8080"
openscad-render:
image: openscad
# build: ./openscad/.
command: openscad.render
# networks:
# - awsland
ports:
- "5052:8080"
openscad-export:
image: openscad
# build: ./openscad/.
@@ -18,3 +29,6 @@ services:
ports:
- "5053:8080"
# networks:
# awsland:
# name: awsland

View File

@@ -5,15 +5,23 @@ const { nanoid } = require('nanoid')
module.exports.runScad = async ({
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 { 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 {
const result = await runCommand(
`xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" openscad -o /tmp/${tempFile}/output.png --imgsize=${x},${y} /tmp/${tempFile}/main.scad`,
10000
)
const result = await runCommand(command, 10000)
return { result, tempFile }
} catch (error) {
return { error, tempFile }

View 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"
}
}

View File

@@ -3,8 +3,8 @@ service: cad-lambdas
#app: your-app-name
#org: your-org-name
plugins:
- serverless-offline
# plugins:
# - serverless-offline
# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details

1479
api/src/docker/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,7 @@
"react-helmet": "^6.1.0",
"react-image-crop": "^8.6.6",
"react-mosaic-component": "^4.1.1",
"react-three-fiber": "^5.3.19",
"rich-markdown-editor": "^11.0.2",
"styled-components": "^5.2.0",
"three": "^0.118.3"

View File

@@ -1,25 +1,133 @@
import { useContext } from 'react'
import { IdeContext } from 'src/components/IdeToolbarNew'
import { useRef, useState, useEffect, useContext, useMemo } from 'react'
import { Canvas, extend, useFrame, useThree } from 'react-three-fiber'
Irev-Dev commented 2021-03-09 22:22:10 +01:00 (Migrated from github.com)
Review

This file is becoming pretty coupled to openscad, but I'm in the "get it working" stage. I'll try and seperate concerns after.

This file is becoming pretty coupled to openscad, but I'm in the "get it working" stage. I'll try and seperate concerns after.
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 { state } = useContext(IdeContext)
const image =
state.objectData?.type === 'png' &&
state.objectData?.data &&
window.URL.createObjectURL(state.objectData?.data)
const { state, dispatch } = useContext(IdeContext)
const [isDragging, setIsDragging] = useState(false)
const image = useMemo(
() =>
state.objectData?.type === 'png' &&
state.objectData?.data &&
window.URL.createObjectURL(state.objectData?.data),
[state.objectData]
)
return (
<div className="p-8 border-2 m-2">
<div className="pb-4">hi I'm viewer</div>
<div>
I should be showing an{' '}
<span className="font-mono uppercase">{state.objectData?.type}</span>{' '}
right now with the data{' '}
<div className="pb-4">
Rotating the Three.js Cube should than update the openscad camera to
view the CAD object from the same angle. But having trouble translating
between the two coordinate systems. OpenScad's camera only changes X and
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>
{image && (
<div>
<img src={image} className="" />
<div className="relative" style={{ height: '500px', width: '500px' }}>
{image && (
<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>
)
}

View File

@@ -1,19 +1,24 @@
// const openScadBaseURL = 'http://localhost:8080' // for local development
const openScadBaseURL =
'https://x2wvhihk56.execute-api.us-east-1.amazonaws.com/dev'
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', {
method: 'POST',
headers: new Headers().append('Content-Type', 'application/json'),
body: JSON.stringify({
settings: {
size: {
x: 700,
y: 300,
},
},
file: code,
}),
headers: {
Irev-Dev commented 2021-03-09 22:21:10 +01:00 (Migrated from github.com)
Review

express did not like this new Hearders business, and aws seems to be fine with regular json so I've changed it 🤷

express did not like this `new Hearders` business, and aws seems to be fine with regular json so I've changed it 🤷
'Content-Type': 'application/json',
},
body,
})
if (response.status === 400) {
const { error } = await response.json()

View File

@@ -22,7 +22,7 @@ export const useIdeState = () => {
second: 'Console',
splitPercentage: 70,
},
}
},
}
const reducer = (state, { type, payload }) => {
switch (type) {
@@ -56,6 +56,8 @@ export const useIdeState = () => {
...state,
layout: payload.message,
}
default:
return state
}
}
@@ -64,7 +66,10 @@ export const useIdeState = () => {
switch (type) {
case 'render':
cadPackages[state.ideType]
.render({ code: payload.code })
.render({
code: payload.code,
settings: { camera: payload.camera },
})
.then(({ objectData, message, isError }) => {
if (isError) {
dispatch({

6333
yarn.lock

File diff suppressed because it is too large Load Diff