format jscad worker
This commit is contained in:
@@ -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)
|
||||||
};
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user