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 agent = require('express-useragent');
|
||||
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 PATH = require('path');
|
||||
|
||||
|
|
@ -58,25 +58,6 @@ netdb.create = async function(map = {}) {
|
|||
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) {
|
||||
const ENV = mod.env;
|
||||
|
||||
|
|
@ -103,173 +84,7 @@ function init(mod) {
|
|||
const callstack = [];
|
||||
let xxxx = false;
|
||||
|
||||
function find_refs(cache, path, ismod) {
|
||||
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]});
|
||||
}
|
||||
generateDevices();
|
||||
|
||||
mod.on.test((req) => {
|
||||
let cookie = cookieValue(req.headers.cookie, "version") || undefined;
|
||||
|
|
@ -295,10 +110,6 @@ function init(mod) {
|
|||
"/meta/index.html" : redir("/meta/", 301)
|
||||
}));
|
||||
mod.add(handleVersion);
|
||||
mod.add(prepath([
|
||||
[ "/code/", handleCode ],
|
||||
// [ "/wasm/", handleWasm ]
|
||||
]));
|
||||
mod.add(fixedmap("/api/", api));
|
||||
if (debug) {
|
||||
mod.static("/mod/", "mod");
|
||||
|
|
@ -309,29 +120,14 @@ function init(mod) {
|
|||
});
|
||||
}
|
||||
mod.add(rewriteHtmlVersion);
|
||||
mod.add((req, res, next) => {
|
||||
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("/lib/", "src");
|
||||
mod.static("/obj/", "web/obj");
|
||||
mod.static("/font/", "web/font");
|
||||
mod.static("/fon2/", "web/fon2");
|
||||
mod.static("/mesh/", "web/mesh");
|
||||
mod.static("/moto/", "web/moto");
|
||||
mod.static("/meta/", "web/meta");
|
||||
mod.static("/kiri/", "web/kiri");
|
||||
|
||||
mod.static("/v2/", "web2");
|
||||
|
||||
function load_modules(root, force) {
|
||||
// load modules
|
||||
lastmod(`${dir}/${root}`) && fs.readdirSync(`${dir}/${root}`).forEach(mdir => {
|
||||
|
|
@ -353,10 +149,10 @@ function init(mod) {
|
|||
}
|
||||
|
||||
// load development and 3rd party modules
|
||||
load_modules('mod');
|
||||
// load_modules('mod');
|
||||
|
||||
// load optional local modules
|
||||
load_modules('mods');
|
||||
// load_modules('mods');
|
||||
|
||||
// run loads injected by modules
|
||||
while (load.length) {
|
||||
|
|
@ -366,9 +162,6 @@ function init(mod) {
|
|||
logger.log({on_load_fail: e});
|
||||
}
|
||||
}
|
||||
|
||||
// runs after module loads / injects
|
||||
prepareScripts();
|
||||
};
|
||||
|
||||
// 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
|
||||
api: api,
|
||||
adm: {
|
||||
reload: prepareScripts,
|
||||
setver: (ver) => { oversion = ver },
|
||||
crossOrigin: (bool) => { crossOrigin = bool }
|
||||
},
|
||||
|
|
@ -446,19 +238,6 @@ function initModule(mod, file, dir) {
|
|||
static: (root, pre) => {
|
||||
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,
|
||||
remap: remap,
|
||||
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
|
||||
const cachever = {};
|
||||
|
||||
|
|
@ -564,28 +298,30 @@ function handleSetup(req, res, next) {
|
|||
|
||||
function handleVersion(req, res, next) {
|
||||
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) {
|
||||
return http.redirect(res, `${req.url},ver:${vstr}`);
|
||||
} else {
|
||||
return http.redirect(res, `${req.url}?ver:${vstr}`);
|
||||
}
|
||||
} else if (!debug) {
|
||||
// in production serve packed bundles
|
||||
let { path } = req.app;
|
||||
if (path === '/v2/lib/mesh/work.js') {
|
||||
req.url = req.app.path = '/v2/lib/pack/mesh-work.js';
|
||||
} else if (path === '/v2/lib/main/mesh.js') {
|
||||
req.url = req.app.path = '/v2/lib/pack/mesh-main.js';
|
||||
} else if (path === '/v2/lib/kiri-run/minion.js') {
|
||||
req.url = req.app.path = '/v2/lib/pack/kiri-pool.js';
|
||||
} else if (path === '/v2/lib/kiri-run/worker.js') {
|
||||
req.url = req.app.path = '/v2/lib/pack/kiri-work.js';
|
||||
} else if (path === '/v2/lib/main/kiri.js') {
|
||||
req.url = req.app.path = '/v2/lib/pack/kiri-main.js';
|
||||
if (path === '/lib/mesh/work.js') {
|
||||
req.url = req.app.path = '/lib/pack/mesh-work.js';
|
||||
} else if (path === '/lib/main/mesh.js') {
|
||||
req.url = req.app.path = '/lib/pack/mesh-main.js';
|
||||
} else if (path === '/lib/kiri-run/minion.js') {
|
||||
req.url = req.app.path = '/lib/pack/kiri-pool.js';
|
||||
} else if (path === '/lib/kiri-run/worker.js') {
|
||||
req.url = req.app.path = '/lib/pack/kiri-work.js';
|
||||
} else if (path === '/lib/main/kiri.js') {
|
||||
req.url = req.app.path = '/lib/pack/kiri-main.js';
|
||||
}
|
||||
// add cors headers on rewrite
|
||||
if (path !== req.url) {
|
||||
console.log('rewrite', path, req.url);
|
||||
addCorsHeaders(req, res);
|
||||
// console.log('rewrite', path, req.url);
|
||||
}
|
||||
next();
|
||||
} 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
|
||||
function generateDevices() {
|
||||
let root = PATH.join(dir,"src","kiri-dev");
|
||||
|
|
@ -698,128 +382,7 @@ function generateDevices() {
|
|||
});
|
||||
let dstr = JSON.stringify(devs);
|
||||
synth.devices = `self.devices = ${dstr};`;
|
||||
fs.writeFileSync(PATH.join(dir,"src2","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;
|
||||
fs.writeFileSync(PATH.join(dir,"src","pack","devices.js"), `export const devices = ${dstr};`);
|
||||
}
|
||||
|
||||
function minify(path) {
|
||||
|
|
@ -942,10 +505,11 @@ function cookieValue(cookie,key) {
|
|||
|
||||
function rewriteHtmlVersion(req, res, next) {
|
||||
if ([
|
||||
"/v2/kiri/",
|
||||
"/v2/lib/kiri-run/minion.js",
|
||||
"/v2/lib/kiri-run/worker.js",
|
||||
"/v2/lib/kiri-run/minion.js"
|
||||
"/kiri/",
|
||||
"/mesh/",
|
||||
"/lib/mesh/work.js",
|
||||
"/lib/kiri-run/worker.js",
|
||||
"/lib/kiri-run/minion.js"
|
||||
].indexOf(req.app.path) >= 0) {
|
||||
addCorsHeaders(req, res);
|
||||
} else if ([
|
||||
|
|
|
|||
|
|
@ -78,8 +78,7 @@
|
|||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"setup": "npm i && cd mods && npm i",
|
||||
"setup2": "npm run setup && npm run dev -- --dryrun && npm run preinstall2",
|
||||
"setup": "npm i && npm run dev -- --dryrun && npx webpack --config src/webpack/webpack-three-esm.js && node src/webpack/esbuild.config.mjs",
|
||||
"dev": "gs-app-server --debug",
|
||||
"prod": "gs-app-server",
|
||||
"prod-dryrun": "gs-app-server --dryrun",
|
||||
|
|
@ -98,14 +97,12 @@
|
|||
"build-mac-intel": "npm run build -- --mac --x64",
|
||||
"mesh-build": "webpack --config webpack.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",
|
||||
"mac-verify": "spctl --assess -vv --type install dist/*/*.app",
|
||||
"clear-cache": "rm -rf data/cache/* dist/ tmp/*",
|
||||
"prebuild": "node bin/electron-pre.js",
|
||||
"postbuild": "node bin/electron-post.js",
|
||||
"preinstall": "node bin/install-pre.js && npx webpack --config bin/webpack-three.js",
|
||||
"preinstall2": "npx webpack --config src2/webpack/webpack-three-esm.js && node src2/webpack/esbuild.config.mjs",
|
||||
"preinstall": "",
|
||||
"docs-dev": "docusaurus start --config conf/docusaurus.config.js",
|
||||
"docs-build": "docusaurus build --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 */
|
||||
|
||||
"use strict";
|
||||
|
||||
// extend Array, String, Number, Math
|
||||
const AP = {};
|
||||
gapp.register("add.array", [], (root, exports) => {
|
||||
|
||||
let AP = {};
|
||||
|
||||
if (!AP.flat) {
|
||||
AP.flat = function() {
|
||||
|
|
@ -212,11 +216,12 @@ Array.handle = function(fn) {
|
|||
}
|
||||
};
|
||||
|
||||
// Apply all methods to Array.prototype
|
||||
Object.assign(Array.prototype, AP);
|
||||
|
||||
// Export for ES modules
|
||||
export { AP as arrayPrototype };
|
||||
for (let i in AP) {
|
||||
Object.defineProperty(Array.prototype, i, {
|
||||
value: AP[i],
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
Float32Array.prototype.toShared = function() {
|
||||
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);
|
||||
return Math.round(this.valueOf() * pow) / pow;
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
||||
|
||||
// use: add.array
|
||||
import { arrayPrototype } from './array.js';
|
||||
"use strict";
|
||||
|
||||
// Make classes available globally
|
||||
class ArrayWriter {
|
||||
// use: add.array
|
||||
gapp.register("add.class", [], (root, exports) => {
|
||||
|
||||
self.ArrayWriter = class ArrayWriter {
|
||||
constructor() {
|
||||
this.pos = 0;
|
||||
this.array = [];
|
||||
|
|
@ -47,7 +48,7 @@ class ArrayWriter {
|
|||
}
|
||||
};
|
||||
|
||||
class DataWriter {
|
||||
self.DataWriter = class DataWriter {
|
||||
constructor(view, pos) {
|
||||
this.view = view;
|
||||
this.pos = pos || 0;
|
||||
|
|
@ -105,7 +106,7 @@ class DataWriter {
|
|||
}
|
||||
}
|
||||
|
||||
class DataReader {
|
||||
self.DataReader = class DataReader {
|
||||
constructor(view, pos) {
|
||||
this.view = view;
|
||||
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 */
|
||||
|
||||
"use strict";
|
||||
|
||||
// dep: ext.three
|
||||
import {
|
||||
gapp.register("add.three", [], (root, exports) => {
|
||||
|
||||
const {
|
||||
THREE, SVGLoader, BufferGeometryUtils,
|
||||
LineMaterial, Line2, LineGeometry, LineSegments2,
|
||||
LineSegmentsGeometry, MeshBVHLib } from '../ext/three.js';
|
||||
|
||||
// Make THREE available globally
|
||||
self.THREE = THREE;
|
||||
LineMaterial, Line2, LineGeometry, LineSegments2, LineSegmentsGeometry
|
||||
} = ThreeBundle;
|
||||
|
||||
root.THREE = THREE;
|
||||
THREE.BufferGeometryUtils = BufferGeometryUtils;
|
||||
THREE.SVGLoader = SVGLoader.SVGLoader;
|
||||
THREE.LineMaterial = LineMaterial;
|
||||
|
|
@ -144,5 +147,4 @@ THREE.Object3D.prototype.removeAll = function() {
|
|||
this.children = [];
|
||||
};
|
||||
|
||||
// Export for ES modules
|
||||
export { THREE };
|
||||
});
|
||||
|
|
@ -1,14 +1,19 @@
|
|||
/** Copyright Stewart Allen -- All Rights Reserved */
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
|
||||
"use strict";
|
||||
|
||||
export const data = {
|
||||
Index: IDBStore,
|
||||
open() {
|
||||
return new IDBStore(...arguments);
|
||||
}
|
||||
gapp.register("data.index", [], (root, exports) => {
|
||||
|
||||
const { data } = root;
|
||||
|
||||
data.Index = IDBStore;
|
||||
|
||||
data.open = function() {
|
||||
return new IDBStore(...arguments);
|
||||
};
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
|
||||
|
||||
let IDB, IRR, local = null;
|
||||
|
||||
try {
|
||||
|
|
@ -314,5 +319,4 @@ SP.clear = function(key, store = this.current) {
|
|||
.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
|
||||
gapp.register("ext.md5", [], (root, exports) => {
|
||||
|
||||
export function hash(e) {
|
||||
exports({ hash });
|
||||
|
||||
function hash(e) {
|
||||
function h(a, b) {
|
||||
var c, d, e, f, g;
|
||||
e = a & 2147483648;
|
||||
|
|
@ -64,4 +67,6 @@ export function hash(e) {
|
|||
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);
|
||||
return (p(a) + p(b) + p(c) + p(d)).toLowerCase()
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -1,8 +1,12 @@
|
|||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||
|
||||
import { newPoint } from './point.js';
|
||||
import earcut from '../ext/earcut.js';
|
||||
"use strict";
|
||||
|
||||
// use: add.array
|
||||
// use: add.class
|
||||
gapp.register("geo.base", [], (root, exports) => {
|
||||
|
||||
const base = root.base = {};
|
||||
const round_decimal_precision = 5;
|
||||
|
||||
function time() {
|
||||
|
|
@ -215,7 +219,7 @@ function intersect(p1, p2, p3, p4, test, parallelok) {
|
|||
if (test === keys.SEGINT && !segint) return null;
|
||||
if (test === keys.RAYINT && !rayint) return null;
|
||||
|
||||
let ip = newPoint(
|
||||
let ip = base.newPoint(
|
||||
p1x + (a * d1x), // x
|
||||
p1y + (a * d1y), // y
|
||||
p3.z || p4.z, // z
|
||||
|
|
@ -260,7 +264,7 @@ function intersectRayLine(ro, s1, p1, p2, infinite) {
|
|||
b = n2 / d;
|
||||
|
||||
if (infinite || (inCloseRange(b, 0, 1) && a >= 0)) {
|
||||
let ip = newPoint(
|
||||
let ip = base.newPoint(
|
||||
p1x + (a * s1x),
|
||||
p1y + (a * s1y),
|
||||
p2.z || ro.z,
|
||||
|
|
@ -524,7 +528,7 @@ function comma(v) {
|
|||
* Connect to base
|
||||
******************************************************************* */
|
||||
|
||||
export const key = {
|
||||
base.key = {
|
||||
NONE: "",
|
||||
PROJECT: "project",
|
||||
SEGINT: "segint",
|
||||
|
|
@ -532,7 +536,7 @@ export const key = {
|
|||
PARALLEL: "parallel"
|
||||
};
|
||||
|
||||
export const config = {
|
||||
base.config = {
|
||||
// size of gcode debug arrow head
|
||||
debug_arrow: 0.25,
|
||||
// default # of decimal places in generated gcode
|
||||
|
|
@ -598,7 +602,7 @@ export const config = {
|
|||
clipperClean: 250
|
||||
};
|
||||
|
||||
export const util = {
|
||||
base.util = {
|
||||
sqr,
|
||||
lerp,
|
||||
time,
|
||||
|
|
@ -635,10 +639,4 @@ export const util = {
|
|||
zInPlane
|
||||
};
|
||||
|
||||
export const base = {
|
||||
config,
|
||||
key,
|
||||
util
|
||||
};
|
||||
|
||||
export { earcut };
|
||||
});
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
/** Copyright 2014-2019 Stewart Allen -- All Rights Reserved */
|
||||
|
||||
import { config, util } from './base.js';
|
||||
import { newPoint } from './point.js';
|
||||
"use strict";
|
||||
|
||||
export class Bounds {
|
||||
// dep: geo.base
|
||||
gapp.register("geo.bounds", [], (root, exports) => {
|
||||
|
||||
const { base } = root;
|
||||
const { config, util } = base;
|
||||
|
||||
class Bounds {
|
||||
constructor() {
|
||||
this.minx = 10e7;
|
||||
this.miny = 10e7;
|
||||
|
|
@ -116,7 +121,7 @@ export class Bounds {
|
|||
}
|
||||
|
||||
center(z = 0) {
|
||||
return newPoint(this.centerx(), this.centery(), z);
|
||||
return base.newPoint(this.centerx(), this.centery(), z);
|
||||
}
|
||||
|
||||
centerx() {
|
||||
|
|
@ -128,6 +133,9 @@ export class Bounds {
|
|||
}
|
||||
}
|
||||
|
||||
export function newBounds() {
|
||||
return new Bounds();
|
||||
}
|
||||
gapp.overlay(base, {
|
||||
Bounds,
|
||||
newBounds() { return new Bounds() }
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -2,17 +2,24 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import { THREE } from '../ext/three.js';
|
||||
import manifold from '../ext/manifold.js';
|
||||
// dep: geo.base
|
||||
// mod: ext.manifold
|
||||
gapp.register("geo.csg", [], (root, exports) => {
|
||||
|
||||
const { base, ext } = root;
|
||||
const debug = true;
|
||||
const precision = 0.001;
|
||||
const factor = 1/precision;
|
||||
|
||||
function log() {
|
||||
console.log(...arguments);
|
||||
if (root?.mesh?.log) {
|
||||
root.mesh.log(...arguments)
|
||||
} else if (root.debug === true) {
|
||||
console.log(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
export const CSG = {
|
||||
const CSG = {
|
||||
// accepts 2 or more arguments with threejs vertex position arrays
|
||||
union() {
|
||||
return CSG.moduleOp('manifold.union', 'union', ...arguments);
|
||||
|
|
@ -116,10 +123,23 @@ function indexVertices(pos) {
|
|||
}
|
||||
|
||||
let Instance;
|
||||
manifold({
|
||||
locateFile() { return "/wasm/manifold.wasm" }
|
||||
}).then(inst => {
|
||||
|
||||
ext.manifold.then(mod => {
|
||||
mod.default({
|
||||
locateFile(a,b,c) {
|
||||
return "/wasm/manifold.wasm";
|
||||
}
|
||||
}).then(inst => {
|
||||
inst.setup();
|
||||
Instance = inst;
|
||||
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 */
|
||||
|
||||
"use strict";
|
||||
|
||||
// dep: geo.base
|
||||
gapp.register("geo.gyroid", [], (root, exports) => {
|
||||
|
||||
const { base } = root;
|
||||
const PI2 = Math.PI * 2;
|
||||
|
||||
let cache = {};
|
||||
|
|
@ -11,7 +17,7 @@ let lastSlice = 0;
|
|||
* @param off {number} z offset value from 0-1
|
||||
* @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
|
||||
// or the requested resolution or tip values have changed
|
||||
let now = Date.now();
|
||||
|
|
@ -164,7 +170,7 @@ export function slice(off, res, val) {
|
|||
}
|
||||
|
||||
// merge co-linear and distance threshold
|
||||
export function filter(poly, inc) {
|
||||
function filter(poly, inc) {
|
||||
if (poly.length <= 2) {
|
||||
return poly;
|
||||
}
|
||||
|
|
@ -194,16 +200,21 @@ export function filter(poly, inc) {
|
|||
}
|
||||
e1 = el;
|
||||
}
|
||||
nupoly.push(poly[poly.length-1]);
|
||||
return nupoly;
|
||||
}
|
||||
|
||||
export function distTo(a, b, dir) {
|
||||
function distTo(a, b, dir) {
|
||||
let dx = a.x - b.x;
|
||||
let dy = a.y - b.y;
|
||||
if (dir === 'lr') {
|
||||
return Math.abs(dx);
|
||||
} else if (dir === 'td') {
|
||||
return Math.abs(dy);
|
||||
}
|
||||
// bias distance by prevailing direction of discovery to join stragglers
|
||||
if (dir === 'lr') dx = dx / 2;
|
||||
if (dir === 'td') dy = dy / 2;
|
||||
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 */
|
||||
|
||||
export class Line {
|
||||
"use strict";
|
||||
|
||||
// dep: geo.base
|
||||
gapp.register("geo.line", [], (root, exports) => {
|
||||
|
||||
const { base } = root;
|
||||
|
||||
class Line {
|
||||
constructor(p1, p2, key) {
|
||||
if (!key) key = [p1.key, p2.key].join(';');
|
||||
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);
|
||||
}
|
||||
|
||||
export function newOrderedLine(p1, p2, key) {
|
||||
function newOrderedLine(p1, p2, 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 */
|
||||
|
||||
import { base } from './base.js';
|
||||
import { newPoint } from './point.js';
|
||||
"use strict";
|
||||
|
||||
// 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
|
||||
|
|
@ -9,21 +21,21 @@ import { newPoint } from './point.js';
|
|||
* elements with { first, last } points and
|
||||
* may be open polys, unlike poly2polyEmit
|
||||
*/
|
||||
export function tip2tipEmit(array, startPoint, emitter) {
|
||||
function tip2tipEmit(array, startPoint, emitter) {
|
||||
let mindist, dist, found, count = 0;
|
||||
for (;;) {
|
||||
found = null;
|
||||
mindist = Infinity;
|
||||
array.forEach(function (el) {
|
||||
array.forEach(function(el) {
|
||||
if (el.delete) return;
|
||||
dist = startPoint.distTo2D(el.first);
|
||||
if (dist < mindist) {
|
||||
found = { el: el, first: el.first, last: el.last };
|
||||
found = {el:el, first:el.first, last:el.last};
|
||||
mindist = dist;
|
||||
}
|
||||
dist = startPoint.distTo2D(el.last);
|
||||
if (dist < mindist) {
|
||||
found = { el: el, first: el.last, last: el.first };
|
||||
found = {el:el, first:el.last, last:el.first};
|
||||
mindist = dist;
|
||||
}
|
||||
});
|
||||
|
|
@ -44,7 +56,7 @@ export function tip2tipEmit(array, startPoint, emitter) {
|
|||
* to be more like outputOrderClosest() and have the option to account for
|
||||
* depth in determining distance
|
||||
*/
|
||||
export function poly2polyEmit(array, startPoint, emitter, opt = {}) {
|
||||
function poly2polyEmit(array, startPoint, emitter, opt = {}) {
|
||||
let marker = opt.mark || 'delete';
|
||||
let mindist, dist, found, count = 0;
|
||||
for (;;) {
|
||||
|
|
@ -62,21 +74,21 @@ export function poly2polyEmit(array, startPoint, emitter, opt = {}) {
|
|||
}
|
||||
if (d2l < mindist && d2l < d2f && opt.swapdir !== false) {
|
||||
poly.reverse();
|
||||
found = { poly: poly, index: 0, point: poly.first() };
|
||||
found = {poly:poly, index:0, point:poly.first()};
|
||||
mindist = d2l;
|
||||
} else if (d2f < mindist) {
|
||||
found = { poly: poly, index: 0, point: poly.first() };
|
||||
found = {poly:poly, index:0, point:poly.first()};
|
||||
mindist = d2f;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let area = poly.open ? 1 : poly.area();
|
||||
poly.forEachPoint(function (point, index) {
|
||||
poly.forEachPoint(function(point, index) {
|
||||
dist = opt.weight ?
|
||||
startPoint.distTo3D(point) * area * area :
|
||||
startPoint.distTo2D(point);
|
||||
if (dist < mindist) {
|
||||
found = { poly: poly, index: index, point: point };
|
||||
found = {poly:poly, index:index, point:point};
|
||||
mindist = dist;
|
||||
}
|
||||
});
|
||||
|
|
@ -90,23 +102,23 @@ export function poly2polyEmit(array, startPoint, emitter, opt = {}) {
|
|||
|
||||
// undo delete marks
|
||||
if (opt.perm !== true) {
|
||||
array.forEach(function (poly) { poly[marker] = false });
|
||||
array.forEach(function(poly) { poly[marker] = false });
|
||||
}
|
||||
|
||||
return startPoint;
|
||||
}
|
||||
|
||||
export function calc_normal(p1, p2) {
|
||||
function calc_normal(p1, p2) {
|
||||
let dx = p2.x - p1.x;
|
||||
let dy = p2.y - p1.y;
|
||||
let len = Math.sqrt(dx * dx + dy * dy);
|
||||
let mn = (1 / len);
|
||||
dx *= mn;
|
||||
dy *= mn;
|
||||
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;
|
||||
if (start) {
|
||||
dx = n2.dx * off;
|
||||
|
|
@ -118,7 +130,7 @@ export function end_vertex(n1, n2, off, start) {
|
|||
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;
|
||||
r = 1 + (n1.dx * n2.dx + n1.dy * n2.dy);
|
||||
q = off / r;
|
||||
|
|
@ -136,7 +148,7 @@ export function calc_vertex(n1, n2, off, vp) {
|
|||
return { dx, dy, vp: vp || n1.p2, io, vl };
|
||||
}
|
||||
|
||||
export function v2pl(rec) {
|
||||
function v2pl(rec) {
|
||||
let p = rec.vp.clone();
|
||||
p.x += rec.dx;
|
||||
p.y += rec.dy;
|
||||
|
|
@ -144,7 +156,7 @@ export function v2pl(rec) {
|
|||
return p;
|
||||
}
|
||||
|
||||
export function v2pr(rec) {
|
||||
function v2pr(rec) {
|
||||
let p = rec.vp.clone();
|
||||
p.x -= rec.dx;
|
||||
p.y -= rec.dy;
|
||||
|
|
@ -152,17 +164,17 @@ export function v2pr(rec) {
|
|||
return p;
|
||||
}
|
||||
|
||||
export function pointsToPath(points, offset, open, miter = 1.5) {
|
||||
function pointsToPath(points, offset, open, miter = 1.5) {
|
||||
const absoff = Math.abs(offset);
|
||||
// calculate segment normals which are used to calculate vertex normals
|
||||
// next segment info is associated with the current point
|
||||
const nupoints = [];
|
||||
const length = points.length;
|
||||
if (length === 2 && points[0].isEqual(points[1])) {
|
||||
return {};
|
||||
return { };
|
||||
}
|
||||
const dedup = (open && length > 2) || (!open && length > 3);
|
||||
for (let i = 0; i < length; i++) {
|
||||
for (let i=0; i<length; i++) {
|
||||
let p1 = points[i];
|
||||
let p2 = points[(i + 1) % length];
|
||||
p1.normal = calc_normal(p1, p2);
|
||||
|
|
@ -179,7 +191,7 @@ export function pointsToPath(points, offset, open, miter = 1.5) {
|
|||
nupoints.push(p1);
|
||||
}
|
||||
if (nupoints.length === 1) {
|
||||
console.log({ points, nupoints });
|
||||
console.log({points, nupoints});
|
||||
}
|
||||
// when points are dropped, we need the new array
|
||||
points = nupoints;
|
||||
|
|
@ -192,10 +204,10 @@ export function pointsToPath(points, offset, open, miter = 1.5) {
|
|||
// calculate vertex normals from segments normals
|
||||
// vertex info is associated with the origin point
|
||||
let fl, fr;
|
||||
for (let i = 0, l = points.length; i < l; i++) {
|
||||
let n1 = points[(i + l - 1) % l].normal;
|
||||
let n2 = points[(i + l) % l].normal;
|
||||
let vn = open && (i === 0 || i === l - 1) ?
|
||||
for (let i=0, l=points.length; i<l; i++) {
|
||||
let n1 = points[(i+l-1)%l].normal;
|
||||
let n2 = points[(i+l)%l].normal;
|
||||
let vn = open && (i === 0 || i === l-1) ?
|
||||
end_vertex(n1, n2, offset, i === 0) :
|
||||
calc_vertex(n1, n2, offset);
|
||||
let { p1, p2 } = n2;
|
||||
|
|
@ -217,7 +229,7 @@ export function pointsToPath(points, offset, open, miter = 1.5) {
|
|||
// shorten each leg and insert new point
|
||||
let delta = 0.1;
|
||||
let np1 = p1.clone().move({ x: n1.dy * delta, y: -n1.dx * delta, z: 0 });
|
||||
let np2 = p1.clone().move({ x: -n2.dy * delta, y: n2.dx * delta, z: 0 });
|
||||
let np2 = p1.clone().move({ x:-n2.dy * delta, y: n2.dx * delta, z: 0 });
|
||||
let sn1 = np1.normal = calc_normal(np1, np2);
|
||||
let sn2 = np2.normal = p1.normal;
|
||||
let nv1 = calc_vertex(n1.p1.normal, sn1, offset, np1);
|
||||
|
|
@ -324,7 +336,7 @@ export function pointsToPath(points, offset, open, miter = 1.5) {
|
|||
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 out = [];
|
||||
const nrm = [];
|
||||
|
|
@ -344,14 +356,14 @@ export function pathTo3D(path, height, z) {
|
|||
}
|
||||
nrm.appendAll(normals);
|
||||
// reverse normals to match faces, but underside so reverse Z as well
|
||||
for (let i = normals.length - 1; i > 0; i -= 3) {
|
||||
nrm.push(normals[i - 2]);
|
||||
nrm.push(normals[i - 1]);
|
||||
nrm.push(-normals[i - 0]);
|
||||
for (let i=normals.length-1; i>0; i-=3) {
|
||||
nrm.push(normals[i-2]);
|
||||
nrm.push(normals[i-1]);
|
||||
nrm.push(-normals[i-0]);
|
||||
}
|
||||
for (let i = 0, l = left.length, tl = open ? l - 1 : l; i < tl; i++) {
|
||||
for (let i=0, l=left.length, tl = open ? l-1 : l; i<tl; i++) {
|
||||
let p0 = left[i];
|
||||
let p1 = left[(i + 1) % l];
|
||||
let p1 = left[(i+1)%l];
|
||||
out.push(p0.x, p0.y, p0.z + height);
|
||||
out.push(p0.x, p0.y, p0.z - height);
|
||||
out.push(p1.x, p1.y, p1.z - height);
|
||||
|
|
@ -360,15 +372,15 @@ export function pathTo3D(path, height, z) {
|
|||
out.push(p0.x, p0.y, p0.z + height);
|
||||
let ln = p0.vp.normal;
|
||||
nrm.push(ln.dx, ln.dy, -1);
|
||||
nrm.push(ln.dx, ln.dy, 1);
|
||||
nrm.push(ln.dx, ln.dy, 1);
|
||||
nrm.push(ln.dx, ln.dy, 1);
|
||||
nrm.push(ln.dx, ln.dy, 1);
|
||||
nrm.push(ln.dx, ln.dy, 1);
|
||||
nrm.push(ln.dx, ln.dy, 1);
|
||||
nrm.push(ln.dx, ln.dy, -1);
|
||||
nrm.push(ln.dx, ln.dy, -1);
|
||||
}
|
||||
for (let i = 0, l = right.length, tl = open ? l - 1 : l; i < tl; i++) {
|
||||
for (let i=0, l=right.length, tl = open ? l-1 : l; i<tl; i++) {
|
||||
let p0 = right[i];
|
||||
let p1 = right[(i + 1) % l];
|
||||
let p1 = right[(i+1)%l];
|
||||
out.push(p0.x, p0.y, p0.z + height);
|
||||
out.push(p1.x, p1.y, p1.z - height);
|
||||
out.push(p0.x, p0.y, p0.z - height);
|
||||
|
|
@ -376,12 +388,12 @@ export function pathTo3D(path, height, z) {
|
|||
out.push(p0.x, p0.y, p0.z + height);
|
||||
out.push(p1.x, p1.y, p1.z + height);
|
||||
let rn = p0.vp.normal;
|
||||
nrm.push(-rn.dy, rn.dx, 1);
|
||||
nrm.push(-rn.dy, rn.dx, 1);
|
||||
nrm.push(-rn.dy, rn.dx, -1);
|
||||
nrm.push(-rn.dy, rn.dx, -1);
|
||||
nrm.push(-rn.dy, rn.dx, -1);
|
||||
nrm.push(-rn.dy, rn.dx, 1);
|
||||
nrm.push(-rn.dy, rn.dx, 1);
|
||||
nrm.push(-rn.dy, rn.dx, 1);
|
||||
nrm.push(-rn.dy, rn.dx, 1);
|
||||
}
|
||||
if (open) {
|
||||
// begin cap
|
||||
|
|
@ -394,12 +406,12 @@ export function pathTo3D(path, height, z) {
|
|||
out.push(r0.x, r0.y, r0.z - height);
|
||||
out.push(l0.x, l0.y, l0.z + height);
|
||||
let ln = l0.vp.normal;
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
// end cap
|
||||
let le = left.peek();
|
||||
let re = right.peek();
|
||||
|
|
@ -410,11 +422,11 @@ export function pathTo3D(path, height, z) {
|
|||
out.push(le.x, le.y, le.z + height);
|
||||
out.push(re.x, re.y, re.z - height);
|
||||
ln = re.vp.normal;
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, 1);
|
||||
nrm.push(-ln.dy, ln.dx, -1);
|
||||
}
|
||||
return { faces: out, normals: nrm };
|
||||
|
|
@ -422,7 +434,7 @@ export function pathTo3D(path, height, z) {
|
|||
|
||||
// produces indexed geometry which isn't ideal for rendering because
|
||||
// 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;
|
||||
|
||||
const profileGeometry = new THREE.ShapeGeometry(shape);
|
||||
|
|
@ -439,9 +451,9 @@ export function shapeToPath(shape, points, closed) {
|
|||
let hA = halfAngle;
|
||||
let tA = v2.angle() + Math.PI * .5;
|
||||
|
||||
if (!closed) {
|
||||
if (i == 0 || i == points.length - 1) { hA = Math.PI * .5; }
|
||||
if (i == points.length - 1) { tA = v1.angle() - Math.PI * .5; }
|
||||
if (!closed){
|
||||
if (i == 0 || i == points.length - 1) {hA = Math.PI * .5;}
|
||||
if (i == points.length - 1) {tA = v1.angle() - Math.PI * .5;}
|
||||
}
|
||||
|
||||
const shift = Math.tan(hA - Math.PI * .5);
|
||||
|
|
@ -476,7 +488,7 @@ export function shapeToPath(shape, points, closed) {
|
|||
}
|
||||
|
||||
const index = [];
|
||||
const lastCorner = closed == false ? points.length - 1 : points.length;
|
||||
const lastCorner = closed == false ? points.length - 1: points.length;
|
||||
|
||||
for (let i = 0; i < lastCorner; i++) {
|
||||
for (let j = 0; j < profile.count; j++) {
|
||||
|
|
@ -512,7 +524,7 @@ export function shapeToPath(shape, points, closed) {
|
|||
index.push(p8, p7, p5);
|
||||
}
|
||||
|
||||
return { index, faces };
|
||||
return {index, faces};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -527,27 +539,31 @@ export function shapeToPath(shape, points, closed) {
|
|||
*
|
||||
* @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;
|
||||
|
||||
// @type {Point}
|
||||
|
||||
|
||||
if (end.x === undefined && end.x === undefined && center === undefined) {
|
||||
// bambu generates loop z or wipe loop arcs in place
|
||||
// console.log({ skip_empty_arc: rec });
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (center) {
|
||||
// center = center.add(start);
|
||||
center.r = center.distTo2D(start);
|
||||
|
||||
} else if (radius !== undefined) {
|
||||
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 pr2;
|
||||
if (Math.abs(dst - radius) < 0.001) {
|
||||
// center point radius
|
||||
pr2 = { x: (end.x + start.x) / 2, y: (end.y + start.y) / 2 };
|
||||
pr2 = { x: (end.x + start.x) / 2, y: (end.y + start.y) / 2};
|
||||
} else {
|
||||
// triangulate
|
||||
pr2 = base.util.center2pr(start, end, radius, clockwise);
|
||||
|
|
@ -556,7 +572,7 @@ export function arcToPath(start, end, arcdivs = 24, opts) {
|
|||
center.y = pr2.y;
|
||||
center.r = radius;
|
||||
} else {
|
||||
console.log({ malfomed_arc: { radius, center, clockwise, start, end } });
|
||||
console.log({malfomed_arc: {radius,center, clockwise, start, end}});
|
||||
}
|
||||
|
||||
//deltas
|
||||
|
|
@ -569,10 +585,10 @@ export function arcToPath(start, end, arcdivs = 24, opts) {
|
|||
let a2 = Math.atan2(center.y - end.y, center.x - end.x) + Math.PI;
|
||||
let ad = base.util.thetaDiff(a1, a2, clockwise); // angle difference in radians
|
||||
let samePoint = Math.abs(ad) < 0.001
|
||||
let ofFull = Math.abs(ad) / (2 * Math.PI);
|
||||
let steps = samePoint ? arcdivs : Math.max(Math.floor(arcdivs * ofFull), 4);
|
||||
let step = (samePoint ? (Math.PI * 2) : ad) / steps;
|
||||
let numPoints = steps / step;
|
||||
let ofFull = Math.abs(ad)/(2*Math.PI);
|
||||
let steps = samePoint? arcdivs : Math.max(Math.floor( arcdivs * ofFull),4);
|
||||
let step = (samePoint? (Math.PI*2) : ad) / steps;
|
||||
let numPoints = steps/ step;
|
||||
let zStart = start.z;
|
||||
let zStep = dz / numPoints;
|
||||
let rot = a1 + step;
|
||||
|
|
@ -591,9 +607,9 @@ export function arcToPath(start, end, arcdivs = 24, opts) {
|
|||
// }
|
||||
|
||||
let arr = [] // point accumulator
|
||||
for (let i = 0; i <= steps - 2; i++) {
|
||||
for (let i=0; i<=steps-2; i++) {
|
||||
if (isNaN(center.r) || isNaN(center.x) || isNaN(center.y)) {
|
||||
console.log({ malfomed_arc: { radius, clockwise, start, end } });
|
||||
console.log({malfomed_arc: {radius, clockwise, start, end}});
|
||||
}
|
||||
arr.push(newPoint(
|
||||
center.x + Math.cos(rot) * center.r,
|
||||
|
|
@ -607,7 +623,7 @@ export function arcToPath(start, end, arcdivs = 24, opts) {
|
|||
return arr
|
||||
}
|
||||
|
||||
export class FloatPacker {
|
||||
class FloatPacker {
|
||||
constructor(size, factor) {
|
||||
this.size = size;
|
||||
this.factor = Math.min(factor || 1.2, 1.1);
|
||||
|
|
@ -626,7 +642,7 @@ export class FloatPacker {
|
|||
this.array = nuarray;
|
||||
this.size = nusize;
|
||||
}
|
||||
for (let i = 0; i < args; i++) {
|
||||
for (let i=0; i<args; i++) {
|
||||
array[this.pos++] = arguments[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -640,11 +656,15 @@ export class FloatPacker {
|
|||
}
|
||||
}
|
||||
|
||||
export const paths = {
|
||||
tip2tipEmit,
|
||||
base.paths = {
|
||||
poly2polyEmit,
|
||||
tip2tipEmit,
|
||||
shapeToPath,
|
||||
pointsToPath,
|
||||
pathTo3D,
|
||||
FloatPacker
|
||||
}
|
||||
arcToPath,
|
||||
vertexNormal: calc_vertex,
|
||||
segmentNormal: calc_normal
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||
|
||||
import { util, config, key } from './base.js';
|
||||
import { newLine } from './line.js';
|
||||
import { newSlope } from './slope.js';
|
||||
"use strict";
|
||||
|
||||
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 {
|
||||
constructor(x = 0, y = 0, z = 0, key) {
|
||||
|
|
@ -39,7 +42,7 @@ class Point {
|
|||
}
|
||||
|
||||
toVector3() {
|
||||
return new Vector3(this.x, this.y, this.z);
|
||||
return new THREE.Vector3(this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
set(x, y, z) {
|
||||
|
|
@ -123,11 +126,11 @@ class Point {
|
|||
}
|
||||
|
||||
slopeTo(p) {
|
||||
return newSlope(this, p);
|
||||
return base.newSlope(this, p);
|
||||
}
|
||||
|
||||
lineTo(p, k) {
|
||||
return newLine(this, p, k);
|
||||
return base.newLine(this, p, k);
|
||||
}
|
||||
|
||||
isNear(p, dist) {
|
||||
|
|
@ -293,7 +296,7 @@ class Point {
|
|||
np2 = newPoint(p2.x - oy, p2.y + ox, p2.z, key.NONE);
|
||||
np1.op = p1;
|
||||
np2.op = p2;
|
||||
return newLine(np1, np2, key.NONE);
|
||||
return base.newLine(np1, np2, key.NONE);
|
||||
}
|
||||
|
||||
offset(x, y, z) {
|
||||
|
|
@ -649,8 +652,10 @@ function pointFromClipper(cp, z) {
|
|||
return newPoint(cp.X / config.clipper, cp.Y / config.clipper, z);
|
||||
}
|
||||
|
||||
export {
|
||||
gapp.overlay(base, {
|
||||
Point,
|
||||
newPoint,
|
||||
pointFromClipper
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -2,14 +2,23 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import { config } from './base.js';
|
||||
import { newPoint } from '../geo/point.js';
|
||||
// dep: geo.base
|
||||
// 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
|
||||
* with optional decimation
|
||||
*/
|
||||
export function verticesToPoints(array, options) {
|
||||
function verticesToPoints(array, options) {
|
||||
let parr = new Array(array.length / 3),
|
||||
i = 0,
|
||||
j = 0,
|
||||
|
|
@ -32,7 +41,7 @@ export function verticesToPoints(array, options) {
|
|||
// replace point objects with their equivalents
|
||||
if (maxpass) {
|
||||
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,
|
||||
m = hash[k];
|
||||
if (!m) {
|
||||
|
|
@ -44,7 +53,7 @@ export function verticesToPoints(array, options) {
|
|||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
export function pointsToVertices(points) {
|
||||
function pointsToVertices(points) {
|
||||
let vertices = new Float32Array(points.length * 3),
|
||||
i = 0, vi = 0;
|
||||
while (i < points.length) {
|
||||
|
|
@ -125,3 +134,5 @@ export function pointsToVertices(points) {
|
|||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
});
|
||||
|
|
@ -2,17 +2,22 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import { base, config, earcut, util } from './base.js';
|
||||
import { ClipperLib } from '../ext/clip2.esm.js';
|
||||
import { newBounds } from './bounds.js';
|
||||
import { paths } from './paths.js';
|
||||
import { newPoint, pointFromClipper } from './point.js';
|
||||
import { polygons as POLY } from './polygons.js';
|
||||
// dep: geo.base
|
||||
// dep: geo.paths
|
||||
// dep: ext.clip2
|
||||
// dep: ext.earcut
|
||||
// dep: geo.point
|
||||
// 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,
|
||||
ClipperLib = self.ClipperLib,
|
||||
Clipper = ClipperLib.Clipper,
|
||||
ClipType = ClipperLib.ClipType,
|
||||
PolyType = ClipperLib.PolyType,
|
||||
|
|
@ -34,7 +39,7 @@ let XAXIS = new Vector3(1,0,0),
|
|||
|
||||
let seqid = Math.round(Math.random() * 0xffffffff);
|
||||
|
||||
export class Polygon {
|
||||
class Polygon {
|
||||
constructor(points) {
|
||||
this.id = seqid++; // polygon unique id
|
||||
this.open = false;
|
||||
|
|
@ -71,11 +76,11 @@ export class Polygon {
|
|||
}
|
||||
|
||||
toPath2D(offset) {
|
||||
return paths.pointsToPath(this.points, offset, this.open);
|
||||
return base.paths.pointsToPath(this.points, offset, this.open);
|
||||
}
|
||||
|
||||
toPath3D(offset, height, z) {
|
||||
return paths.pathTo3D(this.toPath2D(offset), height, z);
|
||||
return base.paths.pathTo3D(this.toPath2D(offset), height, z);
|
||||
}
|
||||
|
||||
toString(verbose) {
|
||||
|
|
@ -230,7 +235,7 @@ export class Polygon {
|
|||
}
|
||||
|
||||
// perform earcut()
|
||||
let cut = earcut(out, holes, 3);
|
||||
let cut = self.earcut(out, holes, 3);
|
||||
let ret = [];
|
||||
|
||||
// preserve swaps in new polys
|
||||
|
|
@ -462,7 +467,7 @@ export class Polygon {
|
|||
return polys
|
||||
.filter(poly => poly.length > 1)
|
||||
.map(poly => {
|
||||
let np = newPolygon().setOpen();
|
||||
let np = base.newPolygon().setOpen();
|
||||
for (let p of poly) {
|
||||
np.push(p);
|
||||
}
|
||||
|
|
@ -1138,7 +1143,7 @@ export class Polygon {
|
|||
applyRotations() {
|
||||
for (let point of this.points) {
|
||||
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);
|
||||
point.x = p2.x;
|
||||
point.y = p2.y;
|
||||
|
|
@ -1200,76 +1205,15 @@ export class Polygon {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Ease down along the polygonal path.
|
||||
*
|
||||
* 1. Travel from fromPoint to closest point on polygon, to rampZ above that that point,
|
||||
* 2. ease-down starts, following the polygonal path, decreasing Z at a fixed slope until target Z is hit,
|
||||
* 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
|
||||
* calls function for each point in this polygon
|
||||
* @param {Function} fn to call for each point
|
||||
* @param {boolean} [close=true] whether to close the loop (last point to first)
|
||||
* @param {number} [start=0] starting index
|
||||
*/
|
||||
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,
|
||||
points = this.points,
|
||||
length = points.length,
|
||||
|
|
@ -2059,7 +2003,7 @@ export class Polygon {
|
|||
return res.map(array => {
|
||||
let poly = newPolygon();
|
||||
for (let pt of array) {
|
||||
poly.push(pointFromClipper(pt, z));
|
||||
poly.push(base.pointFromClipper(pt, z));
|
||||
}
|
||||
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 n2 = s2.angle;
|
||||
let diff = n2 - n1;
|
||||
|
|
@ -2383,17 +2328,28 @@ export function slopeDiff(s1, s2) {
|
|||
return Math.abs(diff);
|
||||
}
|
||||
|
||||
export function fromClipperPath(path, z) {
|
||||
function fromClipperPath(path, z) {
|
||||
let poly = newPolygon(),
|
||||
i = 0,
|
||||
l = path.length;
|
||||
while (i < l) {
|
||||
// poly.push(newPoint(null,null,z,null,path[i++]));
|
||||
poly.push(pointFromClipper(path[i++], z));
|
||||
poly.push(base.pointFromClipper(path[i++], z));
|
||||
}
|
||||
return poly;
|
||||
}
|
||||
|
||||
export function newPolygon(points) {
|
||||
function newPolygon(points) {
|
||||
return new Polygon(points);
|
||||
}
|
||||
|
||||
Polygon.fromArray = function(array) {
|
||||
return newPolygon().fromArray(array);
|
||||
};
|
||||
|
||||
gapp.overlay(base, {
|
||||
Polygon,
|
||||
newPolygon
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -2,45 +2,45 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import { base, util, config } from './base.js';
|
||||
import { newPoint, pointFromClipper } from './point.js';
|
||||
import { newPolygon } from './polygon.js';
|
||||
import { newSlope } from './slope.js';
|
||||
import { paths } from './paths.js';
|
||||
import { ClipperLib } from '../ext/clip2.esm.js';
|
||||
// dep: geo.base
|
||||
// dep: geo.point
|
||||
// use: ext.clip2
|
||||
// use: geo.slope
|
||||
// use: geo.polygon
|
||||
gapp.register("geo.polygons", [], (root, exports) => {
|
||||
|
||||
const geo = base;
|
||||
const { numOrDefault } = util;
|
||||
const { base } = root;
|
||||
const { util, paths, config, newPoint } = base;
|
||||
const { sqr, numOrDefault } = util;
|
||||
|
||||
const DEG2RAD = Math.PI / 180,
|
||||
SQRT = Math.sqrt,
|
||||
SQR = util.sqr,
|
||||
ABS = Math.abs;
|
||||
|
||||
const {
|
||||
Clipper,
|
||||
ClipType,
|
||||
PolyType,
|
||||
PolyFillType,
|
||||
EndType,
|
||||
JoinType,
|
||||
PolyTree,
|
||||
ClipperOffset
|
||||
} = ClipperLib;
|
||||
|
||||
const CleanPolygon = Clipper.CleanPolygon,
|
||||
const ClipperLib = self.ClipperLib,
|
||||
Clipper = ClipperLib.Clipper,
|
||||
ClipType = ClipperLib.ClipType,
|
||||
PolyType = ClipperLib.PolyType,
|
||||
PolyFillType = ClipperLib.PolyFillType,
|
||||
CleanPolygon = Clipper.CleanPolygon,
|
||||
CleanPolygons = Clipper.CleanPolygons,
|
||||
SimplifyPolygons = Clipper.SimplifyPolygons,
|
||||
FillNonZero = PolyFillType.pftNonZero,
|
||||
FillEvenOdd = PolyFillType.pftEvenOdd,
|
||||
PathSubject = PolyType.ptSubject,
|
||||
PathClip = PolyType.ptClip,
|
||||
EndType = ClipperLib.EndType,
|
||||
JoinType = ClipperLib.JoinType,
|
||||
PolyTree = ClipperLib.PolyTree,
|
||||
ClipXOR = ClipType.ctXor,
|
||||
ClipDiff = ClipType.ctDifference,
|
||||
ClipUnion = ClipType.ctUnion,
|
||||
ClipIntersect = ClipType.ctIntersection;
|
||||
ClipIntersect = ClipType.ctIntersection,
|
||||
ClipperOffset = ClipperLib.ClipperOffset
|
||||
;
|
||||
|
||||
const POLYS = {
|
||||
const POLYS = base.polygons = {
|
||||
clearInner,
|
||||
rayIntersect,
|
||||
alignWindings,
|
||||
|
|
@ -73,16 +73,14 @@ const POLYS = {
|
|||
fingerprint
|
||||
};
|
||||
|
||||
export { POLYS };
|
||||
|
||||
export function outer(polys) {
|
||||
function outer(polys) {
|
||||
for (let p of polys) {
|
||||
p.inner = undefined;
|
||||
}
|
||||
return polys;
|
||||
}
|
||||
|
||||
export function inner(polys) {
|
||||
function inner(polys) {
|
||||
const ret = [];
|
||||
for (let p of polys) {
|
||||
if (p.inner) {
|
||||
|
|
@ -92,7 +90,7 @@ export function inner(polys) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
export function length(polys) {
|
||||
function length(polys) {
|
||||
let length = 0;
|
||||
for (let p of polys) {
|
||||
length += p.deepLength;
|
||||
|
|
@ -100,20 +98,20 @@ export function length(polys) {
|
|||
return length;
|
||||
}
|
||||
|
||||
export function setZ(polys, z) {
|
||||
function setZ(polys, z) {
|
||||
for (let poly of polys) {
|
||||
poly.setZ(z);
|
||||
}
|
||||
return polys;
|
||||
}
|
||||
|
||||
export function clearInner(polys) {
|
||||
function clearInner(polys) {
|
||||
for (let p of polys) {
|
||||
p.clearInner();
|
||||
}
|
||||
}
|
||||
|
||||
export function toClipper(polys = []) {
|
||||
function toClipper(polys = []) {
|
||||
let out = [];
|
||||
for (let poly of polys) {
|
||||
poly.toClipper(out);
|
||||
|
|
@ -121,16 +119,16 @@ export function toClipper(polys = []) {
|
|||
return out;
|
||||
}
|
||||
|
||||
export function fromClipperNode(tnode, z) {
|
||||
let poly = newPolygon();
|
||||
function fromClipperNode(tnode, z) {
|
||||
let poly = base.newPolygon();
|
||||
for (let point of tnode.m_polygon) {
|
||||
poly.push(pointFromClipper(point, z));
|
||||
poly.push(base.pointFromClipper(point, z));
|
||||
}
|
||||
poly.open = tnode.IsOpen;
|
||||
return poly;
|
||||
}
|
||||
};
|
||||
|
||||
export function fromClipperTree(tnode, z, tops, parent, minarea) {
|
||||
function fromClipperTree(tnode, z, tops, parent, minarea) {
|
||||
let poly,
|
||||
polys = tops || [],
|
||||
min = numOrDefault(minarea, 0.1);
|
||||
|
|
@ -152,9 +150,9 @@ export function fromClipperTree(tnode, z, tops, parent, minarea) {
|
|||
}
|
||||
|
||||
return polys;
|
||||
}
|
||||
};
|
||||
|
||||
export function fromClipperTreeUnion(tnode, z, minarea, tops, parent) {
|
||||
function fromClipperTreeUnion(tnode, z, minarea, tops, parent) {
|
||||
let polys = tops || [], poly;
|
||||
|
||||
for (let child of tnode.m_Childs) {
|
||||
|
|
@ -173,9 +171,9 @@ export function fromClipperTreeUnion(tnode, z, minarea, tops, parent) {
|
|||
}
|
||||
|
||||
return polys;
|
||||
}
|
||||
};
|
||||
|
||||
export function cleanClipperTree(tree) {
|
||||
function cleanClipperTree(tree) {
|
||||
if (tree.m_Childs)
|
||||
for (let child of tree.m_Childs) {
|
||||
child.m_polygon = CleanPolygon(child.m_polygon, config.clipperClean);
|
||||
|
|
@ -183,9 +181,9 @@ export function cleanClipperTree(tree) {
|
|||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
};
|
||||
|
||||
export function filter(array, output, fn) {
|
||||
function filter(array, output, fn) {
|
||||
for (let poly of array) {
|
||||
poly = fn(poly);
|
||||
if (poly) {
|
||||
|
|
@ -199,14 +197,14 @@ export function filter(array, output, fn) {
|
|||
return output;
|
||||
}
|
||||
|
||||
export function points(polys) {
|
||||
function points(polys) {
|
||||
return polys.length ? polys.map(p => p.deepLength).reduce((a,v) => a+v) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* redo nesting of polygons that might already have inners
|
||||
*/
|
||||
export function renest(polygons, deep) {
|
||||
function renest(polygons, 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
|
||||
* @returns {Polygon[]} top level parent polygons
|
||||
*/
|
||||
export function nest(polygons, deep, opentop) {
|
||||
function nest(polygons, deep, opentop) {
|
||||
if (!polygons) {
|
||||
return polygons;
|
||||
}
|
||||
|
|
@ -286,7 +284,7 @@ export function nest(polygons, deep, opentop) {
|
|||
* @param {boolean} CW
|
||||
* @param {boolean} [recurse]
|
||||
*/
|
||||
export function setWinding(array, CW, recurse) {
|
||||
function setWinding(array, CW, recurse) {
|
||||
if (!array) return;
|
||||
let poly, i = 0;
|
||||
while (i < array.length) {
|
||||
|
|
@ -304,7 +302,7 @@ export function setWinding(array, CW, recurse) {
|
|||
* @param {Polygon[]} polys
|
||||
* @return {boolean} true if aligned clockwise
|
||||
*/
|
||||
export function alignWindings(polys) {
|
||||
function alignWindings(polys) {
|
||||
let len = polys.length,
|
||||
fwd = 0,
|
||||
pts = 0,
|
||||
|
|
@ -325,14 +323,21 @@ export function alignWindings(polys) {
|
|||
return setCW;
|
||||
}
|
||||
|
||||
export function setContains(setA, poly) {
|
||||
function setContains(setA, poly) {
|
||||
for (let i=0; i<setA.length; i++) {
|
||||
if (setA[i].contains(poly)) return true;
|
||||
}
|
||||
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 || [];
|
||||
for (let poly of polys) {
|
||||
poly.flattenTo(to);
|
||||
|
|
@ -353,7 +358,7 @@ export function flatten(polys, to, crush) {
|
|||
* @param {number} [minArea]
|
||||
* @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),
|
||||
out = [];
|
||||
|
||||
|
|
@ -435,63 +440,63 @@ export function subtract(setA, setB, outA, outB, z, minArea, opt = {}) {
|
|||
* @param {Polygon[]} polys
|
||||
* @returns {Polygon[]}
|
||||
*/
|
||||
export function union(polys, minarea, all, opt = {}) {
|
||||
if (polys.length < 2) return polys;
|
||||
let lpre = length(polys);
|
||||
function union(polys, minarea, all, opt = {}) {
|
||||
if (polys.length < 2) return polys;
|
||||
let lpre = length(polys);
|
||||
|
||||
if (opt.wasm && geo.wasm) {
|
||||
let min = minarea ?? 0.01;
|
||||
// let deepLength = polys.map(p => p.deepLength).reduce((a,v) => a+v);
|
||||
// if (deepLength < 15000)
|
||||
try {
|
||||
let out = geo.wasm.js.union(polys, polys[0].getZ()).filter(p => p.area() > min);
|
||||
opt.changes = length(out) - lpre;
|
||||
return out;
|
||||
} catch (e) {
|
||||
console.log({union_fail: polys, minarea, all});
|
||||
}
|
||||
}
|
||||
if (opt.wasm && geo.wasm) {
|
||||
let min = minarea ?? 0.01;
|
||||
// let deepLength = polys.map(p => p.deepLength).reduce((a,v) => a+v);
|
||||
// if (deepLength < 15000)
|
||||
try {
|
||||
let out = geo.wasm.js.union(polys, polys[0].getZ()).filter(p => p.area() > min);
|
||||
opt.changes = length(out) - lpre;
|
||||
return out;
|
||||
} catch (e) {
|
||||
console.log({union_fail: polys, minarea, all});
|
||||
}
|
||||
}
|
||||
|
||||
let out = polys.slice(), i, j, union, uset = [], a, b;
|
||||
let out = polys.slice(), i, j, union, uset = [], a, b;
|
||||
|
||||
outer: for (i=0; i<out.length; i++) {
|
||||
if (!out[i]) continue;
|
||||
for (j=i+1; j<out.length; j++) {
|
||||
if (!out[j]) continue;
|
||||
union = out[i].union(out[j], minarea, all);
|
||||
if (union && union.length) {
|
||||
if (opt.onmerge) {
|
||||
a = out[i];
|
||||
b = out[j];
|
||||
}
|
||||
out[i] = null;
|
||||
out[j] = null;
|
||||
if (all) {
|
||||
out.appendAll(union);
|
||||
} else {
|
||||
out.push(union);
|
||||
}
|
||||
if (opt.onmerge) {
|
||||
opt.onmerge(a, b, union);
|
||||
}
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
outer: for (i=0; i<out.length; i++) {
|
||||
if (!out[i]) continue;
|
||||
for (j=i+1; j<out.length; j++) {
|
||||
if (!out[j]) continue;
|
||||
union = out[i].union(out[j], minarea, all);
|
||||
if (union && union.length) {
|
||||
if (opt.onmerge) {
|
||||
a = out[i];
|
||||
b = out[j];
|
||||
}
|
||||
out[i] = null;
|
||||
out[j] = null;
|
||||
if (all) {
|
||||
out.appendAll(union);
|
||||
} else {
|
||||
out.push(union);
|
||||
}
|
||||
if (opt.onmerge) {
|
||||
opt.onmerge(a, b, union);
|
||||
}
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<out.length; i++) {
|
||||
if (out[i]) uset.push(out[i]);
|
||||
}
|
||||
for (i=0; i<out.length; i++) {
|
||||
if (out[i]) uset.push(out[i]);
|
||||
}
|
||||
|
||||
opt.changes = length(uset) - lpre;
|
||||
return uset;
|
||||
}
|
||||
opt.changes = length(uset) - lpre;
|
||||
return uset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Polygon} poly clipping mask
|
||||
* @returns {?Polygon[]}
|
||||
*/
|
||||
export function diff(setA, setB, z) {
|
||||
function diff(setA, setB, z) {
|
||||
let clip = new Clipper(),
|
||||
tree = new PolyTree(),
|
||||
sp1 = toClipper(setA),
|
||||
|
|
@ -511,7 +516,7 @@ export function diff(setA, setB, z) {
|
|||
* @param {Polygon} poly clipping mask
|
||||
* @returns {?Polygon[]}
|
||||
*/
|
||||
export function xor(set, z) {
|
||||
function xor(set, z) {
|
||||
z = z || set[0].getZ();
|
||||
outer: for (;;) {
|
||||
// sort largest to smallest area
|
||||
|
|
@ -554,7 +559,7 @@ export function xor(set, z) {
|
|||
* @param {Polygon[]} setB mask set
|
||||
* @returns {Polygon[]}
|
||||
*/
|
||||
export function trimTo(setA, setB) {
|
||||
function trimTo(setA, setB) {
|
||||
// handle null/empty slices
|
||||
if (setA === setB || setA === null || setB === null) return null;
|
||||
|
||||
|
|
@ -568,7 +573,7 @@ export function trimTo(setA, setB) {
|
|||
return out;
|
||||
}
|
||||
|
||||
export function sumCirc(polys) {
|
||||
function sumCirc(polys) {
|
||||
let sum = 0.0;
|
||||
polys.forEach(function(poly) {
|
||||
sum += poly.circularityDeep();
|
||||
|
|
@ -586,7 +591,7 @@ export function sumCirc(polys) {
|
|||
* @param {Function} [collector] receives output of each pass
|
||||
* @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], {
|
||||
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
|
||||
* 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) : [];
|
||||
if (open.length) {
|
||||
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
|
||||
|
|
@ -701,7 +706,7 @@ export function offset(polys, dist, opts = {}) {
|
|||
* as performing subtractive analysis between initial layer shell (ref)
|
||||
* 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 layers = [];
|
||||
let ref = polys;
|
||||
|
|
@ -744,7 +749,7 @@ export function inset(polys, dist, count, z, wasm) {
|
|||
* @param {number} [maxLen]
|
||||
* @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;
|
||||
|
||||
let i = 1,
|
||||
|
|
@ -766,7 +771,7 @@ export function fillArea(polys, angle, spacing, output, minLen, maxLen) {
|
|||
while (angle > 90) angle -= 180;
|
||||
|
||||
// X,Y ray slope derived from angle
|
||||
raySlope = newSlope(0,0,
|
||||
raySlope = base.newSlope(0,0,
|
||||
Math.cos(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 (maxlen && plen > maxlen) continue;
|
||||
}
|
||||
let p1 = pointFromClipper(poly.m_polygon[0], zpos);
|
||||
let p2 = pointFromClipper(poly.m_polygon[1], zpos);
|
||||
let p1 = base.pointFromClipper(poly.m_polygon[0], zpos);
|
||||
let p2 = base.pointFromClipper(poly.m_polygon[1], zpos);
|
||||
let od = rayint.origin.distToLineNew(p1,p2) / spacing;
|
||||
lines.push([p1, p2, od]);
|
||||
}
|
||||
|
|
@ -870,7 +875,7 @@ export function fillArea(polys, angle, spacing, output, minLen, maxLen) {
|
|||
* @param {boolean} [for_fill]
|
||||
* @returns {Point[]}
|
||||
*/
|
||||
export function rayIntersect(start, slope, polygons, for_fill) {
|
||||
function rayIntersect(start, slope, polygons, for_fill) {
|
||||
let i = 0,
|
||||
flat = [],
|
||||
points = [],
|
||||
|
|
@ -999,11 +1004,11 @@ export function rayIntersect(start, slope, polygons, for_fill) {
|
|||
return points;
|
||||
}
|
||||
|
||||
export function pd(a,b) {
|
||||
function pd(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 => {
|
||||
return {
|
||||
l: p.length,
|
||||
|
|
@ -1032,7 +1037,7 @@ export function fingerprint(polys) {
|
|||
}
|
||||
|
||||
// compare fingerprint arrays
|
||||
export function fingerprintCompare(a, b) {
|
||||
function fingerprintCompare(a, b) {
|
||||
// true if array is the same object
|
||||
if (a === b) {
|
||||
return true;
|
||||
|
|
@ -1074,7 +1079,7 @@ export function fingerprintCompare(a, b) {
|
|||
|
||||
// plan a route through an array of polygon center points
|
||||
// starting with the polygon center closest to "start"
|
||||
export function route(polys, start) {
|
||||
function route(polys, start) {
|
||||
let centers = [];
|
||||
let first, minDist = Infinity;
|
||||
for (let poly of polys) {
|
||||
|
|
@ -1111,4 +1116,4 @@ export function route(polys, start) {
|
|||
return routed.map(r => r.poly);
|
||||
}
|
||||
|
||||
export const polygons = POLYS;
|
||||
});
|
||||
|
|
@ -1,16 +1,23 @@
|
|||
/** 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
|
||||
* 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';
|
||||
import { newOrderedLine } from './line.js';
|
||||
import { newPolygon } from '../geo/polygon.js';
|
||||
import { polygons } from '../geo/polygons.js';
|
||||
import { newPoint } from '../geo/point.js';
|
||||
import { config } from '../geo/base.js';
|
||||
const { base } = root;
|
||||
const { config, util, polygons } = base
|
||||
const { newOrderedLine, newPolygon, newPoint } = base;
|
||||
|
||||
const POLY = base.polygons;
|
||||
|
||||
function dval(v, dv) {
|
||||
return v !== undefined ? v : dv;
|
||||
|
|
@ -25,7 +32,7 @@ function dval(v, dv) {
|
|||
* @param {Point[]} points vertex array
|
||||
* @param {Object} options slicing parameters
|
||||
*/
|
||||
export async function slice(points, options = {}) {
|
||||
async function slice(points, options = {}) {
|
||||
let zMin = options.zMin || 0,
|
||||
zMax = options.zMax || 0,
|
||||
zInc = options.zInc || 0,
|
||||
|
|
@ -65,6 +72,7 @@ export async function slice(points, options = {}) {
|
|||
for (i = 0; i < points.length; i++) {
|
||||
points[i] = points[i].round(3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// detect faces co-planar with Z and sum the enclosed area
|
||||
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]) {
|
||||
zFlat[zkey] = area;
|
||||
} else {
|
||||
|
|
@ -314,7 +322,7 @@ function makeZLine(phash, p1, p2, coplanar, edge) {
|
|||
*
|
||||
* @param {number} z
|
||||
*/
|
||||
export async function sliceZ(z, points, options = {}) {
|
||||
async function sliceZ(z, points, options = {}) {
|
||||
if (Array.isArray(z)) {
|
||||
return Promise.all(z.map(z => sliceZ(z, points, options)));
|
||||
}
|
||||
|
|
@ -380,14 +388,14 @@ export async function sliceZ(z, points, options = {}) {
|
|||
if (groupFn) {
|
||||
let groups = groupFn(lines, z, options);
|
||||
if (options.xor) {
|
||||
groups = polygons.xor(groups);
|
||||
groups = POLY.xor(groups);
|
||||
}
|
||||
if (options.union) {
|
||||
let points = groups.map(p => p.length);
|
||||
if (points.length > 1) points = points.reduce((a,b) => a + b);
|
||||
// simplistic healing of non-manifold meshes
|
||||
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
|
||||
// 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
|
||||
|
|
@ -401,14 +409,14 @@ export async function sliceZ(z, points, options = {}) {
|
|||
}
|
||||
// track total poly length changes to determine if healed
|
||||
rval.changes = opt.changes;
|
||||
groups = polygons.flatten(union, null, true);
|
||||
groups = POLY.flatten(union, null, true);
|
||||
}
|
||||
rval.groups = groups;
|
||||
}
|
||||
|
||||
// look for driver-specific slice post-processor
|
||||
if (options.post) {
|
||||
let fn = slicer.slicePost[options.post];
|
||||
let fn = base.slicePost[options.post];
|
||||
if (fn) fn(rval, options);
|
||||
}
|
||||
|
||||
|
|
@ -450,7 +458,7 @@ export async function sliceZ(z, points, options = {}) {
|
|||
* @param {number} [index]
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function sliceConnect(input, z, opt = {}) {
|
||||
function sliceConnect(input, z, opt = {}) {
|
||||
let { debug, both } = opt;
|
||||
|
||||
if (both) {
|
||||
|
|
@ -774,7 +782,7 @@ export function sliceConnect(input, z, opt = {}) {
|
|||
* @param {Line[]} lines
|
||||
* @returns {Line[]}
|
||||
*/
|
||||
export function removeDuplicateLines(lines) {
|
||||
function removeDuplicateLines(lines) {
|
||||
let output = [],
|
||||
tmplines = [],
|
||||
points = [],
|
||||
|
|
@ -865,11 +873,12 @@ export function removeDuplicateLines(lines) {
|
|||
return output;
|
||||
}
|
||||
|
||||
export const slicer = {
|
||||
gapp.overlay(base, {
|
||||
slice,
|
||||
sliceZ,
|
||||
slicePost: {},
|
||||
sliceDedup: removeDuplicateLines,
|
||||
sliceConnect
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -2,13 +2,19 @@
|
|||
|
||||
"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,
|
||||
DEG2RAD = Math.PI / 180,
|
||||
RAD2DEG = 180 / Math.PI;
|
||||
|
||||
export class Slope {
|
||||
class Slope {
|
||||
constructor(p1, p2, dx, dy) {
|
||||
this.dx = p1 && p2 ? p2.x - p1.x : dx;
|
||||
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);
|
||||
}
|
||||
|
||||
export function newSlope(p1, p2, dx, dy) {
|
||||
function newSlope(p1, p2, dx, dy) {
|
||||
return new Slope(p1, p2, dx, dy);
|
||||
}
|
||||
|
||||
export function newSlopeFromAngle(angle) {
|
||||
function newSlopeFromAngle(angle) {
|
||||
return newSlope(0,0,
|
||||
Math.cos(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 */
|
||||
|
||||
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 factor = config.clipper;
|
||||
|
||||
export const wasm_ctrl = {
|
||||
const wasm_ctrl = {
|
||||
enable,
|
||||
disable,
|
||||
count: {
|
||||
|
|
@ -16,6 +20,8 @@ export const wasm_ctrl = {
|
|||
}
|
||||
};
|
||||
|
||||
gapp.overlay(base, { wasm_ctrl });
|
||||
|
||||
function log() {
|
||||
console.log(...arguments);
|
||||
}
|
||||
|
|
@ -54,7 +60,7 @@ function writePoly(view, poly, inner) {
|
|||
function readPoly(view, z) {
|
||||
let points = view.readU16(true);
|
||||
if (points === 0) return;
|
||||
let poly = base.newPolygon();
|
||||
let poly = self.base.newPolygon();
|
||||
while (points-- > 0) {
|
||||
poly.add(view.readI32(true)/factor, view.readI32(true)/factor, z || 0);
|
||||
}
|
||||
|
|
@ -73,7 +79,7 @@ function readPolys(view, z, out = []) {
|
|||
return out;
|
||||
}
|
||||
|
||||
export function polyOffset(polys, offset, z, clean, simple) {
|
||||
function polyOffset(polys, offset, z, clean, simple) {
|
||||
wasm_ctrl.count.offset++;
|
||||
let wasm = base.wasm,
|
||||
buffer = wasm.shared,
|
||||
|
|
@ -83,7 +89,7 @@ export function polyOffset(polys, offset, z, clean, simple) {
|
|||
return polyNest(out);
|
||||
}
|
||||
|
||||
export function polyUnion(polys, z) {
|
||||
function polyUnion(polys, z) {
|
||||
wasm_ctrl.count.union++;
|
||||
let wasm = base.wasm,
|
||||
buffer = wasm.shared,
|
||||
|
|
@ -93,7 +99,7 @@ export function polyUnion(polys, z) {
|
|||
return polyNest(out);
|
||||
}
|
||||
|
||||
export function polyDiff(polysA, polysB, z, AB, BA) {
|
||||
function polyDiff(polysA, polysB, z, AB, BA) {
|
||||
wasm_ctrl.count.diff++;
|
||||
let wasm = base.wasm,
|
||||
buffer = wasm.shared,
|
||||
|
|
@ -147,7 +153,7 @@ function readString(pos, len) {
|
|||
return out.join('');
|
||||
}
|
||||
|
||||
export function enable() {
|
||||
function enable() {
|
||||
if (base.wasm || base._wasm) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -194,9 +200,11 @@ export function enable() {
|
|||
});
|
||||
}
|
||||
|
||||
export function disable() {
|
||||
function disable() {
|
||||
if (base.wasm) {
|
||||
base.wasm.free(base.wasm.shared);
|
||||
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