move src2 -> src, web2 -> web, remove old module system, fix deps
This commit is contained in:
parent
1d821d4e24
commit
ce4f5d6e78
488 changed files with 26179 additions and 26609 deletions
486
app.js
486
app.js
|
|
@ -12,7 +12,7 @@ const uglify = require('uglify-js');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const agent = require('express-useragent');
|
const agent = require('express-useragent');
|
||||||
const license = require_fresh('./src/moto/license.js');
|
const license = require_fresh('./src/moto/license.js');
|
||||||
const version = license.VERSION || "rogue";
|
const version = license.version || "rogue";
|
||||||
const netdb = require('@gridspace/net-level-client');
|
const netdb = require('@gridspace/net-level-client');
|
||||||
const PATH = require('path');
|
const PATH = require('path');
|
||||||
|
|
||||||
|
|
@ -58,25 +58,6 @@ netdb.create = async function(map = {}) {
|
||||||
return client;
|
return client;
|
||||||
};
|
};
|
||||||
|
|
||||||
// experimental wrapping of esm modules using embedded data urls
|
|
||||||
function wrap_module(buf, as) {
|
|
||||||
const base64 = Buffer.from(buf).toString('base64');
|
|
||||||
const path = as.split('.');
|
|
||||||
const exp = path.pop();
|
|
||||||
return [
|
|
||||||
`gapp.register("${path.join('.')}", [], (root, exports) => {`,
|
|
||||||
`const url = "data:text/javascript;base64,${base64}";`,
|
|
||||||
`exports({ ["${exp}"]: import(url) });`,
|
|
||||||
"});"
|
|
||||||
].join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function addonce(array, v) {
|
|
||||||
if (array.indexOf(v) < 0) {
|
|
||||||
array.push(v);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function init(mod) {
|
function init(mod) {
|
||||||
const ENV = mod.env;
|
const ENV = mod.env;
|
||||||
|
|
||||||
|
|
@ -103,173 +84,7 @@ function init(mod) {
|
||||||
const callstack = [];
|
const callstack = [];
|
||||||
let xxxx = false;
|
let xxxx = false;
|
||||||
|
|
||||||
function find_refs(cache, path, ismod) {
|
generateDevices();
|
||||||
let rec = refcache[path];
|
|
||||||
if (rec) {
|
|
||||||
let crec = cache[path];
|
|
||||||
if (!crec) {
|
|
||||||
cache[path] = rec;
|
|
||||||
for (let d of rec.deps) find_refs(cache, d);
|
|
||||||
for (let u of rec.uses) find_refs(cache, u);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
callstack.push(path);
|
|
||||||
rec = cache[path] = refcache[path] = {
|
|
||||||
uses: [],
|
|
||||||
deps: [ approot ]
|
|
||||||
};
|
|
||||||
let full = PATH.join(dir,"src",`${path}.js`);
|
|
||||||
try {
|
|
||||||
fs.lstatSync(full);
|
|
||||||
} catch (e) {
|
|
||||||
console.log({missing: full, callstack});
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
// skip interrogating file if it's a module (external compacted)
|
|
||||||
if (ismod) {
|
|
||||||
wrap[`src/${path}.js`] = path.replaceAll('/','.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let lines = fs.readFileSync(full)
|
|
||||||
.toString()
|
|
||||||
.split('\n');
|
|
||||||
for (let line of lines) {
|
|
||||||
let arr, pos;
|
|
||||||
let upos = line.indexOf('// use:');
|
|
||||||
if (upos >= 0) {
|
|
||||||
arr = rec.uses;
|
|
||||||
pos = upos + 7;
|
|
||||||
}
|
|
||||||
let dpos = line.indexOf('// dep:');
|
|
||||||
if (dpos >= 0) {
|
|
||||||
arr = rec.deps;
|
|
||||||
pos = dpos + 7;
|
|
||||||
}
|
|
||||||
let mpos = line.indexOf('// mod:');
|
|
||||||
if (mpos >= 0) {
|
|
||||||
arr = rec.deps;
|
|
||||||
pos = mpos + 7;
|
|
||||||
}
|
|
||||||
if (upos >= 0 && dpos >= 0) {
|
|
||||||
console.log(`invalid line: ${line}`);
|
|
||||||
process.exit();
|
|
||||||
}
|
|
||||||
if (arr && pos >= 0) {
|
|
||||||
let path = line.substring(pos).trim().replace(/\./g,'/').trim();
|
|
||||||
addonce(arr, path);
|
|
||||||
find_refs(cache, path, mpos >= 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if (xxxx) console.log({path, ...rec});
|
|
||||||
if (false) {
|
|
||||||
let seek = 'mesh/api';
|
|
||||||
if (rec.uses.indexOf(seek) >= 0 || rec.deps.indexOf(seek) >= 0) {
|
|
||||||
console.log({PULLS:seek, path});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callstack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// return record position indicated by path
|
|
||||||
function pos(path, list) {
|
|
||||||
for (let i=0; i<list.length; i++) {
|
|
||||||
if (list[i].path === path) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.trace(`not found: ${path}`);
|
|
||||||
process.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
function order_refs(cache) {
|
|
||||||
const recs = Object.entries(cache).map(entry => {
|
|
||||||
return { path: entry[0], deps: entry[1].deps }
|
|
||||||
}).sort((a,b) => {
|
|
||||||
return a.path === b.path ? 0 : a.path < b.path ? -1 : 1;
|
|
||||||
});
|
|
||||||
if (xxxx) console.log({ordering: recs});
|
|
||||||
|
|
||||||
// for each rec, ensure that dependencies are inserted before it
|
|
||||||
let lrec = recs.slice();
|
|
||||||
for (let rec of lrec) {
|
|
||||||
let { path, deps } = rec;
|
|
||||||
for (let dep of deps) {
|
|
||||||
let rpos = pos(path, recs);
|
|
||||||
let dpos = pos(dep, recs);
|
|
||||||
if (dpos > rpos) {
|
|
||||||
let drec = recs[dpos];
|
|
||||||
// remove old dep record
|
|
||||||
recs.splice(dpos, 1);
|
|
||||||
// insert dep before
|
|
||||||
recs.splice(rpos, 0, drec);
|
|
||||||
let nrpos = pos(path, recs);
|
|
||||||
let ndpos = pos(dep, recs);
|
|
||||||
let fail = nrpos != ndpos + 1;
|
|
||||||
// if (xxxx) { console.log({move: dep, dpos, before: path, rpos}); }
|
|
||||||
if (fail) {
|
|
||||||
console.log({move: dep, dpos, before: path, rpos, ndpos, nrpos, recs: recs.slice(0,10)});
|
|
||||||
process.exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (xxxx) console.log({recs});
|
|
||||||
return recs.map(rec => rec.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// process script dependencies, expand paths
|
|
||||||
for (let [ key, val ] of Object.entries(script)) {
|
|
||||||
if (val.indexOf(approot) < 0) {
|
|
||||||
val = [ approot, ...val ];
|
|
||||||
}
|
|
||||||
const list = val.map(p => p.charAt(0) === '&' ? p.substring(1) : p);
|
|
||||||
const cache = {};
|
|
||||||
const roots = [];
|
|
||||||
// xxxx = key === "kiri_work";
|
|
||||||
// for each path in the list, find deps and add to list
|
|
||||||
for (let path of val) {
|
|
||||||
let fc = path.charAt(0);
|
|
||||||
if (fc === '@') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (fc === '#') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (fc === '&') {
|
|
||||||
path = path.substring(1);
|
|
||||||
addonce(roots, path);
|
|
||||||
}
|
|
||||||
find_refs(cache, path);
|
|
||||||
}
|
|
||||||
if (xxxx) console.log({ processing: key, val });
|
|
||||||
let refs = order_refs(cache).filter(p => roots.indexOf(p) < 0);
|
|
||||||
// remove paths that are in refs
|
|
||||||
let paths = list.filter(p => {
|
|
||||||
if (p.charAt(0) === '&') {
|
|
||||||
p = p.substring(1);
|
|
||||||
}
|
|
||||||
return refs.indexOf(p) < 0 && roots.indexOf(p) < 0;
|
|
||||||
});
|
|
||||||
// when dependency roots exist, re-write val array
|
|
||||||
if (roots.length) {
|
|
||||||
val = [...refs, ...paths, ...roots];
|
|
||||||
}
|
|
||||||
// val.splice(1, 0, ...roots);
|
|
||||||
if (xxxx) console.log({key, cache, refs, paths, roots, val});
|
|
||||||
script[key] = val.map(p => {
|
|
||||||
let fc = p.charAt(0);
|
|
||||||
if (fc === '@') return p;
|
|
||||||
if (fc === '#') {
|
|
||||||
fc = p.split('#');
|
|
||||||
let nupath = `src/${fc[1]}.js`;
|
|
||||||
wrap[nupath] = fc[1].replaceAll('/','.');
|
|
||||||
return nupath;
|
|
||||||
}
|
|
||||||
return `src/${p}.js`;
|
|
||||||
});
|
|
||||||
// console.log({script: key, files: script[key]});
|
|
||||||
}
|
|
||||||
|
|
||||||
mod.on.test((req) => {
|
mod.on.test((req) => {
|
||||||
let cookie = cookieValue(req.headers.cookie, "version") || undefined;
|
let cookie = cookieValue(req.headers.cookie, "version") || undefined;
|
||||||
|
|
@ -295,10 +110,6 @@ function init(mod) {
|
||||||
"/meta/index.html" : redir("/meta/", 301)
|
"/meta/index.html" : redir("/meta/", 301)
|
||||||
}));
|
}));
|
||||||
mod.add(handleVersion);
|
mod.add(handleVersion);
|
||||||
mod.add(prepath([
|
|
||||||
[ "/code/", handleCode ],
|
|
||||||
// [ "/wasm/", handleWasm ]
|
|
||||||
]));
|
|
||||||
mod.add(fixedmap("/api/", api));
|
mod.add(fixedmap("/api/", api));
|
||||||
if (debug) {
|
if (debug) {
|
||||||
mod.static("/mod/", "mod");
|
mod.static("/mod/", "mod");
|
||||||
|
|
@ -309,29 +120,14 @@ function init(mod) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
mod.add(rewriteHtmlVersion);
|
mod.add(rewriteHtmlVersion);
|
||||||
mod.add((req, res, next) => {
|
mod.static("/lib/", "src");
|
||||||
const path = req.gs.path.substring(1);
|
|
||||||
if (wrap[path]) {
|
|
||||||
const data = getCachedFile(path, file => {
|
|
||||||
console.log({ hot_wrap: file });
|
|
||||||
return fs.readFileSync(file);
|
|
||||||
});
|
|
||||||
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
|
|
||||||
return res.end(data);
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
mod.static("/src/", "src");
|
|
||||||
mod.static("/obj/", "web/obj");
|
mod.static("/obj/", "web/obj");
|
||||||
mod.static("/font/", "web/font");
|
mod.static("/font/", "web/font");
|
||||||
mod.static("/fon2/", "web/fon2");
|
mod.static("/fon2/", "web/fon2");
|
||||||
mod.static("/mesh/", "web/mesh");
|
mod.static("/mesh/", "web/mesh");
|
||||||
mod.static("/moto/", "web/moto");
|
mod.static("/moto/", "web/moto");
|
||||||
mod.static("/meta/", "web/meta");
|
|
||||||
mod.static("/kiri/", "web/kiri");
|
mod.static("/kiri/", "web/kiri");
|
||||||
|
|
||||||
mod.static("/v2/", "web2");
|
|
||||||
|
|
||||||
function load_modules(root, force) {
|
function load_modules(root, force) {
|
||||||
// load modules
|
// load modules
|
||||||
lastmod(`${dir}/${root}`) && fs.readdirSync(`${dir}/${root}`).forEach(mdir => {
|
lastmod(`${dir}/${root}`) && fs.readdirSync(`${dir}/${root}`).forEach(mdir => {
|
||||||
|
|
@ -353,10 +149,10 @@ function init(mod) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// load development and 3rd party modules
|
// load development and 3rd party modules
|
||||||
load_modules('mod');
|
// load_modules('mod');
|
||||||
|
|
||||||
// load optional local modules
|
// load optional local modules
|
||||||
load_modules('mods');
|
// load_modules('mods');
|
||||||
|
|
||||||
// run loads injected by modules
|
// run loads injected by modules
|
||||||
while (load.length) {
|
while (load.length) {
|
||||||
|
|
@ -366,9 +162,6 @@ function init(mod) {
|
||||||
logger.log({on_load_fail: e});
|
logger.log({on_load_fail: e});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs after module loads / injects
|
|
||||||
prepareScripts();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// either add module assets to path or require(init.js)
|
// either add module assets to path or require(init.js)
|
||||||
|
|
@ -391,7 +184,6 @@ function initModule(mod, file, dir) {
|
||||||
// express functions added here show up at "/api/" url root
|
// express functions added here show up at "/api/" url root
|
||||||
api: api,
|
api: api,
|
||||||
adm: {
|
adm: {
|
||||||
reload: prepareScripts,
|
|
||||||
setver: (ver) => { oversion = ver },
|
setver: (ver) => { oversion = ver },
|
||||||
crossOrigin: (bool) => { crossOrigin = bool }
|
crossOrigin: (bool) => { crossOrigin = bool }
|
||||||
},
|
},
|
||||||
|
|
@ -446,19 +238,6 @@ function initModule(mod, file, dir) {
|
||||||
static: (root, pre) => {
|
static: (root, pre) => {
|
||||||
mod.static(pre || "/", root);
|
mod.static(pre || "/", root);
|
||||||
},
|
},
|
||||||
code: (endpoint, path) => {
|
|
||||||
let fpath = PATH.join(mod.dir, path);
|
|
||||||
if (debug) {
|
|
||||||
code[endpoint] = fs.readFileSync(fpath);
|
|
||||||
} else {
|
|
||||||
code[endpoint] = minify(fpath);
|
|
||||||
}
|
|
||||||
code_src[endpoint] = {
|
|
||||||
endpoint,
|
|
||||||
path: path,
|
|
||||||
mod: lastmod(fpath)
|
|
||||||
};
|
|
||||||
},
|
|
||||||
redir: redir,
|
redir: redir,
|
||||||
remap: remap,
|
remap: remap,
|
||||||
setup: fn => { setupFn = fn }
|
setup: fn => { setupFn = fn }
|
||||||
|
|
@ -482,51 +261,6 @@ function initModule(mod, file, dir) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const script = {
|
|
||||||
kiri : [
|
|
||||||
"@devices",
|
|
||||||
"kiri/ui",
|
|
||||||
"&main/kiri",
|
|
||||||
"&kiri/lang-en"
|
|
||||||
],
|
|
||||||
kiri_work : [
|
|
||||||
"kiri-run/worker",
|
|
||||||
"&main/kiri",
|
|
||||||
],
|
|
||||||
kiri_pool : [
|
|
||||||
"&kiri-run/minion",
|
|
||||||
"&main/kiri",
|
|
||||||
],
|
|
||||||
engine : [
|
|
||||||
"@kiri_work",
|
|
||||||
"&kiri-run/engine",
|
|
||||||
"&main/kiri",
|
|
||||||
],
|
|
||||||
frame : [
|
|
||||||
"kiri-run/frame"
|
|
||||||
],
|
|
||||||
meta : [
|
|
||||||
"main/meta",
|
|
||||||
],
|
|
||||||
mesh : [
|
|
||||||
"&main/mesh"
|
|
||||||
],
|
|
||||||
mesh_work : [
|
|
||||||
"&mesh/work"
|
|
||||||
],
|
|
||||||
mesh_pool : [
|
|
||||||
"&mesh/pool"
|
|
||||||
],
|
|
||||||
cache : [
|
|
||||||
"moto/license",
|
|
||||||
"main/service",
|
|
||||||
],
|
|
||||||
service : [
|
|
||||||
"moto/license",
|
|
||||||
"moto/service"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
// prevent caching of specified modules
|
// prevent caching of specified modules
|
||||||
const cachever = {};
|
const cachever = {};
|
||||||
|
|
||||||
|
|
@ -564,28 +298,30 @@ function handleSetup(req, res, next) {
|
||||||
|
|
||||||
function handleVersion(req, res, next) {
|
function handleVersion(req, res, next) {
|
||||||
let vstr = oversion || dversion || version;
|
let vstr = oversion || dversion || version;
|
||||||
if (["/kiri/","/mesh/","/meta/"].indexOf(req.app.path) >= 0 && req.url.indexOf(vstr) < 0) {
|
if (["/kiri/","/mesh/"].indexOf(req.app.path) >= 0 && req.url.indexOf(vstr) < 0) {
|
||||||
if (req.url.indexOf("?") > 0) {
|
if (req.url.indexOf("?") > 0) {
|
||||||
return http.redirect(res, `${req.url},ver:${vstr}`);
|
return http.redirect(res, `${req.url},ver:${vstr}`);
|
||||||
} else {
|
} else {
|
||||||
return http.redirect(res, `${req.url}?ver:${vstr}`);
|
return http.redirect(res, `${req.url}?ver:${vstr}`);
|
||||||
}
|
}
|
||||||
} else if (!debug) {
|
} else if (!debug) {
|
||||||
|
// in production serve packed bundles
|
||||||
let { path } = req.app;
|
let { path } = req.app;
|
||||||
if (path === '/v2/lib/mesh/work.js') {
|
if (path === '/lib/mesh/work.js') {
|
||||||
req.url = req.app.path = '/v2/lib/pack/mesh-work.js';
|
req.url = req.app.path = '/lib/pack/mesh-work.js';
|
||||||
} else if (path === '/v2/lib/main/mesh.js') {
|
} else if (path === '/lib/main/mesh.js') {
|
||||||
req.url = req.app.path = '/v2/lib/pack/mesh-main.js';
|
req.url = req.app.path = '/lib/pack/mesh-main.js';
|
||||||
} else if (path === '/v2/lib/kiri-run/minion.js') {
|
} else if (path === '/lib/kiri-run/minion.js') {
|
||||||
req.url = req.app.path = '/v2/lib/pack/kiri-pool.js';
|
req.url = req.app.path = '/lib/pack/kiri-pool.js';
|
||||||
} else if (path === '/v2/lib/kiri-run/worker.js') {
|
} else if (path === '/lib/kiri-run/worker.js') {
|
||||||
req.url = req.app.path = '/v2/lib/pack/kiri-work.js';
|
req.url = req.app.path = '/lib/pack/kiri-work.js';
|
||||||
} else if (path === '/v2/lib/main/kiri.js') {
|
} else if (path === '/lib/main/kiri.js') {
|
||||||
req.url = req.app.path = '/v2/lib/pack/kiri-main.js';
|
req.url = req.app.path = '/lib/pack/kiri-main.js';
|
||||||
}
|
}
|
||||||
|
// add cors headers on rewrite
|
||||||
if (path !== req.url) {
|
if (path !== req.url) {
|
||||||
|
console.log('rewrite', path, req.url);
|
||||||
addCorsHeaders(req, res);
|
addCorsHeaders(req, res);
|
||||||
// console.log('rewrite', path, req.url);
|
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -631,58 +367,6 @@ function handleWasm(req, res, next) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCode(req, res, next) {
|
|
||||||
let key = req.app.path.split('/')[2].split('.')[0],
|
|
||||||
ck = code_src[key],
|
|
||||||
js = code[key];
|
|
||||||
|
|
||||||
if (!js) {
|
|
||||||
return http.reply404(req, res);
|
|
||||||
}
|
|
||||||
if (ck) {
|
|
||||||
let mpath = `${dir}/${ck.path}`;
|
|
||||||
let mod = lastmod(mpath);
|
|
||||||
if (mod > ck.mod) {
|
|
||||||
if (debug) {
|
|
||||||
js = code[ck.endpoint] = fs.readFileSync(mpath);
|
|
||||||
} else {
|
|
||||||
js = code[ck.endpoint] = minify(mpath);
|
|
||||||
}
|
|
||||||
ck.mod = mod;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addCorsHeaders(req, res);
|
|
||||||
serveCode(req, res, {
|
|
||||||
code: js,
|
|
||||||
mtime: startTime
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function serveCode(req, res, code) {
|
|
||||||
if (code.deny) {
|
|
||||||
return http.reply404(req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
let imd = ifModifiedDate(req);
|
|
||||||
if (imd && code.mtime <= imd && !code.nocache) {
|
|
||||||
res.writeHead(304, "Not Modified");
|
|
||||||
res.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cacheControl = code.nocache ?
|
|
||||||
'private, max-age=0' :
|
|
||||||
'public, max-age=600';
|
|
||||||
|
|
||||||
res.writeHead(200, {
|
|
||||||
'Content-Type': 'application/javascript',
|
|
||||||
'Cache-Control': cacheControl,
|
|
||||||
'Last-Modified': new Date(code.mtime).toGMTString(),
|
|
||||||
});
|
|
||||||
res.end(code.code);
|
|
||||||
}
|
|
||||||
|
|
||||||
// pack/concat device script strings to inject into /code/ scripts
|
// pack/concat device script strings to inject into /code/ scripts
|
||||||
function generateDevices() {
|
function generateDevices() {
|
||||||
let root = PATH.join(dir,"src","kiri-dev");
|
let root = PATH.join(dir,"src","kiri-dev");
|
||||||
|
|
@ -698,128 +382,7 @@ function generateDevices() {
|
||||||
});
|
});
|
||||||
let dstr = JSON.stringify(devs);
|
let dstr = JSON.stringify(devs);
|
||||||
synth.devices = `self.devices = ${dstr};`;
|
synth.devices = `self.devices = ${dstr};`;
|
||||||
fs.writeFileSync(PATH.join(dir,"src2","pack","devices.js"), `export const devices = ${dstr};`);
|
fs.writeFileSync(PATH.join(dir,"src","pack","devices.js"), `export const devices = ${dstr};`);
|
||||||
}
|
|
||||||
|
|
||||||
// pack/concat code modules served under "/code/"
|
|
||||||
function prepareScripts() {
|
|
||||||
generateDevices();
|
|
||||||
for (let key of Object.keys(script)) {
|
|
||||||
code[key] = concatCode(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function concatCode(key) {
|
|
||||||
let array = script[key];
|
|
||||||
let code = [];
|
|
||||||
let direct = array.filter(f => f.charAt(0) !== '@');
|
|
||||||
let inject = array.filter(f => f.charAt(0) === '@').map(f => f.substring(1));
|
|
||||||
|
|
||||||
synth.inject = "/* injection point */";
|
|
||||||
|
|
||||||
// in debug mode, the script should load dependent
|
|
||||||
// scripts instead of serving a complete bundle
|
|
||||||
if (debug) {
|
|
||||||
inject.forEach(key => {
|
|
||||||
code.push(synth[key]);
|
|
||||||
});
|
|
||||||
code.push(...[
|
|
||||||
oversion ? `self.debug_version='${oversion}';self.enable_service=${serviceWorker};` : '',
|
|
||||||
'self.debug=true;',
|
|
||||||
'(function() { let load = [ '
|
|
||||||
]);
|
|
||||||
direct.forEach(file => {
|
|
||||||
const vers = cachever[file] || oversion || dversion || version;
|
|
||||||
code.push(`"/${file.replace(/\\/g,'/')}?${vers}",`);
|
|
||||||
});
|
|
||||||
code.push([
|
|
||||||
']; function load_next() {',
|
|
||||||
'let file = load.shift();',
|
|
||||||
'if (!file) return;',
|
|
||||||
// 'console.log("loading", file);',
|
|
||||||
'if (!self.document) { importScripts(file); return load_next() }',
|
|
||||||
'let s = document.createElement("script");',
|
|
||||||
's.type = "text/javascript";',
|
|
||||||
's.src = file;',
|
|
||||||
's.onload = load_next;',
|
|
||||||
'document.head.appendChild(s);',
|
|
||||||
'} load_next(); })();'
|
|
||||||
].join('\n'));
|
|
||||||
code = code.join('\n');
|
|
||||||
} else {
|
|
||||||
inject.forEach(key => {
|
|
||||||
code.push(synth[key]);
|
|
||||||
});
|
|
||||||
direct.forEach(file => {
|
|
||||||
let cached = getCachedFile(file, path => {
|
|
||||||
return minify(PATH.join(dir,file));
|
|
||||||
});
|
|
||||||
if (oversion) {
|
|
||||||
cached = `self.debug_version='${oversion}';self.enable_service=${serviceWorker};` + cached;
|
|
||||||
}
|
|
||||||
code.push(cached);
|
|
||||||
});
|
|
||||||
code = code.join('');
|
|
||||||
synth[key] = `self.${key} = "${Buffer.from(code).toString('base64')}";\n`;
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCachedFile(file, fn) {
|
|
||||||
let filePath = PATH.join(dir,file);
|
|
||||||
let cachePath = cacheDir + PATH.sep + file
|
|
||||||
.replace(/\//g,'_')
|
|
||||||
.replace(/\\/g,'_')
|
|
||||||
.replace(/:/g,'_'),
|
|
||||||
cached = fileCache[filePath],
|
|
||||||
now = time();
|
|
||||||
|
|
||||||
if (cached) {
|
|
||||||
if (now - cached.lastcheck > 60000) {
|
|
||||||
let smod = lastmod(filePath),
|
|
||||||
cmod = cached.mtime;
|
|
||||||
|
|
||||||
if (!smod) {
|
|
||||||
throw "missing source file";
|
|
||||||
}
|
|
||||||
if (smod > cmod) {
|
|
||||||
cached = null;
|
|
||||||
} else {
|
|
||||||
cached.lastcheck = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cached) {
|
|
||||||
let smod = lastmod(filePath),
|
|
||||||
cmod = lastmod(cachePath),
|
|
||||||
cacheData;
|
|
||||||
|
|
||||||
if (cmod >= smod || (forceUseCache && cmod)) {
|
|
||||||
cacheData = fs.readFileSync(cachePath);
|
|
||||||
} else {
|
|
||||||
logger.log({update_cache:filePath});
|
|
||||||
cacheData = fn(filePath);
|
|
||||||
// console.log(`NEW_CACHE_FILE: ${cachePath}`);
|
|
||||||
fs.writeFileSync(cachePath, cacheData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wrap[file]) {
|
|
||||||
// console.log('WRAP', filePath, cacheData.length);
|
|
||||||
cacheData = wrap_module(cacheData, wrap[file]);
|
|
||||||
}
|
|
||||||
|
|
||||||
cached = {
|
|
||||||
data: cacheData,
|
|
||||||
mtime: cmod || now,
|
|
||||||
lastcheck: now
|
|
||||||
};
|
|
||||||
|
|
||||||
fileCache[filePath] = cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log('[*]', filePath, cached.data.length);
|
|
||||||
return cached.data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function minify(path) {
|
function minify(path) {
|
||||||
|
|
@ -942,10 +505,11 @@ function cookieValue(cookie,key) {
|
||||||
|
|
||||||
function rewriteHtmlVersion(req, res, next) {
|
function rewriteHtmlVersion(req, res, next) {
|
||||||
if ([
|
if ([
|
||||||
"/v2/kiri/",
|
"/kiri/",
|
||||||
"/v2/lib/kiri-run/minion.js",
|
"/mesh/",
|
||||||
"/v2/lib/kiri-run/worker.js",
|
"/lib/mesh/work.js",
|
||||||
"/v2/lib/kiri-run/minion.js"
|
"/lib/kiri-run/worker.js",
|
||||||
|
"/lib/kiri-run/minion.js"
|
||||||
].indexOf(req.app.path) >= 0) {
|
].indexOf(req.app.path) >= 0) {
|
||||||
addCorsHeaders(req, res);
|
addCorsHeaders(req, res);
|
||||||
} else if ([
|
} else if ([
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,7 @@
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"setup": "npm i && cd mods && npm i",
|
"setup": "npm i && npm run dev -- --dryrun && npx webpack --config src/webpack/webpack-three-esm.js && node src/webpack/esbuild.config.mjs",
|
||||||
"setup2": "npm run setup && npm run dev -- --dryrun && npm run preinstall2",
|
|
||||||
"dev": "gs-app-server --debug",
|
"dev": "gs-app-server --debug",
|
||||||
"prod": "gs-app-server",
|
"prod": "gs-app-server",
|
||||||
"prod-dryrun": "gs-app-server --dryrun",
|
"prod-dryrun": "gs-app-server --dryrun",
|
||||||
|
|
@ -98,14 +97,12 @@
|
||||||
"build-mac-intel": "npm run build -- --mac --x64",
|
"build-mac-intel": "npm run build -- --mac --x64",
|
||||||
"mesh-build": "webpack --config webpack.config.js",
|
"mesh-build": "webpack --config webpack.config.js",
|
||||||
"mesh-build-dev": "webpack --config webpack.dev.config.js",
|
"mesh-build-dev": "webpack --config webpack.dev.config.js",
|
||||||
"serve-mesh": "npx serve dist/v2 -p 8181",
|
|
||||||
"mklinks": "find src web -type l | xargs -I{} sh -c 'echo \"{},$(readlink {})\"' > links.csv",
|
"mklinks": "find src web -type l | xargs -I{} sh -c 'echo \"{},$(readlink {})\"' > links.csv",
|
||||||
"mac-verify": "spctl --assess -vv --type install dist/*/*.app",
|
"mac-verify": "spctl --assess -vv --type install dist/*/*.app",
|
||||||
"clear-cache": "rm -rf data/cache/* dist/ tmp/*",
|
"clear-cache": "rm -rf data/cache/* dist/ tmp/*",
|
||||||
"prebuild": "node bin/electron-pre.js",
|
"prebuild": "node bin/electron-pre.js",
|
||||||
"postbuild": "node bin/electron-post.js",
|
"postbuild": "node bin/electron-post.js",
|
||||||
"preinstall": "node bin/install-pre.js && npx webpack --config bin/webpack-three.js",
|
"preinstall": "",
|
||||||
"preinstall2": "npx webpack --config src2/webpack/webpack-three-esm.js && node src2/webpack/esbuild.config.mjs",
|
|
||||||
"docs-dev": "docusaurus start --config conf/docusaurus.config.js",
|
"docs-dev": "docusaurus start --config conf/docusaurus.config.js",
|
||||||
"docs-build": "docusaurus build --config conf/docusaurus.config.js",
|
"docs-build": "docusaurus build --config conf/docusaurus.config.js",
|
||||||
"docs-serve": "docusaurus serve --config conf/docusaurus.config.js",
|
"docs-serve": "docusaurus serve --config conf/docusaurus.config.js",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
// extend Array, String, Number, Math
|
// extend Array, String, Number, Math
|
||||||
const AP = {};
|
gapp.register("add.array", [], (root, exports) => {
|
||||||
|
|
||||||
|
let AP = {};
|
||||||
|
|
||||||
if (!AP.flat) {
|
if (!AP.flat) {
|
||||||
AP.flat = function() {
|
AP.flat = function() {
|
||||||
|
|
@ -212,11 +216,12 @@ Array.handle = function(fn) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply all methods to Array.prototype
|
for (let i in AP) {
|
||||||
Object.assign(Array.prototype, AP);
|
Object.defineProperty(Array.prototype, i, {
|
||||||
|
value: AP[i],
|
||||||
// Export for ES modules
|
enumerable: false
|
||||||
export { AP as arrayPrototype };
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Float32Array.prototype.toShared = function() {
|
Float32Array.prototype.toShared = function() {
|
||||||
const newvert = new Float32Array(new SharedArrayBuffer(this.buffer.byteLength));
|
const newvert = new Float32Array(new SharedArrayBuffer(this.buffer.byteLength));
|
||||||
|
|
@ -260,3 +265,5 @@ Number.prototype.round = function(digits) {
|
||||||
const pow = Math.pow(10, digits || 3);
|
const pow = Math.pow(10, digits || 3);
|
||||||
return Math.round(this.valueOf() * pow) / pow;
|
return Math.round(this.valueOf() * pow) / pow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
||||||
|
|
||||||
// use: add.array
|
"use strict";
|
||||||
import { arrayPrototype } from './array.js';
|
|
||||||
|
|
||||||
// Make classes available globally
|
// use: add.array
|
||||||
class ArrayWriter {
|
gapp.register("add.class", [], (root, exports) => {
|
||||||
|
|
||||||
|
self.ArrayWriter = class ArrayWriter {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.pos = 0;
|
this.pos = 0;
|
||||||
this.array = [];
|
this.array = [];
|
||||||
|
|
@ -47,7 +48,7 @@ class ArrayWriter {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataWriter {
|
self.DataWriter = class DataWriter {
|
||||||
constructor(view, pos) {
|
constructor(view, pos) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
this.pos = pos || 0;
|
this.pos = pos || 0;
|
||||||
|
|
@ -105,7 +106,7 @@ class DataWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataReader {
|
self.DataReader = class DataReader {
|
||||||
constructor(view, pos) {
|
constructor(view, pos) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
this.pos = pos || 0;
|
this.pos = pos || 0;
|
||||||
|
|
@ -170,9 +171,4 @@ class DataReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ArrayWriter = ArrayWriter;
|
});
|
||||||
self.DataWriter = DataWriter;
|
|
||||||
self.DataReader = DataReader;
|
|
||||||
|
|
||||||
// Export for ES modules
|
|
||||||
export { ArrayWriter, DataWriter, DataReader };
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
||||||
|
|
||||||
// dep: ext.three
|
"use strict";
|
||||||
import {
|
|
||||||
THREE, SVGLoader, BufferGeometryUtils,
|
|
||||||
LineMaterial, Line2, LineGeometry, LineSegments2,
|
|
||||||
LineSegmentsGeometry, MeshBVHLib } from '../ext/three.js';
|
|
||||||
|
|
||||||
// Make THREE available globally
|
// dep: ext.three
|
||||||
self.THREE = THREE;
|
gapp.register("add.three", [], (root, exports) => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
THREE, SVGLoader, BufferGeometryUtils,
|
||||||
|
LineMaterial, Line2, LineGeometry, LineSegments2, LineSegmentsGeometry
|
||||||
|
} = ThreeBundle;
|
||||||
|
|
||||||
|
root.THREE = THREE;
|
||||||
THREE.BufferGeometryUtils = BufferGeometryUtils;
|
THREE.BufferGeometryUtils = BufferGeometryUtils;
|
||||||
THREE.SVGLoader = SVGLoader.SVGLoader;
|
THREE.SVGLoader = SVGLoader.SVGLoader;
|
||||||
THREE.LineMaterial = LineMaterial;
|
THREE.LineMaterial = LineMaterial;
|
||||||
|
|
@ -144,5 +147,4 @@ THREE.Object3D.prototype.removeAll = function() {
|
||||||
this.children = [];
|
this.children = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Export for ES modules
|
});
|
||||||
export { THREE };
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
/** Copyright Stewart Allen -- All Rights Reserved */
|
/** Copyright Stewart Allen -- All Rights Reserved */
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
|
"use strict";
|
||||||
|
|
||||||
export const data = {
|
gapp.register("data.index", [], (root, exports) => {
|
||||||
Index: IDBStore,
|
|
||||||
open() {
|
const { data } = root;
|
||||||
|
|
||||||
|
data.Index = IDBStore;
|
||||||
|
|
||||||
|
data.open = function() {
|
||||||
return new IDBStore(...arguments);
|
return new IDBStore(...arguments);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
|
||||||
|
|
||||||
let IDB, IRR, local = null;
|
let IDB, IRR, local = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -314,5 +319,4 @@ SP.clear = function(key, store = this.current) {
|
||||||
.objectStore(store).clear();
|
.objectStore(store).clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const open = data.open;
|
});
|
||||||
export const Index = IDBStore;
|
|
||||||
47
src.old/data/local.js
Normal file
47
src.old/data/local.js
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
gapp.register("data.local", [], (root, exports) => {
|
||||||
|
|
||||||
|
const { data } = root;
|
||||||
|
|
||||||
|
function Local() {
|
||||||
|
this.__data__ = {};
|
||||||
|
this.__mem__ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var LS = Local.prototype;
|
||||||
|
|
||||||
|
LS.getItem = function(key) {
|
||||||
|
return this[key];
|
||||||
|
};
|
||||||
|
|
||||||
|
LS.setItem = function(key, val) {
|
||||||
|
this.__data__[key] = val;
|
||||||
|
this[key] = val;
|
||||||
|
};
|
||||||
|
|
||||||
|
LS.removeItem = function(key) {
|
||||||
|
delete this.__data__[key];
|
||||||
|
};
|
||||||
|
|
||||||
|
LS.clear = function() {
|
||||||
|
this.__data__ = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// deprecate 'Local' at some point
|
||||||
|
let local = data.local =self.localStorage;
|
||||||
|
let testkey = '__test';
|
||||||
|
local.setItem(testkey, 1);
|
||||||
|
local.getItem(testkey);
|
||||||
|
local.removeItem(testkey);
|
||||||
|
} catch (e) {
|
||||||
|
data.local = new Local();
|
||||||
|
let msg = "localStorage disabled: application may not function properly";
|
||||||
|
console.log(msg);
|
||||||
|
// alert(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
// SOURCE: https://stackoverflow.com/questions/1655769/fastest-md5-implementation-in-javascript
|
// SOURCE: https://stackoverflow.com/questions/1655769/fastest-md5-implementation-in-javascript
|
||||||
|
gapp.register("ext.md5", [], (root, exports) => {
|
||||||
|
|
||||||
export function hash(e) {
|
exports({ hash });
|
||||||
|
|
||||||
|
function hash(e) {
|
||||||
function h(a, b) {
|
function h(a, b) {
|
||||||
var c, d, e, f, g;
|
var c, d, e, f, g;
|
||||||
e = a & 2147483648;
|
e = a & 2147483648;
|
||||||
|
|
@ -64,4 +67,6 @@ export function hash(e) {
|
||||||
d = 271733878;
|
d = 271733878;
|
||||||
for (e = 0; e < f.length; e += 16) q = a, r = b, s = c, t = d, a = k(a, b, c, d, f[e + 0], 7, 3614090360), d = k(d, a, b, c, f[e + 1], 12, 3905402710), c = k(c, d, a, b, f[e + 2], 17, 606105819), b = k(b, c, d, a, f[e + 3], 22, 3250441966), a = k(a, b, c, d, f[e + 4], 7, 4118548399), d = k(d, a, b, c, f[e + 5], 12, 1200080426), c = k(c, d, a, b, f[e + 6], 17, 2821735955), b = k(b, c, d, a, f[e + 7], 22, 4249261313), a = k(a, b, c, d, f[e + 8], 7, 1770035416), d = k(d, a, b, c, f[e + 9], 12, 2336552879), c = k(c, d, a, b, f[e + 10], 17, 4294925233), b = k(b, c, d, a, f[e + 11], 22, 2304563134), a = k(a, b, c, d, f[e + 12], 7, 1804603682), d = k(d, a, b, c, f[e + 13], 12, 4254626195), c = k(c, d, a, b, f[e + 14], 17, 2792965006), b = k(b, c, d, a, f[e + 15], 22, 1236535329), a = l(a, b, c, d, f[e + 1], 5, 4129170786), d = l(d, a, b, c, f[e + 6], 9, 3225465664), c = l(c, d, a, b, f[e + 11], 14, 643717713), b = l(b, c, d, a, f[e + 0], 20, 3921069994), a = l(a, b, c, d, f[e + 5], 5, 3593408605), d = l(d, a, b, c, f[e + 10], 9, 38016083), c = l(c, d, a, b, f[e + 15], 14, 3634488961), b = l(b, c, d, a, f[e + 4], 20, 3889429448), a = l(a, b, c, d, f[e + 9], 5, 568446438), d = l(d, a, b, c, f[e + 14], 9, 3275163606), c = l(c, d, a, b, f[e + 3], 14, 4107603335), b = l(b, c, d, a, f[e + 8], 20, 1163531501), a = l(a, b, c, d, f[e + 13], 5, 2850285829), d = l(d, a, b, c, f[e + 2], 9, 4243563512), c = l(c, d, a, b, f[e + 7], 14, 1735328473), b = l(b, c, d, a, f[e + 12], 20, 2368359562), a = m(a, b, c, d, f[e + 5], 4, 4294588738), d = m(d, a, b, c, f[e + 8], 11, 2272392833), c = m(c, d, a, b, f[e + 11], 16, 1839030562), b = m(b, c, d, a, f[e + 14], 23, 4259657740), a = m(a, b, c, d, f[e + 1], 4, 2763975236), d = m(d, a, b, c, f[e + 4], 11, 1272893353), c = m(c, d, a, b, f[e + 7], 16, 4139469664), b = m(b, c, d, a, f[e + 10], 23, 3200236656), a = m(a, b, c, d, f[e + 13], 4, 681279174), d = m(d, a, b, c, f[e + 0], 11, 3936430074), c = m(c, d, a, b, f[e + 3], 16, 3572445317), b = m(b, c, d, a, f[e + 6], 23, 76029189), a = m(a, b, c, d, f[e + 9], 4, 3654602809), d = m(d, a, b, c, f[e + 12], 11, 3873151461), c = m(c, d, a, b, f[e + 15], 16, 530742520), b = m(b, c, d, a, f[e + 2], 23, 3299628645), a = n(a, b, c, d, f[e + 0], 6, 4096336452), d = n(d, a, b, c, f[e + 7], 10, 1126891415), c = n(c, d, a, b, f[e + 14], 15, 2878612391), b = n(b, c, d, a, f[e + 5], 21, 4237533241), a = n(a, b, c, d, f[e + 12], 6, 1700485571), d = n(d, a, b, c, f[e + 3], 10, 2399980690), c = n(c, d, a, b, f[e + 10], 15, 4293915773), b = n(b, c, d, a, f[e + 1], 21, 2240044497), a = n(a, b, c, d, f[e + 8], 6, 1873313359), d = n(d, a, b, c, f[e + 15], 10, 4264355552), c = n(c, d, a, b, f[e + 6], 15, 2734768916), b = n(b, c, d, a, f[e + 13], 21, 1309151649), a = n(a, b, c, d, f[e + 4], 6, 4149444226), d = n(d, a, b, c, f[e + 11], 10, 3174756917), c = n(c, d, a, b, f[e + 2], 15, 718787259), b = n(b, c, d, a, f[e + 9], 21, 3951481745), a = h(a, q), b = h(b, r), c = h(c, s), d = h(d, t);
|
for (e = 0; e < f.length; e += 16) q = a, r = b, s = c, t = d, a = k(a, b, c, d, f[e + 0], 7, 3614090360), d = k(d, a, b, c, f[e + 1], 12, 3905402710), c = k(c, d, a, b, f[e + 2], 17, 606105819), b = k(b, c, d, a, f[e + 3], 22, 3250441966), a = k(a, b, c, d, f[e + 4], 7, 4118548399), d = k(d, a, b, c, f[e + 5], 12, 1200080426), c = k(c, d, a, b, f[e + 6], 17, 2821735955), b = k(b, c, d, a, f[e + 7], 22, 4249261313), a = k(a, b, c, d, f[e + 8], 7, 1770035416), d = k(d, a, b, c, f[e + 9], 12, 2336552879), c = k(c, d, a, b, f[e + 10], 17, 4294925233), b = k(b, c, d, a, f[e + 11], 22, 2304563134), a = k(a, b, c, d, f[e + 12], 7, 1804603682), d = k(d, a, b, c, f[e + 13], 12, 4254626195), c = k(c, d, a, b, f[e + 14], 17, 2792965006), b = k(b, c, d, a, f[e + 15], 22, 1236535329), a = l(a, b, c, d, f[e + 1], 5, 4129170786), d = l(d, a, b, c, f[e + 6], 9, 3225465664), c = l(c, d, a, b, f[e + 11], 14, 643717713), b = l(b, c, d, a, f[e + 0], 20, 3921069994), a = l(a, b, c, d, f[e + 5], 5, 3593408605), d = l(d, a, b, c, f[e + 10], 9, 38016083), c = l(c, d, a, b, f[e + 15], 14, 3634488961), b = l(b, c, d, a, f[e + 4], 20, 3889429448), a = l(a, b, c, d, f[e + 9], 5, 568446438), d = l(d, a, b, c, f[e + 14], 9, 3275163606), c = l(c, d, a, b, f[e + 3], 14, 4107603335), b = l(b, c, d, a, f[e + 8], 20, 1163531501), a = l(a, b, c, d, f[e + 13], 5, 2850285829), d = l(d, a, b, c, f[e + 2], 9, 4243563512), c = l(c, d, a, b, f[e + 7], 14, 1735328473), b = l(b, c, d, a, f[e + 12], 20, 2368359562), a = m(a, b, c, d, f[e + 5], 4, 4294588738), d = m(d, a, b, c, f[e + 8], 11, 2272392833), c = m(c, d, a, b, f[e + 11], 16, 1839030562), b = m(b, c, d, a, f[e + 14], 23, 4259657740), a = m(a, b, c, d, f[e + 1], 4, 2763975236), d = m(d, a, b, c, f[e + 4], 11, 1272893353), c = m(c, d, a, b, f[e + 7], 16, 4139469664), b = m(b, c, d, a, f[e + 10], 23, 3200236656), a = m(a, b, c, d, f[e + 13], 4, 681279174), d = m(d, a, b, c, f[e + 0], 11, 3936430074), c = m(c, d, a, b, f[e + 3], 16, 3572445317), b = m(b, c, d, a, f[e + 6], 23, 76029189), a = m(a, b, c, d, f[e + 9], 4, 3654602809), d = m(d, a, b, c, f[e + 12], 11, 3873151461), c = m(c, d, a, b, f[e + 15], 16, 530742520), b = m(b, c, d, a, f[e + 2], 23, 3299628645), a = n(a, b, c, d, f[e + 0], 6, 4096336452), d = n(d, a, b, c, f[e + 7], 10, 1126891415), c = n(c, d, a, b, f[e + 14], 15, 2878612391), b = n(b, c, d, a, f[e + 5], 21, 4237533241), a = n(a, b, c, d, f[e + 12], 6, 1700485571), d = n(d, a, b, c, f[e + 3], 10, 2399980690), c = n(c, d, a, b, f[e + 10], 15, 4293915773), b = n(b, c, d, a, f[e + 1], 21, 2240044497), a = n(a, b, c, d, f[e + 8], 6, 1873313359), d = n(d, a, b, c, f[e + 15], 10, 4264355552), c = n(c, d, a, b, f[e + 6], 15, 2734768916), b = n(b, c, d, a, f[e + 13], 21, 1309151649), a = n(a, b, c, d, f[e + 4], 6, 4149444226), d = n(d, a, b, c, f[e + 11], 10, 3174756917), c = n(c, d, a, b, f[e + 2], 15, 718787259), b = n(b, c, d, a, f[e + 9], 21, 3951481745), a = h(a, q), b = h(b, r), c = h(c, s), d = h(d, t);
|
||||||
return (p(a) + p(b) + p(c) + p(d)).toLowerCase()
|
return (p(a) + p(b) + p(c) + p(d)).toLowerCase()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
import { newPoint } from './point.js';
|
"use strict";
|
||||||
import earcut from '../ext/earcut.js';
|
|
||||||
|
|
||||||
|
// use: add.array
|
||||||
|
// use: add.class
|
||||||
|
gapp.register("geo.base", [], (root, exports) => {
|
||||||
|
|
||||||
|
const base = root.base = {};
|
||||||
const round_decimal_precision = 5;
|
const round_decimal_precision = 5;
|
||||||
|
|
||||||
function time() {
|
function time() {
|
||||||
|
|
@ -215,7 +219,7 @@ function intersect(p1, p2, p3, p4, test, parallelok) {
|
||||||
if (test === keys.SEGINT && !segint) return null;
|
if (test === keys.SEGINT && !segint) return null;
|
||||||
if (test === keys.RAYINT && !rayint) return null;
|
if (test === keys.RAYINT && !rayint) return null;
|
||||||
|
|
||||||
let ip = newPoint(
|
let ip = base.newPoint(
|
||||||
p1x + (a * d1x), // x
|
p1x + (a * d1x), // x
|
||||||
p1y + (a * d1y), // y
|
p1y + (a * d1y), // y
|
||||||
p3.z || p4.z, // z
|
p3.z || p4.z, // z
|
||||||
|
|
@ -260,7 +264,7 @@ function intersectRayLine(ro, s1, p1, p2, infinite) {
|
||||||
b = n2 / d;
|
b = n2 / d;
|
||||||
|
|
||||||
if (infinite || (inCloseRange(b, 0, 1) && a >= 0)) {
|
if (infinite || (inCloseRange(b, 0, 1) && a >= 0)) {
|
||||||
let ip = newPoint(
|
let ip = base.newPoint(
|
||||||
p1x + (a * s1x),
|
p1x + (a * s1x),
|
||||||
p1y + (a * s1y),
|
p1y + (a * s1y),
|
||||||
p2.z || ro.z,
|
p2.z || ro.z,
|
||||||
|
|
@ -524,7 +528,7 @@ function comma(v) {
|
||||||
* Connect to base
|
* Connect to base
|
||||||
******************************************************************* */
|
******************************************************************* */
|
||||||
|
|
||||||
export const key = {
|
base.key = {
|
||||||
NONE: "",
|
NONE: "",
|
||||||
PROJECT: "project",
|
PROJECT: "project",
|
||||||
SEGINT: "segint",
|
SEGINT: "segint",
|
||||||
|
|
@ -532,7 +536,7 @@ export const key = {
|
||||||
PARALLEL: "parallel"
|
PARALLEL: "parallel"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const config = {
|
base.config = {
|
||||||
// size of gcode debug arrow head
|
// size of gcode debug arrow head
|
||||||
debug_arrow: 0.25,
|
debug_arrow: 0.25,
|
||||||
// default # of decimal places in generated gcode
|
// default # of decimal places in generated gcode
|
||||||
|
|
@ -598,7 +602,7 @@ export const config = {
|
||||||
clipperClean: 250
|
clipperClean: 250
|
||||||
};
|
};
|
||||||
|
|
||||||
export const util = {
|
base.util = {
|
||||||
sqr,
|
sqr,
|
||||||
lerp,
|
lerp,
|
||||||
time,
|
time,
|
||||||
|
|
@ -635,10 +639,4 @@ export const util = {
|
||||||
zInPlane
|
zInPlane
|
||||||
};
|
};
|
||||||
|
|
||||||
export const base = {
|
});
|
||||||
config,
|
|
||||||
key,
|
|
||||||
util
|
|
||||||
};
|
|
||||||
|
|
||||||
export { earcut };
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
||||||
|
|
||||||
import { config, util } from './base.js';
|
"use strict";
|
||||||
import { newPoint } from './point.js';
|
|
||||||
|
|
||||||
export class Bounds {
|
// dep: geo.base
|
||||||
|
gapp.register("geo.bounds", [], (root, exports) => {
|
||||||
|
|
||||||
|
const { base } = root;
|
||||||
|
const { config, util } = base;
|
||||||
|
|
||||||
|
class Bounds {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.minx = 10e7;
|
this.minx = 10e7;
|
||||||
this.miny = 10e7;
|
this.miny = 10e7;
|
||||||
|
|
@ -116,7 +121,7 @@ export class Bounds {
|
||||||
}
|
}
|
||||||
|
|
||||||
center(z = 0) {
|
center(z = 0) {
|
||||||
return newPoint(this.centerx(), this.centery(), z);
|
return base.newPoint(this.centerx(), this.centery(), z);
|
||||||
}
|
}
|
||||||
|
|
||||||
centerx() {
|
centerx() {
|
||||||
|
|
@ -128,6 +133,9 @@ export class Bounds {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newBounds() {
|
gapp.overlay(base, {
|
||||||
return new Bounds();
|
Bounds,
|
||||||
}
|
newBounds() { return new Bounds() }
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -2,17 +2,24 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { THREE } from '../ext/three.js';
|
// dep: geo.base
|
||||||
import manifold from '../ext/manifold.js';
|
// mod: ext.manifold
|
||||||
|
gapp.register("geo.csg", [], (root, exports) => {
|
||||||
|
|
||||||
|
const { base, ext } = root;
|
||||||
|
const debug = true;
|
||||||
const precision = 0.001;
|
const precision = 0.001;
|
||||||
const factor = 1/precision;
|
const factor = 1/precision;
|
||||||
|
|
||||||
function log() {
|
function log() {
|
||||||
|
if (root?.mesh?.log) {
|
||||||
|
root.mesh.log(...arguments)
|
||||||
|
} else if (root.debug === true) {
|
||||||
console.log(...arguments);
|
console.log(...arguments);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const CSG = {
|
const CSG = {
|
||||||
// accepts 2 or more arguments with threejs vertex position arrays
|
// accepts 2 or more arguments with threejs vertex position arrays
|
||||||
union() {
|
union() {
|
||||||
return CSG.moduleOp('manifold.union', 'union', ...arguments);
|
return CSG.moduleOp('manifold.union', 'union', ...arguments);
|
||||||
|
|
@ -116,10 +123,23 @@ function indexVertices(pos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let Instance;
|
let Instance;
|
||||||
manifold({
|
|
||||||
locateFile() { return "/wasm/manifold.wasm" }
|
ext.manifold.then(mod => {
|
||||||
|
mod.default({
|
||||||
|
locateFile(a,b,c) {
|
||||||
|
return "/wasm/manifold.wasm";
|
||||||
|
}
|
||||||
}).then(inst => {
|
}).then(inst => {
|
||||||
inst.setup();
|
inst.setup();
|
||||||
Instance = inst;
|
Instance = inst;
|
||||||
CSG.Instance = () => { return Instance };
|
CSG.Instance = () => { return Instance };
|
||||||
|
}).catch(error => {
|
||||||
|
console.log('manifold load error', error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gapp.overlay(base, {
|
||||||
|
CSG
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
/** Copyright Stewart Allen -- All Rights Reserved */
|
/** Copyright Stewart Allen -- All Rights Reserved */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// dep: geo.base
|
||||||
|
gapp.register("geo.gyroid", [], (root, exports) => {
|
||||||
|
|
||||||
|
const { base } = root;
|
||||||
const PI2 = Math.PI * 2;
|
const PI2 = Math.PI * 2;
|
||||||
|
|
||||||
let cache = {};
|
let cache = {};
|
||||||
|
|
@ -11,7 +17,7 @@ let lastSlice = 0;
|
||||||
* @param off {number} z offset value from 0-1
|
* @param off {number} z offset value from 0-1
|
||||||
* @param res {number} resolution (pixels/slices per side)
|
* @param res {number} resolution (pixels/slices per side)
|
||||||
*/
|
*/
|
||||||
export function slice(off, res, val) {
|
function slice(off, res, val) {
|
||||||
// auto clear cach if it hasn't been hit in the last 20 seconds
|
// auto clear cach if it hasn't been hit in the last 20 seconds
|
||||||
// or the requested resolution or tip values have changed
|
// or the requested resolution or tip values have changed
|
||||||
let now = Date.now();
|
let now = Date.now();
|
||||||
|
|
@ -164,7 +170,7 @@ export function slice(off, res, val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge co-linear and distance threshold
|
// merge co-linear and distance threshold
|
||||||
export function filter(poly, inc) {
|
function filter(poly, inc) {
|
||||||
if (poly.length <= 2) {
|
if (poly.length <= 2) {
|
||||||
return poly;
|
return poly;
|
||||||
}
|
}
|
||||||
|
|
@ -194,16 +200,21 @@ export function filter(poly, inc) {
|
||||||
}
|
}
|
||||||
e1 = el;
|
e1 = el;
|
||||||
}
|
}
|
||||||
|
nupoly.push(poly[poly.length-1]);
|
||||||
return nupoly;
|
return nupoly;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function distTo(a, b, dir) {
|
function distTo(a, b, dir) {
|
||||||
let dx = a.x - b.x;
|
let dx = a.x - b.x;
|
||||||
let dy = a.y - b.y;
|
let dy = a.y - b.y;
|
||||||
if (dir === 'lr') {
|
// bias distance by prevailing direction of discovery to join stragglers
|
||||||
return Math.abs(dx);
|
if (dir === 'lr') dx = dx / 2;
|
||||||
} else if (dir === 'td') {
|
if (dir === 'td') dy = dy / 2;
|
||||||
return Math.abs(dy);
|
|
||||||
}
|
|
||||||
return Math.sqrt(dx * dx + dy * dy);
|
return Math.sqrt(dx * dx + dy * dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gapp.overlay(base, {
|
||||||
|
gyroid: { slice }
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
export class Line {
|
"use strict";
|
||||||
|
|
||||||
|
// dep: geo.base
|
||||||
|
gapp.register("geo.line", [], (root, exports) => {
|
||||||
|
|
||||||
|
const { base } = root;
|
||||||
|
|
||||||
|
class Line {
|
||||||
constructor(p1, p2, key) {
|
constructor(p1, p2, key) {
|
||||||
if (!key) key = [p1.key, p2.key].join(';');
|
if (!key) key = [p1.key, p2.key].join(';');
|
||||||
this.p1 = p1;
|
this.p1 = p1;
|
||||||
|
|
@ -47,10 +54,18 @@ export class Line {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newLine(p1, p2, key) {
|
function newLine(p1, p2, key) {
|
||||||
return new Line(p1, p2, key);
|
return new Line(p1, p2, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newOrderedLine(p1, p2, key) {
|
function newOrderedLine(p1, p2, key) {
|
||||||
return p1.key < p2.key ? newLine(p1,p2,key) : newLine(p2,p1,key);
|
return p1.key < p2.key ? newLine(p1,p2,key) : newLine(p2,p1,key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gapp.overlay(base, {
|
||||||
|
Line,
|
||||||
|
newLine,
|
||||||
|
newOrderedLine
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -1,7 +1,19 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
import { base } from './base.js';
|
"use strict";
|
||||||
import { newPoint } from './point.js';
|
|
||||||
|
// path & routing output utilities
|
||||||
|
|
||||||
|
// dep: geo.base
|
||||||
|
// dep: geo.point
|
||||||
|
|
||||||
|
gapp.register("geo.paths", [], (root, exports) => {
|
||||||
|
|
||||||
|
const { base } = root;
|
||||||
|
const { util, config, newPoint } = base;
|
||||||
|
const { sqr, numOrDefault } = util;
|
||||||
|
|
||||||
|
const DEG2RAD = Math.PI / 180;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* emit each element in an array based on
|
* emit each element in an array based on
|
||||||
|
|
@ -9,7 +21,7 @@ import { newPoint } from './point.js';
|
||||||
* elements with { first, last } points and
|
* elements with { first, last } points and
|
||||||
* may be open polys, unlike poly2polyEmit
|
* may be open polys, unlike poly2polyEmit
|
||||||
*/
|
*/
|
||||||
export function tip2tipEmit(array, startPoint, emitter) {
|
function tip2tipEmit(array, startPoint, emitter) {
|
||||||
let mindist, dist, found, count = 0;
|
let mindist, dist, found, count = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
found = null;
|
found = null;
|
||||||
|
|
@ -44,7 +56,7 @@ export function tip2tipEmit(array, startPoint, emitter) {
|
||||||
* to be more like outputOrderClosest() and have the option to account for
|
* to be more like outputOrderClosest() and have the option to account for
|
||||||
* depth in determining distance
|
* depth in determining distance
|
||||||
*/
|
*/
|
||||||
export function poly2polyEmit(array, startPoint, emitter, opt = {}) {
|
function poly2polyEmit(array, startPoint, emitter, opt = {}) {
|
||||||
let marker = opt.mark || 'delete';
|
let marker = opt.mark || 'delete';
|
||||||
let mindist, dist, found, count = 0;
|
let mindist, dist, found, count = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
@ -96,7 +108,7 @@ export function poly2polyEmit(array, startPoint, emitter, opt = {}) {
|
||||||
return startPoint;
|
return startPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calc_normal(p1, p2) {
|
function calc_normal(p1, p2) {
|
||||||
let dx = p2.x - p1.x;
|
let dx = p2.x - p1.x;
|
||||||
let dy = p2.y - p1.y;
|
let dy = p2.y - p1.y;
|
||||||
let len = Math.sqrt(dx * dx + dy * dy);
|
let len = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
@ -106,7 +118,7 @@ export function calc_normal(p1, p2) {
|
||||||
return({ dx: dy, dy: -dx, p1, p2, len });
|
return({ dx: dy, dy: -dx, p1, p2, len });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function end_vertex(n1, n2, off, start) {
|
function end_vertex(n1, n2, off, start) {
|
||||||
let dx, dy;
|
let dx, dy;
|
||||||
if (start) {
|
if (start) {
|
||||||
dx = n2.dx * off;
|
dx = n2.dx * off;
|
||||||
|
|
@ -118,7 +130,7 @@ export function end_vertex(n1, n2, off, start) {
|
||||||
return { dx, dy, vp: n1.p2 };
|
return { dx, dy, vp: n1.p2 };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calc_vertex(n1, n2, off, vp) {
|
function calc_vertex(n1, n2, off, vp) {
|
||||||
let dx, dy, io, vl, q, r;
|
let dx, dy, io, vl, q, r;
|
||||||
r = 1 + (n1.dx * n2.dx + n1.dy * n2.dy);
|
r = 1 + (n1.dx * n2.dx + n1.dy * n2.dy);
|
||||||
q = off / r;
|
q = off / r;
|
||||||
|
|
@ -136,7 +148,7 @@ export function calc_vertex(n1, n2, off, vp) {
|
||||||
return { dx, dy, vp: vp || n1.p2, io, vl };
|
return { dx, dy, vp: vp || n1.p2, io, vl };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function v2pl(rec) {
|
function v2pl(rec) {
|
||||||
let p = rec.vp.clone();
|
let p = rec.vp.clone();
|
||||||
p.x += rec.dx;
|
p.x += rec.dx;
|
||||||
p.y += rec.dy;
|
p.y += rec.dy;
|
||||||
|
|
@ -144,7 +156,7 @@ export function v2pl(rec) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function v2pr(rec) {
|
function v2pr(rec) {
|
||||||
let p = rec.vp.clone();
|
let p = rec.vp.clone();
|
||||||
p.x -= rec.dx;
|
p.x -= rec.dx;
|
||||||
p.y -= rec.dy;
|
p.y -= rec.dy;
|
||||||
|
|
@ -152,7 +164,7 @@ export function v2pr(rec) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pointsToPath(points, offset, open, miter = 1.5) {
|
function pointsToPath(points, offset, open, miter = 1.5) {
|
||||||
const absoff = Math.abs(offset);
|
const absoff = Math.abs(offset);
|
||||||
// calculate segment normals which are used to calculate vertex normals
|
// calculate segment normals which are used to calculate vertex normals
|
||||||
// next segment info is associated with the current point
|
// next segment info is associated with the current point
|
||||||
|
|
@ -324,7 +336,7 @@ export function pointsToPath(points, offset, open, miter = 1.5) {
|
||||||
return { left, right, faces, normals, open };
|
return { left, right, faces, normals, open };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pathTo3D(path, height, z) {
|
function pathTo3D(path, height, z) {
|
||||||
const { faces, normals, left, right, open } = path;
|
const { faces, normals, left, right, open } = path;
|
||||||
const out = [];
|
const out = [];
|
||||||
const nrm = [];
|
const nrm = [];
|
||||||
|
|
@ -422,7 +434,7 @@ export function pathTo3D(path, height, z) {
|
||||||
|
|
||||||
// produces indexed geometry which isn't ideal for rendering because
|
// produces indexed geometry which isn't ideal for rendering because
|
||||||
// the default threejs generated vertex normals aren't accurate
|
// the default threejs generated vertex normals aren't accurate
|
||||||
export function shapeToPath(shape, points, closed) {
|
function shapeToPath(shape, points, closed) {
|
||||||
closed = closed !== undefined ? closed : true;
|
closed = closed !== undefined ? closed : true;
|
||||||
|
|
||||||
const profileGeometry = new THREE.ShapeGeometry(shape);
|
const profileGeometry = new THREE.ShapeGeometry(shape);
|
||||||
|
|
@ -527,20 +539,24 @@ export function shapeToPath(shape, points, closed) {
|
||||||
*
|
*
|
||||||
* @return {Array<Point>} an array of points representing the arc.
|
* @return {Array<Point>} an array of points representing the arc.
|
||||||
*/
|
*/
|
||||||
export function arcToPath(start, end, arcdivs = 24, opts) {
|
function arcToPath( start, end,arcdivs=24,opts) {
|
||||||
|
|
||||||
let { clockwise, center, radius } = opts;
|
let { clockwise, center, radius } = opts;
|
||||||
|
|
||||||
// @type {Point}
|
// @type {Point}
|
||||||
|
|
||||||
|
|
||||||
if (end.x === undefined && end.x === undefined && center === undefined) {
|
if (end.x === undefined && end.x === undefined && center === undefined) {
|
||||||
// bambu generates loop z or wipe loop arcs in place
|
// bambu generates loop z or wipe loop arcs in place
|
||||||
// console.log({ skip_empty_arc: rec });
|
// console.log({ skip_empty_arc: rec });
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (center) {
|
if (center) {
|
||||||
// center = center.add(start);
|
// center = center.add(start);
|
||||||
center.r = center.distTo2D(start);
|
center.r = center.distTo2D(start);
|
||||||
|
|
||||||
} else if (radius !== undefined) {
|
} else if (radius !== undefined) {
|
||||||
let pd = { x: end.x - start.x, y: end.y - start.y }; //position delta
|
let pd = { x: end.x - start.x, y: end.y - start.y }; //position delta
|
||||||
let dst = Math.sqrt(pd.x * pd.x + pd.y * pd.y) / 2; // distance
|
let dst = Math.sqrt(pd.x * pd.x + pd.y * pd.y) / 2; // distance
|
||||||
|
|
@ -607,7 +623,7 @@ export function arcToPath(start, end, arcdivs = 24, opts) {
|
||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FloatPacker {
|
class FloatPacker {
|
||||||
constructor(size, factor) {
|
constructor(size, factor) {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.factor = Math.min(factor || 1.2, 1.1);
|
this.factor = Math.min(factor || 1.2, 1.1);
|
||||||
|
|
@ -640,11 +656,15 @@ export class FloatPacker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const paths = {
|
base.paths = {
|
||||||
tip2tipEmit,
|
|
||||||
poly2polyEmit,
|
poly2polyEmit,
|
||||||
|
tip2tipEmit,
|
||||||
shapeToPath,
|
shapeToPath,
|
||||||
pointsToPath,
|
pointsToPath,
|
||||||
pathTo3D,
|
pathTo3D,
|
||||||
FloatPacker
|
arcToPath,
|
||||||
}
|
vertexNormal: calc_vertex,
|
||||||
|
segmentNormal: calc_normal
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
import { util, config, key } from './base.js';
|
"use strict";
|
||||||
import { newLine } from './line.js';
|
|
||||||
import { newSlope } from './slope.js';
|
|
||||||
|
|
||||||
const { Vector3 } = THREE;
|
// dep: geo.base
|
||||||
|
gapp.register("geo.point", [], (root, exports) => {
|
||||||
|
|
||||||
|
const { base } = root;
|
||||||
|
const { util, config, key } = base;
|
||||||
|
const { round } = util;
|
||||||
|
|
||||||
class Point {
|
class Point {
|
||||||
constructor(x = 0, y = 0, z = 0, key) {
|
constructor(x = 0, y = 0, z = 0, key) {
|
||||||
|
|
@ -39,7 +42,7 @@ class Point {
|
||||||
}
|
}
|
||||||
|
|
||||||
toVector3() {
|
toVector3() {
|
||||||
return new Vector3(this.x, this.y, this.z);
|
return new THREE.Vector3(this.x, this.y, this.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(x, y, z) {
|
set(x, y, z) {
|
||||||
|
|
@ -123,11 +126,11 @@ class Point {
|
||||||
}
|
}
|
||||||
|
|
||||||
slopeTo(p) {
|
slopeTo(p) {
|
||||||
return newSlope(this, p);
|
return base.newSlope(this, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
lineTo(p, k) {
|
lineTo(p, k) {
|
||||||
return newLine(this, p, k);
|
return base.newLine(this, p, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
isNear(p, dist) {
|
isNear(p, dist) {
|
||||||
|
|
@ -293,7 +296,7 @@ class Point {
|
||||||
np2 = newPoint(p2.x - oy, p2.y + ox, p2.z, key.NONE);
|
np2 = newPoint(p2.x - oy, p2.y + ox, p2.z, key.NONE);
|
||||||
np1.op = p1;
|
np1.op = p1;
|
||||||
np2.op = p2;
|
np2.op = p2;
|
||||||
return newLine(np1, np2, key.NONE);
|
return base.newLine(np1, np2, key.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
offset(x, y, z) {
|
offset(x, y, z) {
|
||||||
|
|
@ -649,8 +652,10 @@ function pointFromClipper(cp, z) {
|
||||||
return newPoint(cp.X / config.clipper, cp.Y / config.clipper, z);
|
return newPoint(cp.X / config.clipper, cp.Y / config.clipper, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
gapp.overlay(base, {
|
||||||
Point,
|
Point,
|
||||||
newPoint,
|
newPoint,
|
||||||
pointFromClipper
|
pointFromClipper
|
||||||
};
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -2,14 +2,23 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { config } from './base.js';
|
// dep: geo.base
|
||||||
import { newPoint } from '../geo/point.js';
|
// dep: geo.point
|
||||||
|
gapp.register("geo.points", [], (root, exports) => {
|
||||||
|
|
||||||
|
const { base } = root;
|
||||||
|
const { config } = base;
|
||||||
|
|
||||||
|
gapp.overlay(base, {
|
||||||
|
verticesToPoints,
|
||||||
|
pointsToVertices
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* converts a geometry point array into a kiri point array
|
* converts a geometry point array into a kiri point array
|
||||||
* with optional decimation
|
* with optional decimation
|
||||||
*/
|
*/
|
||||||
export function verticesToPoints(array, options) {
|
function verticesToPoints(array, options) {
|
||||||
let parr = new Array(array.length / 3),
|
let parr = new Array(array.length / 3),
|
||||||
i = 0,
|
i = 0,
|
||||||
j = 0,
|
j = 0,
|
||||||
|
|
@ -32,7 +41,7 @@ export function verticesToPoints(array, options) {
|
||||||
// replace point objects with their equivalents
|
// replace point objects with their equivalents
|
||||||
if (maxpass) {
|
if (maxpass) {
|
||||||
while (i < array.length) {
|
while (i < array.length) {
|
||||||
let p = newPoint(array[i++], array[i++], array[i++]),
|
let p = base.newPoint(array[i++], array[i++], array[i++]),
|
||||||
k = p.key,
|
k = p.key,
|
||||||
m = hash[k];
|
m = hash[k];
|
||||||
if (!m) {
|
if (!m) {
|
||||||
|
|
@ -44,7 +53,7 @@ export function verticesToPoints(array, options) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (i < array.length) {
|
while (i < array.length) {
|
||||||
parr[j++] = newPoint(array[i++], array[i++], array[i++]);
|
parr[j++] = base.newPoint(array[i++], array[i++], array[i++]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,7 +124,7 @@ export function verticesToPoints(array, options) {
|
||||||
return parr;
|
return parr;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pointsToVertices(points) {
|
function pointsToVertices(points) {
|
||||||
let vertices = new Float32Array(points.length * 3),
|
let vertices = new Float32Array(points.length * 3),
|
||||||
i = 0, vi = 0;
|
i = 0, vi = 0;
|
||||||
while (i < points.length) {
|
while (i < points.length) {
|
||||||
|
|
@ -125,3 +134,5 @@ export function pointsToVertices(points) {
|
||||||
}
|
}
|
||||||
return vertices;
|
return vertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -2,17 +2,22 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { base, config, earcut, util } from './base.js';
|
// dep: geo.base
|
||||||
import { ClipperLib } from '../ext/clip2.esm.js';
|
// dep: geo.paths
|
||||||
import { newBounds } from './bounds.js';
|
// dep: ext.clip2
|
||||||
import { paths } from './paths.js';
|
// dep: ext.earcut
|
||||||
import { newPoint, pointFromClipper } from './point.js';
|
// dep: geo.point
|
||||||
import { polygons as POLY } from './polygons.js';
|
// dep: geo.bounds
|
||||||
|
// dep: geo.polygons
|
||||||
|
gapp.register("geo.polygon", [], (root, exports) => {
|
||||||
|
|
||||||
const { Vector3 } = THREE;
|
const { base } = root;
|
||||||
|
const { config, util, polygons, newBounds, newPoint } = base;
|
||||||
|
|
||||||
let XAXIS = new Vector3(1,0,0),
|
const POLY = polygons,
|
||||||
|
XAXIS = new THREE.Vector3(1,0,0),
|
||||||
DEG2RAD = Math.PI / 180,
|
DEG2RAD = Math.PI / 180,
|
||||||
|
ClipperLib = self.ClipperLib,
|
||||||
Clipper = ClipperLib.Clipper,
|
Clipper = ClipperLib.Clipper,
|
||||||
ClipType = ClipperLib.ClipType,
|
ClipType = ClipperLib.ClipType,
|
||||||
PolyType = ClipperLib.PolyType,
|
PolyType = ClipperLib.PolyType,
|
||||||
|
|
@ -34,7 +39,7 @@ let XAXIS = new Vector3(1,0,0),
|
||||||
|
|
||||||
let seqid = Math.round(Math.random() * 0xffffffff);
|
let seqid = Math.round(Math.random() * 0xffffffff);
|
||||||
|
|
||||||
export class Polygon {
|
class Polygon {
|
||||||
constructor(points) {
|
constructor(points) {
|
||||||
this.id = seqid++; // polygon unique id
|
this.id = seqid++; // polygon unique id
|
||||||
this.open = false;
|
this.open = false;
|
||||||
|
|
@ -71,11 +76,11 @@ export class Polygon {
|
||||||
}
|
}
|
||||||
|
|
||||||
toPath2D(offset) {
|
toPath2D(offset) {
|
||||||
return paths.pointsToPath(this.points, offset, this.open);
|
return base.paths.pointsToPath(this.points, offset, this.open);
|
||||||
}
|
}
|
||||||
|
|
||||||
toPath3D(offset, height, z) {
|
toPath3D(offset, height, z) {
|
||||||
return paths.pathTo3D(this.toPath2D(offset), height, z);
|
return base.paths.pathTo3D(this.toPath2D(offset), height, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(verbose) {
|
toString(verbose) {
|
||||||
|
|
@ -230,7 +235,7 @@ export class Polygon {
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform earcut()
|
// perform earcut()
|
||||||
let cut = earcut(out, holes, 3);
|
let cut = self.earcut(out, holes, 3);
|
||||||
let ret = [];
|
let ret = [];
|
||||||
|
|
||||||
// preserve swaps in new polys
|
// preserve swaps in new polys
|
||||||
|
|
@ -462,7 +467,7 @@ export class Polygon {
|
||||||
return polys
|
return polys
|
||||||
.filter(poly => poly.length > 1)
|
.filter(poly => poly.length > 1)
|
||||||
.map(poly => {
|
.map(poly => {
|
||||||
let np = newPolygon().setOpen();
|
let np = base.newPolygon().setOpen();
|
||||||
for (let p of poly) {
|
for (let p of poly) {
|
||||||
np.push(p);
|
np.push(p);
|
||||||
}
|
}
|
||||||
|
|
@ -1138,7 +1143,7 @@ export class Polygon {
|
||||||
applyRotations() {
|
applyRotations() {
|
||||||
for (let point of this.points) {
|
for (let point of this.points) {
|
||||||
if (point.a) {
|
if (point.a) {
|
||||||
let p2 = new Vector3(point.x, point.y, point.z)
|
let p2 = new THREE.Vector3(point.x, point.y, point.z)
|
||||||
.applyAxisAngle(XAXIS, point.a * DEG2RAD);
|
.applyAxisAngle(XAXIS, point.a * DEG2RAD);
|
||||||
point.x = p2.x;
|
point.x = p2.x;
|
||||||
point.y = p2.y;
|
point.y = p2.y;
|
||||||
|
|
@ -1200,75 +1205,14 @@ export class Polygon {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ease down along the polygonal path.
|
* calls function for each point in this polygon
|
||||||
*
|
* @param {Function} fn to call for each point
|
||||||
* 1. Travel from fromPoint to closest point on polygon, to rampZ above that that point,
|
* @param {boolean} [close=true] whether to close the loop (last point to first)
|
||||||
* 2. ease-down starts, following the polygonal path, decreasing Z at a fixed slope until target Z is hit,
|
* @param {number} [start=0] starting index
|
||||||
* 3. then the rest of the path is completed and repeated at target Z until touchdown point is reached.
|
|
||||||
* 4. this function should probably move to CAM prepare since it's only called from there
|
|
||||||
*/
|
*/
|
||||||
forEachPointEaseDown(fn, fromPoint, degrees = 45) {
|
|
||||||
let index = this.findClosestPointTo(fromPoint).index,
|
|
||||||
fromZ = fromPoint.z,
|
|
||||||
offset = 0,
|
|
||||||
points = this.points,
|
|
||||||
length = points.length,
|
|
||||||
touch = -1, // first point to touch target z
|
|
||||||
targetZ = points[0].z,
|
|
||||||
dist2next,
|
|
||||||
last,
|
|
||||||
next,
|
|
||||||
done;
|
|
||||||
|
|
||||||
// Slope for computations.
|
|
||||||
const slope = Math.tan((degrees * Math.PI) / 180);
|
|
||||||
// Z height above polygon Z from which to start the ease-down.
|
|
||||||
// Machine will travel from "fromPoint" to "nearest point x, y, z' => with z' = point z + rampZ",
|
|
||||||
// then start the ease down along path.
|
|
||||||
const rampZ = 2.0;
|
|
||||||
while (true) {
|
|
||||||
next = points[index % length];
|
|
||||||
if (last && next.z < fromZ) {
|
|
||||||
// When "in Ease-Down" (ie. while target Z not yet reached) - follow path while slowly decreasing Z.
|
|
||||||
let deltaZ = fromZ - next.z;
|
|
||||||
dist2next = last.distTo2D(next);
|
|
||||||
let deltaZFullMove = dist2next * slope;
|
|
||||||
|
|
||||||
if (deltaZFullMove > deltaZ) {
|
|
||||||
// Too long: easing along full path would overshoot depth, synth intermediate point at target Z.
|
|
||||||
//
|
|
||||||
// XXX: please check my super basic trig - this should follow from `last` to `next` up until the
|
|
||||||
// intersect at the target Z distance.
|
|
||||||
fn(last.followTo(next, dist2next * deltaZ / deltaZFullMove).setZ(next.z), offset++);
|
|
||||||
} else {
|
|
||||||
// Ok: execute full move at desired slope.
|
|
||||||
next = next.clone().setZ(fromZ - deltaZFullMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
fromZ = next.z;
|
|
||||||
} else if (offset === 0 && next.z < fromZ) {
|
|
||||||
// First point, move to rampZ height above next.
|
|
||||||
let deltaZ = fromZ - next.z;
|
|
||||||
fromZ = next.z + Math.min(deltaZ, rampZ)
|
|
||||||
next = next.clone().setZ(fromZ);
|
|
||||||
}
|
|
||||||
last = next;
|
|
||||||
fn(next, offset++);
|
|
||||||
if ((index % length) === touch) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (touch < 0 && next.z <= targetZ) {
|
|
||||||
// Save touch-down index so as to be able to "complete" the full cut at target Z,
|
|
||||||
// i.e. keep following the path loop until the touch down point is reached again.
|
|
||||||
touch = ((index + length) % length);
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return last;
|
|
||||||
}
|
|
||||||
|
|
||||||
forEachPoint(fn, close, start) {
|
forEachPoint(fn, close, start) {
|
||||||
let index = start || 0,
|
let index = start || 0,
|
||||||
points = this.points,
|
points = this.points,
|
||||||
|
|
@ -2059,7 +2003,7 @@ export class Polygon {
|
||||||
return res.map(array => {
|
return res.map(array => {
|
||||||
let poly = newPolygon();
|
let poly = newPolygon();
|
||||||
for (let pt of array) {
|
for (let pt of array) {
|
||||||
poly.push(pointFromClipper(pt, z));
|
poly.push(base.pointFromClipper(pt, z));
|
||||||
}
|
}
|
||||||
return poly;
|
return poly;
|
||||||
});
|
});
|
||||||
|
|
@ -2374,7 +2318,8 @@ export class Polygon {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function slopeDiff(s1, s2) {
|
// use Slope.angleDiff() then re-test path mitering / rendering
|
||||||
|
function slopeDiff(s1, s2) {
|
||||||
const n1 = s1.angle;
|
const n1 = s1.angle;
|
||||||
const n2 = s2.angle;
|
const n2 = s2.angle;
|
||||||
let diff = n2 - n1;
|
let diff = n2 - n1;
|
||||||
|
|
@ -2383,17 +2328,28 @@ export function slopeDiff(s1, s2) {
|
||||||
return Math.abs(diff);
|
return Math.abs(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fromClipperPath(path, z) {
|
function fromClipperPath(path, z) {
|
||||||
let poly = newPolygon(),
|
let poly = newPolygon(),
|
||||||
i = 0,
|
i = 0,
|
||||||
l = path.length;
|
l = path.length;
|
||||||
while (i < l) {
|
while (i < l) {
|
||||||
// poly.push(newPoint(null,null,z,null,path[i++]));
|
// poly.push(newPoint(null,null,z,null,path[i++]));
|
||||||
poly.push(pointFromClipper(path[i++], z));
|
poly.push(base.pointFromClipper(path[i++], z));
|
||||||
}
|
}
|
||||||
return poly;
|
return poly;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newPolygon(points) {
|
function newPolygon(points) {
|
||||||
return new Polygon(points);
|
return new Polygon(points);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polygon.fromArray = function(array) {
|
||||||
|
return newPolygon().fromArray(array);
|
||||||
|
};
|
||||||
|
|
||||||
|
gapp.overlay(base, {
|
||||||
|
Polygon,
|
||||||
|
newPolygon
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -2,45 +2,45 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { base, util, config } from './base.js';
|
// dep: geo.base
|
||||||
import { newPoint, pointFromClipper } from './point.js';
|
// dep: geo.point
|
||||||
import { newPolygon } from './polygon.js';
|
// use: ext.clip2
|
||||||
import { newSlope } from './slope.js';
|
// use: geo.slope
|
||||||
import { paths } from './paths.js';
|
// use: geo.polygon
|
||||||
import { ClipperLib } from '../ext/clip2.esm.js';
|
gapp.register("geo.polygons", [], (root, exports) => {
|
||||||
|
|
||||||
const geo = base;
|
const { base } = root;
|
||||||
const { numOrDefault } = util;
|
const { util, paths, config, newPoint } = base;
|
||||||
|
const { sqr, numOrDefault } = util;
|
||||||
|
|
||||||
const DEG2RAD = Math.PI / 180,
|
const DEG2RAD = Math.PI / 180,
|
||||||
SQRT = Math.sqrt,
|
SQRT = Math.sqrt,
|
||||||
SQR = util.sqr,
|
SQR = util.sqr,
|
||||||
ABS = Math.abs;
|
ABS = Math.abs;
|
||||||
|
|
||||||
const {
|
const ClipperLib = self.ClipperLib,
|
||||||
Clipper,
|
Clipper = ClipperLib.Clipper,
|
||||||
ClipType,
|
ClipType = ClipperLib.ClipType,
|
||||||
PolyType,
|
PolyType = ClipperLib.PolyType,
|
||||||
PolyFillType,
|
PolyFillType = ClipperLib.PolyFillType,
|
||||||
EndType,
|
CleanPolygon = Clipper.CleanPolygon,
|
||||||
JoinType,
|
|
||||||
PolyTree,
|
|
||||||
ClipperOffset
|
|
||||||
} = ClipperLib;
|
|
||||||
|
|
||||||
const CleanPolygon = Clipper.CleanPolygon,
|
|
||||||
CleanPolygons = Clipper.CleanPolygons,
|
CleanPolygons = Clipper.CleanPolygons,
|
||||||
SimplifyPolygons = Clipper.SimplifyPolygons,
|
SimplifyPolygons = Clipper.SimplifyPolygons,
|
||||||
FillNonZero = PolyFillType.pftNonZero,
|
FillNonZero = PolyFillType.pftNonZero,
|
||||||
FillEvenOdd = PolyFillType.pftEvenOdd,
|
FillEvenOdd = PolyFillType.pftEvenOdd,
|
||||||
PathSubject = PolyType.ptSubject,
|
PathSubject = PolyType.ptSubject,
|
||||||
PathClip = PolyType.ptClip,
|
PathClip = PolyType.ptClip,
|
||||||
|
EndType = ClipperLib.EndType,
|
||||||
|
JoinType = ClipperLib.JoinType,
|
||||||
|
PolyTree = ClipperLib.PolyTree,
|
||||||
ClipXOR = ClipType.ctXor,
|
ClipXOR = ClipType.ctXor,
|
||||||
ClipDiff = ClipType.ctDifference,
|
ClipDiff = ClipType.ctDifference,
|
||||||
ClipUnion = ClipType.ctUnion,
|
ClipUnion = ClipType.ctUnion,
|
||||||
ClipIntersect = ClipType.ctIntersection;
|
ClipIntersect = ClipType.ctIntersection,
|
||||||
|
ClipperOffset = ClipperLib.ClipperOffset
|
||||||
|
;
|
||||||
|
|
||||||
const POLYS = {
|
const POLYS = base.polygons = {
|
||||||
clearInner,
|
clearInner,
|
||||||
rayIntersect,
|
rayIntersect,
|
||||||
alignWindings,
|
alignWindings,
|
||||||
|
|
@ -73,16 +73,14 @@ const POLYS = {
|
||||||
fingerprint
|
fingerprint
|
||||||
};
|
};
|
||||||
|
|
||||||
export { POLYS };
|
function outer(polys) {
|
||||||
|
|
||||||
export function outer(polys) {
|
|
||||||
for (let p of polys) {
|
for (let p of polys) {
|
||||||
p.inner = undefined;
|
p.inner = undefined;
|
||||||
}
|
}
|
||||||
return polys;
|
return polys;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function inner(polys) {
|
function inner(polys) {
|
||||||
const ret = [];
|
const ret = [];
|
||||||
for (let p of polys) {
|
for (let p of polys) {
|
||||||
if (p.inner) {
|
if (p.inner) {
|
||||||
|
|
@ -92,7 +90,7 @@ export function inner(polys) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function length(polys) {
|
function length(polys) {
|
||||||
let length = 0;
|
let length = 0;
|
||||||
for (let p of polys) {
|
for (let p of polys) {
|
||||||
length += p.deepLength;
|
length += p.deepLength;
|
||||||
|
|
@ -100,20 +98,20 @@ export function length(polys) {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setZ(polys, z) {
|
function setZ(polys, z) {
|
||||||
for (let poly of polys) {
|
for (let poly of polys) {
|
||||||
poly.setZ(z);
|
poly.setZ(z);
|
||||||
}
|
}
|
||||||
return polys;
|
return polys;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearInner(polys) {
|
function clearInner(polys) {
|
||||||
for (let p of polys) {
|
for (let p of polys) {
|
||||||
p.clearInner();
|
p.clearInner();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toClipper(polys = []) {
|
function toClipper(polys = []) {
|
||||||
let out = [];
|
let out = [];
|
||||||
for (let poly of polys) {
|
for (let poly of polys) {
|
||||||
poly.toClipper(out);
|
poly.toClipper(out);
|
||||||
|
|
@ -121,16 +119,16 @@ export function toClipper(polys = []) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fromClipperNode(tnode, z) {
|
function fromClipperNode(tnode, z) {
|
||||||
let poly = newPolygon();
|
let poly = base.newPolygon();
|
||||||
for (let point of tnode.m_polygon) {
|
for (let point of tnode.m_polygon) {
|
||||||
poly.push(pointFromClipper(point, z));
|
poly.push(base.pointFromClipper(point, z));
|
||||||
}
|
}
|
||||||
poly.open = tnode.IsOpen;
|
poly.open = tnode.IsOpen;
|
||||||
return poly;
|
return poly;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function fromClipperTree(tnode, z, tops, parent, minarea) {
|
function fromClipperTree(tnode, z, tops, parent, minarea) {
|
||||||
let poly,
|
let poly,
|
||||||
polys = tops || [],
|
polys = tops || [],
|
||||||
min = numOrDefault(minarea, 0.1);
|
min = numOrDefault(minarea, 0.1);
|
||||||
|
|
@ -152,9 +150,9 @@ export function fromClipperTree(tnode, z, tops, parent, minarea) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return polys;
|
return polys;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function fromClipperTreeUnion(tnode, z, minarea, tops, parent) {
|
function fromClipperTreeUnion(tnode, z, minarea, tops, parent) {
|
||||||
let polys = tops || [], poly;
|
let polys = tops || [], poly;
|
||||||
|
|
||||||
for (let child of tnode.m_Childs) {
|
for (let child of tnode.m_Childs) {
|
||||||
|
|
@ -173,9 +171,9 @@ export function fromClipperTreeUnion(tnode, z, minarea, tops, parent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return polys;
|
return polys;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function cleanClipperTree(tree) {
|
function cleanClipperTree(tree) {
|
||||||
if (tree.m_Childs)
|
if (tree.m_Childs)
|
||||||
for (let child of tree.m_Childs) {
|
for (let child of tree.m_Childs) {
|
||||||
child.m_polygon = CleanPolygon(child.m_polygon, config.clipperClean);
|
child.m_polygon = CleanPolygon(child.m_polygon, config.clipperClean);
|
||||||
|
|
@ -183,9 +181,9 @@ export function cleanClipperTree(tree) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function filter(array, output, fn) {
|
function filter(array, output, fn) {
|
||||||
for (let poly of array) {
|
for (let poly of array) {
|
||||||
poly = fn(poly);
|
poly = fn(poly);
|
||||||
if (poly) {
|
if (poly) {
|
||||||
|
|
@ -199,14 +197,14 @@ export function filter(array, output, fn) {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function points(polys) {
|
function points(polys) {
|
||||||
return polys.length ? polys.map(p => p.deepLength).reduce((a,v) => a+v) : 0;
|
return polys.length ? polys.map(p => p.deepLength).reduce((a,v) => a+v) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* redo nesting of polygons that might already have inners
|
* redo nesting of polygons that might already have inners
|
||||||
*/
|
*/
|
||||||
export function renest(polygons, deep) {
|
function renest(polygons, deep) {
|
||||||
return nest(flatten(polygons, [], true), deep);
|
return nest(flatten(polygons, [], true), deep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,7 +220,7 @@ export function renest(polygons, deep) {
|
||||||
* @param {boolean} opentop prevent open polygons from having inners
|
* @param {boolean} opentop prevent open polygons from having inners
|
||||||
* @returns {Polygon[]} top level parent polygons
|
* @returns {Polygon[]} top level parent polygons
|
||||||
*/
|
*/
|
||||||
export function nest(polygons, deep, opentop) {
|
function nest(polygons, deep, opentop) {
|
||||||
if (!polygons) {
|
if (!polygons) {
|
||||||
return polygons;
|
return polygons;
|
||||||
}
|
}
|
||||||
|
|
@ -286,7 +284,7 @@ export function nest(polygons, deep, opentop) {
|
||||||
* @param {boolean} CW
|
* @param {boolean} CW
|
||||||
* @param {boolean} [recurse]
|
* @param {boolean} [recurse]
|
||||||
*/
|
*/
|
||||||
export function setWinding(array, CW, recurse) {
|
function setWinding(array, CW, recurse) {
|
||||||
if (!array) return;
|
if (!array) return;
|
||||||
let poly, i = 0;
|
let poly, i = 0;
|
||||||
while (i < array.length) {
|
while (i < array.length) {
|
||||||
|
|
@ -304,7 +302,7 @@ export function setWinding(array, CW, recurse) {
|
||||||
* @param {Polygon[]} polys
|
* @param {Polygon[]} polys
|
||||||
* @return {boolean} true if aligned clockwise
|
* @return {boolean} true if aligned clockwise
|
||||||
*/
|
*/
|
||||||
export function alignWindings(polys) {
|
function alignWindings(polys) {
|
||||||
let len = polys.length,
|
let len = polys.length,
|
||||||
fwd = 0,
|
fwd = 0,
|
||||||
pts = 0,
|
pts = 0,
|
||||||
|
|
@ -325,14 +323,21 @@ export function alignWindings(polys) {
|
||||||
return setCW;
|
return setCW;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setContains(setA, poly) {
|
function setContains(setA, poly) {
|
||||||
for (let i=0; i<setA.length; i++) {
|
for (let i=0; i<setA.length; i++) {
|
||||||
if (setA[i].contains(poly)) return true;
|
if (setA[i].contains(poly)) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flatten(polys, to, crush) {
|
/**
|
||||||
|
* Flatten an array of polygons into a single array of polygons.
|
||||||
|
* @param {Polygon[]} polys - input array of polygons
|
||||||
|
* @param {Polygon[]} [to] - output array. if omitted, a new array will be created
|
||||||
|
* @param {boolean} [crush] - if true, remove the inner array after flattening
|
||||||
|
* @returns {Polygon[]} - the flattened array
|
||||||
|
*/
|
||||||
|
function flatten(polys, to, crush) {
|
||||||
to = to || [];
|
to = to || [];
|
||||||
for (let poly of polys) {
|
for (let poly of polys) {
|
||||||
poly.flattenTo(to);
|
poly.flattenTo(to);
|
||||||
|
|
@ -353,7 +358,7 @@ export function flatten(polys, to, crush) {
|
||||||
* @param {number} [minArea]
|
* @param {number} [minArea]
|
||||||
* @returns {Polygon[]} out
|
* @returns {Polygon[]} out
|
||||||
*/
|
*/
|
||||||
export function subtract(setA, setB, outA, outB, z, minArea, opt = {}) {
|
function subtract(setA, setB, outA, outB, z, minArea, opt = {}) {
|
||||||
let min = numOrDefault(minArea, 0.1),
|
let min = numOrDefault(minArea, 0.1),
|
||||||
out = [];
|
out = [];
|
||||||
|
|
||||||
|
|
@ -435,7 +440,7 @@ export function subtract(setA, setB, outA, outB, z, minArea, opt = {}) {
|
||||||
* @param {Polygon[]} polys
|
* @param {Polygon[]} polys
|
||||||
* @returns {Polygon[]}
|
* @returns {Polygon[]}
|
||||||
*/
|
*/
|
||||||
export function union(polys, minarea, all, opt = {}) {
|
function union(polys, minarea, all, opt = {}) {
|
||||||
if (polys.length < 2) return polys;
|
if (polys.length < 2) return polys;
|
||||||
let lpre = length(polys);
|
let lpre = length(polys);
|
||||||
|
|
||||||
|
|
@ -491,7 +496,7 @@ export function union(polys, minarea, all, opt = {}) {
|
||||||
* @param {Polygon} poly clipping mask
|
* @param {Polygon} poly clipping mask
|
||||||
* @returns {?Polygon[]}
|
* @returns {?Polygon[]}
|
||||||
*/
|
*/
|
||||||
export function diff(setA, setB, z) {
|
function diff(setA, setB, z) {
|
||||||
let clip = new Clipper(),
|
let clip = new Clipper(),
|
||||||
tree = new PolyTree(),
|
tree = new PolyTree(),
|
||||||
sp1 = toClipper(setA),
|
sp1 = toClipper(setA),
|
||||||
|
|
@ -511,7 +516,7 @@ export function diff(setA, setB, z) {
|
||||||
* @param {Polygon} poly clipping mask
|
* @param {Polygon} poly clipping mask
|
||||||
* @returns {?Polygon[]}
|
* @returns {?Polygon[]}
|
||||||
*/
|
*/
|
||||||
export function xor(set, z) {
|
function xor(set, z) {
|
||||||
z = z || set[0].getZ();
|
z = z || set[0].getZ();
|
||||||
outer: for (;;) {
|
outer: for (;;) {
|
||||||
// sort largest to smallest area
|
// sort largest to smallest area
|
||||||
|
|
@ -554,7 +559,7 @@ export function xor(set, z) {
|
||||||
* @param {Polygon[]} setB mask set
|
* @param {Polygon[]} setB mask set
|
||||||
* @returns {Polygon[]}
|
* @returns {Polygon[]}
|
||||||
*/
|
*/
|
||||||
export function trimTo(setA, setB) {
|
function trimTo(setA, setB) {
|
||||||
// handle null/empty slices
|
// handle null/empty slices
|
||||||
if (setA === setB || setA === null || setB === null) return null;
|
if (setA === setB || setA === null || setB === null) return null;
|
||||||
|
|
||||||
|
|
@ -568,7 +573,7 @@ export function trimTo(setA, setB) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sumCirc(polys) {
|
function sumCirc(polys) {
|
||||||
let sum = 0.0;
|
let sum = 0.0;
|
||||||
polys.forEach(function(poly) {
|
polys.forEach(function(poly) {
|
||||||
sum += poly.circularityDeep();
|
sum += poly.circularityDeep();
|
||||||
|
|
@ -586,7 +591,7 @@ export function sumCirc(polys) {
|
||||||
* @param {Function} [collector] receives output of each pass
|
* @param {Function} [collector] receives output of each pass
|
||||||
* @returns {Polygon[]} last offset
|
* @returns {Polygon[]} last offset
|
||||||
*/
|
*/
|
||||||
export function expand(polys, distance, z, out, count, distance2, collector, min) {
|
function expand(polys, distance, z, out, count, distance2, collector, min) {
|
||||||
return offset(polys, [distance, distance2 || distance], {
|
return offset(polys, [distance, distance2 || distance], {
|
||||||
z, outs: out, call: collector, minArea: min, count, flat: true
|
z, outs: out, call: collector, minArea: min, count, flat: true
|
||||||
});
|
});
|
||||||
|
|
@ -597,11 +602,11 @@ export function expand(polys, distance, z, out, count, distance2, collector, min
|
||||||
* and return resulting gaps from offsets for thin wall detection in
|
* and return resulting gaps from offsets for thin wall detection in
|
||||||
* in FDM mode and uncleared areas in CAM mode.
|
* in FDM mode and uncleared areas in CAM mode.
|
||||||
*/
|
*/
|
||||||
export function offset(polys, dist, opts = {}) {
|
function offset(polys, dist, opts = {}) {
|
||||||
let open = opts.open ? polys.filter(p => p.open) : [];
|
let open = opts.open ? polys.filter(p => p.open) : [];
|
||||||
if (open.length) {
|
if (open.length) {
|
||||||
open = open.map(p => paths.pointsToPath(p.points, dist, true));
|
open = open.map(p => paths.pointsToPath(p.points, dist, true));
|
||||||
open = open.map(p => newPolygon().setOpen().addPoints(p.right));
|
open = open.map(p => base.newPolygon().setOpen().addPoints(p.right));
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not use clipper to offset open lines
|
// do not use clipper to offset open lines
|
||||||
|
|
@ -701,7 +706,7 @@ export function offset(polys, dist, opts = {}) {
|
||||||
* as performing subtractive analysis between initial layer shell (ref)
|
* as performing subtractive analysis between initial layer shell (ref)
|
||||||
* and last offset (cmp) to produce gap candidates (for thinfill)
|
* and last offset (cmp) to produce gap candidates (for thinfill)
|
||||||
*/
|
*/
|
||||||
export function inset(polys, dist, count, z, wasm) {
|
function inset(polys, dist, count, z, wasm) {
|
||||||
let total = count;
|
let total = count;
|
||||||
let layers = [];
|
let layers = [];
|
||||||
let ref = polys;
|
let ref = polys;
|
||||||
|
|
@ -744,7 +749,7 @@ export function inset(polys, dist, count, z, wasm) {
|
||||||
* @param {number} [maxLen]
|
* @param {number} [maxLen]
|
||||||
* @returns {Point[]} supplied output or new array
|
* @returns {Point[]} supplied output or new array
|
||||||
*/
|
*/
|
||||||
export function fillArea(polys, angle, spacing, output, minLen, maxLen) {
|
function fillArea(polys, angle, spacing, output, minLen, maxLen) {
|
||||||
if (polys.length === 0) return;
|
if (polys.length === 0) return;
|
||||||
|
|
||||||
let i = 1,
|
let i = 1,
|
||||||
|
|
@ -766,7 +771,7 @@ export function fillArea(polys, angle, spacing, output, minLen, maxLen) {
|
||||||
while (angle > 90) angle -= 180;
|
while (angle > 90) angle -= 180;
|
||||||
|
|
||||||
// X,Y ray slope derived from angle
|
// X,Y ray slope derived from angle
|
||||||
raySlope = newSlope(0,0,
|
raySlope = base.newSlope(0,0,
|
||||||
Math.cos(angle * DEG2RAD) * spacing,
|
Math.cos(angle * DEG2RAD) * spacing,
|
||||||
Math.sin(angle * DEG2RAD) * spacing
|
Math.sin(angle * DEG2RAD) * spacing
|
||||||
);
|
);
|
||||||
|
|
@ -838,8 +843,8 @@ export function fillArea(polys, angle, spacing, output, minLen, maxLen) {
|
||||||
if (minlen && plen < minlen) continue;
|
if (minlen && plen < minlen) continue;
|
||||||
if (maxlen && plen > maxlen) continue;
|
if (maxlen && plen > maxlen) continue;
|
||||||
}
|
}
|
||||||
let p1 = pointFromClipper(poly.m_polygon[0], zpos);
|
let p1 = base.pointFromClipper(poly.m_polygon[0], zpos);
|
||||||
let p2 = pointFromClipper(poly.m_polygon[1], zpos);
|
let p2 = base.pointFromClipper(poly.m_polygon[1], zpos);
|
||||||
let od = rayint.origin.distToLineNew(p1,p2) / spacing;
|
let od = rayint.origin.distToLineNew(p1,p2) / spacing;
|
||||||
lines.push([p1, p2, od]);
|
lines.push([p1, p2, od]);
|
||||||
}
|
}
|
||||||
|
|
@ -870,7 +875,7 @@ export function fillArea(polys, angle, spacing, output, minLen, maxLen) {
|
||||||
* @param {boolean} [for_fill]
|
* @param {boolean} [for_fill]
|
||||||
* @returns {Point[]}
|
* @returns {Point[]}
|
||||||
*/
|
*/
|
||||||
export function rayIntersect(start, slope, polygons, for_fill) {
|
function rayIntersect(start, slope, polygons, for_fill) {
|
||||||
let i = 0,
|
let i = 0,
|
||||||
flat = [],
|
flat = [],
|
||||||
points = [],
|
points = [],
|
||||||
|
|
@ -999,11 +1004,11 @@ export function rayIntersect(start, slope, polygons, for_fill) {
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pd(a,b) {
|
function pd(a,b) {
|
||||||
return a > b ? Math.abs(1-b/a) : Math.abs(1-a/b);
|
return a > b ? Math.abs(1-b/a) : Math.abs(1-a/b);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fingerprint(polys) {
|
function fingerprint(polys) {
|
||||||
let recs = flatten(polys).map(p => {
|
let recs = flatten(polys).map(p => {
|
||||||
return {
|
return {
|
||||||
l: p.length,
|
l: p.length,
|
||||||
|
|
@ -1032,7 +1037,7 @@ export function fingerprint(polys) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare fingerprint arrays
|
// compare fingerprint arrays
|
||||||
export function fingerprintCompare(a, b) {
|
function fingerprintCompare(a, b) {
|
||||||
// true if array is the same object
|
// true if array is the same object
|
||||||
if (a === b) {
|
if (a === b) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1074,7 +1079,7 @@ export function fingerprintCompare(a, b) {
|
||||||
|
|
||||||
// plan a route through an array of polygon center points
|
// plan a route through an array of polygon center points
|
||||||
// starting with the polygon center closest to "start"
|
// starting with the polygon center closest to "start"
|
||||||
export function route(polys, start) {
|
function route(polys, start) {
|
||||||
let centers = [];
|
let centers = [];
|
||||||
let first, minDist = Infinity;
|
let first, minDist = Infinity;
|
||||||
for (let poly of polys) {
|
for (let poly of polys) {
|
||||||
|
|
@ -1111,4 +1116,4 @@ export function route(polys, start) {
|
||||||
return routed.map(r => r.poly);
|
return routed.map(r => r.poly);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const polygons = POLYS;
|
});
|
||||||
|
|
@ -1,16 +1,23 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* basic slice and line connection. In future, replace kiri's fdm and cam slicers
|
* basic slice and line connection. In future, replace kiri's fdm and cam slicers
|
||||||
* with wrappers on this one.
|
* with wrappers on this one.
|
||||||
*/
|
*/
|
||||||
|
// dep: geo.base
|
||||||
|
// dep: geo.line
|
||||||
|
// dep: geo.point
|
||||||
|
// dep: geo.polygon
|
||||||
|
// dep: geo.polygons
|
||||||
|
gapp.register("geo.slicer", [], (root, exports) => {
|
||||||
|
|
||||||
import { base } from './base.js';
|
const { base } = root;
|
||||||
import { newOrderedLine } from './line.js';
|
const { config, util, polygons } = base
|
||||||
import { newPolygon } from '../geo/polygon.js';
|
const { newOrderedLine, newPolygon, newPoint } = base;
|
||||||
import { polygons } from '../geo/polygons.js';
|
|
||||||
import { newPoint } from '../geo/point.js';
|
const POLY = base.polygons;
|
||||||
import { config } from '../geo/base.js';
|
|
||||||
|
|
||||||
function dval(v, dv) {
|
function dval(v, dv) {
|
||||||
return v !== undefined ? v : dv;
|
return v !== undefined ? v : dv;
|
||||||
|
|
@ -25,7 +32,7 @@ function dval(v, dv) {
|
||||||
* @param {Point[]} points vertex array
|
* @param {Point[]} points vertex array
|
||||||
* @param {Object} options slicing parameters
|
* @param {Object} options slicing parameters
|
||||||
*/
|
*/
|
||||||
export async function slice(points, options = {}) {
|
async function slice(points, options = {}) {
|
||||||
let zMin = options.zMin || 0,
|
let zMin = options.zMin || 0,
|
||||||
zMax = options.zMax || 0,
|
zMax = options.zMax || 0,
|
||||||
zInc = options.zInc || 0,
|
zInc = options.zInc || 0,
|
||||||
|
|
@ -65,6 +72,7 @@ export async function slice(points, options = {}) {
|
||||||
for (i = 0; i < points.length; i++) {
|
for (i = 0; i < points.length; i++) {
|
||||||
points[i] = points[i].round(3);
|
points[i] = points[i].round(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gather z-index stats
|
// gather z-index stats
|
||||||
|
|
@ -78,7 +86,7 @@ export async function slice(points, options = {}) {
|
||||||
if (p1.z === p2.z && p2.z === p3.z && p1.z >= zMin) {
|
if (p1.z === p2.z && p2.z === p3.z && p1.z >= zMin) {
|
||||||
// detect faces co-planar with Z and sum the enclosed area
|
// detect faces co-planar with Z and sum the enclosed area
|
||||||
let zkey = p1.z,
|
let zkey = p1.z,
|
||||||
area = Math.abs(base.util.area2(p1,p2,p3)) / 2;
|
area = Math.abs(util.area2(p1,p2,p3)) / 2;
|
||||||
if (!zFlat[zkey]) {
|
if (!zFlat[zkey]) {
|
||||||
zFlat[zkey] = area;
|
zFlat[zkey] = area;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -314,7 +322,7 @@ function makeZLine(phash, p1, p2, coplanar, edge) {
|
||||||
*
|
*
|
||||||
* @param {number} z
|
* @param {number} z
|
||||||
*/
|
*/
|
||||||
export async function sliceZ(z, points, options = {}) {
|
async function sliceZ(z, points, options = {}) {
|
||||||
if (Array.isArray(z)) {
|
if (Array.isArray(z)) {
|
||||||
return Promise.all(z.map(z => sliceZ(z, points, options)));
|
return Promise.all(z.map(z => sliceZ(z, points, options)));
|
||||||
}
|
}
|
||||||
|
|
@ -380,14 +388,14 @@ export async function sliceZ(z, points, options = {}) {
|
||||||
if (groupFn) {
|
if (groupFn) {
|
||||||
let groups = groupFn(lines, z, options);
|
let groups = groupFn(lines, z, options);
|
||||||
if (options.xor) {
|
if (options.xor) {
|
||||||
groups = polygons.xor(groups);
|
groups = POLY.xor(groups);
|
||||||
}
|
}
|
||||||
if (options.union) {
|
if (options.union) {
|
||||||
let points = groups.map(p => p.length);
|
let points = groups.map(p => p.length);
|
||||||
if (points.length > 1) points = points.reduce((a,b) => a + b);
|
if (points.length > 1) points = points.reduce((a,b) => a + b);
|
||||||
// simplistic healing of non-manifold meshes
|
// simplistic healing of non-manifold meshes
|
||||||
let opt = { x: 1 };
|
let opt = { x: 1 };
|
||||||
let union = polygons.union(polygons.nest(groups), 0.1, true, opt);
|
let union = POLY.union(POLY.nest(groups), 0.1, true, opt);
|
||||||
// fall back to xor'ing polygons that might overlap
|
// fall back to xor'ing polygons that might overlap
|
||||||
// when one does not cleanly contain the other and we lose lots of points
|
// when one does not cleanly contain the other and we lose lots of points
|
||||||
// trigger when 2 polygons and we lose > 40% of points in the union
|
// trigger when 2 polygons and we lose > 40% of points in the union
|
||||||
|
|
@ -401,14 +409,14 @@ export async function sliceZ(z, points, options = {}) {
|
||||||
}
|
}
|
||||||
// track total poly length changes to determine if healed
|
// track total poly length changes to determine if healed
|
||||||
rval.changes = opt.changes;
|
rval.changes = opt.changes;
|
||||||
groups = polygons.flatten(union, null, true);
|
groups = POLY.flatten(union, null, true);
|
||||||
}
|
}
|
||||||
rval.groups = groups;
|
rval.groups = groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for driver-specific slice post-processor
|
// look for driver-specific slice post-processor
|
||||||
if (options.post) {
|
if (options.post) {
|
||||||
let fn = slicer.slicePost[options.post];
|
let fn = base.slicePost[options.post];
|
||||||
if (fn) fn(rval, options);
|
if (fn) fn(rval, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -450,7 +458,7 @@ export async function sliceZ(z, points, options = {}) {
|
||||||
* @param {number} [index]
|
* @param {number} [index]
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
export function sliceConnect(input, z, opt = {}) {
|
function sliceConnect(input, z, opt = {}) {
|
||||||
let { debug, both } = opt;
|
let { debug, both } = opt;
|
||||||
|
|
||||||
if (both) {
|
if (both) {
|
||||||
|
|
@ -774,7 +782,7 @@ export function sliceConnect(input, z, opt = {}) {
|
||||||
* @param {Line[]} lines
|
* @param {Line[]} lines
|
||||||
* @returns {Line[]}
|
* @returns {Line[]}
|
||||||
*/
|
*/
|
||||||
export function removeDuplicateLines(lines) {
|
function removeDuplicateLines(lines) {
|
||||||
let output = [],
|
let output = [],
|
||||||
tmplines = [],
|
tmplines = [],
|
||||||
points = [],
|
points = [],
|
||||||
|
|
@ -865,11 +873,12 @@ export function removeDuplicateLines(lines) {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const slicer = {
|
gapp.overlay(base, {
|
||||||
slice,
|
slice,
|
||||||
sliceZ,
|
sliceZ,
|
||||||
slicePost: {},
|
slicePost: {},
|
||||||
sliceDedup: removeDuplicateLines,
|
sliceDedup: removeDuplicateLines,
|
||||||
sliceConnect
|
sliceConnect
|
||||||
}
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -2,13 +2,19 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { config } from './base.js';
|
// dep: geo.base
|
||||||
|
// use: geo.line
|
||||||
|
// use: geo.point
|
||||||
|
gapp.register("geo.slope", [], (root, exports) => {
|
||||||
|
|
||||||
|
const { base } = root;
|
||||||
|
const { config } = base;
|
||||||
|
|
||||||
const ABS = Math.abs,
|
const ABS = Math.abs,
|
||||||
DEG2RAD = Math.PI / 180,
|
DEG2RAD = Math.PI / 180,
|
||||||
RAD2DEG = 180 / Math.PI;
|
RAD2DEG = 180 / Math.PI;
|
||||||
|
|
||||||
export class Slope {
|
class Slope {
|
||||||
constructor(p1, p2, dx, dy) {
|
constructor(p1, p2, dx, dy) {
|
||||||
this.dx = p1 && p2 ? p2.x - p1.x : dx;
|
this.dx = p1 && p2 ? p2.x - p1.x : dx;
|
||||||
this.dy = p1 && p2 ? p2.y - p1.y : dy;
|
this.dy = p1 && p2 ? p2.y - p1.y : dy;
|
||||||
|
|
@ -88,13 +94,21 @@ function angleWithinDelta(a1, a2, delta) {
|
||||||
return (ABS(a1-a2) <= delta || 360-ABS(a1-a2) <= delta);
|
return (ABS(a1-a2) <= delta || 360-ABS(a1-a2) <= delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newSlope(p1, p2, dx, dy) {
|
function newSlope(p1, p2, dx, dy) {
|
||||||
return new Slope(p1, p2, dx, dy);
|
return new Slope(p1, p2, dx, dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newSlopeFromAngle(angle) {
|
function newSlopeFromAngle(angle) {
|
||||||
return newSlope(0,0,
|
return newSlope(0,0,
|
||||||
Math.cos(angle * DEG2RAD),
|
Math.cos(angle * DEG2RAD),
|
||||||
Math.sin(angle * DEG2RAD)
|
Math.sin(angle * DEG2RAD)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gapp.overlay(base, {
|
||||||
|
Slope,
|
||||||
|
newSlope,
|
||||||
|
newSlopeFromAngle
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
import { base } from '../geo/base.js';
|
"use strict";
|
||||||
|
|
||||||
|
// dep: geo.base
|
||||||
|
// dep: ext.clip2
|
||||||
|
gapp.register("geo.wasm", [], (root, exports) => {
|
||||||
|
|
||||||
|
const { base } = root;
|
||||||
const { config } = base;
|
const { config } = base;
|
||||||
|
|
||||||
const factor = config.clipper;
|
const factor = config.clipper;
|
||||||
|
const wasm_ctrl = {
|
||||||
export const wasm_ctrl = {
|
|
||||||
enable,
|
enable,
|
||||||
disable,
|
disable,
|
||||||
count: {
|
count: {
|
||||||
|
|
@ -16,6 +20,8 @@ export const wasm_ctrl = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
gapp.overlay(base, { wasm_ctrl });
|
||||||
|
|
||||||
function log() {
|
function log() {
|
||||||
console.log(...arguments);
|
console.log(...arguments);
|
||||||
}
|
}
|
||||||
|
|
@ -54,7 +60,7 @@ function writePoly(view, poly, inner) {
|
||||||
function readPoly(view, z) {
|
function readPoly(view, z) {
|
||||||
let points = view.readU16(true);
|
let points = view.readU16(true);
|
||||||
if (points === 0) return;
|
if (points === 0) return;
|
||||||
let poly = base.newPolygon();
|
let poly = self.base.newPolygon();
|
||||||
while (points-- > 0) {
|
while (points-- > 0) {
|
||||||
poly.add(view.readI32(true)/factor, view.readI32(true)/factor, z || 0);
|
poly.add(view.readI32(true)/factor, view.readI32(true)/factor, z || 0);
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +79,7 @@ function readPolys(view, z, out = []) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function polyOffset(polys, offset, z, clean, simple) {
|
function polyOffset(polys, offset, z, clean, simple) {
|
||||||
wasm_ctrl.count.offset++;
|
wasm_ctrl.count.offset++;
|
||||||
let wasm = base.wasm,
|
let wasm = base.wasm,
|
||||||
buffer = wasm.shared,
|
buffer = wasm.shared,
|
||||||
|
|
@ -83,7 +89,7 @@ export function polyOffset(polys, offset, z, clean, simple) {
|
||||||
return polyNest(out);
|
return polyNest(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function polyUnion(polys, z) {
|
function polyUnion(polys, z) {
|
||||||
wasm_ctrl.count.union++;
|
wasm_ctrl.count.union++;
|
||||||
let wasm = base.wasm,
|
let wasm = base.wasm,
|
||||||
buffer = wasm.shared,
|
buffer = wasm.shared,
|
||||||
|
|
@ -93,7 +99,7 @@ export function polyUnion(polys, z) {
|
||||||
return polyNest(out);
|
return polyNest(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function polyDiff(polysA, polysB, z, AB, BA) {
|
function polyDiff(polysA, polysB, z, AB, BA) {
|
||||||
wasm_ctrl.count.diff++;
|
wasm_ctrl.count.diff++;
|
||||||
let wasm = base.wasm,
|
let wasm = base.wasm,
|
||||||
buffer = wasm.shared,
|
buffer = wasm.shared,
|
||||||
|
|
@ -147,7 +153,7 @@ function readString(pos, len) {
|
||||||
return out.join('');
|
return out.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function enable() {
|
function enable() {
|
||||||
if (base.wasm || base._wasm) {
|
if (base.wasm || base._wasm) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -194,9 +200,11 @@ export function enable() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function disable() {
|
function disable() {
|
||||||
if (base.wasm) {
|
if (base.wasm) {
|
||||||
base.wasm.free(base.wasm.shared);
|
|
||||||
delete base.wasm;
|
delete base.wasm;
|
||||||
|
// console.log({disabled: geo});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue