For fun, I just tried running one of the JSCAD demos:
Note: If you want to try running this, you need the fetchInject plugin from several posts above.
<script setup>
const fetchInject = await import('plugin-library:fetch-inject');
import { onMounted, ref } from "vue";
const jscadDiv = ref(null);
onMounted(async () => {
await fetchInject([
'https://unpkg.com/@jscad/modeling@2.6.1',
'https://cdn.jsdelivr.net/gh/jscad/OpenJSCAD.org@%40jscad%2Fweb%402.6.1/packages/utils/regl-renderer/dist/jscad-regl-renderer.min.js',
]);
// Copied/modified from https://github.com/jscad/OpenJSCAD.org/blob/f245ea3a5072024b789f276c6fb9ce6c3eb3fd0d/packages/utils/regl-renderer/demo.html
// ********************
// The design to render.
// ********************
const { booleans, colors, primitives } = jscadModeling // modeling comes from the included MODELING library
const { intersect, subtract } = booleans
const { colorize } = colors
const { cube, cuboid, line, sphere, star } = primitives
const demo = (parameters) => {
const logo = [
colorize([1.0, 0.4, 1.0], subtract(
cube({ size: 300 }),
sphere({ radius: 200 })
)),
colorize([1.0, 1.0, 0], intersect(
sphere({ radius: 130 }),
cube({ size: 210 })
))
]
const transpCube = colorize([1, 0, 0, 0.75], cuboid({ size: [100 * parameters.scale, 100, 210 + (200 * parameters.scale)] }))
const star2D = star({ vertices: 8, innerRadius: 300, outerRadius: 400 })
const line2D = colorize([1.0, 0, 0], line([[260, 260], [-260, 260], [-260, -260], [260, -260], [260, 260]]))
// some colors are intentionally without alpha channel to test geom2ToGeometries will add alpha channel
const colorChange = [
[1, 0, 0, 1],
[1, 0.5, 0],
[1, 0, 1],
[0, 1, 0],
[0, 0, 0.7]
]
star2D.sides.forEach((side, i) => {
if (i >= 2) side.color = colorChange[i % colorChange.length]
})
return [transpCube, star2D, line2D, ...logo]
}
// ********************
// Renderer configuration and initiation.
// ********************
const { prepareRender, drawCommands, cameras, controls, entitiesFromSolids } = jscadReglRenderer
const perspectiveCamera = cameras.perspective
const orbitControls = controls.orbit
const containerElement = jscadDiv.value;
const width = containerElement.clientWidth
const height = containerElement.clientHeight
const state = {}
// prepare the camera
state.camera = Object.assign({}, perspectiveCamera.defaults)
perspectiveCamera.setProjection(state.camera, state.camera, { width, height })
perspectiveCamera.update(state.camera, state.camera)
// prepare the controls
state.controls = orbitControls.defaults
// prepare the renderer
const setupOptions = {
glOptions: { container: containerElement },
}
const renderer = prepareRender(setupOptions)
const gridOptions = {
visuals: {
drawCmd: 'drawGrid',
show: true
},
size: [500, 500],
ticks: [25, 5],
// color: [0, 0, 1, 1],
// subColor: [0, 0, 1, 0.5]
}
const axisOptions = {
visuals: {
drawCmd: 'drawAxis',
show: true
},
size: 300,
// alwaysVisible: false,
// xColor: [0, 0, 1, 1],
// yColor: [1, 0, 1, 1],
// zColor: [0, 0, 0, 1]
}
const entities = entitiesFromSolids({}, demo({ scale: 1 }))
// assemble the options for rendering
const renderOptions = {
camera: state.camera,
drawCommands: {
drawAxis: drawCommands.drawAxis,
drawGrid: drawCommands.drawGrid,
drawLines: drawCommands.drawLines,
drawMesh: drawCommands.drawMesh
},
// define the visual content
entities: [
gridOptions,
axisOptions,
...entities
]
}
// the heart of rendering, as themes, controls, etc change
let updateView = true
const doRotatePanZoom = () => {
if (rotateDelta[0] || rotateDelta[1]) {
const updated = orbitControls.rotate({ controls: state.controls, camera: state.camera, speed: rotateSpeed }, rotateDelta)
state.controls = { ...state.controls, ...updated.controls }
updateView = true
rotateDelta = [0, 0]
}
if (panDelta[0] || panDelta[1]) {
const updated = orbitControls.pan({ controls:state.controls, camera:state.camera, speed: panSpeed }, panDelta)
state.controls = { ...state.controls, ...updated.controls }
panDelta = [0, 0]
state.camera.position = updated.camera.position
state.camera.target = updated.camera.target
updateView = true
}
if (zoomDelta) {
const updated = orbitControls.zoom({ controls:state.controls, camera:state.camera, speed: zoomSpeed }, zoomDelta)
state.controls = { ...state.controls, ...updated.controls }
zoomDelta = 0
updateView = true
}
}
const updateAndRender = (timestamp) => {
doRotatePanZoom()
if (updateView) {
const updates = orbitControls.update({ controls: state.controls, camera: state.camera })
state.controls = { ...state.controls, ...updates.controls }
updateView = state.controls.changed // for elasticity in rotate / zoom
state.camera.position = updates.camera.position
perspectiveCamera.update(state.camera)
renderer(renderOptions)
}
window.requestAnimationFrame(updateAndRender)
}
window.requestAnimationFrame(updateAndRender)
// convert HTML events (mouse movement) to viewer changes
let lastX = 0
let lastY = 0
const rotateSpeed = 0.002
const panSpeed = 1
const zoomSpeed = 0.08
let rotateDelta = [0, 0]
let panDelta = [0, 0]
let zoomDelta = 0
let pointerDown = false
const moveHandler = (ev) => {
if(!pointerDown) return
const dx = lastX - ev.pageX
const dy = ev.pageY - lastY
const shiftKey = (ev.shiftKey === true) || (ev.touches && ev.touches.length > 2)
if (shiftKey) {
panDelta[0] += dx
panDelta[1] += dy
} else {
rotateDelta[0] -= dx
rotateDelta[1] -= dy
}
lastX = ev.pageX
lastY = ev.pageY
ev.preventDefault()
}
const downHandler = (ev) => {
pointerDown = true
lastX = ev.pageX
lastY = ev.pageY
containerElement.setPointerCapture(ev.pointerId)
}
const upHandler = (ev) => {
pointerDown = false
containerElement.releasePointerCapture(ev.pointerId)
}
const wheelHandler = (ev) => {
zoomDelta += ev.deltaY
ev.preventDefault()
}
containerElement.onpointermove = moveHandler
containerElement.onpointerdown = downHandler
containerElement.onpointerup = upHandler
containerElement.onwheel = wheelHandler
});
</script>
<template alink-route[com.example.farmos_asset_link.routes.v0.jscad_test]="/jscad-test">
<div ref="jscadDiv" id="jscad"></div>
</template>
<style>
#jscad {
width: 15cm;
height: 15cm;
margin: 0;
outline: 1px solid black;
}
</style>