format jscad worker

This commit is contained in:
Kurt Hutten
2021-09-08 06:18:11 +10:00
parent 22da074965
commit 58b618cf5f

View File

@@ -1,18 +1,18 @@
const setPoints = (points, p, i) => {
const setPoints = (points, p, i)=>{
points[i++] = p[0] points[i++] = p[0]
points[i++] = p[1] points[i++] = p[1]
points[i++] = p[2] || 0 points[i++] = p[2] || 0
} }
function CSG2Vertices(csg){ function CSG2Vertices(csg) {
let idx = 0 const idx = 0
let vLen = 0, iLen = 0 let vLen = 0,
for (let poly of csg.polygons){ iLen = 0
let len = poly.vertices.length for (const poly of csg.polygons) {
vLen += len *3 const len = poly.vertices.length
iLen += 3 * (len-2) vLen += len * 3
iLen += 3 * (len - 2)
} }
const vertices = new Float32Array(vLen) const vertices = new Float32Array(vLen)
const indices = vLen > 65535 ? new Uint32Array(iLen) : new Uint16Array(iLen) const indices = vLen > 65535 ? new Uint32Array(iLen) : new Uint16Array(iLen)
@@ -21,197 +21,209 @@ function CSG2Vertices(csg){
let indOffset = 0 let indOffset = 0
let posOffset = 0 let posOffset = 0
let first = 0 let first = 0
for (let poly of csg.polygons){ for (const poly of csg.polygons) {
let arr = poly.vertices const arr = poly.vertices
let len = arr.length const len = arr.length
first = posOffset first = posOffset
vertices.set(arr[0], vertOffset) vertices.set(arr[0], vertOffset)
vertOffset +=3 vertOffset += 3
vertices.set(arr[1], vertOffset) vertices.set(arr[1], vertOffset)
vertOffset +=3 vertOffset += 3
posOffset +=2 posOffset += 2
for(let i=2; i<len; i++){ for (let i = 2; i < len; i++) {
vertices.set(arr[i], vertOffset) vertices.set(arr[i], vertOffset)
indices[indOffset++] = first indices[indOffset++] = first
indices[indOffset++] = first + i -1 indices[indOffset++] = first + i - 1
indices[indOffset++] = first + i indices[indOffset++] = first + i
vertOffset += 3 vertOffset += 3
posOffset += 1 posOffset += 1
} }
} }
return {vertices, indices, type:'mesh'} return { vertices, indices, type: 'mesh' }
} }
function CSG2LineVertices(csg) {
function CSG2LineVertices(csg){
let vLen = csg.points.length * 3 let vLen = csg.points.length * 3
if(csg.isClosed) vLen += 3 if (csg.isClosed) vLen += 3
var vertices = new Float32Array(vLen) const vertices = new Float32Array(vLen)
csg.points.forEach((p, idx) => setPoints(vertices, p, idx * 3))
csg.points.forEach((p,idx)=>setPoints(vertices, p, idx * 3 )) if (csg.isClosed) {
setPoints(vertices, csg.points[0], vertices.length - 3)
if(csg.isClosed){
setPoints(vertices, csg.points[0], vertices.length - 3 )
} }
return {vertices, type:'line'} return { vertices, type: 'line' }
} }
function CSG2LineSegmentsVertices(csg){ function CSG2LineSegmentsVertices(csg) {
let vLen = csg.sides.length * 6 const vLen = csg.sides.length * 6
var vertices = new Float32Array(vLen) const vertices = new Float32Array(vLen)
csg.sides.forEach((side,idx)=>{ csg.sides.forEach((side, idx) => {
let i = idx * 6 const i = idx * 6
setPoints(vertices, side[0], i) setPoints(vertices, side[0], i)
setPoints(vertices, side[1], i+3) setPoints(vertices, side[1], i + 3)
}) })
return {vertices, type:'lines'} return { vertices, type: 'lines' }
} }
function CSGCached(func, data, cacheKey, transferable){ function CSGCached(func, data, cacheKey, transferable) {
cacheKey = cacheKey || data cacheKey = cacheKey || data
let geo = CSGToBuffers.cache.get(cacheKey) let geo = CSGToBuffers.cache.get(cacheKey)
if(geo) return geo if (geo) return geo
geo = func(data) geo = func(data)
// fill transferable array for postMessage optimization // fill transferable array for postMessage optimization
if(transferable){ if (transferable) {
const {vertices, indices} = geo const { vertices, indices } = geo
transferable.push(vertices) transferable.push(vertices)
if(indices) transferable.push(indices) if (indices) transferable.push(indices)
} }
CSGToBuffers.cache.set(cacheKey, geo) CSGToBuffers.cache.set(cacheKey, geo)
return geo return geo
} }
function CSGToBuffers(csg, transferable){ function CSGToBuffers(csg, transferable) {
let obj let obj
if(csg.polygons) obj = CSGCached(CSG2Vertices,csg,csg.polygons, transferable) if (csg.polygons)
if(csg.sides && !csg.points) obj = CSGCached(CSG2LineSegmentsVertices,csg,csg.sides, transferable) obj = CSGCached(CSG2Vertices, csg, csg.polygons, transferable)
if(csg.points) obj = CSGCached(CSG2LineVertices,csg,csg.points, transferable) if (csg.sides && !csg.points)
obj = CSGCached(CSG2LineSegmentsVertices, csg, csg.sides, transferable)
if (csg.points)
obj = CSGCached(CSG2LineVertices, csg, csg.points, transferable)
return obj return obj
} }
CSGToBuffers.clearCache = ()=>{CSGToBuffers.cache = new WeakMap()} CSGToBuffers.clearCache = () => {
CSGToBuffers.cache = new WeakMap()
}
CSGToBuffers.clearCache() CSGToBuffers.clearCache()
let workerBaseURI let workerBaseURI
function require(url){ function require(url) {
url = require.alias[url] || url url = require.alias[url] || url
if(url[0] != '/' && url.substr(0,2) != './' && url.substr(0,4) != 'http') url = 'https://unpkg.com/'+url if (url[0] != '/' && url.substr(0, 2) != './' && url.substr(0, 4) != 'http')
let exports=require.cache[url]; //get from cache url = 'https://unpkg.com/' + url
if (!exports) { //not cached let exports = require.cache[url] //get from cache
let module = requireModule(url) if (!exports) {
require.cache[url] = exports = module.exports; //cache obj exported by module //not cached
const module = requireModule(url)
require.cache[url] = exports = module.exports //cache obj exported by module
} }
return exports; //require returns object exported by module return exports //require returns object exported by module
} }
function requireFile(url){ function requireFile(url) {
try{ try {
let X=new XMLHttpRequest(); const X = new XMLHttpRequest()
X.open("GET", new URL(url,workerBaseURI), 0); // sync X.open('GET', new URL(url, workerBaseURI), 0) // sync
X.send(); X.send()
if (X.status && X.status !== 200) throw new Error(X.statusText); if (X.status && X.status !== 200) throw new Error(X.statusText)
return X.responseText; return X.responseText
}catch(e){ } catch (e) {
console.log('problem loading url ',url,'base',workerBaseURI,' error:',e.message) console.log(
'problem loading url ',
url,
'base',
workerBaseURI,
' error:',
e.message
)
throw e throw e
} }
} }
function requireModule(url, source){ function requireModule(url, source) {
try { try {
const exports={}; const exports = {}
if(!source) source = requireFile(url) if (!source) source = requireFile(url)
const module = { id: url, uri: url, exports:exports, source }; //according to node.js modules const module = { id: url, uri: url, exports: exports, source } //according to node.js modules
// fix, add comment to show source on Chrome Dev Tools // fix, add comment to show source on Chrome Dev Tools
source="//@ sourceURL="+url+"\n" + source; source = '//@ sourceURL=' + url + '\n' + source
//------ //------
const anonFn = new Function("require", "exports", "module", source); //create a Fn with module code, and 3 params: require, exports & module const anonFn = new Function('require', 'exports', 'module', source) //create a Fn with module code, and 3 params: require, exports & module
anonFn(require, exports, module); // call the Fn, Execute the module anonFn(require, exports, module) // call the Fn, Execute the module
return module return module
} catch (err) { } catch (err) {
console.error("Error loading module "+url, err.message); console.error('Error loading module ' + url, err.message)
throw err; throw err
} }
} }
require.cache = {} require.cache = {}
require.alias = {} require.alias = {}
const cmdHandler = (handlers) => (cmd) => {
const cmdHandler = (handlers)=>(cmd)=>{
const fn = handlers[cmd.action] const fn = handlers[cmd.action]
if (!fn) throw new Error('no handler for type: ' + cmd.action) if (!fn) throw new Error('no handler for type: ' + cmd.action)
fn(cmd); fn(cmd)
} }
function parseParams(script) {
let lines = script.split('\n').map((l) => l.trim())
lines = lines
.map((l, i) => {
return { code: l, line: i + 1, group: l[0] == '/' && !lines[i + 1] }
})
.filter((l) => l.code)
let i = 0,
function parseParams(script){ line,
let lines = script.split('\n').map(l=>l.trim()) next,
lineNum
lines = lines.map((l,i)=>{ while (i < lines.length) {
return {code:l, line:i+1, group: l[0] == '/' && !lines[i+1]}
}).filter(l=>l.code)
let i = 0, line, next, lineNum
while(i<lines.length){
line = lines[i].code.trim() line = lines[i].code.trim()
i++ i++
if(line.length>12 && line.substring(line.length-13) == '//jscadparams') break; if (line.length > 12 && line.substring(line.length - 13) == '//jscadparams')
if(line.length>12 && line.indexOf('@jscad-params') != -1) break; break
if (line.length > 12 && line.indexOf('@jscad-params') != -1) break
} }
let groupIndex = 1 let groupIndex = 1
const defs = [] const defs = []
while(i<lines.length){ while (i < lines.length) {
line = lines[i].code line = lines[i].code
lineNum = lines[i].line lineNum = lines[i].line
next = lines[i+1] ? lines[i+1].code : '' next = lines[i + 1] ? lines[i + 1].code : ''
if(line[0] === '}') break if (line[0] === '}') break
if(line[0] === '/'){ if (line[0] === '/') {
// group // group
const def = parseComment(line, lineNum) const def = parseComment(line, lineNum)
let name = '_group_' +(groupIndex++) let name = '_group_' + groupIndex++
let caption = def.caption let caption = def.caption
const idx = caption.lastIndexOf(':') const idx = caption.lastIndexOf(':')
if(idx !== -1){ if (idx !== -1) {
name = caption.substring(idx+1).trim() name = caption.substring(idx + 1).trim()
caption = caption.substring(0,idx).trim() caption = caption.substring(0, idx).trim()
} }
defs.push({name, type: 'group', caption, ...def.options}) defs.push({ name, type: 'group', caption, ...def.options })
} else {
}else{
const idx = line.indexOf('/') const idx = line.indexOf('/')
if(idx === -1){ if (idx === -1) {
const def = parseDef(line, lineNum) const def = parseDef(line, lineNum)
def.caption = def.name def.caption = def.name
defs.push(def) defs.push(def)
}else{ } else {
defs.push(parseOne( defs.push(
parseOne(
line.substring(idx).trim(), line.substring(idx).trim(),
line.substring(0,idx).trim(), line.substring(0, idx).trim(),
lineNum,lineNum lineNum,
)) lineNum
)
)
} }
} }
i++ i++
@@ -220,44 +232,45 @@ function parseParams(script){
return defs return defs
} }
function parseOne(comment, code, line1, line2){ function parseOne(comment, code, line1, line2) {
const {caption, options} = parseComment(comment, line1) const { caption, options } = parseComment(comment, line1)
let def = {caption, ...parseDef(code, line2)} let def = { caption, ...parseDef(code, line2) }
def.caption = def.caption || def.name def.caption = def.caption || def.name
if(options){ if (options) {
def = {...def, ...options} def = { ...def, ...options }
if(def.type === 'checkbox' && def.hasOwnProperty('initial')) def.checked = true if (def.type === 'checkbox' && def.hasOwnProperty('initial'))
if(def.type === 'slider'){ def.checked = true
if(def.min === undefined){ if (def.type === 'slider') {
def.min=0 if (def.min === undefined) {
def.min = 0
} }
if(def.max === undefined){ if (def.max === undefined) {
def.max = def.initial * 2 ||100 def.max = def.initial * 2 || 100
} }
} }
} }
return def; return def
} }
function parseComment(comment, line){ function parseComment(comment, line) {
const prefix = comment.substring(0,2) const prefix = comment.substring(0, 2)
if(prefix === '//') comment = comment.substring(2) if (prefix === '//') comment = comment.substring(2)
if(prefix === '/*') comment = comment.substring(2, comment.length-2) if (prefix === '/*') comment = comment.substring(2, comment.length - 2)
comment = comment.trim() comment = comment.trim()
const ret = {} const ret = {}
const idx = comment.indexOf('{') const idx = comment.indexOf('{')
if(idx !== -1){ if (idx !== -1) {
try{ try {
ret.options = eval('('+comment.substring(idx)+')') ret.options = eval('(' + comment.substring(idx) + ')')
}catch(e){ } catch (e) {
console.log('Error in line '+line); console.log('Error in line ' + line)
console.log(comment); console.log(comment)
throw e throw e
} }
comment = comment.substring(0,idx).trim() comment = comment.substring(0, idx).trim()
} }
ret.caption = comment ret.caption = comment
@@ -265,38 +278,37 @@ function parseComment(comment, line){
return ret return ret
} }
function parseDef(code, line){ function parseDef(code, line) {
if(code[code.length-1] == ',') code = code.substring(0,code.length-1).trim() if (code[code.length - 1] == ',')
code = code.substring(0, code.length - 1).trim()
let idx = code.indexOf('=') let idx = code.indexOf('=')
if(idx == -1) idx = code.indexOf(':') if (idx == -1) idx = code.indexOf(':')
if(idx == -1){ if (idx == -1) {
return {name:code, type:'text'} return { name: code, type: 'text' }
}else{ } else {
let initial = code.substring(idx+1).trim() const initial = code.substring(idx + 1).trim()
const ret = {type:'text', name:code.substring(0,idx).trim()} const ret = { type: 'text', name: code.substring(0, idx).trim() }
if(initial === 'true' || initial === 'false'){ if (initial === 'true' || initial === 'false') {
ret.type = 'checkbox' ret.type = 'checkbox'
ret.checked = initial === 'true' ret.checked = initial === 'true'
} else if (/^[0-9]+$/.test(initial)) {
}else if(/^[0-9]+$/.test(initial)){
ret.type = 'int' ret.type = 'int'
ret.initial = parseFloat(initial) ret.initial = parseFloat(initial)
} else if (/^[0-9]+\.[0-9]+$/.test(initial)) {
}else if(/^[0-9]+\.[0-9]+$/.test(initial)){
ret.type = 'number' ret.type = 'number'
ret.initial = parseFloat(initial) ret.initial = parseFloat(initial)
}else{ } else {
try { try {
ret.initial = eval(initial) ret.initial = eval(initial)
} catch (e) { } catch (e) {
console.log('Error in line '+line); console.log('Error in line ' + line)
console.log(code); console.log(code)
console.log('problem evaluating inital value:', initial) console.log('problem evaluating inital value:', initial)
e = new EvalError(e.message, 'code', line); e = new EvalError(e.message, 'code', line)
e.lineNumber = line e.lineNumber = line
throw e throw e
} }
@@ -306,105 +318,123 @@ function parseDef(code, line){
} }
} }
const makeScriptWorker = ({ callback, convertToSolids }) => {
const makeScriptWorker = ({callback, convertToSolids})=>{
let onInit, main, scriptStats, entities let onInit, main, scriptStats, entities
function runMain(params = {}) {
function runMain(params={}){
let time = Date.now() let time = Date.now()
let solids let solids
let transfer = [] const transfer = []
try{ try {
let tmp = main(params) const tmp = main(params)
solids = [] solids = []
function flatten(arr){ function flatten(arr) {
if(arr){ if (arr) {
if(arr instanceof Array) if (arr instanceof Array) arr.forEach(flatten)
arr.forEach(flatten) else solids.push(arr)
else
solids.push(arr)
} }
} }
flatten(tmp) flatten(tmp)
} catch (e) {
}catch(e){ callback(
callback({action:'entities', worker:'render', error:e.message, stack:e.stack.toString()}, transfer) {
action: 'entities',
worker: 'render',
error: e.message,
stack: e.stack.toString(),
},
transfer
)
return return
} }
let solidsTime = Date.now() - time const solidsTime = Date.now() - time
scriptStats = `generate solids ${solidsTime}ms` scriptStats = `generate solids ${solidsTime}ms`
if(convertToSolids === 'buffers'){ if (convertToSolids === 'buffers') {
CSGToBuffers.clearCache() CSGToBuffers.clearCache()
entities = solids.filter(s=>s).map((csg)=>{ entities = solids
let obj = CSGToBuffers(csg, transfer) .filter((s) => s)
.map((csg) => {
const obj = CSGToBuffers(csg, transfer)
obj.color = csg.color obj.color = csg.color
obj.transforms = csg.transforms obj.transforms = csg.transforms
return obj return obj
}) })
}else if(convertToSolids === 'regl'){ } else if (convertToSolids === 'regl') {
const { entitiesFromSolids } = require('@jscad/regl-renderer') const { entitiesFromSolids } = require('@jscad/regl-renderer')
time = Date.now() time = Date.now()
entities = entitiesFromSolids({}, solids) entities = entitiesFromSolids({}, solids)
scriptStats += ` convert to entities ${Date.now()-time}ms` scriptStats += ` convert to entities ${Date.now() - time}ms`
}else{ } else {
entities = solids entities = solids
} }
callback({action:'entities', worker:'render', entities, scriptStats}, transfer) callback(
{ action: 'entities', worker: 'render', entities, scriptStats },
transfer
)
} }
let initialized = false let initialized = false
const handlers = { const handlers = {
runScript: ({script,url, params={}})=>{ runScript: ({ script, url, params = {} }) => {
if(!initialized){ if (!initialized) {
onInit = ()=>handlers.runScript({script,url, params}) onInit = () => handlers.runScript({ script, url, params })
} }
let script_module let script_module
try{ try {
script_module = requireModule(url,script) script_module = requireModule(url, script)
}catch(e){ } catch (e) {
callback({action:'entities', worker:'render', error:e.message, stack:e.stack.toString()}) callback({
action: 'entities',
worker: 'render',
error: e.message,
stack: e.stack.toString(),
})
return return
} }
main = script_module.exports.main main = script_module.exports.main
let gp = script_module.exports.getParameterDefinitions const gp = script_module.exports.getParameterDefinitions
let paramsDef = parseParams(script) || [] const paramsDef = parseParams(script) || []
if(gp){ if (gp) {
gp().forEach(p=>{ gp().forEach((p) => {
let idx = paramsDef.findIndex(old=>old.name == p.name) const idx = paramsDef.findIndex((old) => old.name == p.name)
if(idx === -1){ if (idx === -1) {
paramsDef.push(p) paramsDef.push(p)
}else{ } else {
paramsDef.splice(idx,1,p) paramsDef.splice(idx, 1, p)
} }
}) })
} }
if(paramsDef.length) callback({action:'parameterDefinitions', worker:'main', data:paramsDef}) if (paramsDef.length)
callback({
action: 'parameterDefinitions',
worker: 'main',
data: paramsDef,
})
runMain(params) runMain(params)
}, },
updateParams: ({params={}})=>{ updateParams: ({ params = {} }) => {
runMain(params) runMain(params)
}, },
init: (params)=>{ init: (params) => {
let {baseURI, alias=[]} = params let { baseURI, alias = [] } = params
if(!baseURI && typeof document != 'undefined' && document.baseURI){ if (!baseURI && typeof document != 'undefined' && document.baseURI) {
baseURI = document.baseURI baseURI = document.baseURI
} }
if(baseURI) workerBaseURI = baseURI.toString() if (baseURI) workerBaseURI = baseURI.toString()
alias.forEach(arr=>{ alias.forEach((arr) => {
let [orig, ...aliases] = arr const [orig, ...aliases] = arr
aliases.forEach(a=>{ aliases.forEach((a) => {
require.alias[a] = orig require.alias[a] = orig
if(a.toLowerCase().substr(-3)!=='.js') require.alias[a+'.js'] = orig if (a.toLowerCase().substr(-3) !== '.js')
require.alias[a + '.js'] = orig
}) })
}) })
initialized = true initialized = true
if(onInit) onInit() if (onInit) onInit()
}, },
} }
@@ -414,23 +444,10 @@ const makeScriptWorker = ({callback, convertToSolids})=>{
} }
} }
/** Make render worker */ /** Make render worker */
const makeRenderWorker = ()=>{ const makeRenderWorker = () => {
let perspectiveCamera let perspectiveCamera
const state = {} const state = {}
const rotateSpeed = 0.002 const rotateSpeed = 0.002
@@ -444,10 +461,10 @@ let perspectiveCamera
let entities = [] let entities = []
function createContext (canvas, contextAttributes) { function createContext(canvas, contextAttributes) {
function get (type) { function get(type) {
try { try {
return {gl:canvas.getContext(type, contextAttributes), type} return { gl: canvas.getContext(type, contextAttributes), type }
} catch (e) { } catch (e) {
return null return null
} }
@@ -460,27 +477,38 @@ let perspectiveCamera
) )
} }
const startRenderer = ({canvas, cameraPosition, cameraTarget, axis={}, grid={}})=>{ const startRenderer = ({
const { prepareRender, drawCommands, cameras, controls } = require('@jscad/regl-renderer') canvas,
cameraPosition,
cameraTarget,
axis = {},
grid = {},
}) => {
const {
prepareRender,
drawCommands,
cameras,
controls,
} = require('@jscad/regl-renderer')
perspectiveCamera = cameras.perspective perspectiveCamera = cameras.perspective
orbitControls = controls.orbit orbitControls = controls.orbit
state.canvas = canvas state.canvas = canvas
state.camera = Object.assign({}, perspectiveCamera.defaults) state.camera = { ...perspectiveCamera.defaults }
if(cameraPosition) state.camera.position = cameraPosition if (cameraPosition) state.camera.position = cameraPosition
if(cameraTarget) state.camera.target = cameraTarget if (cameraTarget) state.camera.target = cameraTarget
resize({ width:canvas.width, height:canvas.height }) resize({ width: canvas.width, height: canvas.height })
state.controls = orbitControls.defaults state.controls = orbitControls.defaults
const {gl, type} = createContext(canvas) const { gl, type } = createContext(canvas)
// prepare the renderer // prepare the renderer
const setupOptions = { const setupOptions = {
glOptions: {gl} glOptions: { gl },
} }
if(type == 'webgl'){ if (type == 'webgl') {
setupOptions.glOptions.optionalExtensions = ['oes_element_index_uint'] setupOptions.glOptions.optionalExtensions = ['oes_element_index_uint']
} }
renderer = prepareRender(setupOptions) renderer = prepareRender(setupOptions)
@@ -488,20 +516,20 @@ let perspectiveCamera
gridOptions = { gridOptions = {
visuals: { visuals: {
drawCmd: 'drawGrid', drawCmd: 'drawGrid',
show: grid.show || grid.show === undefined , show: grid.show || grid.show === undefined,
color: grid.color || [0, 0, 0, 1], color: grid.color || [0, 0, 0, 1],
subColor: grid.subColor || [0, 0, 1, 0.5], subColor: grid.subColor || [0, 0, 1, 0.5],
fadeOut: false, fadeOut: false,
transparent: true transparent: true,
}, },
size: grid.size || [200, 200], size: grid.size || [200, 200],
ticks: grid.ticks || [10, 1] ticks: grid.ticks || [10, 1],
} }
axisOptions = { axisOptions = {
visuals: { visuals: {
drawCmd: 'drawAxis', drawCmd: 'drawAxis',
show: axis.show || axis.show === undefined show: axis.show || axis.show === undefined,
}, },
size: axis.size || 100, size: axis.size || 100,
} }
@@ -513,14 +541,10 @@ let perspectiveCamera
drawAxis: drawCommands.drawAxis, drawAxis: drawCommands.drawAxis,
drawGrid: drawCommands.drawGrid, drawGrid: drawCommands.drawGrid,
drawLines: drawCommands.drawLines, drawLines: drawCommands.drawLines,
drawMesh: drawCommands.drawMesh drawMesh: drawCommands.drawMesh,
}, },
// define the visual content // define the visual content
entities: [ entities: [gridOptions, axisOptions, ...entities],
gridOptions,
axisOptions,
...entities
]
} }
// the heart of rendering, as themes, controls, etc change // the heart of rendering, as themes, controls, etc change
@@ -528,23 +552,31 @@ let perspectiveCamera
} }
let renderTimer let renderTimer
const tmFunc = typeof requestAnimationFrame === 'undefined' ? setTimeout : requestAnimationFrame const tmFunc =
typeof requestAnimationFrame === 'undefined'
? setTimeout
: requestAnimationFrame
function updateView(delay=8){ function updateView(delay = 8) {
if(renderTimer || !renderer) return if (renderTimer || !renderer) return
renderTimer = tmFunc(updateAndRender,delay) renderTimer = tmFunc(updateAndRender, delay)
} }
const doRotatePanZoom = () => { const doRotatePanZoom = () => {
if (rotateDelta[0] || rotateDelta[1]) { if (rotateDelta[0] || rotateDelta[1]) {
const updated = orbitControls.rotate({ controls: state.controls, camera: state.camera, speed: rotateSpeed }, rotateDelta) const updated = orbitControls.rotate(
{ controls: state.controls, camera: state.camera, speed: rotateSpeed },
rotateDelta
)
state.controls = { ...state.controls, ...updated.controls } state.controls = { ...state.controls, ...updated.controls }
rotateDelta = [0, 0] rotateDelta = [0, 0]
} }
if (panDelta[0] || panDelta[1]) { if (panDelta[0] || panDelta[1]) {
const updated = orbitControls.pan({ controls:state.controls, camera:state.camera, speed: panSpeed }, panDelta) const updated = orbitControls.pan(
{ controls: state.controls, camera: state.camera, speed: panSpeed },
panDelta
)
state.controls = { ...state.controls, ...updated.controls } state.controls = { ...state.controls, ...updated.controls }
panDelta = [0, 0] panDelta = [0, 0]
state.camera.position = updated.camera.position state.camera.position = updated.camera.position
@@ -552,7 +584,10 @@ let perspectiveCamera
} }
if (zoomDelta) { if (zoomDelta) {
const updated = orbitControls.zoom({ controls:state.controls, camera:state.camera, speed: zoomSpeed }, zoomDelta) const updated = orbitControls.zoom(
{ controls: state.controls, camera: state.camera, speed: zoomSpeed },
zoomDelta
)
state.controls = { ...state.controls, ...updated.controls } state.controls = { ...state.controls, ...updated.controls }
zoomDelta = 0 zoomDelta = 0
} }
@@ -562,55 +597,57 @@ let perspectiveCamera
renderTimer = null renderTimer = null
doRotatePanZoom() doRotatePanZoom()
const updates = orbitControls.update({ controls: state.controls, camera: state.camera }) const updates = orbitControls.update({
controls: state.controls,
camera: state.camera,
})
state.controls = { ...state.controls, ...updates.controls } state.controls = { ...state.controls, ...updates.controls }
if(state.controls.changed) updateView(16) // for elasticity in rotate / zoom if (state.controls.changed) updateView(16) // for elasticity in rotate / zoom
state.camera.position = updates.camera.position state.camera.position = updates.camera.position
perspectiveCamera.update(state.camera) perspectiveCamera.update(state.camera)
renderOptions.entities = [ renderOptions.entities = [gridOptions, axisOptions, ...entities]
gridOptions, const time = Date.now()
axisOptions,
...entities
]
let time = Date.now()
renderer(renderOptions) renderer(renderOptions)
if(updateRender){ if (updateRender) {
updateRender = ''; updateRender = ''
} }
} }
function resize({width,height}){ function resize({ width, height }) {
state.canvas.width = width state.canvas.width = width
state.canvas.height = height state.canvas.height = height
perspectiveCamera.setProjection(state.camera, state.camera, { width, height }) perspectiveCamera.setProjection(state.camera, state.camera, {
width,
height,
})
perspectiveCamera.update(state.camera, state.camera) perspectiveCamera.update(state.camera, state.camera)
updateView() updateView()
} }
const handlers = { const handlers = {
pan: ({dx,dy})=>{ pan: ({ dx, dy }) => {
panDelta[0] += dx panDelta[0] += dx
panDelta[1] += dy panDelta[1] += dy
updateView() updateView()
}, },
rotate: ({dx,dy})=>{ rotate: ({ dx, dy }) => {
rotateDelta[0] -= dx rotateDelta[0] -= dx
rotateDelta[1] -= dy rotateDelta[1] -= dy
updateView() updateView()
}, },
zoom: ({dy})=>{ zoom: ({ dy }) => {
zoomDelta += dy zoomDelta += dy
updateView() updateView()
}, },
resize, resize,
entities: (params)=>{ entities: (params) => {
entities = params.entities entities = params.entities
updateRender = params.scriptStats updateRender = params.scriptStats
updateView() updateView()
}, },
init: (params)=>{ init: (params) => {
if(params.canvas) startRenderer(params) if (params.canvas) startRenderer(params)
initialized = true initialized = true
}, },
} }
@@ -621,42 +658,38 @@ let perspectiveCamera
} }
} }
function start(params) { function start(params) {
let { const { callback = () => {}, convertToSolids = false } = params
callback=()=>{},
convertToSolids=false
} = params
// by default 'render' messages go outside of this instance (result of modeling) // by default 'render' messages go outside of this instance (result of modeling)
let scriptWorker let scriptWorker
const sendCmd = (params, transfer)=>{ const sendCmd = (params, transfer) => {
if(params.worker === 'script') scriptWorker.postMessage(params, transfer) if (params.worker === 'script') scriptWorker.postMessage(params, transfer)
else{ else {
callback(params, transfer) callback(params, transfer)
} }
} }
scriptWorker = makeScriptWorker({ callback: sendCmd, convertToSolids })
callback({ action: 'workerInit', worker: 'main' })
scriptWorker = makeScriptWorker({callback:sendCmd, convertToSolids})
callback({action:'workerInit',worker:'main'})
return { return {
updateParams:({params={}})=>sendCmd({ action:'updateParams', worker:'script', params}), updateParams: ({ params = {} }) =>
runScript: ({script,url=''})=>sendCmd({ action:'runScript', worker:'script', script, url}), sendCmd({ action: 'updateParams', worker: 'script', params }),
runScript: ({ script, url = '' }) =>
sendCmd({ action: 'runScript', worker: 'script', script, url }),
postMessage: sendCmd, postMessage: sendCmd,
} }
} }
const init = start({ const init = start({
convertToSolids: 'buffers', convertToSolids: 'buffers',
callback:(params)=>self.postMessage(params), callback: (params) => self.postMessage(params),
}) })
self.onmessage = ({data}) => { self.onmessage = ({ data }) => {
if (data.action === 'init') { if (data.action === 'init') {
workerBaseURI = data.baseURI workerBaseURI = data.baseURI
} }
init.postMessage(data, null) init.postMessage(data, null)
}; }