Merge branch 'master' of https://github.com/GridSpace/grid-apps into drill-optimization
2
.github/workflows/build-docs.yml
vendored
|
|
@ -2,6 +2,8 @@ name: 'docs-publish'
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
|
paths:
|
||||||
|
- 'docs/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
2
.github/workflows/build-test.yml
vendored
|
|
@ -3,6 +3,8 @@ name: Test Electron App Production Build
|
||||||
on:
|
on:
|
||||||
# Runs on pushes targeting the default branch
|
# Runs on pushes targeting the default branch
|
||||||
push:
|
push:
|
||||||
|
paths:
|
||||||
|
- '!docs/**'
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
|
|
||||||
2
.github/workflows/prettier-check.yml
vendored
|
|
@ -2,7 +2,7 @@ name: 'Docs Fomatting Check'
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- './docs/**'
|
- 'docs/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
|
||||||
4
.github/workflows/prod-test.yml
vendored
|
|
@ -3,6 +3,8 @@ name: Test NPM Production Build
|
||||||
on:
|
on:
|
||||||
# Runs on pushes targeting the default branch
|
# Runs on pushes targeting the default branch
|
||||||
push:
|
push:
|
||||||
|
paths:
|
||||||
|
- '!docs/**'
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
|
@ -24,4 +26,4 @@ jobs:
|
||||||
run: npm run setup
|
run: npm run setup
|
||||||
|
|
||||||
- name: dry run production build
|
- name: dry run production build
|
||||||
run: npm run prod-dryrun
|
run: npm run dryrun-prod
|
||||||
|
|
|
||||||
5
.gitignore
vendored
|
|
@ -1,6 +1,8 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.bache
|
||||||
.docusaurus
|
.docusaurus
|
||||||
.env
|
.env
|
||||||
|
alt
|
||||||
app.json
|
app.json
|
||||||
build
|
build
|
||||||
data
|
data
|
||||||
|
|
@ -9,7 +11,8 @@ jszip-esm.js
|
||||||
logs
|
logs
|
||||||
mod
|
mod
|
||||||
node_modules
|
node_modules
|
||||||
|
pack
|
||||||
package-lock.json
|
package-lock.json
|
||||||
three.js
|
three.js
|
||||||
tmp
|
tmp
|
||||||
pack
|
web/boot/bundle*
|
||||||
|
|
|
||||||
37
app-el.js
|
|
@ -9,20 +9,11 @@ const appDir = path.join(usrDir, 'gapp');
|
||||||
const cnfDir = path.join(appDir, 'conf');
|
const cnfDir = path.join(appDir, 'conf');
|
||||||
const logDir = path.join(appDir, 'logs');
|
const logDir = path.join(appDir, 'logs');
|
||||||
const datDir = path.join(appDir, 'data');
|
const datDir = path.join(appDir, 'data');
|
||||||
const debug = process.argv.slice(2).map(v => v.replaceAll('-', '')).contains('debugg');
|
const debug = process.argv.slice(2).map(v => v.replaceAll('-', '')).indexOf('debugg') >= 0;
|
||||||
const devel = process.argv.slice(2).map(v => v.replaceAll('-', '')).contains('devel');
|
const devel = process.argv.slice(2).map(v => v.replaceAll('-', '')).indexOf('devel') >= 0;
|
||||||
|
|
||||||
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true;
|
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true;
|
||||||
|
|
||||||
// console.log({ appDir, usrDir, logDir, datDir, basDir });
|
|
||||||
// console.log({ argv: process.argv, debug, devel });
|
|
||||||
// console.log({
|
|
||||||
// cwd: process.cwd(),
|
|
||||||
// dir: require('fs').readdirSync('.'),
|
|
||||||
// out: require('fs').readdirSync(appDir),
|
|
||||||
// ___: require('fs').readdirSync(basDir)
|
|
||||||
// });
|
|
||||||
|
|
||||||
server({
|
server({
|
||||||
port: 5309,
|
port: 5309,
|
||||||
apps: basDir,
|
apps: basDir,
|
||||||
|
|
@ -40,23 +31,31 @@ function createWindow() {
|
||||||
width: 1600,
|
width: 1600,
|
||||||
height: 900,
|
height: 900,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
|
// contextIsolation: true,
|
||||||
|
// nodeIntegration: true,
|
||||||
|
// sandbox: true,
|
||||||
|
// preload: undefined
|
||||||
|
// nodeIntegration: true,
|
||||||
|
// contextIsolation: false
|
||||||
// preload: path.join(__dirname, 'preload.js')
|
// preload: path.join(__dirname, 'preload.js')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { webContents } = mainWindow;
|
||||||
|
|
||||||
mainWindow.loadURL('http://localhost:5309/kiri');
|
mainWindow.loadURL('http://localhost:5309/kiri');
|
||||||
|
|
||||||
// default normal url navigation or page opens to happen outside Electron
|
// default normal url navigation or page opens to happen outside Electron
|
||||||
mainWindow.webContents.setWindowOpenHandler((details) => {
|
webContents.setWindowOpenHandler((details) => {
|
||||||
// console.log('EXTERNAL', details);
|
// console.log('EXTERNAL', details);
|
||||||
shell.openExternal(details.url);
|
shell.openExternal(details.url);
|
||||||
return { action: 'deny' }
|
return { action: 'deny' }
|
||||||
});
|
});
|
||||||
|
|
||||||
// prevent "other" urls from opening inside Electron (alerts are problematic)
|
// prevent "other" urls from opening inside Electron (alerts are problematic)
|
||||||
mainWindow.webContents.on('will-navigate', (event, url) => {
|
webContents.on('will-navigate', (event, url) => {
|
||||||
// console.log('DIVERT', url);
|
// console.log('DIVERT', url);
|
||||||
if (url.endsWith('/kiri') || url.endsWith('/kiri/')) {
|
if (url.endsWith('/kiri') || url.endsWith('/kiri/')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (url.endsWith('/mesh') || url.endsWith('/mesh/')) {
|
if (url.endsWith('/mesh') || url.endsWith('/mesh/')) {
|
||||||
|
|
@ -66,9 +65,13 @@ function createWindow() {
|
||||||
shell.openExternal(url);
|
shell.openExternal(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
webContents.on('did-finish-load', () => {
|
||||||
|
mainWindow.webContents.executeJavaScript(`{ let x = document.getElementById('app-quit'); if (x) { x.onclick = () => window.close() } }; null;`);
|
||||||
|
});
|
||||||
|
|
||||||
if (devel) {
|
if (devel) {
|
||||||
console.log("opening developer tools");
|
console.log("opening developer tools");
|
||||||
mainWindow.webContents.openDevTools();
|
webContents.openDevTools();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,9 +80,7 @@ app.on('ready', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
if (process.platform !== 'darwin') {
|
app.quit();
|
||||||
app.quit();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('activate', () => {
|
app.on('activate', () => {
|
||||||
|
|
|
||||||
131
app.js
|
|
@ -15,6 +15,10 @@ 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');
|
||||||
|
const isElectron = !!process.versions.electron ||
|
||||||
|
process.env.ELECTRON_RUN_AS_NODE === '1' ||
|
||||||
|
process.env.ELECTRON_NO_ATTACH === '1' ||
|
||||||
|
process.env.ELECTRON_BUILD === '1';
|
||||||
|
|
||||||
const append = { mesh:'', kiri:'' };
|
const append = { mesh:'', kiri:'' };
|
||||||
const code = {};
|
const code = {};
|
||||||
|
|
@ -31,6 +35,8 @@ let oversion;
|
||||||
let dversion;
|
let dversion;
|
||||||
let lastmod;
|
let lastmod;
|
||||||
let logger;
|
let logger;
|
||||||
|
let single;
|
||||||
|
let dryrun;
|
||||||
let debug;
|
let debug;
|
||||||
let http;
|
let http;
|
||||||
let util;
|
let util;
|
||||||
|
|
@ -60,12 +66,15 @@ netdb.create = async function(map = {}) {
|
||||||
|
|
||||||
function init(mod) {
|
function init(mod) {
|
||||||
const ENV = mod.env;
|
const ENV = mod.env;
|
||||||
|
const inElectron = isElectron || ENV.electron;
|
||||||
|
|
||||||
startTime = time();
|
startTime = time();
|
||||||
lastmod = mod.util.lastmod;
|
lastmod = mod.util.lastmod;
|
||||||
logger = mod.log;
|
logger = mod.log;
|
||||||
pre = ENV.pre || mod.meta.pre;
|
pre = ENV.pre || mod.meta.pre;
|
||||||
debug = ENV.debug || mod.meta.debug;
|
debug = ENV.debug || mod.meta.debug;
|
||||||
|
dryrun = ENV.dryrun;
|
||||||
|
single = ENV.single;
|
||||||
oversion = ENV.over || mod.meta.over;
|
oversion = ENV.over || mod.meta.over;
|
||||||
crossOrigin = ENV.xorigin || mod.meta.xorigin || false;
|
crossOrigin = ENV.xorigin || mod.meta.xorigin || false;
|
||||||
serviceWorker = (ENV.service || mod.meta.service) !== false;
|
serviceWorker = (ENV.service || mod.meta.service) !== false;
|
||||||
|
|
@ -74,14 +83,10 @@ function init(mod) {
|
||||||
dir = mod.dir;
|
dir = mod.dir;
|
||||||
log = mod.log;
|
log = mod.log;
|
||||||
|
|
||||||
if (ENV.single) console.log({ cwd: process.cwd(), env: ENV });
|
if (single) console.log({ cwd: process.cwd(), env: ENV });
|
||||||
dversion = debug ? `_${version}` : version;
|
dversion = debug ? `_${version}` : version;
|
||||||
forceUseCache = ENV.cache ? true : false;
|
forceUseCache = ENV.cache ? true : false;
|
||||||
|
|
||||||
if (!ENV.electron) {
|
|
||||||
generateDevices();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pre) {
|
if (pre) {
|
||||||
for (let [k,v] of Object.entries(productionMap)) {
|
for (let [k,v] of Object.entries(productionMap)) {
|
||||||
productionMap[`${pre}${k}`] = `${pre}${v}`;
|
productionMap[`${pre}${k}`] = `${pre}${v}`;
|
||||||
|
|
@ -93,6 +98,7 @@ function init(mod) {
|
||||||
req.url = req.url.substring(pre.length);
|
req.url = req.url.substring(pre.length);
|
||||||
req.app.path = req.app.path.substring(pre.length);
|
req.app.path = req.app.path.substring(pre.length);
|
||||||
req.app.ispre = true;
|
req.app.ispre = true;
|
||||||
|
// console.log('MATCH1', dir, cookie, req.url);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let cookie = cookieValue(req.headers.cookie, "version") || '';
|
let cookie = cookieValue(req.headers.cookie, "version") || '';
|
||||||
|
|
@ -101,9 +107,12 @@ function init(mod) {
|
||||||
vmatch = [ vmatch ];
|
vmatch = [ vmatch ];
|
||||||
}
|
}
|
||||||
if (vmatch.indexOf("*") >= 0) {
|
if (vmatch.indexOf("*") >= 0) {
|
||||||
|
// console.log('MATCH2', dir, cookie, req.url);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return vmatch.indexOf(cookie) >= 0;
|
let match = vmatch.indexOf(cookie) >= 0;
|
||||||
|
// console.log('MATCH3', dir, cookie, req.url);
|
||||||
|
return match;
|
||||||
});
|
});
|
||||||
|
|
||||||
mod.on.testv((version) => {
|
mod.on.testv((version) => {
|
||||||
|
|
@ -122,6 +131,7 @@ function init(mod) {
|
||||||
mod.add(serveWasm);
|
mod.add(serveWasm);
|
||||||
mod.add(serveCode);
|
mod.add(serveCode);
|
||||||
mod.add(fullpath({
|
mod.add(fullpath({
|
||||||
|
"/boot" : redir((pre??"") + "/boot/", 301),
|
||||||
"/kiri" : redir((pre??"") + "/kiri/", 301),
|
"/kiri" : redir((pre??"") + "/kiri/", 301),
|
||||||
"/mesh" : redir((pre??"") + "/mesh/", 301),
|
"/mesh" : redir((pre??"") + "/mesh/", 301),
|
||||||
"/meta" : redir((pre??"") + "/meta/", 301),
|
"/meta" : redir((pre??"") + "/meta/", 301),
|
||||||
|
|
@ -139,7 +149,8 @@ function init(mod) {
|
||||||
return "reload";
|
return "reload";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
mod.add(rewriteHtmlVersion);
|
mod.add(addAppHeaders);
|
||||||
|
mod.static("/lib/", "alt");
|
||||||
mod.static("/lib/", "src");
|
mod.static("/lib/", "src");
|
||||||
mod.static("/obj/", "web/obj");
|
mod.static("/obj/", "web/obj");
|
||||||
mod.static("/font/", "web/font");
|
mod.static("/font/", "web/font");
|
||||||
|
|
@ -147,19 +158,26 @@ function init(mod) {
|
||||||
mod.static("/mesh/", "web/mesh");
|
mod.static("/mesh/", "web/mesh");
|
||||||
mod.static("/moto/", "web/moto");
|
mod.static("/moto/", "web/moto");
|
||||||
mod.static("/kiri/", "web/kiri");
|
mod.static("/kiri/", "web/kiri");
|
||||||
|
mod.static("/boot/", "web/boot");
|
||||||
|
|
||||||
// module loader
|
// module loader
|
||||||
function load_modules(root, force) {
|
function load_modules(root, force) {
|
||||||
lastmod(`${dir}/${root}`) && fs.readdirSync(`${dir}/${root}`).forEach(mdir => {
|
lastmod(`${dir}/${root}`) && fs.readdirSync(`${dir}/${root}`).forEach(mdir => {
|
||||||
const modpath = `${root}/${mdir}`;
|
const modpath = `${root}/${mdir}`;
|
||||||
if (dir.charAt(0) === '.' && !ENV.single) return;
|
if (dir.charAt(0) === '.' && !single) return;
|
||||||
const stats = fs.lstatSync(`${mod.dir}/${modpath}`);
|
const stats = fs.lstatSync(`${mod.dir}/${modpath}`);
|
||||||
if (!(stats.isDirectory() || stats.isSymbolicLink())) return;
|
if (!(stats.isDirectory() || stats.isSymbolicLink())) return;
|
||||||
if (util.isfile(PATH.join(mod.dir,modpath,".disable"))) return;
|
if (util.isfile(PATH.join(mod.dir,modpath,".disable"))) return;
|
||||||
const isDebugMod = util.isfile(PATH.join(mod.dir,modpath,".debug"));
|
const isDebugMod = util.isfile(PATH.join(mod.dir,modpath,".debug"));
|
||||||
const isElectronMod = util.isfile(PATH.join(mod.dir,modpath,".electron"));
|
const isElectronMod = util.isfile(PATH.join(mod.dir,modpath,".electron"));
|
||||||
if (force || (ENV.electron && !isElectronMod)) return;
|
if (inElectron && !isElectronMod) {
|
||||||
if (force || (!ENV.electron && isElectronMod && !isDebugMod)) return;
|
logger.log({ skip_non_electron_mod: modpath });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!inElectron && isElectronMod && !isDebugMod) {
|
||||||
|
logger.log({ skip_electron_mod: modpath });
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
loadModule(mod, modpath);
|
loadModule(mod, modpath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -189,6 +207,33 @@ function init(mod) {
|
||||||
append[key] = minify(append[key]);
|
append[key] = minify(append[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create alt artifacts with module extensions
|
||||||
|
if (dryrun || !isElectron) {
|
||||||
|
logger.log('creating artifacts', Object.keys(append));
|
||||||
|
for (let [ key, val ] of Object.entries(append)) {
|
||||||
|
// append mains
|
||||||
|
let src = `${dir}/src/main/${key}.js`;
|
||||||
|
if (!fs.existsSync(src)) {
|
||||||
|
logger.log('missing', src);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fs.mkdirSync(`${dir}/alt/main`, { recursive: true });
|
||||||
|
let body = fs.readFileSync(src);
|
||||||
|
fs.writeFileSync(`${dir}/alt/main/${key}.js`, body + val);
|
||||||
|
// append packed mains
|
||||||
|
src= `${dir}/src/pack/${key}-main.js`;
|
||||||
|
if (!fs.existsSync(src)) {
|
||||||
|
logger.log('missing', src);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fs.mkdirSync(`${dir}/alt/pack`, { recursive: true });
|
||||||
|
body = fs.readFileSync(src);
|
||||||
|
fs.writeFileSync(`${dir}/alt/pack/${key}-main.js`, body + val);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.log('skipping artifacts');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// either add module assets to path or require(init.js)
|
// either add module assets to path or require(init.js)
|
||||||
|
|
@ -253,11 +298,9 @@ function initModule(mod, file, dir) {
|
||||||
const [ path, file ] = [ ...arguments ];
|
const [ path, file ] = [ ...arguments ];
|
||||||
if (lastmod(file)) {
|
if (lastmod(file)) {
|
||||||
code[path] = fs.readFileSync(file);
|
code[path] = fs.readFileSync(file);
|
||||||
// console.log({ CODE: path, file });
|
|
||||||
} else if (lastmod(mod.dir + '/' + file)) {
|
} else if (lastmod(mod.dir + '/' + file)) {
|
||||||
const alt = mod.dir + '/' + file;
|
const alt = mod.dir + '/' + file;
|
||||||
code[path] = fs.readFileSync(alt);
|
code[path] = fs.readFileSync(alt);
|
||||||
// console.log({ CODE: path, alt });
|
|
||||||
} else {
|
} else {
|
||||||
console.log({ MISSING_CODE: path, file });
|
console.log({ MISSING_CODE: path, file });
|
||||||
}
|
}
|
||||||
|
|
@ -286,14 +329,14 @@ function initModule(mod, file, dir) {
|
||||||
const path = mod.dir + '/' + dir + '/' + file;
|
const path = mod.dir + '/' + dir + '/' + file;
|
||||||
try {
|
try {
|
||||||
const body = fs.readFileSync(path);
|
const body = fs.readFileSync(path);
|
||||||
// console.log({ inject: code, file, opt });
|
if (debug && !single) logger.log({ inject: code, file, opt });
|
||||||
if (opt.first) {
|
if (opt.first) {
|
||||||
append[code] = body.toString() + '\n' + append[code];
|
append[code] = body.toString() + '\n' + append[code];
|
||||||
} else {
|
} else {
|
||||||
append[code] += body.toString() + '\n';
|
append[code] += body.toString() + '\n';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log({ missing_file: path, dir, mod });
|
logger.log({ missing_file: path, dir, mod });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onload(fn) {
|
onload(fn) {
|
||||||
|
|
@ -412,24 +455,6 @@ function serveWasm(req, res, next) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pack/concat device script strings to inject into /code/ scripts
|
|
||||||
function generateDevices() {
|
|
||||||
let root = PATH.join(dir,"src","kiri","dev");
|
|
||||||
let devs = {};
|
|
||||||
fs.readdirSync(root).forEach(type => {
|
|
||||||
let map = devs[type] = devs[type] || {};
|
|
||||||
fs.readdirSync(PATH.join(root,type)).forEach(device => {
|
|
||||||
let deviceName = device.endsWith('.json')
|
|
||||||
? device.substring(0,device.length-5)
|
|
||||||
: device;
|
|
||||||
map[deviceName] = JSON.parse(fs.readFileSync(PATH.join(root,type,device)));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
let dstr = JSON.stringify(devs);
|
|
||||||
util.mkdir(PATH.join(dir,"src","pack"));
|
|
||||||
fs.writeFileSync(PATH.join(dir,"src","pack","kiri-devs.js"), `export const devices = ${dstr};`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function minify(code) {
|
function minify(code) {
|
||||||
let mini = uglify.minify(code.toString(), {
|
let mini = uglify.minify(code.toString(), {
|
||||||
compress: {
|
compress: {
|
||||||
|
|
@ -524,7 +549,13 @@ function fixedmap(prefix, map) {
|
||||||
// HTTP 307 redirect
|
// HTTP 307 redirect
|
||||||
function redir(path, type) {
|
function redir(path, type) {
|
||||||
return (req, res, next) => {
|
return (req, res, next) => {
|
||||||
http.redirect(res, path, type);
|
let query = req.url.split('?')[1];
|
||||||
|
let rpath = path;
|
||||||
|
if (query && path.indexOf('?') < 0) {
|
||||||
|
rpath += `?${query}`;
|
||||||
|
}
|
||||||
|
// console.log({ redir: req.url, to: path, query, new_path: rpath });
|
||||||
|
http.redirect(res, rpath, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -547,10 +578,12 @@ function cookieValue(cookie,key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function rewriteHtmlVersion(req, res, next) {
|
function addAppHeaders(req, res, next) {
|
||||||
if ([
|
if ([
|
||||||
"/kiri/",
|
"/kiri/",
|
||||||
"/mesh/",
|
"/mesh/",
|
||||||
|
"/lib/gpu/raster.js",
|
||||||
|
"/lib/gpu/raster-worker.js",
|
||||||
"/lib/mesh/work.js",
|
"/lib/mesh/work.js",
|
||||||
"/lib/kiri/run/worker.js",
|
"/lib/kiri/run/worker.js",
|
||||||
"/lib/kiri/run/minion.js",
|
"/lib/kiri/run/minion.js",
|
||||||
|
|
@ -559,36 +592,6 @@ function rewriteHtmlVersion(req, res, next) {
|
||||||
].indexOf(req.app.path) >= 0) {
|
].indexOf(req.app.path) >= 0) {
|
||||||
addCorsHeaders(req, res);
|
addCorsHeaders(req, res);
|
||||||
}
|
}
|
||||||
if ([
|
|
||||||
"/lib/main/kiri.js",
|
|
||||||
"/lib/main/mesh.js"
|
|
||||||
].indexOf(req.app.path) >= 0) {
|
|
||||||
addCorsHeaders(req, res);
|
|
||||||
// const data = append[req.app.path.split('/')[3].split('.')[0]];
|
|
||||||
const data = append[req.app.path.split('.')[0].split('/').pop()];
|
|
||||||
|
|
||||||
if (!data) return next();
|
|
||||||
if (debug) logger.log({ append: req.app.path, data: data.length });
|
|
||||||
|
|
||||||
const real_write = res.write;
|
|
||||||
const real_end = res.end;
|
|
||||||
let body = '';
|
|
||||||
|
|
||||||
res.write = function (chunk, encoding) {
|
|
||||||
body += chunk.toString(encoding);
|
|
||||||
};
|
|
||||||
|
|
||||||
res.end = function (chunk, encoding) {
|
|
||||||
if (chunk) {
|
|
||||||
body += chunk.toString(encoding);
|
|
||||||
}
|
|
||||||
body += data;
|
|
||||||
res.setHeader('Content-Length', Buffer.byteLength(body));
|
|
||||||
real_write.call(res, body);
|
|
||||||
real_end.call(res);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
tag=${1:-latest}
|
tag=${1:-latest}
|
||||||
npm run clear-cache
|
npm run clean
|
||||||
./bin/build-upload win ${tag} && \
|
./bin/build-upload win ${tag} && \
|
||||||
./bin/build-upload linux ${tag} && \
|
./bin/build-upload linux ${tag} && \
|
||||||
./bin/build-upload mac ${tag}
|
./bin/build-upload mac ${tag}
|
||||||
|
|
|
||||||
23
bin/bundle-prod.config.json
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"compress": true,
|
||||||
|
"outputs": {
|
||||||
|
"bundle": "./web/boot/bundle-{version}.bin"
|
||||||
|
},
|
||||||
|
"inputs": [
|
||||||
|
{ "root": "web/moto", "prefix": "moto" },
|
||||||
|
{ "root": "web/kiri", "prefix": "kiri" },
|
||||||
|
{ "root": "web/font", "prefix": "font" },
|
||||||
|
{ "root": "src/wasm", "prefix": "wasm" },
|
||||||
|
{ "root": "src/wasm", "prefix": "lib/kiri/wasm" },
|
||||||
|
{ "src": "alt/pack/kiri-main.js", "dst": "lib/main/kiri.js" },
|
||||||
|
{ "src": "src/pack/kiri-main.js", "dst": "lib/main/kiri.js" },
|
||||||
|
{ "src": "src/pack/kiri-work.js", "dst": "lib/kiri/run/worker.js" },
|
||||||
|
{ "src": "src/pack/kiri-pool.js", "dst": "lib/kiri/run/minion.js" },
|
||||||
|
{ "src": "src/gpu/raster-worker.js", "dst": "lib/gpu/raster-worker.js" },
|
||||||
|
{ "src": "src/ext/tween.js", "dst": "lib/ext/tween.js" }
|
||||||
|
],
|
||||||
|
"excludes": [
|
||||||
|
"src/pack",
|
||||||
|
"web/boot/bundle*"
|
||||||
|
]
|
||||||
|
}
|
||||||
17
bin/bundle.config.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"compress": false,
|
||||||
|
"outputs": {
|
||||||
|
"bundle": "./web/boot/bundle-{version}.bin"
|
||||||
|
},
|
||||||
|
"inputs": [
|
||||||
|
{ "root": "web", "prefix": "" },
|
||||||
|
{ "root": "alt", "prefix": "lib"},
|
||||||
|
{ "root": "src", "prefix": "lib"},
|
||||||
|
{ "root": "src/wasm", "prefix": "wasm" },
|
||||||
|
{ "root": "src/wasm", "prefix": "lib/kiri/wasm" }
|
||||||
|
],
|
||||||
|
"excludes": [
|
||||||
|
"src/pack",
|
||||||
|
"web/boot/bundle*"
|
||||||
|
]
|
||||||
|
}
|
||||||
166
bin/bundler.mjs
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import uglify from 'uglify-js';
|
||||||
|
import { Buffer } from 'buffer';
|
||||||
|
|
||||||
|
const version = JSON.parse(fs.readFileSync('package.json', 'utf8')).version;
|
||||||
|
const cfg = JSON.parse(fs.readFileSync(process.argv[2] || './bin/bundle.config.json', 'utf8'));
|
||||||
|
const { inputs, outputs, excludes, compress } = cfg;
|
||||||
|
const excludePre = (excludes ?? []).filter(s => s.indexOf('*') >= 0).map(s => s.replaceAll('*',''));
|
||||||
|
const envcomp = process.env.COMPRESS ? JSON.parse(process.env.COMPRESS) : undefined;
|
||||||
|
const debug = process.env.DEBUG;
|
||||||
|
|
||||||
|
// --- recursive directory walker (follows symlinks safely) ---
|
||||||
|
function* walk(dir, seen = new Set()) {
|
||||||
|
dir = path.normalize(dir);
|
||||||
|
if (excludes.indexOf(dir) >= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const real = fs.realpathSync(dir);
|
||||||
|
if (seen.has(real)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(real);
|
||||||
|
|
||||||
|
for (const name of fs.readdirSync(dir)) {
|
||||||
|
const p = path.join(dir, name);
|
||||||
|
let st;
|
||||||
|
try { st = fs.statSync(p); } catch { continue; }
|
||||||
|
if (st.isDirectory()) yield* walk(p, seen);
|
||||||
|
else yield p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- collect all files according to config ---
|
||||||
|
let entries = [];
|
||||||
|
let virtMap = new Set();
|
||||||
|
for (const input of inputs) {
|
||||||
|
const { root, prefix, src, dst } = input;
|
||||||
|
|
||||||
|
if (src && dst) {
|
||||||
|
if (!fs.existsSync(src)) {
|
||||||
|
console.log({ src_missing: src });
|
||||||
|
} else if (virtMap.has(dst)) {
|
||||||
|
console.log('pre-existing', dst);
|
||||||
|
} else {
|
||||||
|
entries.push({ file: src, virt: dst });
|
||||||
|
virtMap.add(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root)
|
||||||
|
for (const file of walk(root)) {
|
||||||
|
const rel = path.relative(root, file).replace(/\\/g, '/');
|
||||||
|
const virt = prefix ? `${prefix}/${rel}` : rel;
|
||||||
|
if (virtMap.has(virt)) {
|
||||||
|
console.log('pre-existing', virt, rel, root);
|
||||||
|
} else {
|
||||||
|
entries.push({ file, virt });
|
||||||
|
virtMap.add(virt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- build header and data ---
|
||||||
|
const dataParts = [];
|
||||||
|
const entryMeta = [];
|
||||||
|
let offset = 0;
|
||||||
|
let cache = new Map();
|
||||||
|
let compEnable = (envcomp ?? compress);
|
||||||
|
let cacheDir = cfg.cacheDir ?? ".bcache";
|
||||||
|
|
||||||
|
if (compEnable) {
|
||||||
|
fs.mkdirSync(cacheDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileTime(path) {
|
||||||
|
try {
|
||||||
|
return (fs.statSync(path).ctimeMs / 1000) | 0;
|
||||||
|
} catch (e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { file, virt } of entries) {
|
||||||
|
if (excludes.indexOf(file) >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (excludePre.filter(ex => file.startsWith(ex)).length) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let record = cache.get(file);
|
||||||
|
if (record) {
|
||||||
|
if (debug) console.log({ alias: virt, from: file });
|
||||||
|
entryMeta.push({ ...record, virt });
|
||||||
|
} else {
|
||||||
|
if (debug) console.log({ write: virt, from: file });
|
||||||
|
let data = fs.readFileSync(file);
|
||||||
|
if (compEnable && file.endsWith("js")) {
|
||||||
|
let cpath = path.resolve(cacheDir, file);
|
||||||
|
if (fileTime(cpath) > fileTime(file)) {
|
||||||
|
data = fs.readFileSync(cpath);
|
||||||
|
// console.log('cached', file);
|
||||||
|
} else {
|
||||||
|
console.log('compress', file);
|
||||||
|
data = uglify.minify(data.toString(), {
|
||||||
|
compress: {
|
||||||
|
merge_vars: false,
|
||||||
|
unused: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// console.log({ to: typeof(data), data });
|
||||||
|
if (!data.code) {
|
||||||
|
if (debug) console.log({ skip: file });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
data = Buffer.from(data.code);
|
||||||
|
fs.mkdirSync(path.dirname(cpath), { recursive: true });
|
||||||
|
fs.writeFileSync(cpath, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
record = { virt, offset, length: data.length };
|
||||||
|
cache.set(file, record);
|
||||||
|
entryMeta.push(record);
|
||||||
|
dataParts.push(data);
|
||||||
|
offset += data.length;
|
||||||
|
if (debug) console.log('write', virt, data.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// temporary header without offsets to measure total size
|
||||||
|
let tempHeaderParts = [];
|
||||||
|
for (const { virt, offset, length } of entryMeta) {
|
||||||
|
const name = Buffer.from(virt, 'utf8');
|
||||||
|
const nlen = Buffer.alloc(2); nlen.writeUInt16LE(name.length);
|
||||||
|
const offb = Buffer.alloc(4); offb.writeUInt32LE(0); // placeholder
|
||||||
|
const lenb = Buffer.alloc(4); lenb.writeUInt32LE(length);
|
||||||
|
tempHeaderParts.push(nlen, name, offb, lenb);
|
||||||
|
}
|
||||||
|
const count = Buffer.alloc(4); count.writeUInt32LE(entries.length);
|
||||||
|
const tempHeader = Buffer.concat([count, ...tempHeaderParts]);
|
||||||
|
const headerSize = tempHeader.length; // true data offset base
|
||||||
|
|
||||||
|
// now rebuild header with correct absolute offsets
|
||||||
|
let headerParts = [];
|
||||||
|
for (const { virt, offset, length } of entryMeta) {
|
||||||
|
const name = Buffer.from(virt, 'utf8');
|
||||||
|
const nlen = Buffer.alloc(2); nlen.writeUInt16LE(name.length);
|
||||||
|
const offb = Buffer.alloc(4); offb.writeUInt32LE(headerSize + offset);
|
||||||
|
const lenb = Buffer.alloc(4); lenb.writeUInt32LE(length);
|
||||||
|
headerParts.push(nlen, name, offb, lenb);
|
||||||
|
// console.log(virt);
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = Buffer.concat([count, ...headerParts]);
|
||||||
|
const full = Buffer.concat([header, ...dataParts]);
|
||||||
|
|
||||||
|
// --- write final bundle ---
|
||||||
|
const outPath = (outputs.bundle || './bundle.bin').replace('{version}', version);
|
||||||
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
||||||
|
fs.writeFileSync(outPath, full); // ⚡ write raw binary
|
||||||
|
|
||||||
|
console.log(`✅ Bundled ${entries.length} files → ${outPath} (${full.length} bytes)`);
|
||||||
|
|
@ -5,6 +5,7 @@ exports.default = async function notarizing(context) {
|
||||||
const { electronPlatformName, appOutDir } = context;
|
const { electronPlatformName, appOutDir } = context;
|
||||||
|
|
||||||
if (process.env.SKIPNOT || electronPlatformName !== 'darwin') {
|
if (process.env.SKIPNOT || electronPlatformName !== 'darwin') {
|
||||||
|
console.log(' ** skip notarizing:', electronPlatformName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,30 @@ const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const server = require('@gridspace/app-server');
|
const server = require('@gridspace/app-server');
|
||||||
|
|
||||||
|
const altTmp = path.join('tmp','alt');
|
||||||
|
fs.copySync("alt", altTmp, { dereference: true });
|
||||||
|
|
||||||
// deref src, web, mod for windows
|
// deref src, web, mod for windows
|
||||||
const srcTmp = path.join('tmp','src');
|
const srcTmp = path.join('tmp','src');
|
||||||
fs.copySync("src", srcTmp, { dereference: true });
|
fs.copySync("src", srcTmp, { dereference: true, filter:(src) => {
|
||||||
|
return true || src.indexOf('src/main') < 0;
|
||||||
|
} });
|
||||||
|
|
||||||
const webTmp = path.join('tmp','web');
|
const webTmp = path.join('tmp','web');
|
||||||
fs.copySync("web", webTmp, { dereference: true });
|
fs.copySync("web", webTmp, { dereference: true, filter:(src) => {
|
||||||
|
return src.indexOf('web/boot') < 0;
|
||||||
|
} });
|
||||||
|
|
||||||
const modTmp = path.join('tmp','mod');
|
const modTmp = path.join('tmp','mod');
|
||||||
if (fs.existsSync("mod"))
|
if (fs.existsSync("mod"))
|
||||||
fs.copySync("mod", modTmp, { dereference: true, filter:(src,dst) => {
|
fs.copySync("mod", modTmp, { dereference: true, filter:(src,dst) => {
|
||||||
const ok =
|
const ok = src === 'mod' || src.indexOf('mod/standalone') === 0;
|
||||||
src === 'mod' ||
|
|
||||||
src.indexOf('mod/standalone') === 0 ||
|
|
||||||
src.indexOf('mod/node_modules') === 0;
|
|
||||||
return ok;
|
return ok;
|
||||||
} });
|
} });
|
||||||
|
|
||||||
const modsTmp = path.join('tmp','mods');
|
const modsTmp = path.join('tmp','mods');
|
||||||
fs.copySync("mods", modsTmp, { dereference: true, filter:(src,dst) => {
|
fs.copySync("mods", modsTmp, { dereference: true, filter:(src,dst) => {
|
||||||
const ok =
|
const ok = src === 'mods' || src.indexOf('mods/bambu') === 0;
|
||||||
src === 'mods' ||
|
|
||||||
src.indexOf('mods/bambu') === 0 ||
|
|
||||||
src.indexOf('mods/electron') === 0 ||
|
|
||||||
src.indexOf('mods/node_modules') === 0;
|
|
||||||
return ok;
|
return ok;
|
||||||
} });
|
} });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { build, transform } from 'esbuild';
|
import { build, transform } from 'esbuild';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
|
import path from 'path';
|
||||||
import glob from 'fast-glob';
|
import glob from 'fast-glob';
|
||||||
|
|
||||||
// Get build mode from command line argument
|
// Get build mode from command line argument
|
||||||
|
|
@ -38,6 +39,30 @@ async function appendExtraModules(extras, outfile, minify = false) {
|
||||||
console.log(`Appended ${files.length} module(s) to ${outfile}`);
|
console.log(`Appended ${files.length} module(s) to ${outfile}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function generateDevices(dir = '') {
|
||||||
|
let root = path.join(dir, "src", "kiri", "dev");
|
||||||
|
let devs = {};
|
||||||
|
let files = await fs.readdir(root);
|
||||||
|
for (let type of files) {
|
||||||
|
let map = devs[type] = devs[type] || {};
|
||||||
|
let devices = await fs.readdir(path.join(root, type));
|
||||||
|
for (let device of devices) {
|
||||||
|
let deviceName = device.endsWith('.json')
|
||||||
|
? device.substring(0, device.length - 5)
|
||||||
|
: device;
|
||||||
|
map[deviceName] = JSON.parse(await fs.readFile(path.join(root, type, device)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let dstr = JSON.stringify(devs);
|
||||||
|
fs.mkdir(path.join(dir, "src", "pack"), { recursive: true });
|
||||||
|
await fs.writeFile(path.join(dir, "src", "pack", "kiri-devs.js"), `export const devices = ${dstr};`);
|
||||||
|
if (true) {
|
||||||
|
// create alt artifacts
|
||||||
|
await fs.mkdir('alt/pack', { recursive: true });
|
||||||
|
await fs.writeFile(path.join(dir, "alt", "pack", "kiri-devs.js"), `export const devices = ${dstr};`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const logOverride = {
|
const logOverride = {
|
||||||
'duplicate-class-member': 'silent',
|
'duplicate-class-member': 'silent',
|
||||||
'duplicate-object-key': 'silent',
|
'duplicate-object-key': 'silent',
|
||||||
|
|
@ -66,12 +91,15 @@ const rec = {
|
||||||
logOverride,
|
logOverride,
|
||||||
minify: isProd, // false for dev, true for prod
|
minify: isProd, // false for dev, true for prod
|
||||||
platform: 'browser',
|
platform: 'browser',
|
||||||
sourcemap: !isProd, // true for dev, false for prod
|
sourcemap: false,
|
||||||
target: 'es2020',
|
target: 'es2020',
|
||||||
};
|
};
|
||||||
|
|
||||||
async function buildApp() {
|
async function buildApp() {
|
||||||
try {
|
try {
|
||||||
|
// Concatenate kiri devices
|
||||||
|
generateDevices();
|
||||||
|
|
||||||
// Bundle mesh main app
|
// Bundle mesh main app
|
||||||
await build(Object.assign({}, rec, {
|
await build(Object.assign({}, rec, {
|
||||||
entryPoints: [ 'src/main/mesh.js' ],
|
entryPoints: [ 'src/main/mesh.js' ],
|
||||||
|
|
|
||||||
23
bin/update-version.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Usage: $0 <version>"
|
||||||
|
echo "Example: $0 4.5.0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION=$1
|
||||||
|
|
||||||
|
# Update package.json (line 3, no indentation)
|
||||||
|
awk -v ver="$VERSION" '/"version":/ {print " \"version\": \"" ver "\","; next} 1' package.json > package.json.tmp && mv package.json.tmp package.json
|
||||||
|
|
||||||
|
# Update src/moto/license.js (line 6, VERSION field)
|
||||||
|
awk -v ver="$VERSION" '/VERSION:/ {print " VERSION: \"" ver "\""; next} 1' src/moto/license.js > src/moto/license.js.tmp && mv src/moto/license.js.tmp src/moto/license.js
|
||||||
|
|
||||||
|
# Update web/kiri/manifest.json (line 2, preserves "Kiri:Moto" prefix)
|
||||||
|
awk -v ver="$VERSION" '/"name":/ {print " \"name\": \"Kiri:Moto " ver "\","; next} 1' web/kiri/manifest.json > web/kiri/manifest.json.tmp && mv web/kiri/manifest.json.tmp web/kiri/manifest.json
|
||||||
|
|
||||||
|
echo "Updated version to $VERSION in:"
|
||||||
|
echo " - package.json"
|
||||||
|
echo " - src/moto/license.js"
|
||||||
|
echo " - web/kiri/manifest.json"
|
||||||
|
|
@ -7,7 +7,28 @@ module.exports = {
|
||||||
favicon: 'img/gs-logo.png',
|
favicon: 'img/gs-logo.png',
|
||||||
staticDirectories: ['./docs/static/'],
|
staticDirectories: ['./docs/static/'],
|
||||||
// ...
|
// ...
|
||||||
plugins: [require.resolve('docusaurus-lunr-search')],
|
plugins: [
|
||||||
|
require.resolve('docusaurus-lunr-search'),
|
||||||
|
['@docusaurus/plugin-client-redirects',
|
||||||
|
{
|
||||||
|
redirects: [
|
||||||
|
// /docs/oldDoc -> /docs/newDoc
|
||||||
|
{
|
||||||
|
to: '/kiri-moto',
|
||||||
|
from: '/projects/kiri-moto',
|
||||||
|
},
|
||||||
|
// Redirect from multiple old paths to the new path
|
||||||
|
{
|
||||||
|
to: '/gridbot',
|
||||||
|
from: '/projects/gridbot',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
to: '/mesh-tool',
|
||||||
|
from: '/projects/mesh-tool',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
],
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
'@docusaurus/preset-classic',
|
'@docusaurus/preset-classic',
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@ const config = {
|
||||||
useTabs: false,
|
useTabs: false,
|
||||||
bracketSpacing: true,
|
bracketSpacing: true,
|
||||||
objectWrap: "preserve",
|
objectWrap: "preserve",
|
||||||
tabWidth: 4,
|
tabWidth: 2,
|
||||||
semi: false,
|
semi: true,
|
||||||
singleQuote: true,
|
singleQuote: false,
|
||||||
bracketSameLine: true,
|
bracketSameLine: true,
|
||||||
arrowParens: "always",
|
arrowParens: "always",
|
||||||
endOfLine: "crlf"
|
endOfLine: "lf"
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = config
|
module.exports = config
|
||||||
|
|
@ -7,16 +7,10 @@ This is a community driven project, and we welcome any contributions you'd like
|
||||||
## Running Locally
|
## Running Locally
|
||||||
|
|
||||||
- Ensure you have [node](https://nodejs.org/en/download/), or an equivalent installed
|
- Ensure you have [node](https://nodejs.org/en/download/), or an equivalent installed
|
||||||
- Clone the [repository](https://github.com/GridSpace/grid-apps)
|
- Clone the [repository](https://github.com/GridSpace/grid-apps) from https://github.com/GridSpace/grid-apps
|
||||||
- Run `npm run setup`
|
- `npm run setup`
|
||||||
- Run `npm run dev`
|
- `npm run dev`
|
||||||
|
- Open browser [http://localhost:8080/kiri](http://localhost:8080/kiri)
|
||||||
## Running Docs Locally
|
|
||||||
|
|
||||||
- Ensure you have [node](https://nodejs.org/en/download/), or an equivalent installed
|
|
||||||
- Clone the [repository](https://github.com/GridSpace/grid-apps)
|
|
||||||
- Run `npm run setup`
|
|
||||||
- Run `npm run docs-dev`
|
|
||||||
|
|
||||||
|
|
||||||
## How to add a new machine
|
## How to add a new machine
|
||||||
|
|
|
||||||
29
docs/kiri-moto/CAM/interface.mdx
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
description: Overview of CAM mode
|
||||||
|
---
|
||||||
|
|
||||||
|
import { ImageCarousel } from "@site/docs/src/components/carousel.js";
|
||||||
|
|
||||||
|
# Interface
|
||||||
|
|
||||||
|
The CAM interface is, in most respects, the same as the [general interface](/kiri-moto/interface). It is highly recommended to familiarize yourself with the general interface before using the CAM interface.
|
||||||
|
|
||||||
|
With that in mind, here are the key differences:
|
||||||
|
|
||||||
|
## Operation List
|
||||||
|
|
||||||
|
The Operation List defines the order in which operations are
|
||||||
|
performed on the model. Selecting the **plus** button will open a
|
||||||
|
popup menu for choosing an operation to add. The operations are
|
||||||
|
draggable, enabling easy re-ordering.
|
||||||
|
|
||||||
|
- An operation can be disabled by **Ctrl+clicking** it. Disabled operations will be skipped in the slice/preview/export process.
|
||||||
|
- The thin blue bar in the Operation List is also draggable. Moving the bar automatically disables all operations below it. This allows you to slice, preview, and export up to a specific point in the timeline.
|
||||||
|
|
||||||
|
<ImageCarousel base="/img/CAM/interface/" images="camInterface" sml />
|
||||||
|
|
||||||
|
## Origin Selection
|
||||||
|
|
||||||
|
Origin Selection (introduced in v4.3) enables automatically setting an origin to points of interest on the model, including corners and arc centers.
|
||||||
|
|
||||||
|

|
||||||
|
|
@ -39,7 +39,6 @@ Customize how your G-code is formatted for your controller or post-processor:
|
||||||
- **Enable Laser** — Enables [Laser](./ops#laser-operations) operations (typically uses `M3/M5`; adjusts movement strategy)
|
- **Enable Laser** — Enables [Laser](./ops#laser-operations) operations (typically uses `M3/M5`; adjusts movement strategy)
|
||||||
- **File Extension** — Optional override for exported file type (e.g., `.nc`, `.ngc`, `.cnc`, `.cam`, `.gcode`, `.txt`)
|
- **File Extension** — Optional override for exported file type (e.g., `.nc`, `.ngc`, `.cnc`, `.cam`, `.gcode`, `.txt`)
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## G-code Macros
|
## G-code Macros
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ description: Variable Substitutions and Expressions in GCode Macros
|
||||||
- \{pos_y\} = last output Y position
|
- \{pos_y\} = last output Y position
|
||||||
- \{pos_z\} = last output Z position
|
- \{pos_z\} = last output Z position
|
||||||
- \{progress\} = 0-100% state of print output
|
- \{progress\} = 0-100% state of print output
|
||||||
|
- \{remain_time\} = print time remaining in minutes
|
||||||
- \{tool\} | \{nozzle\} = current tool in use
|
- \{tool\} | \{nozzle\} = current tool in use
|
||||||
- \{tool_count\} = number of tools used in print
|
- \{tool_count\} = number of tools used in print
|
||||||
- \{tool_used\__n_\} = whether tool # _n_ is used in print
|
- \{tool_used\__n_\} = whether tool # _n_ is used in print
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
---
|
|
||||||
description: A tour of Kiri:Moto's web-based interface
|
|
||||||
---
|
|
||||||
|
|
||||||
# Interface
|
|
||||||
|
|
||||||
Kiri:Moto's interface is designed for maximum access to the workspace and to present only the options that are necessary and relevant to the current action.
|
|
||||||
|
|
||||||
The left menus control workspace, work mode, and objects while.
|
|
||||||
|
|
||||||
The right menus control the behavior and settings for slicing, path routing, and path to code exports.
|
|
||||||
|
|
||||||
[OBJ](https://www.cs.cmu.edu/~mbz/personal/graphics/obj.html), [STL](<https://en.wikipedia.org/wiki/STL_(file_format)>), and [3MF](https://docs.fileformat.com/3d/3mf/) files import natively. [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics), [JPG](https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format), and [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics) are auto-extruded into a 3D parts.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
In CNC mode, there is an additional storyboard menu at the bottom where you can plan and re-order milling operations.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
The Setup menu offers access to Device selection and customization. 
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Application preferences are mode-dependent. For example, CNC preferences show animation controls.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
54
docs/kiri-moto/interface.mdx
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
---
|
||||||
|
description: A tour of Kiri:Moto's web-based interface
|
||||||
|
---
|
||||||
|
|
||||||
|
import { ImageCarousel } from "@site/docs/src/components/carousel.js";
|
||||||
|
|
||||||
|
# Interface
|
||||||
|
|
||||||
|
Kiri:Moto's interface is designed for maximum access to the workspace, showing only the options necessary and relevant to the current action.
|
||||||
|
|
||||||
|
[OBJ](https://www.cs.cmu.edu/~mbz/personal/graphics/obj.html), [STL](<https://en.wikipedia.org/wiki/STL_(file_format)>), and [3MF](https://docs.fileformat.com/3d/3mf/) files import natively. [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics), [JPG](https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format), and [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics) files are auto-extruded into 3D parts.
|
||||||
|
|
||||||
|
For the CAM interface, click [here](/kiri-moto/CAM).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Top Menu
|
||||||
|
|
||||||
|
- **Left:** Buttons to move through the `Arrange → Slice → Preview → Export` workflow. These steps are also available as [keyboard shortcuts](/kiri-moto/controls). Selecting an option here will automatically run prior steps as needed.
|
||||||
|
- **Center:** Clicking the [logo](/kiri-moto) opens a dialog with version selection, helpful links, and your current Kiri:Moto version. If you are unsure which version to use, check the [release notes](https://github.com/GridSpace/grid-apps/releases/) on GitHub.
|
||||||
|
- **Right:** Options for importing/exporting the workspace, manipulating the view, changing modes, and setting preferences. Kiri:Moto currently supports CAM, FDM, SLA, and LASER modes. Some other modes are extensions of the LASER workflow, but do not have first-class support. The [preferences menu](#preferences) will be covered later in this section.
|
||||||
|
- Changing the mode updates the left menu and enables/disables mode-specific options such as the `CAM Operation` menu and the `CAM Animate` step.
|
||||||
|
- Learn about K:M file types [here](/kiri-moto/faq#what-are-the-kmz-and-km-file-formats-short-for).
|
||||||
|
|
||||||
|
## Left Menu
|
||||||
|
|
||||||
|
The left menu contains options for the current mode. These vary widely:
|
||||||
|
|
||||||
|
- CAM has ample documentation on [Process Options](/kiri-moto/CAM/processOpts).
|
||||||
|
- Other modes are less documented but many options are self-explanatory if you're familiar with the manufacturing process.
|
||||||
|
- Hovering over an option provides tooltips. When in doubt, a quick search usually explains the option in detail.
|
||||||
|
|
||||||
|
## Right Menu
|
||||||
|
|
||||||
|
The right menu contains controls for objects in the workspace.
|
||||||
|
|
||||||
|
- Hovering over the arrow next to an object reveals options for deleting, renaming, disabling, or replacing it.
|
||||||
|
- Disabled objects remain visible but are ignored during slicing and exporting.
|
||||||
|
|
||||||
|
## Machines Dialog
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The device dialog is accessed through the settings dropdown. It allows you to configure a device or select a predefined one.
|
||||||
|
|
||||||
|
- The dropdown only shows devices available for the current mode.
|
||||||
|
- Stock device settings are greyed out. To modify them, click `Customize`.
|
||||||
|
- Learn more about how K:M handles CAM devices [here](/kiri-moto/CAM/machines).
|
||||||
|
|
||||||
|
## Preferences
|
||||||
|
|
||||||
|
Application preferences are also accessed through the settings dropdown. These are mode-dependent but share many common options. For example, CNC preferences include animation controls.
|
||||||
|
|
||||||
|
<ImageCarousel base="/img/interface/" images="prefs" />
|
||||||
|
|
@ -35,6 +35,7 @@ const sidebars = {
|
||||||
id: "kiri-moto/CAM/index",
|
id: "kiri-moto/CAM/index",
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
|
"kiri-moto/CAM/interface",
|
||||||
"kiri-moto/CAM/processOpts",
|
"kiri-moto/CAM/processOpts",
|
||||||
"kiri-moto/CAM/machines",
|
"kiri-moto/CAM/machines",
|
||||||
"kiri-moto/CAM/tools",
|
"kiri-moto/CAM/tools",
|
||||||
|
|
|
||||||
|
|
@ -4,59 +4,69 @@ import "react-responsive-carousel/lib/styles/carousel.min.css";
|
||||||
|
|
||||||
const imageDescripts = {
|
const imageDescripts = {
|
||||||
outline: [
|
outline: [
|
||||||
["/1.png", "outline of a complex part"],
|
["/1.png", "Outline of a complex part"],
|
||||||
["/2.png", "outline of a non-flat part with inside holes"],
|
["/2.png", "Outline of a non-flat part with inside holes"],
|
||||||
["/3.png", "outside-only outline"],
|
["/3.png", "Outside-only outline"],
|
||||||
["/4.png", "inside-only outline"],
|
["/4.png", "Inside-only outline"],
|
||||||
],
|
],
|
||||||
level: [
|
level: [
|
||||||
["/1.png", "level of a large part"],
|
["/1.png", "Level of a large part"],
|
||||||
["/2.png", `level of a square part's stock`],
|
["/2.png", `Level of a square part's stock`],
|
||||||
["/3.png", `level of a part with a non-uniform top surface`],
|
["/3.png", `Level of a part with a non-uniform top surface`],
|
||||||
],
|
],
|
||||||
rough: [
|
rough: [
|
||||||
["/1.png", "rough cut of two parts"],
|
["/1.png", "Rough cut of two parts"],
|
||||||
["/2.png", "side-view of a rough cut"],
|
["/2.png", "Side-view of a rough cut"],
|
||||||
["/3.png", "rough cut of a part with a complex top surface"],
|
["/3.png", "Rough cut of a part with a complex top surface"],
|
||||||
["/4.gif", "animate of complex rough cut"],
|
["/4.gif", "Animate of complex rough cut"],
|
||||||
],
|
],
|
||||||
contour: [
|
contour: [
|
||||||
["/1.png", "close-up of a precise contour"],
|
["/1.png", "Close-up of a precise contour"],
|
||||||
["/2.png", "contour of multiple parts at once"],
|
["/2.png", "Contour of multiple parts at once"],
|
||||||
["/3.png", "low precision contour"],
|
["/3.png", "Low precision contour"],
|
||||||
],
|
],
|
||||||
register: [
|
register: [
|
||||||
["/1.png", "2-drill register"],
|
["/1.png", "2-drill register"],
|
||||||
["/2.png", "3-drill register"],
|
["/2.png", "3-drill register"],
|
||||||
["/3.png", "jigsaw register"],
|
["/3.png", "Jigsaw register"],
|
||||||
],
|
],
|
||||||
drill: [
|
drill: [
|
||||||
["/1.png", "drills for a robot baseplate"],
|
["/1.png", "Drills for a robot baseplate"],
|
||||||
["/2.png", "drills for a pegboard"],
|
["/2.png", "Drills for a pegboard"],
|
||||||
["/3.png", "selecting only matching-size holes"],
|
["/3.png", "Selecting only matching-size holes"],
|
||||||
],
|
],
|
||||||
trace: [
|
trace: [
|
||||||
["/1.png", "toolpath tracing a design into a part"],
|
["/1.png", "Toolpath tracing a design into a part"],
|
||||||
["/2.png", "selecting loops to trace"],
|
["/2.png", "Selecting loops to trace"],
|
||||||
["/3.png", "tracing edges for deburring"],
|
["/3.png", "Tracing edges for deburring"],
|
||||||
["/4.png", "trace to clear a pocket"],
|
["/4.png", "Trace to clear a pocket"],
|
||||||
["/5.png", "arbitrary offset outline"],
|
["/5.png", "Arbitrary offset outline"],
|
||||||
],
|
],
|
||||||
pocket: [
|
pocket: [
|
||||||
["/1.png", "selecting areas to pocket"],
|
["/1.png", "Selecting areas to pocket"],
|
||||||
["/2.png", "toolpath for pocket"],
|
["/2.png", "Toolpath for pocket"],
|
||||||
["/3.gif", "pocket animation"],
|
["/3.gif", "Pocket animation"],
|
||||||
["/4.png", "toolpath for v-bit carve"],
|
["/4.png", "Toolpath for v-bit carve"],
|
||||||
["/5.png", "many-pocketed part"],
|
["/5.png", "Many-pocketed part"],
|
||||||
],
|
],
|
||||||
gcode: [["/1.png", "the gcode editor popup"]],
|
gcode: [["/1.png", "The gcode editor popup"]],
|
||||||
laserOn: [],
|
laserOn: [],
|
||||||
laserOff: [],
|
laserOff: [],
|
||||||
index: [],
|
index: [],
|
||||||
lathe: [],
|
lathe: [],
|
||||||
tabs: [
|
tabs: [
|
||||||
["/1.png", "tabs added to part"],
|
["/1.png", "Tabs added to part"],
|
||||||
["/2.png", "sliced part with tabs"],
|
["/2.png", "Sliced part with tabs"],
|
||||||
|
],
|
||||||
|
camInterface: [
|
||||||
|
["/1.png", "Empty Ops list"],
|
||||||
|
["/2.png", "Sample Ops list"],
|
||||||
|
["/3.png", "Ops list with history bar"],
|
||||||
|
],
|
||||||
|
prefs: [
|
||||||
|
["/prefs-CAM.png", "Preferences in CAM mode"],
|
||||||
|
["/prefs-FDM.png", "Preferences in FDM mode"],
|
||||||
|
["/prefs-LASER.png", "Preferences in laser mode"],
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -66,15 +76,17 @@ const imageDescripts = {
|
||||||
* @param {keyof typeof imageDescripts} images - The key to an array of images and descriptions.
|
* @param {keyof typeof imageDescripts} images - The key to an array of images and descriptions.
|
||||||
* @returns {ReactElement} - A carousel of the given images.
|
* @returns {ReactElement} - A carousel of the given images.
|
||||||
*/
|
*/
|
||||||
export function ImageCarousel({ base, images }) {
|
export function ImageCarousel({ base, images, sml }) {
|
||||||
return (
|
return (
|
||||||
<Carousel showThumbs={false}>
|
<div className={sml ? "carouselWrapper" : ""}>
|
||||||
{imageDescripts[images].map(([image, caption], index) => (
|
<Carousel showThumbs={false}>
|
||||||
<div>
|
{imageDescripts[images].map(([image, caption], index) => (
|
||||||
<img src={base + image} alt="" />
|
<div>
|
||||||
<p className="caption">{caption}</p>
|
<img src={base + image} alt="" />
|
||||||
</div>
|
<p className="caption">{caption}</p>
|
||||||
))}
|
</div>
|
||||||
</Carousel>
|
))}
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,14 @@
|
||||||
.navbar .navbar__items {
|
.navbar .navbar__items {
|
||||||
height: 4.5rem;
|
height: 4.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.carouselWrapper {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carouselWrapper .carousel-root {
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 20rem;
|
||||||
|
}
|
||||||
|
|
|
||||||
BIN
docs/static/img/CAM/interface/1.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
docs/static/img/CAM/interface/2.png
vendored
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
docs/static/img/CAM/interface/3.png
vendored
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/static/img/CAM/interface/originSelect.png
vendored
Normal file
|
After Width: | Height: | Size: 221 KiB |
BIN
docs/static/img/device-dialog.png
vendored
|
Before Width: | Height: | Size: 232 KiB |
BIN
docs/static/img/interface/CAMDevice.png
vendored
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
docs/static/img/interface/main.png
vendored
Normal file
|
After Width: | Height: | Size: 508 KiB |
BIN
docs/static/img/interface/prefs-CAM.png
vendored
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/static/img/interface/prefs-FDM.png
vendored
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/static/img/interface/prefs-LASER.png
vendored
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
docs/static/img/preferences-cam.png
vendored
|
Before Width: | Height: | Size: 68 KiB |
BIN
docs/static/img/preferences-fdm.png
vendored
|
Before Width: | Height: | Size: 70 KiB |
|
|
@ -34,7 +34,7 @@ self.kiri.load(api => {
|
||||||
}).map(v => `${v.substring(1)}FF`);
|
}).map(v => `${v.substring(1)}FF`);
|
||||||
|
|
||||||
let local_port = 5309;
|
let local_port = 5309;
|
||||||
let local_url = `http://localhost:${local_port}`;
|
let local_host = `localhost:${local_port}`;
|
||||||
let sequence_id = (Math.random() * 0xfff) | 0;
|
let sequence_id = (Math.random() * 0xfff) | 0;
|
||||||
let user_id = ((Math.random() * 0xfffffff) | 0).toString();
|
let user_id = ((Math.random() * 0xfffffff) | 0).toString();
|
||||||
let init = false;
|
let init = false;
|
||||||
|
|
@ -53,7 +53,8 @@ self.kiri.load(api => {
|
||||||
if (socket.ws) {
|
if (socket.ws) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let ws_url = api.onshape ? `${local_url}/bambu` : "/bambu";
|
let ws_url = api.onshape ? `ws://${local_host}/bambu` : "/bambu";
|
||||||
|
console.log({ bambu_ws_url: ws_url });
|
||||||
let ws = socket.ws = new WebSocket(ws_url);
|
let ws = socket.ws = new WebSocket(ws_url);
|
||||||
ws.onerror = (error) => {
|
ws.onerror = (error) => {
|
||||||
socket.open = false;
|
socket.open = false;
|
||||||
|
|
@ -1288,7 +1289,7 @@ self.kiri.load(api => {
|
||||||
|
|
||||||
function send(filename, gcode, start) {
|
function send(filename, gcode, start) {
|
||||||
const spool = print_ams_select;
|
const spool = print_ams_select;
|
||||||
const baseUrl = api.onshape ? `${local_url}/api/bambu_send` : "/api/bambu_send";
|
const baseUrl = api.onshape ? `http://${local_host}/api/bambu_send` : "/api/bambu_send";
|
||||||
const url = new URL(baseUrl, window.location.origin);
|
const url = new URL(baseUrl, window.location.origin);
|
||||||
url.searchParams.append('host', host);
|
url.searchParams.append('host', host);
|
||||||
url.searchParams.append('code', password);
|
url.searchParams.append('code', password);
|
||||||
|
|
|
||||||
25
mods/devel/init.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// back end for device export in dev mode
|
||||||
|
module.exports = function(server) {
|
||||||
|
server.inject("kiri", "kiri.js");
|
||||||
|
if (!server.env.debug) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fs = require('fs');
|
||||||
|
server.api.postDevice = (req, res, next) => {
|
||||||
|
server.handler.decodePost(req, res, () => {
|
||||||
|
// console.log({decodePost: req.app.post});
|
||||||
|
const { device, process, profiles } = JSON.parse(req.app.post);
|
||||||
|
if (profiles) {
|
||||||
|
device.profiles = Object.entries(profiles).filter(v => v[0] !== 'default').map(v => v[1]);
|
||||||
|
} else {
|
||||||
|
device.profiles = [ process ];
|
||||||
|
}
|
||||||
|
const { mode } = device;
|
||||||
|
const { deviceName } = device;
|
||||||
|
const path = `${server.const.rootdir}/src/kiri-dev/${mode.toLowerCase()}/${deviceName.replace(/\ /g,'.')}`;
|
||||||
|
// console.log({ mode, device, process, path });
|
||||||
|
fs.writeFileSync(path, JSON.stringify(device, undefined, 4));
|
||||||
|
res.end('profile saved');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
23
mods/devel/kiri.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
// provides a device export to local disk option
|
||||||
|
self.kiri.load(api => {
|
||||||
|
api.event.on('load-done', () => {
|
||||||
|
let deviceExport = api.device.export;
|
||||||
|
api.device.export = (exp, name, opt = {}) => {
|
||||||
|
const { event, record } = opt;
|
||||||
|
if (event && event.shiftKey & api.const.LOCAL) {
|
||||||
|
const { code, process, profiles } = record;
|
||||||
|
fetch("/api/postDevice", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ device: code, process, profiles })
|
||||||
|
})
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(t => {
|
||||||
|
// console.log({server_said: t});
|
||||||
|
api.show.alert('profile saved to server');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
deviceExport(exp, name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
if (self.kiri && !self.kiri.electron) {
|
|
||||||
self.kiri.electron = {};
|
|
||||||
self.kiri.load(api => {
|
|
||||||
console.log('ELECTRON MODULE RUNNING');
|
|
||||||
api.event.on('load-done', () => {
|
|
||||||
$('app-name-text').innerText = "More Info";
|
|
||||||
$('top-sep').style.display = 'flex';
|
|
||||||
});
|
|
||||||
api.stats.set('kiri', api.version + 'e');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.mesh && self.mesh) {
|
|
||||||
self.mesh.electron = {};
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
/**
|
|
||||||
* for electron standalone build support
|
|
||||||
*/
|
|
||||||
module.exports = function(server) {
|
|
||||||
// insert script before all others in kiri client
|
|
||||||
server.inject("kiri", "electron.js");
|
|
||||||
// insert scripts into mesh client
|
|
||||||
server.inject("mesh", "electron.js");
|
|
||||||
};
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"name": "grid-apps-mods",
|
|
||||||
"version": "4.1.0",
|
|
||||||
"description": "grid.space app modules",
|
|
||||||
"author": "Stewart Allen <sa@grid.space>",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/gridspace/apps.git"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@gridspace/basic-ftp": "^5.0.5",
|
|
||||||
"aedes": "^0.51.3",
|
|
||||||
"hexer": "^1.5.0",
|
|
||||||
"mqtt": "^5.10.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
6
notes.md
|
|
@ -15,6 +15,7 @@
|
||||||
* `P` move all persisted / workspace settings/data to IndexedDB (LS limitations)
|
* `P` move all persisted / workspace settings/data to IndexedDB (LS limitations)
|
||||||
* `P` faster ray intersect https://github.com/gkjohnson/three-mesh-bvh/
|
* `P` faster ray intersect https://github.com/gkjohnson/three-mesh-bvh/
|
||||||
* `P` try material clipping planes for slice range selection
|
* `P` try material clipping planes for slice range selection
|
||||||
|
* `P` switch png lib to https://github.com/photopea/UPNG.js
|
||||||
|
|
||||||
* `F` edit in Mesh:Tool
|
* `F` edit in Mesh:Tool
|
||||||
* `F` custom device vars for profiles / ranges / gcode
|
* `F` custom device vars for profiles / ranges / gcode
|
||||||
|
|
@ -31,10 +32,7 @@
|
||||||
* `B` multi-extruder rendering of raft fails to offset the rest of the print
|
* `B` multi-extruder rendering of raft fails to offset the rest of the print
|
||||||
* `B` multi-extruder purge blocks fail to generate properly for rafts
|
* `B` multi-extruder purge blocks fail to generate properly for rafts
|
||||||
|
|
||||||
* `F` new parameter to cap output flow rate (on printer device)
|
|
||||||
* `F` new parameters for bridging speed / bridge fan control
|
* `F` new parameters for bridging speed / bridge fan control
|
||||||
* `F` new base paremeter for hole and perimeter compensation
|
|
||||||
* `F` new parameters for inner / outer perimeter compensation
|
|
||||||
* `F` convert ranges to z offsets while continuing to show layer #
|
* `F` convert ranges to z offsets while continuing to show layer #
|
||||||
* `F` support pillar top/bottom should conform to part
|
* `F` support pillar top/bottom should conform to part
|
||||||
* `F` more explicit line width control with ranges and min/max adaptive
|
* `F` more explicit line width control with ranges and min/max adaptive
|
||||||
|
|
@ -61,9 +59,7 @@
|
||||||
|
|
||||||
* `B` rapid moves should be max of terrain zmax and last cut layer height (roughing)
|
* `B` rapid moves should be max of terrain zmax and last cut layer height (roughing)
|
||||||
* `B` feed rate for next tool set before tool change (push/pop feed rates?)
|
* `B` feed rate for next tool set before tool change (push/pop feed rates?)
|
||||||
* `B` tab cuts cause loss of direction, parenting, depth, and it's hard to fix
|
|
||||||
* `B` tabs do not properly track widget mirror events
|
* `B` tabs do not properly track widget mirror events
|
||||||
* `B` tabs are not cut to exact height
|
|
||||||
* `B` contour does not honor clip to stock
|
* `B` contour does not honor clip to stock
|
||||||
|
|
||||||
* `F` add lathe step down to eliminate the need for roughing
|
* `F` add lathe step down to eliminate the need for roughing
|
||||||
|
|
|
||||||
112
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "grid-apps",
|
"name": "grid-apps",
|
||||||
"version": "4.3.1",
|
"version": "4.5.0",
|
||||||
"description": "grid.space 3d slicing & modeling tools",
|
"description": "grid.space 3d slicing & modeling tools",
|
||||||
"author": "Stewart Allen <sa@grid.space>",
|
"author": "Stewart Allen <sa@grid.space>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
@ -35,12 +35,15 @@
|
||||||
"node": ">=22.0.0"
|
"node": ">=22.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@docusaurus/plugin-client-redirects": "^3.9.0",
|
||||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
"@fortawesome/fontawesome-free": "^6.1.1",
|
||||||
"@gridspace/app-server": "^0.0.18",
|
"@gridspace/app-server": "^0.0.20",
|
||||||
"@gridspace/basic-ftp": "^5.0.5",
|
"@gridspace/basic-ftp": "github:gridspace/basic-ftp#v5.0.5-gridspace",
|
||||||
"@gridspace/net-level-client": "^0.2.3",
|
"@gridspace/net-level-client": "^0.2.3",
|
||||||
|
"@gridspace/raster-path": "^1.0.7",
|
||||||
"@tracespace/parser": "^5.0.0-next.0",
|
"@tracespace/parser": "^5.0.0-next.0",
|
||||||
"@tweenjs/tween.js": "^16.6.0",
|
"@tweenjs/tween.js": "^16.6.0",
|
||||||
|
"aedes": "^0.51.3",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"bootstrap-icons": "^1.11.3",
|
"bootstrap-icons": "^1.11.3",
|
||||||
"buffer-crc32": "^0.2.13",
|
"buffer-crc32": "^0.2.13",
|
||||||
|
|
@ -49,25 +52,28 @@
|
||||||
"earcut": "^3.0.1",
|
"earcut": "^3.0.1",
|
||||||
"express-useragent": "^1.0.13",
|
"express-useragent": "^1.0.13",
|
||||||
"fast-glob": "^3.3.3",
|
"fast-glob": "^3.3.3",
|
||||||
|
"hexer": "^1.5.0",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"jszip": "^3.7.1",
|
"jszip": "^3.7.1",
|
||||||
"manifold-3d": "^3.2.1",
|
"manifold-3d": "^3.2.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"mqtt": "^5.10.3",
|
||||||
"npm": "^11.4.2",
|
"npm": "^11.4.2",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"react-responsive-carousel": "^3.2.23",
|
"react-responsive-carousel": "^3.2.23",
|
||||||
"serve-static": "^1.14.1",
|
"serve-static": "^2.2.0",
|
||||||
"three": "^0.178.0",
|
"three": "^0.178.0",
|
||||||
"three-mesh-bvh": "^0.7.6",
|
"three-mesh-bvh": "^0.7.6",
|
||||||
"uglify-js": "3.14.5",
|
"uglify-js": "3.14.5",
|
||||||
"validator": ">=13.7.0",
|
"validator": ">=13.15.15",
|
||||||
"ws": "^8.18.2"
|
"ws": "^8.18.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/core": "^3.7.0",
|
"@docusaurus/core": "^3.9.0",
|
||||||
"@docusaurus/preset-classic": "^3.7.0",
|
"@docusaurus/preset-classic": "^3.9.0",
|
||||||
"@electron/notarize": "^3.0.0",
|
"@electron/notarize": "^3.0.0",
|
||||||
"copy-webpack-plugin": "^12.0.2",
|
"copy-webpack-plugin": "^12.0.2",
|
||||||
|
"cross-env": "^10.1.0",
|
||||||
"docusaurus-lunr-search": "^3.6.0",
|
"docusaurus-lunr-search": "^3.6.0",
|
||||||
"dotenv": "latest",
|
"dotenv": "latest",
|
||||||
"electron": "^35.0.1",
|
"electron": "^35.0.1",
|
||||||
|
|
@ -81,34 +87,39 @@
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"setup": "npm i && npm run clean && npm run dev -- --dryrun && npm run webpack-ext",
|
"build-debug": "npm run prebuild && cross-env DEBUG=electron-builder electron-builder",
|
||||||
"build": "npm run prebuild && electron-builder",
|
|
||||||
"build-nopublish": "npm run prebuild && electron-builder --publish never",
|
|
||||||
"build-debug": "npm run prebuild && DEBUG=electron-builder electron-builder",
|
|
||||||
"build-linux": "npm run build -- --linux --x64",
|
|
||||||
"build-linux-arm": "npm run build -- --linux --arm64",
|
"build-linux-arm": "npm run build -- --linux --arm64",
|
||||||
"build-win": "npm run build -- --win --x64",
|
"build-linux": "npm run build -- --linux --x64",
|
||||||
"build-win-arm": "npm run build -- --win --arm64",
|
|
||||||
"build-mac": "npm run build -- --mac --arm64",
|
|
||||||
"build-mac-intel": "npm run build -- --mac --x64",
|
"build-mac-intel": "npm run build -- --mac --x64",
|
||||||
"clean": "rm -rf build data dist src/pack tmp src.old web.old",
|
"build-mac": "npm run build -- --mac --arm64",
|
||||||
"docs-dev": "docusaurus start --config conf/docusaurus.config.js",
|
"build-nopublish": "npm run prebuild && electron-builder --publish never",
|
||||||
|
"build-win-arm": "npm run build -- --win --arm64",
|
||||||
|
"build-win": "npm run build -- --win --x64",
|
||||||
|
"build": "npm run prebuild && electron-builder",
|
||||||
|
"bundle:dev": "node bin/bundler.mjs bin/bundle.config.json",
|
||||||
|
"bundle:prod": "node bin/bundler.mjs bin/bundle-prod.config.json",
|
||||||
|
"bundle": "cross-env COMPRESS=1 npm run bundle:prod",
|
||||||
|
"clean": "rm -rf alt build data dist src/pack src.old tmp web.old .bcache",
|
||||||
|
"dev": "npm run pack-dev && gs-app-server --debug --single",
|
||||||
"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-check": "prettier --config ./conf/prettier.config.js ./docs --check",
|
"docs-check": "prettier --config ./conf/prettier.config.js ./docs --check",
|
||||||
"dev": "npm run webpack-ext && gs-app-server --debug --single",
|
"docs-dev": "docusaurus start --config conf/docusaurus.config.js",
|
||||||
"prod": "npm run webpack-ext && npm run webpack-src prod && gs-app-server",
|
"docs-serve": "docusaurus serve --config conf/docusaurus.config.js",
|
||||||
"prod-pack": "npm run webpack-ext && npm run webpack-src prod",
|
"dryrun": "gs-app-server --debug --single --dryrun",
|
||||||
"prod-dryrun": "gs-app-server --dryrun",
|
"dryrun-prod": "gs-app-server --single --dryrun",
|
||||||
"start": "npm run prebuild prod && electron .",
|
"mac-verify": "spctl --assess -vv --type install dist/*/*.app",
|
||||||
"start-dev": "npm run prebuild prod && electron . --devel",
|
"mk-links": "find src web -type l | xargs -I{} sh -c 'echo \"{},$(readlink {})\"' > conf/links.csv",
|
||||||
|
"pack-dev": "npm run webpack-src && npm run dryrun && npm run bundle:dev",
|
||||||
|
"pack-prod": "npm run webpack-src prod && npm run dryrun && npm run bundle",
|
||||||
|
"postbuild": "node bin/electron-post.js",
|
||||||
|
"prebuild": "npm run setup && cross-env ELECTRON_BUILD=1 npm run pack-dev && node bin/electron-pre.js",
|
||||||
|
"preinstall": "node bin/install-pre.js",
|
||||||
|
"prod": "npm run pack-prod && gs-app-server",
|
||||||
|
"setup": "npm run clean && npm i && npm run webpack-ext",
|
||||||
"start-dbg": "npm run prebuild && electron . --debugg",
|
"start-dbg": "npm run prebuild && electron . --debugg",
|
||||||
"start-ddb": "npm run prebuild && electron . --devel --debugg",
|
"start-ddb": "npm run prebuild && electron . --devel --debugg",
|
||||||
"mk-links": "find src web -type l | xargs -I{} sh -c 'echo \"{},$(readlink {})\"' > conf/links.csv",
|
"start-dev": "npm run prebuild prod && electron . --devel",
|
||||||
"mac-verify": "spctl --assess -vv --type install dist/*/*.app",
|
"start": "npm run prebuild && electron .",
|
||||||
"prebuild": "npm run setup && npm run webpack-src prod && node bin/electron-pre.js",
|
|
||||||
"postbuild": "node bin/electron-post.js",
|
|
||||||
"preinstall": "node bin/install-pre.js",
|
|
||||||
"webpack-ext": "npm run webpack-three && npm run webpack-zip",
|
"webpack-ext": "npm run webpack-three && npm run webpack-zip",
|
||||||
"webpack-src": "node bin/esbuild.config.mjs",
|
"webpack-src": "node bin/esbuild.config.mjs",
|
||||||
"webpack-three": "npx webpack --config bin/webpack-three-esm.js",
|
"webpack-three": "npx webpack --config bin/webpack-three-esm.js",
|
||||||
|
|
@ -122,52 +133,17 @@
|
||||||
"artifactName": "KiriMoto-${os}-${arch}.${ext}",
|
"artifactName": "KiriMoto-${os}-${arch}.${ext}",
|
||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
"from": "tmp/src",
|
"from": "tmp",
|
||||||
"to": "src",
|
"to": "",
|
||||||
"filter": [
|
|
||||||
"**/*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "tmp/web",
|
|
||||||
"to": "web",
|
|
||||||
"filter": [
|
|
||||||
"**/*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "tmp/mod",
|
|
||||||
"to": "mod",
|
|
||||||
"filter": [
|
|
||||||
"**/*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "tmp/mod/node_modules",
|
|
||||||
"to": "mod/node_modules",
|
|
||||||
"filter": [
|
|
||||||
"**/*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "tmp/mods",
|
|
||||||
"to": "mods",
|
|
||||||
"filter": [
|
|
||||||
"**/*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": "tmp/mods/node_modules",
|
|
||||||
"to": "mods/node_modules",
|
|
||||||
"filter": [
|
"filter": [
|
||||||
"**/*"
|
"**/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"app-el.js",
|
||||||
|
"app.js",
|
||||||
"bin/*",
|
"bin/*",
|
||||||
"conf/**/*",
|
"conf/**/*",
|
||||||
"data/cache/**/*",
|
"data/cache/**/*",
|
||||||
"app-el.js",
|
|
||||||
"app.js",
|
|
||||||
"package.json"
|
"package.json"
|
||||||
],
|
],
|
||||||
"extraFiles": [],
|
"extraFiles": [],
|
||||||
|
|
|
||||||
|
|
@ -9,27 +9,27 @@
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
# Community
|
# Community
|
||||||
|
|
||||||
[Discord](https://discord.com/invite/suyCCgr) -- Live Chat
|
[Discord](https://discord.com/invite/suyCCgr) -- Live Chat
|
||||||
[Forums](https://forum.grid.space/) -- Long Form and Archival Discussion
|
[Forums](https://forum.grid.space/) -- Long Form and Archival Discussion
|
||||||
[BlueSky](https://bsky.app/profile/grid.space) -- Like the Good 'Ol Days
|
|
||||||
[YouTube](https://www.youtube.com/c/gridspace) -- Content when I have time
|
[YouTube](https://www.youtube.com/c/gridspace) -- Content when I have time
|
||||||
|
|
||||||
# Free and Open Source
|
# Free and Open Source
|
||||||
|
|
||||||
Kiri:Moto and Mesh:Tool are completely open source and free for use without restriction. Over 12 years in development, this passion project has grown well beyond its original scope. It has consumed most of my free time for many years. Please consider donating to support continued development GitHub sponsorship or PayPal.
|
Kiri:Moto and Mesh:Tool are completely open source and free for use without restriction. Over 13 years in development, this passion project has grown well beyond its original scope. It has consumed most of my free time for many years. Please consider donating to support continued development GitHub sponsorship or PayPal.
|
||||||
|
|
||||||
[](https://github.com/sponsors/GridSpace)
|
[](https://github.com/sponsors/GridSpace)
|
||||||
[](https://paypal.me/gridspace3d?locale.x=en_US)
|
[](https://paypal.me/gridspace3d?locale.x=en_US)
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
[Documentation](https://docs.grid.space/) -- Could really use help with this
|
[Documentation](https://docs.grid.space/) -- Could really use help with this
|
||||||
|
|
||||||
Docs are build with [Docusaurus](https://docusaurus.io/) and served using [GitHub Pages](https://pages.github.com/).
|
Docs are build with [Docusaurus](https://docusaurus.io/) and served using [GitHub Pages](https://pages.github.com/).
|
||||||
|
|
||||||
You can build and view the docs locally with:
|
You can build and view the docs locally with:
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone git@github.com:GridSpace/grid-apps.git
|
git clone git@github.com:GridSpace/grid-apps.git
|
||||||
cd grid-apps
|
cd grid-apps
|
||||||
|
|
@ -45,7 +45,6 @@ npm run docs-dev
|
||||||
|
|
||||||
[`Mesh:Tool`](https://grid.space/mesh) is a browser-based mesh repair and editing tool
|
[`Mesh:Tool`](https://grid.space/mesh) is a browser-based mesh repair and editing tool
|
||||||
|
|
||||||
|
|
||||||
# Electron Builds (Desktop Binaries)
|
# Electron Builds (Desktop Binaries)
|
||||||
|
|
||||||
https://github.com/GridSpace/grid-apps/releases/
|
https://github.com/GridSpace/grid-apps/releases/
|
||||||
|
|
@ -62,7 +61,6 @@ chmod 755 KiriMoto-linux-x86_64.AppImage
|
||||||
|
|
||||||
The Windows and Mac binaries are not signed, so you will need to jump through a few safety hoops to get them to open the first time.
|
The Windows and Mac binaries are not signed, so you will need to jump through a few safety hoops to get them to open the first time.
|
||||||
|
|
||||||
|
|
||||||
# Linux / Mac Developers
|
# Linux / Mac Developers
|
||||||
|
|
||||||
## Testing Locally (with Docker)
|
## Testing Locally (with Docker)
|
||||||
|
|
|
||||||
156
release.md
|
|
@ -2,6 +2,162 @@
|
||||||
|
|
||||||
Full docs @ https://docs.grid.space/projects/kiri-moto
|
Full docs @ https://docs.grid.space/projects/kiri-moto
|
||||||
|
|
||||||
|
# Release 4.4.0
|
||||||
|
|
||||||
|
## General
|
||||||
|
|
||||||
|
* add version numbering utility script
|
||||||
|
* split out help/info menu and language menus
|
||||||
|
* move install/uninstall/quit menu to center app menu
|
||||||
|
* add help menu visual callout
|
||||||
|
* improve language translation coverage
|
||||||
|
* improve build and serve workflow for alternate builds
|
||||||
|
* update service worker configuration
|
||||||
|
* add install/uninstall options in setup menu
|
||||||
|
|
||||||
|
## CAM
|
||||||
|
|
||||||
|
* implement tab hopping - toolpath goes up and over tab instead of interrupting cut (fixes #207)
|
||||||
|
* add GPU accelerated contouring for faster operations
|
||||||
|
* add radial GPU raster support (experimental)
|
||||||
|
* add new register mode that marks bottom cutout tabs
|
||||||
|
* add curves-only support to GPU contour
|
||||||
|
* add zsafe and move to safe z at start of new operations
|
||||||
|
* fix 'safe' moves during operation changes to be moves, not cuts
|
||||||
|
* fix tolerance/resolution for GPU raster mesh calculations
|
||||||
|
* fix NullPointerException with tabs in contour
|
||||||
|
* improve merge/co-planar point handling
|
||||||
|
* add slice layer naming using operation notes (fixes #447)
|
||||||
|
* fix level step down units (fixes #446)
|
||||||
|
|
||||||
|
## FDM
|
||||||
|
|
||||||
|
* document remain_time macro variable (refs #339)
|
||||||
|
* re-add base flow rate multiplier for belt mode (fixes #444)
|
||||||
|
* fix belt fan control ordering (fixes #438)
|
||||||
|
* exclude belt from bridge fan layer
|
||||||
|
* add centering feature for fill areas (fixes #448)
|
||||||
|
|
||||||
|
## Mesh:Tool
|
||||||
|
|
||||||
|
* add STEP export capability
|
||||||
|
* add STEP import with face generation
|
||||||
|
* improve face generation with inner holes
|
||||||
|
* add mesh scripting tool and persistence
|
||||||
|
* add plane and plane.loft operations
|
||||||
|
* add global edge map and edge reuse for better topology
|
||||||
|
* add devel device export option
|
||||||
|
|
||||||
|
## Electron
|
||||||
|
|
||||||
|
* clean up electron build process
|
||||||
|
* merge electron mod into core
|
||||||
|
* add service routes when available
|
||||||
|
* set proper content-type for appended code bodies
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
* update @gridspace/raster-path to latest version with GPU acceleration
|
||||||
|
* update @gridspace/app-server for proper fallthrough handling
|
||||||
|
* switch from npmjs.org to git repos for @gridspace packages
|
||||||
|
* update WebGPU implementation
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* fix Bambu Lab local URL and module load order
|
||||||
|
* fix progress reporting for raster operations
|
||||||
|
* fix GPU lathe progress and contour API usage
|
||||||
|
* fix tool offsets when using GPU acceleration
|
||||||
|
* fix indexed rotations epsilon value for accurate zflat calculation
|
||||||
|
* fix traceload debug option
|
||||||
|
* sanitize topo3 resolution to avoid GPU floating-point errors
|
||||||
|
|
||||||
|
|
||||||
|
# Release 4.3
|
||||||
|
|
||||||
|
## General
|
||||||
|
|
||||||
|
* add lathe mode for CAM operations
|
||||||
|
* add volumetric flow calculations for FDM
|
||||||
|
* improve bundler with better dependency tracking
|
||||||
|
* migrate to ESM module format across codebase
|
||||||
|
* improve arc support in gcode generation
|
||||||
|
|
||||||
|
## FDM
|
||||||
|
|
||||||
|
* add scarf seams for better surface finish
|
||||||
|
* add hole compensation feature
|
||||||
|
* add spiral layer start option
|
||||||
|
* overhaul thin wall detection and handling
|
||||||
|
* improve volumetric flow rate controls
|
||||||
|
* add retraction tuning for better print quality
|
||||||
|
|
||||||
|
## CAM
|
||||||
|
|
||||||
|
* add lathe operation support with threading
|
||||||
|
* improve arc support for smoother toolpaths
|
||||||
|
* add drill from stock top option
|
||||||
|
* improve tab positioning and generation
|
||||||
|
* enhance surface selection and filtering
|
||||||
|
* add 4th axis lathe debug and testing mode
|
||||||
|
* improve tool path optimization
|
||||||
|
|
||||||
|
## Devices
|
||||||
|
|
||||||
|
* add new machine profiles
|
||||||
|
* improve device profile management
|
||||||
|
|
||||||
|
|
||||||
|
# Release 4.2
|
||||||
|
|
||||||
|
## CAM
|
||||||
|
|
||||||
|
* add arc support to gcode output for smoother paths
|
||||||
|
* add drill from stock top feature
|
||||||
|
* improve contour operations
|
||||||
|
* fix animation issues with shared tool numbers
|
||||||
|
* enhance trace operations with arc support
|
||||||
|
* improve pocket smoothing algorithms
|
||||||
|
|
||||||
|
## FDM
|
||||||
|
|
||||||
|
* improve support generation
|
||||||
|
* add new retraction options
|
||||||
|
* enhance layer time controls
|
||||||
|
|
||||||
|
## General
|
||||||
|
|
||||||
|
* improve gcode arc import and export
|
||||||
|
* fix file loading edge cases
|
||||||
|
* update device profiles
|
||||||
|
|
||||||
|
|
||||||
|
# Release 4.1
|
||||||
|
|
||||||
|
## CAM
|
||||||
|
|
||||||
|
* improve drill operations and positioning
|
||||||
|
* fix multi-part drilling bugs
|
||||||
|
* enhance trace line selection
|
||||||
|
* add dogbone support to trace ops
|
||||||
|
* improve pocket operation reliability
|
||||||
|
* fix trace selection with flip operations
|
||||||
|
|
||||||
|
## FDM
|
||||||
|
|
||||||
|
* add RatRig machine profiles
|
||||||
|
* improve Bambu Lab device control integration
|
||||||
|
* enhance support placement algorithms
|
||||||
|
* fix belt mode support generation
|
||||||
|
|
||||||
|
## General
|
||||||
|
|
||||||
|
* improve file import handling
|
||||||
|
* fix workspace restore for complex projects
|
||||||
|
* enhance mobile touch interactions
|
||||||
|
* update machine profiles for popular devices
|
||||||
|
|
||||||
|
|
||||||
# Release 4.0 (2024-01-21)
|
# Release 4.0 (2024-01-21)
|
||||||
|
|
||||||
* major UI refactor
|
* major UI refactor
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ SP.init = function(options = {}) {
|
||||||
request = IDB.open(name, this.version);
|
request = IDB.open(name, this.version);
|
||||||
|
|
||||||
request.onupgradeneeded = function(event) {
|
request.onupgradeneeded = function(event) {
|
||||||
let db = storage.db = request.result;
|
let db = request.result;
|
||||||
let current = [...db.objectStoreNames];
|
let current = [...db.objectStoreNames];
|
||||||
let stores = storage.stores;
|
let stores = storage.stores;
|
||||||
// add missing stores
|
// add missing stores
|
||||||
|
|
@ -184,13 +184,12 @@ SP.init = function(options = {}) {
|
||||||
// remove obsolete stores
|
// remove obsolete stores
|
||||||
for (let curr of current) {
|
for (let curr of current) {
|
||||||
if (stores.indexOf(curr) < 0) {
|
if (stores.indexOf(curr) < 0) {
|
||||||
storage.db.deleteObjectStore(curr);
|
db.deleteObjectStore(curr);
|
||||||
console.log({index_dropped: curr});
|
console.log({index_dropped: curr});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event.target.transaction.oncomplete = function(event) {
|
// allow transactions
|
||||||
storage.runQueue();
|
storage.db = db;
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = function(event) {
|
request.onsuccess = function(event) {
|
||||||
|
|
@ -217,6 +216,7 @@ SP.runQueue = function() {
|
||||||
let i = 0, q = this.queue, e;
|
let i = 0, q = this.queue, e;
|
||||||
while (i < q.length) {
|
while (i < q.length) {
|
||||||
e = q[i++];
|
e = q[i++];
|
||||||
|
// console.log('runQ', i, ...e);
|
||||||
switch (e.shift()) {
|
switch (e.shift()) {
|
||||||
case 'iterate': this.iterate(...e); break;
|
case 'iterate': this.iterate(...e); break;
|
||||||
case 'keys': this.keys(...e); break;
|
case 'keys': this.keys(...e); break;
|
||||||
|
|
@ -252,7 +252,11 @@ SP.put = function(key, value, callback, store = this.current) {
|
||||||
req.onerror = function(event) { callback(false) };
|
req.onerror = function(event) { callback(false) };
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
if (e.name === 'InvalidStateError') {
|
||||||
|
console.log({ putRetry: key });
|
||||||
|
return this.queue.push(['put', key, value, callback, store]);
|
||||||
|
}
|
||||||
|
console.log(e.name, e);
|
||||||
if (callback) callback(false);
|
if (callback) callback(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -275,6 +279,10 @@ SP.get = function(key, callback, store = this.current) {
|
||||||
req.onerror = function(event) { callback(null) };
|
req.onerror = function(event) { callback(null) };
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (e.name === 'InvalidStateError') {
|
||||||
|
console.log({ getRetry: key });
|
||||||
|
return this.queue.push(['get', key, callback, store]);
|
||||||
|
}
|
||||||
console.log(e);
|
console.log(e);
|
||||||
if (callback) callback(null);
|
if (callback) callback(null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@
|
||||||
import './pngjs.js';
|
import './pngjs.js';
|
||||||
|
|
||||||
// Re-export the PNG object
|
// Re-export the PNG object
|
||||||
export const PNG = self.png ? self.png.PNG : null;
|
export const PNG = globalThis.png ? globalThis.png.PNG : null;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.png = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
(function(f){globalThis.png = f();if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.png = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||||
(function (Buffer){
|
(function (Buffer){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,12 @@ class Point {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// annotate instance with other field data (see clone())
|
||||||
|
annotate(obj = {}) {
|
||||||
|
Object.assign(this, obj);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
slopeTo(p) {
|
slopeTo(p) {
|
||||||
return newSlope(this, p);
|
return newSlope(this, p);
|
||||||
}
|
}
|
||||||
|
|
@ -441,10 +447,6 @@ class Point {
|
||||||
return this === p || (this.x === p.x && this.y === p.y && this.z === p.z);
|
return this === p || (this.x === p.x && this.y === p.y && this.z === p.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
isEqual2D(p) {
|
|
||||||
return this === p || (this.x === p.x && this.y === p.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns true if points are close enough to be considered equivalent
|
* returns true if points are close enough to be considered equivalent
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -908,6 +908,7 @@ export class Polygon {
|
||||||
* todo make more efficient
|
* todo make more efficient
|
||||||
*
|
*
|
||||||
* @param {Boolean} deep
|
* @param {Boolean} deep
|
||||||
|
* @param {String[]} fields to copy beyond class fields
|
||||||
* @returns {Polygon}
|
* @returns {Polygon}
|
||||||
*/
|
*/
|
||||||
clone(deep, fields) {
|
clone(deep, fields) {
|
||||||
|
|
@ -2145,6 +2146,7 @@ export class Polygon {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// annotate instance with other field data (see clone())
|
||||||
annotate(obj = {}) {
|
annotate(obj = {}) {
|
||||||
Object.assign(this, obj);
|
Object.assign(this, obj);
|
||||||
return this;
|
return this;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ import { polygons } from '../geo/polygons.js';
|
||||||
import { newPoint } from '../geo/point.js';
|
import { newPoint } from '../geo/point.js';
|
||||||
import { config } from '../geo/base.js';
|
import { config } from '../geo/base.js';
|
||||||
|
|
||||||
|
const epsilon = 10e-5;
|
||||||
|
|
||||||
function dval(v, dv) {
|
function dval(v, dv) {
|
||||||
return v !== undefined ? v : dv;
|
return v !== undefined ? v : dv;
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +77,7 @@ export async function slice(points, options = {}) {
|
||||||
// used to calculate buckets (rough sum of z span)
|
// used to calculate buckets (rough sum of z span)
|
||||||
zSum += (Math.abs(p1.z - p2.z) + Math.abs(p2.z - p3.z) + Math.abs(p3.z - p1.z));
|
zSum += (Math.abs(p1.z - p2.z) + Math.abs(p2.z - p3.z) + Math.abs(p3.z - p1.z));
|
||||||
// use co-flat and co-line detection to adjust slice Z
|
// use co-flat and co-line detection to adjust slice Z
|
||||||
if (p1.z === p2.z && p2.z === p3.z && p1.z >= zMin) {
|
if (Math.abs(p1.z - p2.z) < epsilon && Math.abs(p2.z - p3.z) < epsilon && 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(base.util.area2(p1,p2,p3)) / 2;
|
||||||
|
|
|
||||||
|
|
@ -60,13 +60,6 @@ export class Slope {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
invert() {
|
|
||||||
this.dx = -this.dx;
|
|
||||||
this.dy = -this.dy;
|
|
||||||
this.angle = 180 - this.angle;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
angleDiff(s2,sign) {
|
angleDiff(s2,sign) {
|
||||||
const n1 = this.angle;
|
const n1 = this.angle;
|
||||||
const n2 = s2.angle;
|
const n2 = s2.angle;
|
||||||
|
|
|
||||||
1
src/gpu/raster-worker.js
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../node_modules/@gridspace/raster-path/build/raster-worker.js
|
||||||
1
src/gpu/raster.js
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../node_modules/@gridspace/raster-path/build/raster-path.js
|
||||||
|
|
@ -9,7 +9,7 @@ import { client as work } from './client.js';
|
||||||
import { consts, COLOR as color, LISTS as lists } from './consts.js';
|
import { consts, COLOR as color, LISTS as lists } from './consts.js';
|
||||||
import { device, devices } from './devices.js';
|
import { device, devices } from './devices.js';
|
||||||
import { catalog, dialog, event, group, help, hide, image } from './main.js';
|
import { catalog, dialog, event, group, help, hide, image } from './main.js';
|
||||||
import { modal, mode, probe, process, show, space, util, view } from './main.js';
|
import { modal, mode, process, show, space, util, view } from './main.js';
|
||||||
import { local as dataLocal } from '../../data/local.js';
|
import { local as dataLocal } from '../../data/local.js';
|
||||||
import { noop, ajax, o2js, js2o } from './utils.js';
|
import { noop, ajax, o2js, js2o } from './utils.js';
|
||||||
import { functions } from './function.js';
|
import { functions } from './function.js';
|
||||||
|
|
@ -96,6 +96,7 @@ let UC = UI.prefix('kiri').inputAction(settings.conf.update),
|
||||||
put: (key, val) => localSet(key, val),
|
put: (key, val) => localSet(key, val),
|
||||||
set: (key, val) => localSet(key, val),
|
set: (key, val) => localSet(key, val),
|
||||||
setItem: (key, val) => localSet(key, val),
|
setItem: (key, val) => localSet(key, val),
|
||||||
|
removeItem: (key) => localRemove(key)
|
||||||
},
|
},
|
||||||
tweak = {
|
tweak = {
|
||||||
line_precision(v) { api.work.config({ base: { clipperClean: v } }) },
|
line_precision(v) { api.work.config({ base: { clipperClean: v } }) },
|
||||||
|
|
@ -119,6 +120,11 @@ function localSet(key, val) {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function localRemove(key) {
|
||||||
|
let sloc = api.conf.get().local;
|
||||||
|
return delete sloc[key];
|
||||||
|
}
|
||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
ajax,
|
ajax,
|
||||||
beta,
|
beta,
|
||||||
|
|
@ -137,6 +143,7 @@ export const api = {
|
||||||
dialog,
|
dialog,
|
||||||
doit,
|
doit,
|
||||||
event,
|
event,
|
||||||
|
electron: navigator.userAgent.includes('Electron'),
|
||||||
feature,
|
feature,
|
||||||
function: functions,
|
function: functions,
|
||||||
group,
|
group,
|
||||||
|
|
@ -156,7 +163,6 @@ export const api = {
|
||||||
o2js,
|
o2js,
|
||||||
onkey,
|
onkey,
|
||||||
platform,
|
platform,
|
||||||
probe,
|
|
||||||
process,
|
process,
|
||||||
sdb: dataLocal,
|
sdb: dataLocal,
|
||||||
selection,
|
selection,
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,9 @@ Widget.prototype.encode = function(state) {
|
||||||
json: json, // safe for JSON (float32array mess)
|
json: json, // safe for JSON (float32array mess)
|
||||||
group: this.group.id,
|
group: this.group.id,
|
||||||
track: this.track,
|
track: this.track,
|
||||||
geo: json ? Array.from(geo) : geo
|
geo: json ? Array.from(geo) : geo,
|
||||||
|
meta: this.meta,
|
||||||
|
anno: this.anno,
|
||||||
};
|
};
|
||||||
return coded;
|
return coded;
|
||||||
};
|
};
|
||||||
|
|
@ -234,8 +236,12 @@ registerDecoder(TYPE.WIDGET, function(v, state) {
|
||||||
const id = v.id,
|
const id = v.id,
|
||||||
group = v.group || id,
|
group = v.group || id,
|
||||||
track = v.track || undefined,
|
track = v.track || undefined,
|
||||||
widget = newWidget(id, Widget.Groups.forid(group));
|
widget = newWidget(id, Widget.Groups.forid(group)),
|
||||||
|
meta= v.meta || widget.meta,
|
||||||
|
anno = v.anno || widget.anno;
|
||||||
|
|
||||||
|
widget.meta = meta;
|
||||||
|
widget.anno = anno;
|
||||||
widget.loadVertices(v.json ? v.geo.toFloat32() : v.geo);
|
widget.loadVertices(v.json ? v.geo.toFloat32() : v.geo);
|
||||||
widget.saved = Date.now();
|
widget.saved = Date.now();
|
||||||
if (track && track.pos) {
|
if (track && track.pos) {
|
||||||
|
|
|
||||||
|
|
@ -182,33 +182,33 @@ function normalize(settings) {
|
||||||
|
|
||||||
// auto field renaming on import
|
// auto field renaming on import
|
||||||
const renamed = {
|
const renamed = {
|
||||||
roughingTool: "camRoughTool",
|
camWideCutout: "camOutlineWide",
|
||||||
roughingSpindle: "camRoughSpindle",
|
drillDown: "camDrillDown",
|
||||||
roughingDown: "camRoughDown",
|
drillDownSpeed: "camDrillDownSpeed",
|
||||||
roughingOver: "camRoughOver",
|
drillDwell: "camDrillDwell",
|
||||||
roughingSpeed: "camRoughSpeed",
|
drillingOn: "camDrillingOn",
|
||||||
roughingPlunge: "camRoughPlunge",
|
drillLift: "camDrillLift",
|
||||||
roughingStock: "camRoughStock",
|
drillSpindle: "camDrillSpindle",
|
||||||
roughingPocket: "camRoughVoid",
|
drillTool: "camDrillTool",
|
||||||
roughingOn: "camRoughOn",
|
|
||||||
finishingTool: "camOutlineTool",
|
|
||||||
finishingSpindle: "camOutlineSpindle",
|
|
||||||
finishingDown: "camOutlineDown",
|
finishingDown: "camOutlineDown",
|
||||||
finishingOver: "camContourOver",
|
|
||||||
finishingSpeed: "camOutlineSpeed",
|
|
||||||
finishingPlunge: "camOutlinePlunge",
|
|
||||||
finishingOn: "camOutlineOn",
|
finishingOn: "camOutlineOn",
|
||||||
|
finishingOver: "camContourOver",
|
||||||
|
finishingPlunge: "camOutlinePlunge",
|
||||||
|
finishingSpeed: "camOutlineSpeed",
|
||||||
|
finishingSpindle: "camOutlineSpindle",
|
||||||
|
finishingTool: "camOutlineTool",
|
||||||
finishingXOn: "camContourXOn",
|
finishingXOn: "camContourXOn",
|
||||||
finishingYOn: "camContourYOn",
|
finishingYOn: "camContourYOn",
|
||||||
drillTool: "camDrillTool",
|
outputClockwise: "camConventional",
|
||||||
drillSpindle: "camDrillSpindle",
|
roughingDown: "camRoughDown",
|
||||||
drillDownSpeed: "camDrillDownSpeed",
|
roughingOn: "camRoughOn",
|
||||||
drillDown: "camDrillDown",
|
roughingOver: "camRoughOver",
|
||||||
drillDwell: "camDrillDwell",
|
roughingPlunge: "camRoughPlunge",
|
||||||
drillLift: "camDrillLift",
|
roughingPocket: "camRoughVoid",
|
||||||
drillingOn: "camDrillingOn",
|
roughingSpeed: "camRoughSpeed",
|
||||||
camWideCutout: "camOutlineWide",
|
roughingSpindle: "camRoughSpindle",
|
||||||
outputClockwise: "camConventional"
|
roughingStock: "camRoughStock",
|
||||||
|
roughingTool: "camRoughTool",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const conf = {
|
export const conf = {
|
||||||
|
|
@ -258,95 +258,95 @@ export const conf = {
|
||||||
},
|
},
|
||||||
// process defaults FDM:Process
|
// process defaults FDM:Process
|
||||||
p:{
|
p:{
|
||||||
|
antiBacklash: 0,
|
||||||
|
arcTolerance: 0,
|
||||||
|
firstLayerBedTemp: 0,
|
||||||
|
firstLayerBeltBump: 0,
|
||||||
|
firstLayerBeltLead: 3,
|
||||||
|
firstLayerBeltFact: 1,
|
||||||
|
firstLayerBrim: 0,
|
||||||
|
firstLayerBrimGap: 0,
|
||||||
|
firstLayerBrimIn: 0,
|
||||||
|
firstLayerBrimTrig: 0,
|
||||||
|
firstLayerFanSpeed: 0,
|
||||||
|
firstLayerFillRate: 35,
|
||||||
|
firstLayerFlatten: 0,
|
||||||
|
firstLayerNozzleTemp: 0,
|
||||||
|
firstLayerRate: 30,
|
||||||
|
firstLayerYOffset: 0,
|
||||||
|
firstSliceHeight: 0.25,
|
||||||
|
outputAlternating: false,
|
||||||
|
outputAvoidGaps: true,
|
||||||
|
outputBedTemp: 60,
|
||||||
|
outputBeltFirst: false,
|
||||||
|
outputBrimCount: 2,
|
||||||
|
outputBrimOffset: 2,
|
||||||
|
outputCoastDist: 0,
|
||||||
|
outputDraftShield: false,
|
||||||
|
outputFanLayer: 1,
|
||||||
|
outputFanSpeed: 255,
|
||||||
|
outputFeedrate: 50,
|
||||||
|
outputFillMult: 1.25,
|
||||||
|
outputFinishrate: 50,
|
||||||
|
outputInvertX: false,
|
||||||
|
outputInvertY: false,
|
||||||
|
outputLayerRetract: false,
|
||||||
|
outputLoops: 0,
|
||||||
|
outputMaxFlowrate: 15,
|
||||||
|
outputMinLayerTime: 10,
|
||||||
|
outputMinSpeed: 5.0,
|
||||||
|
outputPurgeTower: 0,
|
||||||
|
outputRaft: false,
|
||||||
|
outputRaftSpacing: 0.2,
|
||||||
|
outputRetractDist: 1.5,
|
||||||
|
outputRetractDwell: 20,
|
||||||
|
outputRetractSpeed: 40,
|
||||||
|
outputRetractWipe: 0,
|
||||||
|
outputScarfLength: 0,
|
||||||
|
outputSeekrate: 80,
|
||||||
|
outputShellMult: 1.25,
|
||||||
|
outputShortPoly: 0.0,
|
||||||
|
outputSparseMult: 1.25,
|
||||||
|
outputTemp: 200,
|
||||||
processName: "default",
|
processName: "default",
|
||||||
|
ranges: [],
|
||||||
|
sliceAdaptive: false,
|
||||||
sliceAngle: 45,
|
sliceAngle: 45,
|
||||||
|
sliceBottomLayers: 3,
|
||||||
|
sliceCompInner: 0,
|
||||||
|
sliceCompOuter: 0,
|
||||||
|
sliceDetectThin: "basic",
|
||||||
|
sliceFillAngle: 45,
|
||||||
|
sliceFillGrow: 0,
|
||||||
|
sliceFillOverlap: 0.35,
|
||||||
|
sliceFillRepeat: 1,
|
||||||
|
sliceFillSparse: 0.25,
|
||||||
|
sliceFillType: "hex",
|
||||||
|
sliceFillWidth: 1,
|
||||||
sliceHeight: 0.25,
|
sliceHeight: 0.25,
|
||||||
sliceShells: 3,
|
|
||||||
sliceShellOrder: "in-out",
|
|
||||||
sliceLayerStart: "last",
|
sliceLayerStart: "last",
|
||||||
sliceLayerStartX: 0,
|
sliceLayerStartX: 0,
|
||||||
sliceLayerStartY: 0,
|
sliceLayerStartY: 0,
|
||||||
sliceLineWidth: 0,
|
sliceLineWidth: 0,
|
||||||
sliceFillGrow: 0,
|
|
||||||
sliceFillAngle: 45,
|
|
||||||
sliceFillWidth: 1,
|
|
||||||
sliceFillOverlap: 0.35,
|
|
||||||
sliceFillSparse: 0.25,
|
|
||||||
sliceFillRepeat: 1,
|
|
||||||
sliceFillRate: 0,
|
|
||||||
sliceSolidRate: 0,
|
|
||||||
sliceFillType: "hex",
|
|
||||||
sliceSupportDensity: 0.1,
|
|
||||||
sliceSupportOffset: 1.0,
|
|
||||||
sliceSupportGap: 1,
|
|
||||||
sliceSupportSize: 5,
|
|
||||||
sliceSupportArea: 0.1,
|
|
||||||
sliceSupportSpan: 5,
|
|
||||||
sliceSupportGrow: 0,
|
|
||||||
sliceSupportExtra: 0,
|
|
||||||
sliceSupportAngle: 50,
|
|
||||||
sliceSupportNozzle: 0,
|
|
||||||
sliceSupportEnable: false,
|
|
||||||
sliceSupportOutline: true,
|
|
||||||
sliceZInterleave: false,
|
|
||||||
sliceSolidMinArea: 1,
|
|
||||||
sliceBottomLayers: 3,
|
|
||||||
sliceTopLayers: 3,
|
|
||||||
firstSliceHeight: 0.25,
|
|
||||||
firstLayerRate: 30,
|
|
||||||
firstLayerFillRate: 35,
|
|
||||||
firstLayerPrintMult: 1.0,
|
|
||||||
firstLayerLineMult: 1.0,
|
|
||||||
firstLayerYOffset: 0,
|
|
||||||
firstLayerNozzleTemp: 0,
|
|
||||||
firstLayerBedTemp: 0,
|
|
||||||
firstLayerBrim: 0,
|
|
||||||
firstLayerBrimIn: 0,
|
|
||||||
firstLayerBrimTrig: 0,
|
|
||||||
firstLayerBrimGap: 0,
|
|
||||||
firstLayerBeltLead: 3,
|
|
||||||
firstLayerBeltBump: 0,
|
|
||||||
firstLayerFanSpeed: 0,
|
|
||||||
firstLayerFlatten: 0,
|
|
||||||
outputRaft: false,
|
|
||||||
outputRaftSpacing: 0.2,
|
|
||||||
outputDraftShield: false,
|
|
||||||
outputTemp: 200,
|
|
||||||
outputBedTemp: 60,
|
|
||||||
outputFeedrate: 50,
|
|
||||||
outputFinishrate: 50,
|
|
||||||
outputSeekrate: 80,
|
|
||||||
outputShellMult: 1.25,
|
|
||||||
outputFillMult: 1.25,
|
|
||||||
outputSparseMult: 1.25,
|
|
||||||
outputFanLayer: 1,
|
|
||||||
outputFanSpeed: 255,
|
|
||||||
outputRetractDist: 1.5,
|
|
||||||
outputRetractSpeed: 40,
|
|
||||||
outputRetractWipe: 0,
|
|
||||||
outputRetractDwell: 20,
|
|
||||||
outputBrimCount: 2,
|
|
||||||
outputBrimOffset: 2,
|
|
||||||
outputScarfLength: 0,
|
|
||||||
outputShortPoly: 100.0,
|
|
||||||
outputMinSpeed: 10.0,
|
|
||||||
outputMinLayerTime: 0,
|
|
||||||
outputCoastDist: 0,
|
|
||||||
outputPurgeTower: 0,
|
|
||||||
outputBeltFirst: false,
|
|
||||||
outputAvoidGaps: true,
|
|
||||||
outputAlternating: false,
|
|
||||||
outputLayerRetract: false,
|
|
||||||
outputLoops: 0,
|
|
||||||
outputInvertX: false,
|
|
||||||
outputInvertY: false,
|
|
||||||
sliceDetectThin: "off",
|
|
||||||
sliceMinHeight: 0,
|
sliceMinHeight: 0,
|
||||||
sliceAdaptive: false,
|
sliceShellOrder: "in-out",
|
||||||
|
sliceShells: 3,
|
||||||
|
sliceSolidMinArea: 1,
|
||||||
|
sliceSupportAngle: 50,
|
||||||
|
sliceSupportArea: 0.1,
|
||||||
|
sliceSupportDensity: 0.1,
|
||||||
|
sliceSupportEnable: false,
|
||||||
|
sliceSupportExtra: 0,
|
||||||
|
sliceSupportGap: 1,
|
||||||
|
sliceSupportGrow: 0,
|
||||||
|
sliceSupportNozzle: 0,
|
||||||
|
sliceSupportOffset: 1.0,
|
||||||
|
sliceSupportOutline: true,
|
||||||
|
sliceSupportSize: 5,
|
||||||
|
sliceSupportSpan: 5,
|
||||||
|
sliceTopLayers: 3,
|
||||||
|
sliceZInterleave: false,
|
||||||
zHopDistance: 0.2,
|
zHopDistance: 0.2,
|
||||||
arcTolerance: 0,
|
|
||||||
antiBacklash: 0,
|
|
||||||
ranges: []
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sla:{
|
sla:{
|
||||||
|
|
@ -420,186 +420,187 @@ export const conf = {
|
||||||
},
|
},
|
||||||
// process defaults CAM:Process
|
// process defaults CAM:Process
|
||||||
p:{
|
p:{
|
||||||
processName: "default",
|
camArcEnabled: false,
|
||||||
camLevelTool: 1000,
|
camArcResolution: 5,
|
||||||
camLevelSpindle: 1000,
|
camArcTolerance: 0.15,
|
||||||
camLevelOver: 0.75,
|
|
||||||
camLevelStepZ: 0,
|
|
||||||
camLevelSpeed: 1000,
|
|
||||||
camLevelDown: 0,
|
|
||||||
camLevelInset: 0.5,
|
|
||||||
camLevelStock: true,
|
|
||||||
camRoughTool: 1000,
|
|
||||||
camRoughSpindle: 1000,
|
|
||||||
camRoughDown: 2,
|
|
||||||
camRoughOver: 0.4,
|
|
||||||
camRoughSpeed: 1000,
|
|
||||||
camRoughPlunge: 250,
|
|
||||||
camRoughStock: 0,
|
|
||||||
camRoughStockZ: 0,
|
|
||||||
camRoughAll: true,
|
|
||||||
camRoughVoid: false,
|
|
||||||
camRoughFlat: true,
|
|
||||||
camRoughTop: true,
|
|
||||||
camRoughIn: true,
|
|
||||||
camRoughOn: true,
|
|
||||||
camRoughOmitThru: false,
|
|
||||||
camRoughOmitVoid: false,
|
|
||||||
camOutlineTool: 1000,
|
|
||||||
camOutlineSpindle: 1000,
|
|
||||||
camOutlineTop: true,
|
|
||||||
camOutlineDown: 3,
|
|
||||||
camOutlineOver: 0.4,
|
|
||||||
camOutlineOverCount: 1,
|
|
||||||
camOutlineSpeed: 800,
|
|
||||||
camOutlinePlunge: 250,
|
|
||||||
camOutlineWide: false,
|
|
||||||
camOutlineDogbone: false,
|
|
||||||
camOutlineOmitThru: false,
|
|
||||||
camOutlineOmitVoid: false,
|
|
||||||
camOutlineOut: true,
|
|
||||||
camOutlineIn: false,
|
|
||||||
camOutlineOn: true,
|
|
||||||
camContourTool: 1000,
|
|
||||||
camContourSpindle: 1000,
|
|
||||||
camContourOver: 0.5,
|
|
||||||
camContourSpeed: 1000,
|
|
||||||
camContourAngle: 85,
|
camContourAngle: 85,
|
||||||
camContourLeave: 0,
|
|
||||||
camContourReduce: 2,
|
|
||||||
camContourBottom: false,
|
camContourBottom: false,
|
||||||
|
camContourBridge: 0,
|
||||||
camContourCurves: false,
|
camContourCurves: false,
|
||||||
camContourIn: false,
|
camContourIn: false,
|
||||||
|
camContourLeave: 0,
|
||||||
|
camContourOver: 0.5,
|
||||||
|
camContourReduce: 2,
|
||||||
|
camContourSpeed: 1000,
|
||||||
|
camContourSpindle: 1000,
|
||||||
|
camContourTool: 1000,
|
||||||
camContourXOn: true,
|
camContourXOn: true,
|
||||||
camContourYOn: true,
|
camContourYOn: true,
|
||||||
camLatheTool: 1000,
|
camConventional: false, // outputClockwise
|
||||||
camLatheSpindle: 1000,
|
camDepthFirst: false,
|
||||||
camLatheOver: 0.1,
|
|
||||||
camLatheAngle: 1,
|
|
||||||
camLatheSpeed: 500,
|
|
||||||
camLatheLinear: true,
|
|
||||||
camTolerance: 0,
|
|
||||||
camTraceTool: 1000,
|
|
||||||
camTraceSpindle: 1000,
|
|
||||||
camTraceType: "follow",
|
|
||||||
camTraceOver: 0.5,
|
|
||||||
camTraceDown: 0,
|
|
||||||
camTraceThru: 0,
|
|
||||||
camTraceSpeed: 250,
|
|
||||||
camTracePlunge: 200,
|
|
||||||
camTraceOffOver: 0,
|
|
||||||
camTraceDogbone: false,
|
|
||||||
camTraceMerge: false,
|
|
||||||
camTraceLines: false,
|
|
||||||
camTraceZTop: 0,
|
|
||||||
camTraceZBottom: 0,
|
|
||||||
camPocketSpindle: 1000,
|
|
||||||
camPocketTool: 1000,
|
|
||||||
camPocketOver: 0.25,
|
|
||||||
camPocketDown: 1,
|
|
||||||
camPocketSpeed: 250,
|
|
||||||
camPocketPlunge: 200,
|
|
||||||
camPocketExpand: 0,
|
|
||||||
camPocketSmooth: 0,
|
|
||||||
camPocketRefine: 20,
|
|
||||||
camPocketFollow: 5,
|
|
||||||
camPocketContour: false,
|
|
||||||
camPocketEngrave: false,
|
|
||||||
camPocketOutline: false,
|
|
||||||
camPocketZTop: 0,
|
|
||||||
camPocketZBottom: 0,
|
|
||||||
camDrillTool: 1006,
|
|
||||||
camDrillSpindle: 1000,
|
|
||||||
camDrillDownSpeed: 250,
|
|
||||||
camDrillDown: 5,
|
camDrillDown: 5,
|
||||||
|
camDrillDownSpeed: 250,
|
||||||
camDrillDwell: 250,
|
camDrillDwell: 250,
|
||||||
|
camDrillFromStockTop: false,
|
||||||
|
camDrillingOn: false,
|
||||||
camDrillLift: 2,
|
camDrillLift: 2,
|
||||||
camDrillMark: false,
|
camDrillMark: false,
|
||||||
camDrillFromStockTop: false,
|
|
||||||
camDrillThru: 5,
|
|
||||||
camDrillPrecision: 1,
|
camDrillPrecision: 1,
|
||||||
camDrillingOn: false,
|
camDrillSpindle: 1000,
|
||||||
camRegisterSpeed: 1000,
|
camDrillThru: 5,
|
||||||
camRegisterThru: 5,
|
camDrillTool: 1006,
|
||||||
camRegisterOffset: 10,
|
|
||||||
camHelicalTool: 1000,
|
|
||||||
camHelicalSpindle: 1000,
|
|
||||||
camHelicalDownSpeed: 250,
|
|
||||||
camHelicalSpeed: 1000,
|
|
||||||
camHelicalDown: 5,
|
|
||||||
camHelicalBottomFinish: true,
|
|
||||||
camHelicalThru: 0,
|
|
||||||
camHelicalOffset: "auto",
|
|
||||||
camHelicalForceStartAngle: false,
|
|
||||||
camHelicalStartAngle: 0,
|
|
||||||
camHelicalOffsetOverride: 0,
|
|
||||||
camHelicalEntry: false,
|
|
||||||
camHelicalEntryOffset:0,
|
|
||||||
camHelicalReverse: false,
|
|
||||||
camHelicalClockwise: true,
|
|
||||||
camFlipAxis: "X",
|
|
||||||
camFlipOther: "",
|
|
||||||
camLaserEnable: ["M321"],
|
|
||||||
camLaserDisable: ["M322"],
|
|
||||||
camLaserOn: ["M3"],
|
|
||||||
camLaserOff: ["M5"],
|
|
||||||
camLaserSpeed: 100,
|
|
||||||
camLaserPower: 1,
|
|
||||||
camLaserAdaptive: false,
|
|
||||||
camLaserAdaptMod: false,
|
|
||||||
camLaserFlatten: false,
|
|
||||||
camLaserFlatZ: 0,
|
|
||||||
camLaserPowerMin: 0,
|
|
||||||
camLaserPowerMax: 1,
|
|
||||||
camLaserZMin: 0,
|
|
||||||
camLaserZMax: 0,
|
|
||||||
camTabsWidth: 5,
|
|
||||||
camTabsHeight: 5,
|
|
||||||
camTabsDepth: 5,
|
|
||||||
camTabsMidline: false,
|
|
||||||
camDepthFirst: false,
|
|
||||||
camInnerFirst: false,
|
|
||||||
camEaseDown: false,
|
|
||||||
camEaseAngle: 10,
|
camEaseAngle: 10,
|
||||||
camOriginTop: true,
|
camEaseDown: false,
|
||||||
camZAnchor: "middle",
|
camExpertFast: false,
|
||||||
camZOffset: 0,
|
|
||||||
camZTop: 0,
|
|
||||||
camZBottom: 0,
|
|
||||||
camZClearance: 1,
|
|
||||||
camZThru: 0,
|
|
||||||
camFastFeed: 6000,
|
camFastFeed: 6000,
|
||||||
camFastFeedZ: 300,
|
camFastFeedZ: 300,
|
||||||
camTolerance: 0,
|
camFirstZMax: false,
|
||||||
camFlatness: 0.001,
|
camFlatness: 0.001,
|
||||||
camContourBridge: 0,
|
camFlipAxis: "X",
|
||||||
camStockX: 5,
|
camFlipOther: "",
|
||||||
camStockY: 5,
|
camForceZMax: false,
|
||||||
camStockZ: 5,
|
camFullEngage: 0.8,
|
||||||
camStockOffset: true,
|
camHelicalBottomFinish: true,
|
||||||
camStockClipTo: false,
|
camHelicalClockwise: true,
|
||||||
camStockIndexed: false,
|
camHelicalDown: 5,
|
||||||
camStockIndexGrid: true,
|
camHelicalDownSpeed: 250,
|
||||||
camIndexAxis: 0,
|
camHelicalEntry: false,
|
||||||
|
camHelicalEntryOffset:0,
|
||||||
|
camHelicalForceStartAngle: false,
|
||||||
|
camHelicalOffset: "auto",
|
||||||
|
camHelicalOffsetOverride: 0,
|
||||||
|
camHelicalReverse: false,
|
||||||
|
camHelicalSpeed: 1000,
|
||||||
|
camHelicalSpindle: 1000,
|
||||||
|
camHelicalStartAngle: 0,
|
||||||
|
camHelicalThru: 0,
|
||||||
|
camHelicalTool: 1000,
|
||||||
camIndexAbs: true,
|
camIndexAbs: true,
|
||||||
camConventional: false, // outputClockwise
|
camIndexAxis: 0,
|
||||||
|
camInnerFirst: false,
|
||||||
|
camLaserAdaptive: false,
|
||||||
|
camLaserAdaptMod: false,
|
||||||
|
camLaserDisable: ["M322"],
|
||||||
|
camLaserEnable: ["M321"],
|
||||||
|
camLaserFlatten: false,
|
||||||
|
camLaserFlatZ: 0,
|
||||||
|
camLaserOff: ["M5"],
|
||||||
|
camLaserOn: ["M3"],
|
||||||
|
camLaserPower: 1,
|
||||||
|
camLaserPowerMax: 1,
|
||||||
|
camLaserPowerMin: 0,
|
||||||
|
camLaserSpeed: 100,
|
||||||
|
camLaserZMax: 0,
|
||||||
|
camLaserZMin: 0,
|
||||||
|
camLatheAngle: 1,
|
||||||
|
camLatheLinear: true,
|
||||||
|
camLatheOver: 0.1,
|
||||||
|
camLatheSpeed: 500,
|
||||||
|
camLatheSpindle: 1000,
|
||||||
|
camLatheTool: 1000,
|
||||||
|
camLatheOffStart: 0,
|
||||||
|
camLatheOffEnd: 0,
|
||||||
|
camLevelDown: 0,
|
||||||
|
camLevelInset: 0.5,
|
||||||
|
camLevelOver: 0.75,
|
||||||
|
camLevelSpeed: 1000,
|
||||||
|
camLevelSpindle: 1000,
|
||||||
|
camLevelStepZ: 0,
|
||||||
|
camLevelStock: true,
|
||||||
|
camLevelTool: 1000,
|
||||||
camOriginCenter: false,
|
camOriginCenter: false,
|
||||||
camOriginOffX: 0,
|
camOriginOffX: 0,
|
||||||
camOriginOffY: 0,
|
camOriginOffY: 0,
|
||||||
camOriginOffZ: 0,
|
camOriginOffZ: 0,
|
||||||
|
camOriginTop: true,
|
||||||
|
camOutlineDogbone: false,
|
||||||
|
camOutlineDown: 3,
|
||||||
|
camOutlineIn: false,
|
||||||
|
camOutlineOmitThru: false,
|
||||||
|
camOutlineOmitVoid: false,
|
||||||
|
camOutlineOn: true,
|
||||||
|
camOutlineOut: true,
|
||||||
|
camOutlineOver: 0.4,
|
||||||
|
camOutlineOverCount: 1,
|
||||||
|
camOutlinePlunge: 250,
|
||||||
|
camOutlineSpeed: 800,
|
||||||
|
camOutlineSpindle: 1000,
|
||||||
|
camOutlineTool: 1000,
|
||||||
|
camOutlineTop: true,
|
||||||
|
camOutlineWide: false,
|
||||||
|
camPocketContour: false,
|
||||||
|
camPocketDown: 1,
|
||||||
|
camPocketEngrave: false,
|
||||||
|
camPocketExpand: 0,
|
||||||
|
camPocketFollow: 5,
|
||||||
|
camPocketOutline: false,
|
||||||
|
camPocketOver: 0.25,
|
||||||
|
camPocketPlunge: 200,
|
||||||
|
camPocketRefine: 20,
|
||||||
|
camPocketSmooth: 1,
|
||||||
|
camPocketSpeed: 250,
|
||||||
|
camPocketSpindle: 1000,
|
||||||
|
camPocketTool: 1000,
|
||||||
|
camPocketZBottom: 0,
|
||||||
|
camPocketZTop: 0,
|
||||||
|
camRegisterOffset: 10,
|
||||||
|
camRegisterSpeed: 1000,
|
||||||
|
camRegisterThru: 5,
|
||||||
|
camRoughAll: true,
|
||||||
|
camRoughDown: 2,
|
||||||
|
camRoughFlat: true,
|
||||||
|
camRoughIn: true,
|
||||||
|
camRoughOmitThru: false,
|
||||||
|
camRoughOmitVoid: false,
|
||||||
|
camRoughOn: true,
|
||||||
|
camRoughOver: 0.4,
|
||||||
|
camRoughPlunge: 250,
|
||||||
|
camRoughSpeed: 1000,
|
||||||
|
camRoughSpindle: 1000,
|
||||||
|
camRoughStock: 0,
|
||||||
|
camRoughStockZ: 0,
|
||||||
|
camRoughTool: 1000,
|
||||||
|
camRoughTop: true,
|
||||||
|
camRoughVoid: false,
|
||||||
|
camStockClipTo: false,
|
||||||
|
camStockIndexed: false,
|
||||||
|
camStockIndexGrid: true,
|
||||||
|
camStockOffset: true,
|
||||||
|
camStockX: 5,
|
||||||
|
camStockY: 5,
|
||||||
|
camStockZ: 5,
|
||||||
|
camTabsDepth: 5,
|
||||||
|
camTabsHeight: 5,
|
||||||
|
camTabsMidline: false,
|
||||||
|
camTabsWidth: 5,
|
||||||
|
camTolerance: 0,
|
||||||
|
camToolInit: true,
|
||||||
|
camTraceDogbone: false,
|
||||||
|
camTraceDown: 0,
|
||||||
|
camTraceLines: false,
|
||||||
|
camTraceMerge: false,
|
||||||
|
camTraceOffOver: 0,
|
||||||
|
camTraceOver: 0.5,
|
||||||
|
camTracePlunge: 200,
|
||||||
|
camTraceSpeed: 250,
|
||||||
|
camTraceSpindle: 1000,
|
||||||
|
camTraceThru: 0,
|
||||||
|
camTraceTool: 1000,
|
||||||
|
camTraceType: "follow",
|
||||||
|
camTraceZBottom: 0,
|
||||||
|
camTraceZTop: 0,
|
||||||
|
camTrueShadow: false,
|
||||||
|
camZAnchor: "middle",
|
||||||
|
camZBottom: 0,
|
||||||
|
camZClearance: 1,
|
||||||
|
camZOffset: 0,
|
||||||
|
camZThru: 0,
|
||||||
|
camZTop: 0,
|
||||||
|
op2: [], // flip ops
|
||||||
|
ops: [], // current ops
|
||||||
outputInvertX: false,
|
outputInvertX: false,
|
||||||
outputInvertY: false,
|
outputInvertY: false,
|
||||||
camExpertFast: false,
|
processName: "default",
|
||||||
camTrueShadow: false,
|
|
||||||
camArcEnabled: false,
|
|
||||||
camArcTolerance: 0.15,
|
|
||||||
camArcResolution: 5,
|
|
||||||
camForceZMax: false,
|
|
||||||
camFirstZMax: false,
|
|
||||||
camToolInit: true,
|
|
||||||
camFullEngage: 0.8,
|
|
||||||
ops: [], // current ops
|
|
||||||
op2: [] // flip ops
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
laser: {
|
laser: {
|
||||||
|
|
@ -915,37 +916,37 @@ export const conf = {
|
||||||
devproc: {},
|
devproc: {},
|
||||||
// application ui and control preferences (Q menu)
|
// application ui and control preferences (Q menu)
|
||||||
controller:{
|
controller:{
|
||||||
view: null,
|
animesh: "800",
|
||||||
dark: false,
|
|
||||||
shiny: false,
|
|
||||||
drawer: false,
|
|
||||||
scrolls: true,
|
|
||||||
zoomSpeed: 1.0,
|
|
||||||
lineType: "path",
|
|
||||||
autoSave: true,
|
|
||||||
antiAlias: true,
|
antiAlias: true,
|
||||||
|
assembly: false,
|
||||||
|
autoLayout: true,
|
||||||
|
autoSave: true,
|
||||||
|
dark: false,
|
||||||
|
detail: "50",
|
||||||
|
devel: false,
|
||||||
|
drawer: false,
|
||||||
|
edgeangle: 20,
|
||||||
|
exportOcto: false,
|
||||||
|
exportPreview: false,
|
||||||
|
exportThumb: false,
|
||||||
|
freeLayout: true,
|
||||||
|
healMesh: false,
|
||||||
|
lineType: "path",
|
||||||
|
manifold: false,
|
||||||
|
ortho: false,
|
||||||
reverseZoom: true,
|
reverseZoom: true,
|
||||||
|
scrolls: true,
|
||||||
|
shiny: true,
|
||||||
showOrigin: false,
|
showOrigin: false,
|
||||||
showRulers: true,
|
showRulers: true,
|
||||||
showSpeeds: true,
|
showSpeeds: true,
|
||||||
freeLayout: true,
|
|
||||||
autoLayout: true,
|
|
||||||
spaceRandoX: false,
|
|
||||||
spaceLayout: 1,
|
spaceLayout: 1,
|
||||||
edgeangle: 20,
|
spaceRandoX: false,
|
||||||
units: "mm",
|
|
||||||
exportOcto: false,
|
|
||||||
exportGhost: false,
|
|
||||||
exportLocal: false,
|
|
||||||
exportThumb: false,
|
|
||||||
exportPreview: false,
|
|
||||||
detail: "50",
|
|
||||||
animesh: "800",
|
|
||||||
healMesh: false,
|
|
||||||
threaded: true,
|
threaded: true,
|
||||||
assembly: false,
|
units: "mm",
|
||||||
ortho: false,
|
view: null,
|
||||||
devel: false
|
webGPU: false,
|
||||||
|
zoomSpeed: 1.0,
|
||||||
},
|
},
|
||||||
// default hidden ui groups
|
// default hidden ui groups
|
||||||
hidden: {
|
hidden: {
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,8 @@ const LISTS = {
|
||||||
regaxis: [
|
regaxis: [
|
||||||
{ name: "X" },
|
{ name: "X" },
|
||||||
{ name: "Y" },
|
{ name: "Y" },
|
||||||
{ name: "-" }
|
{ name: "-" },
|
||||||
|
{ name: "=" }
|
||||||
],
|
],
|
||||||
regpoints: [
|
regpoints: [
|
||||||
{ name: "2" },
|
{ name: "2" },
|
||||||
|
|
|
||||||
|
|
@ -218,207 +218,6 @@ function exportGCodeDialog(gcode, sections, info, names) {
|
||||||
ajax.send(form);
|
ajax.send(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
function gridhost_tracker(host,key) {
|
|
||||||
ajax(host+"/api/check?key="+key, function(data) {
|
|
||||||
data = js2o(data);
|
|
||||||
console.log(data);
|
|
||||||
if (!(data.done || data.error)) {
|
|
||||||
setTimeout(function() { gridhost_tracker(host,key) }, 1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function gridlocal_probe(ev, devs) {
|
|
||||||
if (ev && ev.code !== 'Enter') return;
|
|
||||||
|
|
||||||
if (!devs && api.probe.local(gridlocal_probe)) return;
|
|
||||||
|
|
||||||
grid_local = devs;
|
|
||||||
|
|
||||||
let gdev = localGet('grid-local');
|
|
||||||
let gloc = $('grid-local');
|
|
||||||
let html = [];
|
|
||||||
for (let uuid in devs) {
|
|
||||||
gdev = gdev || uuid;
|
|
||||||
let dev = devs[uuid];
|
|
||||||
let sel = uuid === gdev ? ' selected' : '';
|
|
||||||
html.push(`<option id="gl-${uuid}" value="${uuid}" ${sel}>${dev.stat.device.name}</option>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (html.length) {
|
|
||||||
gloc.innerHTML = html.join('\n');
|
|
||||||
gloc.onchange = (ev) => {
|
|
||||||
localSet('grid-local', gloc.options[gloc.selectedIndex].value);
|
|
||||||
};
|
|
||||||
$('send-to-gridhead').style.display = 'flex';
|
|
||||||
$('send-to-gridspool').style.display = 'flex';
|
|
||||||
} else {
|
|
||||||
$('send-to-gridhead').style.display = '';
|
|
||||||
$('send-to-gridspool').style.display = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendto_gridlocal() {
|
|
||||||
let uuid = $('grid-local').value;
|
|
||||||
let dev = grid_local[uuid];
|
|
||||||
if (dev) {
|
|
||||||
let file = $('print-filename').value;
|
|
||||||
fetch(
|
|
||||||
`${api.probe.live}/api/grid_send?uuid=${uuid}&file=${encodeURIComponent(file + "." + fileext)}`,
|
|
||||||
{method: "POST", body: gcode}
|
|
||||||
)
|
|
||||||
.then(t => t.text())
|
|
||||||
.then(t => {
|
|
||||||
api.stats.add(`ua_${api.mode.get_lower()}_print_local_ok`);
|
|
||||||
console.log({grid_spool_said: t});
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
api.stats.add(`ua_${api.mode.get_lower()}_print_local_err`);
|
|
||||||
console.log({grid_local_spool_error: e});
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
api.modal.hide();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function admin_gridlocal() {
|
|
||||||
let dev = grid_local[$('grid-local').value];
|
|
||||||
if (dev && dev.stat && dev.stat.device) {
|
|
||||||
let dsd = dev.stat.device;
|
|
||||||
window.open(`http://${dsd.addr[0]}:${dsd.port || 4080}`, "_grid_admin");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function gridhost_probe(ev, set_host) {
|
|
||||||
if (ev && ev.code !== 'Enter') return;
|
|
||||||
if (!(grid_host && grid_apik)) return;
|
|
||||||
|
|
||||||
if (set_host) grid_host.value = set_host;
|
|
||||||
|
|
||||||
let xhtr = new XMLHttpRequest(),
|
|
||||||
host = grid_host.value,
|
|
||||||
apik = grid_apik.value,
|
|
||||||
target = grid_target.value;
|
|
||||||
|
|
||||||
if (!apik) $('gpapik').style.display = 'none';
|
|
||||||
|
|
||||||
if (!host && api.probe.grid(gridhost_probe)) return;
|
|
||||||
|
|
||||||
if (!host) return;
|
|
||||||
|
|
||||||
xhtr.onreadystatechange = function() {
|
|
||||||
if (xhtr.readyState === 4) {
|
|
||||||
if (xhtr.status >= 200 && xhtr.status < 300) {
|
|
||||||
localSet('grid-host', host);
|
|
||||||
localSet('grid-apik', apik);
|
|
||||||
let res = JSON.parse(xhtr.responseText);
|
|
||||||
let sel = false;
|
|
||||||
let match = false;
|
|
||||||
let first = null;
|
|
||||||
let html = [];
|
|
||||||
grid_targets = {};
|
|
||||||
for (let key in res) {
|
|
||||||
first = first || key;
|
|
||||||
if (!localGet('grid-target')) {
|
|
||||||
localSet('grid-target', key);
|
|
||||||
sel = true;
|
|
||||||
} else {
|
|
||||||
sel = localGet('grid-target') === key;
|
|
||||||
}
|
|
||||||
match = match || sel;
|
|
||||||
grid_targets[html.length] = key;
|
|
||||||
html.push(
|
|
||||||
"<option id='gpo-'" + key + " value='" +key + "'" +
|
|
||||||
(sel ? " selected" : "") +
|
|
||||||
">" +
|
|
||||||
(res[key].comment || key) +
|
|
||||||
"</option>"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!match) {
|
|
||||||
localSet('grid-target', first);
|
|
||||||
}
|
|
||||||
grid_target.innerHTML = html.join('\n');
|
|
||||||
} else if (xhtr.status === 401) {
|
|
||||||
$('gpapik').style.display = '';
|
|
||||||
} else {
|
|
||||||
local.removeItem('grid-host');
|
|
||||||
local.removeItem('grid-apik');
|
|
||||||
console.log("invalid grid:host url");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhtr.open("GET", host + "/api/active?key=" + apik);
|
|
||||||
xhtr.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendto_gridhost() {
|
|
||||||
if (!(grid_host && grid_apik)) return;
|
|
||||||
|
|
||||||
let xhtr = new XMLHttpRequest(),
|
|
||||||
host = grid_host.value,
|
|
||||||
apik = grid_apik.value,
|
|
||||||
target = localGet('grid-target') || '';
|
|
||||||
|
|
||||||
if (target === '') {
|
|
||||||
api.show.alert('invalid or missing target');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (host.indexOf("http") !== 0) {
|
|
||||||
api.show.alert("host missing protocol (http:// or https://)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (host.indexOf("://") < 0) {
|
|
||||||
api.show.alert("host:port malformed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (api.const.SECURE && !api.util.isSecure(host)) {
|
|
||||||
api.show.alert("host must begin with 'https' on a secure site");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
localSet('grid-host', host.trim());
|
|
||||||
localSet('grid-apik', apik.trim());
|
|
||||||
|
|
||||||
xhtr.onreadystatechange = function() {
|
|
||||||
if (xhtr.readyState === 4) {
|
|
||||||
let status = xhtr.status;
|
|
||||||
api.stats.add(`ua_${api.mode.get_lower()}_print_grid_${status}`);
|
|
||||||
if (status >= 200 && status < 300) {
|
|
||||||
let json = js2o(xhtr.responseText);
|
|
||||||
gridhost_tracker(host,json.key);
|
|
||||||
api.ajax(host+"/api/wait?key="+json.key, function(data) {
|
|
||||||
data = js2o(data);
|
|
||||||
console.log(data);
|
|
||||||
api.show.alert("print to "+target+": "+data.status, 600);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
api.show.alert("grid:host error\nstatus: "+status+"\nmessage: "+xhtr.responseText, 10000);
|
|
||||||
}
|
|
||||||
api.show.progress(0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhtr.upload.addEventListener('progress', function(evt) {
|
|
||||||
api.show.progress(evt.loaded/evt.total, "sending");
|
|
||||||
});
|
|
||||||
filename = $('print-filename').value;
|
|
||||||
xhtr.open("POST",
|
|
||||||
host + "/api/print?" +
|
|
||||||
"filename=" + filename +
|
|
||||||
"&target=" + target +
|
|
||||||
"&key=" + apik +
|
|
||||||
"&time=" + Math.round(info.time) +
|
|
||||||
"&length=" + Math.round(info.distance) +
|
|
||||||
"&image=" + filename
|
|
||||||
);
|
|
||||||
xhtr.setRequestHeader("Content-Type", "text/plain");
|
|
||||||
let snapshot = api.view.snapshot;
|
|
||||||
xhtr.send(snapshot ? [gcode,snapshot].join("\0") : gcode);
|
|
||||||
api.modal.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
function download() {
|
function download() {
|
||||||
filename = $('print-filename').value;
|
filename = $('print-filename').value;
|
||||||
api.util.download(gcode, filename + "." + fileext);
|
api.util.download(gcode, filename + "." + fileext);
|
||||||
|
|
@ -463,8 +262,6 @@ function exportGCodeDialog(gcode, sections, info, names) {
|
||||||
let set = api.conf.get();
|
let set = api.conf.get();
|
||||||
let fdm = MODE === MODES.FDM;
|
let fdm = MODE === MODES.FDM;
|
||||||
let octo = set.controller.exportOcto && MODE !== MODES.CAM;
|
let octo = set.controller.exportOcto && MODE !== MODES.CAM;
|
||||||
let ghost = set.controller.exportGhost;
|
|
||||||
let local = set.controller.exportLocal;
|
|
||||||
let preview = set.controller.exportPreview;
|
let preview = set.controller.exportPreview;
|
||||||
$('code-preview-head').style.display = preview ? '' : 'none';
|
$('code-preview-head').style.display = preview ? '' : 'none';
|
||||||
$('code-preview').style.display = preview ? '' : 'none';
|
$('code-preview').style.display = preview ? '' : 'none';
|
||||||
|
|
@ -484,8 +281,6 @@ function exportGCodeDialog(gcode, sections, info, names) {
|
||||||
// persist fields when changed
|
// persist fields when changed
|
||||||
bindField('octo-host', 'octo-host');
|
bindField('octo-host', 'octo-host');
|
||||||
bindField('octo-apik', 'octo-apik');
|
bindField('octo-apik', 'octo-apik');
|
||||||
bindField('grid-host', 'grid-host');
|
|
||||||
bindField('grid-apik', 'grid-apik');
|
|
||||||
|
|
||||||
// in cam mode, show zip file option
|
// in cam mode, show zip file option
|
||||||
let downloadZip = $('print-zip');
|
let downloadZip = $('print-zip');
|
||||||
|
|
@ -699,37 +494,11 @@ function exportGCodeDialog(gcode, sections, info, names) {
|
||||||
$('ophost').style.display = 'none';
|
$('ophost').style.display = 'none';
|
||||||
$('opapik').style.display = 'none';
|
$('opapik').style.display = 'none';
|
||||||
$('oph1nt').style.display = 'none';
|
$('oph1nt').style.display = 'none';
|
||||||
$('send-to-gridhost').style.display = 'none';
|
|
||||||
}
|
}
|
||||||
octo_host.value = localGet('octo-host') || '';
|
octo_host.value = localGet('octo-host') || '';
|
||||||
octo_apik.value = localGet('octo-apik') || '';
|
octo_apik.value = localGet('octo-apik') || '';
|
||||||
} catch (e) { console.log(e) }
|
} catch (e) { console.log(e) }
|
||||||
|
|
||||||
// grid:host setup
|
|
||||||
$('send-to-hosthead').style.display = ghost ? '' : 'none';
|
|
||||||
$('send-to-gridhost').style.display = ghost ? '' : 'none';
|
|
||||||
if (ghost) try {
|
|
||||||
$('print-gridhost').onclick = sendto_gridhost;
|
|
||||||
$('grid-host').onkeyup = gridhost_probe;
|
|
||||||
$('grid-apik').onkeyup = gridhost_probe;
|
|
||||||
grid_host = $('grid-host');
|
|
||||||
grid_apik = $('grid-apik');
|
|
||||||
grid_target = $('grid-target');
|
|
||||||
grid_target.onchange = function(ev) {
|
|
||||||
localSet('grid-target', grid_targets[grid_target.selectedIndex]);
|
|
||||||
};
|
|
||||||
grid_host.value = localGet('grid-host') || '';
|
|
||||||
grid_apik.value = localGet('grid-apik') || '';
|
|
||||||
gridhost_probe();
|
|
||||||
} catch (e) { console.log(e) }
|
|
||||||
|
|
||||||
// grid:local setup
|
|
||||||
if (local) try {
|
|
||||||
gridlocal_probe();
|
|
||||||
$('print-gridlocal').onclick = sendto_gridlocal;
|
|
||||||
$('admin-gridlocal').onclick = admin_gridlocal;
|
|
||||||
} catch (e) { console.log(e) }
|
|
||||||
|
|
||||||
// preview of the generated GCODE (first 64k max)
|
// preview of the generated GCODE (first 64k max)
|
||||||
if (preview && gcode) $('code-preview-textarea').value = gcode.substring(0,65535);
|
if (preview && gcode) $('code-preview-textarea').value = gcode.substring(0,65535);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,28 +142,28 @@ function booleanSave() {
|
||||||
if (control.antiAlias != ui.antiAlias.checked) {
|
if (control.antiAlias != ui.antiAlias.checked) {
|
||||||
api.show.alert('Page Reload Required to Change Aliasing');
|
api.show.alert('Page Reload Required to Change Aliasing');
|
||||||
}
|
}
|
||||||
control.shiny = ui.shiny.checked;
|
control.antiAlias = ui.antiAlias.checked;
|
||||||
|
control.assembly = ui.assembly.checked;
|
||||||
|
control.autoLayout = ui.autoLayout.checked;
|
||||||
|
control.autoSave = ui.autoSave.checked;
|
||||||
|
control.dark = ui.dark.checked;
|
||||||
|
control.devel = ui.devel.checked;
|
||||||
control.drawer = ui.drawer.checked;
|
control.drawer = ui.drawer.checked;
|
||||||
|
control.exportOcto = ui.exportOcto.checked;
|
||||||
|
control.exportPreview = ui.exportPreview.checked;
|
||||||
|
control.exportThumb = ui.exportThumb.checked;
|
||||||
|
control.freeLayout = ui.freeLayout.checked;
|
||||||
|
control.healMesh = ui.healMesh.checked;
|
||||||
|
control.ortho = ui.ortho.checked;
|
||||||
|
control.manifold = ui.manifold.checked;
|
||||||
|
control.reverseZoom = ui.reverseZoom.checked;
|
||||||
control.scrolls = ui.scrolls.checked;
|
control.scrolls = ui.scrolls.checked;
|
||||||
|
control.shiny = ui.shiny.checked;
|
||||||
control.showOrigin = ui.showOrigin.checked;
|
control.showOrigin = ui.showOrigin.checked;
|
||||||
control.showRulers = ui.showRulers.checked;
|
control.showRulers = ui.showRulers.checked;
|
||||||
control.autoLayout = ui.autoLayout.checked;
|
|
||||||
control.freeLayout = ui.freeLayout.checked;
|
|
||||||
control.spaceRandoX = ui.spaceRandoX.checked;
|
control.spaceRandoX = ui.spaceRandoX.checked;
|
||||||
control.autoSave = ui.autoSave.checked;
|
|
||||||
control.antiAlias = ui.antiAlias.checked;
|
|
||||||
control.reverseZoom = ui.reverseZoom.checked;
|
|
||||||
control.dark = ui.dark.checked;
|
|
||||||
control.exportOcto = ui.exportOcto.checked;
|
|
||||||
control.exportGhost = ui.exportGhost.checked;
|
|
||||||
control.exportLocal = ui.exportLocal.checked;
|
|
||||||
control.exportThumb = ui.exportThumb.checked;
|
|
||||||
control.exportPreview = ui.exportPreview.checked;
|
|
||||||
control.healMesh = ui.healMesh.checked;
|
|
||||||
control.threaded = setThreaded(ui.threaded.checked);
|
control.threaded = setThreaded(ui.threaded.checked);
|
||||||
control.assembly = ui.assembly.checked;
|
control.webGPU = ui.webGPU.checked;
|
||||||
control.ortho = ui.ortho.checked;
|
|
||||||
control.devel = ui.devel.checked;
|
|
||||||
space.view.setZoom(control.reverseZoom, control.zoomSpeed);
|
space.view.setZoom(control.reverseZoom, control.zoomSpeed);
|
||||||
// platform.layout();
|
// platform.layout();
|
||||||
api.conf.save();
|
api.conf.save();
|
||||||
|
|
@ -904,7 +904,6 @@ function init_one() {
|
||||||
xany: $('mod-x-any'),
|
xany: $('mod-x-any'),
|
||||||
xsla: $('mod-x-sla'),
|
xsla: $('mod-x-sla'),
|
||||||
xlaser: $('mod-x-laser'),
|
xlaser: $('mod-x-laser'),
|
||||||
local: $('mod-local'),
|
|
||||||
don8: $('mod-don8'),
|
don8: $('mod-don8'),
|
||||||
any: $('mod-any'),
|
any: $('mod-any'),
|
||||||
},
|
},
|
||||||
|
|
@ -1048,6 +1047,7 @@ function init_one() {
|
||||||
showSpeeds: newBoolean(LANG.op_sped_s, speedSave, {title:LANG.op_sped_l}),
|
showSpeeds: newBoolean(LANG.op_sped_s, speedSave, {title:LANG.op_sped_l}),
|
||||||
shiny: newBoolean(LANG.op_shny_s, booleanSave, {title:LANG.op_shny_l, modes:FDM}),
|
shiny: newBoolean(LANG.op_shny_s, booleanSave, {title:LANG.op_shny_l, modes:FDM}),
|
||||||
lineType: newSelect(LANG.op_line_s, {title: LANG.op_line_l, action: lineTypeSave, modes:FDM}, "linetype"),
|
lineType: newSelect(LANG.op_line_s, {title: LANG.op_line_l, action: lineTypeSave, modes:FDM}, "linetype"),
|
||||||
|
manifold: newBoolean(LANG.op_mani_s, booleanSave, {title: LANG.op_mani_l, modes:CAM}, "manifold"),
|
||||||
animesh: newSelect(LANG.op_anim_s, {title: LANG.op_anim_l, action: aniMeshSave, modes:CAM}, "animesh"),
|
animesh: newSelect(LANG.op_anim_s, {title: LANG.op_anim_l, action: aniMeshSave, modes:CAM}, "animesh"),
|
||||||
units: newSelect(LANG.op_unit_s, {title: LANG.op_unit_l, action: unitsSave, modes:CAM}, "units"),
|
units: newSelect(LANG.op_unit_s, {title: LANG.op_unit_l, action: unitsSave, modes:CAM}, "units"),
|
||||||
edgeangle: newInput(LANG.op_spoa_s, {title:LANG.op_spoa_l, convert:toFloat, size:3}),
|
edgeangle: newInput(LANG.op_spoa_s, {title:LANG.op_spoa_l, convert:toFloat, size:3}),
|
||||||
|
|
@ -1058,8 +1058,6 @@ function init_one() {
|
||||||
spaceRandoX: newBoolean(LANG.op_spcx_s, booleanSave, {title:LANG.op_spcx_l, show:isBelt}),
|
spaceRandoX: newBoolean(LANG.op_spcx_s, booleanSave, {title:LANG.op_spcx_l, show:isBelt}),
|
||||||
spaceLayout: newInput(LANG.op_spcr_s, {title:LANG.op_spcr_l, convert:toFloat, size:3, units}),
|
spaceLayout: newInput(LANG.op_spcr_s, {title:LANG.op_spcr_l, convert:toFloat, size:3, units}),
|
||||||
_____: newGroup(LANG.xp_menu, $('prefs-xpo'), {inline: true}),
|
_____: newGroup(LANG.xp_menu, $('prefs-xpo'), {inline: true}),
|
||||||
exportLocal: newBoolean(`Grid:Local`, booleanSave, {title:LANG.op_exgl_l}),
|
|
||||||
exportGhost: newBoolean(`Grid:Host`, booleanSave, {title:LANG.op_exgh_l}),
|
|
||||||
exportOcto: newBoolean(`OctoPrint`, booleanSave, {title:LANG.op_exop_l}),
|
exportOcto: newBoolean(`OctoPrint`, booleanSave, {title:LANG.op_exop_l}),
|
||||||
exportThumb: newBoolean(`Thumbnail`, booleanSave, {modes:FDM}),
|
exportThumb: newBoolean(`Thumbnail`, booleanSave, {modes:FDM}),
|
||||||
exportPreview: newBoolean(`Code Preview`, booleanSave),
|
exportPreview: newBoolean(`Code Preview`, booleanSave),
|
||||||
|
|
@ -1068,6 +1066,7 @@ function init_one() {
|
||||||
healMesh: newBoolean(LANG.pt_heal_s, booleanSave, {title: LANG.pt_heal_l}),
|
healMesh: newBoolean(LANG.pt_heal_s, booleanSave, {title: LANG.pt_heal_l}),
|
||||||
threaded: newBoolean(LANG.pt_thrd_s, booleanSave, {title: LANG.pt_thrd_l, modes:THREED}),
|
threaded: newBoolean(LANG.pt_thrd_s, booleanSave, {title: LANG.pt_thrd_l, modes:THREED}),
|
||||||
assembly: newBoolean(LANG.pt_assy_s, booleanSave, {title: LANG.pt_assy_l, modes:THREED}),
|
assembly: newBoolean(LANG.pt_assy_s, booleanSave, {title: LANG.pt_assy_l, modes:THREED}),
|
||||||
|
webGPU: newBoolean(LANG.pt_wgpu_s, booleanSave, {title: LANG.pt_wgpu_l, modes:THREED}),
|
||||||
|
|
||||||
prefadd: uc.checkpoint($('prefs-add')),
|
prefadd: uc.checkpoint($('prefs-add')),
|
||||||
|
|
||||||
|
|
@ -1635,20 +1634,23 @@ function init_two() {
|
||||||
space.platform.setZOff(0.05);
|
space.platform.setZOff(0.05);
|
||||||
|
|
||||||
// restore UI state from settings
|
// restore UI state from settings
|
||||||
|
ui.antiAlias.checked = control.antiAlias;
|
||||||
|
ui.assembly.checked = control.assembly;
|
||||||
|
ui.autoLayout.checked = control.autoLayout;
|
||||||
|
ui.autoSave.checked = control.autoSave;
|
||||||
|
ui.devel.checked = control.devel;
|
||||||
|
ui.freeLayout.checked = control.freeLayout;
|
||||||
|
ui.healMesh.checked = control.healMesh;
|
||||||
|
ui.manifold.checked = control.manifold;
|
||||||
|
ui.ortho.checked = control.ortho;
|
||||||
|
ui.reverseZoom.checked = control.reverseZoom;
|
||||||
ui.showOrigin.checked = control.showOrigin;
|
ui.showOrigin.checked = control.showOrigin;
|
||||||
ui.showRulers.checked = control.showRulers;
|
ui.showRulers.checked = control.showRulers;
|
||||||
ui.showSpeeds.checked = control.showSpeeds;
|
ui.showSpeeds.checked = control.showSpeeds;
|
||||||
ui.freeLayout.checked = control.freeLayout;
|
|
||||||
ui.autoLayout.checked = control.autoLayout;
|
|
||||||
ui.spaceRandoX.checked = control.spaceRandoX;
|
ui.spaceRandoX.checked = control.spaceRandoX;
|
||||||
ui.antiAlias.checked = control.antiAlias;
|
|
||||||
ui.reverseZoom.checked = control.reverseZoom;
|
|
||||||
ui.autoSave.checked = control.autoSave;
|
|
||||||
ui.healMesh.checked = control.healMesh;
|
|
||||||
ui.threaded.checked = setThreaded(control.threaded);
|
ui.threaded.checked = setThreaded(control.threaded);
|
||||||
ui.assembly.checked = control.assembly;
|
ui.webGPU.checked = control.webGPU;
|
||||||
ui.ortho.checked = control.ortho;
|
|
||||||
ui.devel.checked = control.devel;
|
|
||||||
lineTypeSave();
|
lineTypeSave();
|
||||||
detailSave();
|
detailSave();
|
||||||
updateStats();
|
updateStats();
|
||||||
|
|
@ -1789,6 +1791,7 @@ function init_two() {
|
||||||
$('context-duplicate').onclick = duplicateSelection;
|
$('context-duplicate').onclick = duplicateSelection;
|
||||||
$('context-mirror').onclick = mirrorSelection;
|
$('context-mirror').onclick = mirrorSelection;
|
||||||
$('context-layflat').onclick = () => { api.event.emit("tool.mesh.lay-flat") };
|
$('context-layflat').onclick = () => { api.event.emit("tool.mesh.lay-flat") };
|
||||||
|
$('context-lefty').onclick = () => { api.event.emit("tool.mesh.lefty") };
|
||||||
$('context-setfocus').onclick = () => {
|
$('context-setfocus').onclick = () => {
|
||||||
api.event.emit(
|
api.event.emit(
|
||||||
"tool.camera.focus",
|
"tool.camera.focus",
|
||||||
|
|
|
||||||
|
|
@ -114,12 +114,6 @@ export const mode = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const probe = {
|
|
||||||
live: "https://live.grid.space",
|
|
||||||
grid: noop,
|
|
||||||
local: noop
|
|
||||||
};
|
|
||||||
|
|
||||||
export const process = {
|
export const process = {
|
||||||
code: currentProcessCode,
|
code: currentProcessCode,
|
||||||
get: currentProcessName
|
get: currentProcessName
|
||||||
|
|
@ -784,43 +778,7 @@ function showHelpFile(local,then) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showLocal() {
|
function showLocal() {
|
||||||
showModal('local');
|
// deprecated
|
||||||
api.probe.local((err,data) => {
|
|
||||||
let devc = 0;
|
|
||||||
let bind = [];
|
|
||||||
let html = ['<table>'];
|
|
||||||
html.push(`<thead><tr><th>device</th><th>type</th><th>status</th><th></th></tr></thead>`);
|
|
||||||
html.push(`<tbody>`);
|
|
||||||
let recs = [];
|
|
||||||
for (let k in data) {
|
|
||||||
recs.push(data[k].stat);
|
|
||||||
}
|
|
||||||
recs.sort((a,b) => {
|
|
||||||
return a.device.name < b.device.name ? -1 : 1;
|
|
||||||
});
|
|
||||||
for (let r of recs) {
|
|
||||||
bind.push({uuid: r.device.uuid, host: r.device.addr[0], port: r.device.port});
|
|
||||||
html.push(`<tr>`);
|
|
||||||
html.push(`<td>${r.device.name}</td>`);
|
|
||||||
html.push(`<td>${r.device.mode}</td>`);
|
|
||||||
html.push(`<td>${r.state}</td>`);
|
|
||||||
html.push(`<td><button id="${r.device.uuid}">admin</button></td>`);
|
|
||||||
html.push(`</tr>`);
|
|
||||||
devc++;
|
|
||||||
}
|
|
||||||
html.push(`</tbody>`);
|
|
||||||
html.push(`</table>`);
|
|
||||||
if (devc) {
|
|
||||||
$('mod-local').innerHTML = html.join('');
|
|
||||||
} else {
|
|
||||||
$('mod-local').innerHTML = `<br><b>no local devices</b>`;
|
|
||||||
}
|
|
||||||
bind.forEach(rec => {
|
|
||||||
$(rec.uuid).onclick = () => {
|
|
||||||
window.open(`http://${rec.host}:${rec.port||4080}/`);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setViewMode(mode) {
|
function setViewMode(mode) {
|
||||||
|
|
|
||||||
|
|
@ -163,19 +163,19 @@ function platformUpdateTopZ() {
|
||||||
const wzmax = bounds.max.z;
|
const wzmax = bounds.max.z;
|
||||||
const zdelta = process.camZOffset || 0;
|
const zdelta = process.camZOffset || 0;
|
||||||
switch (process.camZAnchor) {
|
switch (process.camZAnchor) {
|
||||||
case 'top':
|
case 'top':
|
||||||
widget.setTopZ(stock.z - zdelta);
|
widget.setTopZ(stock.z - zdelta, true);
|
||||||
break;
|
break;
|
||||||
case 'middle':
|
case 'middle':
|
||||||
if (widget.isIndexed) {
|
if (widget.isIndexed) {
|
||||||
widget.setTopZ(zdelta);
|
widget.setTopZ(zdelta, true);
|
||||||
} else {
|
} else {
|
||||||
widget.setTopZ(stock.z - (stock.z - wzmax) / 2 + zdelta);
|
widget.setTopZ(stock.z - (stock.z - wzmax) / 2 + zdelta, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'bottom':
|
case 'bottom':
|
||||||
widget.setTopZ(wzmax + zdelta);
|
widget.setTopZ(wzmax + zdelta, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
widget.setTopZ(0);
|
widget.setTopZ(0);
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@ class Print {
|
||||||
// drop duplicates (usually intruced by FDM bisections)
|
// drop duplicates (usually intruced by FDM bisections)
|
||||||
if (lastPoint && point && !arc && type !== 'lerp') {
|
if (lastPoint && point && !arc && type !== 'lerp') {
|
||||||
// nested due to uglify confusing browser
|
// nested due to uglify confusing browser
|
||||||
const { x, y, z } = lastPoint;
|
const { x, y, z, a } = lastPoint;
|
||||||
if (point.x == x && point.y == y && point.z == z && lastEmit == emit) {
|
if (point.x == x && point.y == y && point.z == z && point.a == z && lastEmit == emit) {
|
||||||
return lastOut;
|
return lastOut;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -129,6 +129,7 @@ class Print {
|
||||||
close = !options.open,
|
close = !options.open,
|
||||||
scarf = !poly.open ? (options.scarf ?? 0) : false,
|
scarf = !poly.open ? (options.scarf ?? 0) : false,
|
||||||
tool = options.tool,
|
tool = options.tool,
|
||||||
|
zmax = options.zmax,
|
||||||
last = startPoint,
|
last = startPoint,
|
||||||
first = true;
|
first = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ function rotate(x, y, z) {
|
||||||
space.update();
|
space.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
function merge() {
|
function merge({ deleteMerged = true }) {
|
||||||
let sel = widgets();
|
let sel = widgets();
|
||||||
if (sel.length === 0) {
|
if (sel.length === 0) {
|
||||||
sel = api.widgets.all();
|
sel = api.widgets.all();
|
||||||
|
|
@ -207,7 +207,7 @@ function merge() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let w = api.platform.load_verts([], obj, "merged");
|
let w = api.platform.load_verts([], obj, "merged");
|
||||||
api.platform.delete(sel);
|
if (deleteMerged) api.platform.delete(sel);
|
||||||
api.platform.select(w);
|
api.platform.select(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,7 +234,7 @@ function isolateBodies(){
|
||||||
if(bodies.length ==1){
|
if(bodies.length ==1){
|
||||||
api.platform.select(widget);
|
api.platform.select(widget);
|
||||||
}else{
|
}else{
|
||||||
console.log(bodies,Array.from(bodies.entries()))
|
// console.log(bodies,Array.from(bodies.entries()))
|
||||||
for(let [i,verts] of bodies.entries()){
|
for(let [i,verts] of bodies.entries()){
|
||||||
let w = api.platform.load_verts([], verts, `isolate ${i} of ${ widget.anno.file}`);
|
let w = api.platform.load_verts([], verts, `isolate ${i} of ${ widget.anno.file}`);
|
||||||
api.platform.select(w)
|
api.platform.select(w)
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,24 @@ function onLayFlatSelect() {
|
||||||
// endIt();
|
// endIt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onLeftySelect() {
|
||||||
|
// Project the face normal onto the XY plane (ignore Z component)
|
||||||
|
let projectedNormal = new Vector3(lastface.normal.x, lastface.normal.y, 0).normalize();
|
||||||
|
|
||||||
|
// Target direction on XY plane (negative Y axis)
|
||||||
|
let targetDir = new Vector3(0, -1, 0);
|
||||||
|
|
||||||
|
// Calculate angle between projected normal and target
|
||||||
|
let angle = Math.atan2(
|
||||||
|
projectedNormal.x * targetDir.y - projectedNormal.y * targetDir.x, // cross product Z component
|
||||||
|
projectedNormal.x * targetDir.x + projectedNormal.y * targetDir.y // dot product
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create quaternion for Z-axis rotation only
|
||||||
|
let q = new Quaternion().setFromAxisAngle(ZAXIS, angle);
|
||||||
|
api.selection.rotate(q);
|
||||||
|
}
|
||||||
|
|
||||||
function onFaceUpSelect() {
|
function onFaceUpSelect() {
|
||||||
api.event.emit('tool.mesh.face-normal', lastface.normal);
|
api.event.emit('tool.mesh.face-normal', lastface.normal);
|
||||||
// endIt();
|
// endIt();
|
||||||
|
|
@ -105,6 +123,12 @@ api.event.on('tool.mesh.lay-flat', () => {
|
||||||
startIt();
|
startIt();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
api.event.on('tool.mesh.lefty', () => {
|
||||||
|
opName = 'align y axis';
|
||||||
|
onDone = onLeftySelect;
|
||||||
|
startIt();
|
||||||
|
});
|
||||||
|
|
||||||
function scale() {
|
function scale() {
|
||||||
let cam = space.internals().camera;
|
let cam = space.internals().camera;
|
||||||
let dist = cam.position.distanceTo(pmesh.position);
|
let dist = cam.position.distanceTo(pmesh.position);
|
||||||
|
|
|
||||||
|
|
@ -473,10 +473,12 @@ class Widget {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* moves top of widget to given Z
|
* moves top of widget to given Z
|
||||||
|
* only non-zero in CAM mode
|
||||||
*
|
*
|
||||||
* @param {number} z position
|
* @param {number} z position
|
||||||
|
* @param {boolean} cam mode
|
||||||
*/
|
*/
|
||||||
setTopZ(z) {
|
setTopZ(z, cam) {
|
||||||
let mesh = this.mesh,
|
let mesh = this.mesh,
|
||||||
track = this.track,
|
track = this.track,
|
||||||
mbb = mesh.getBoundingBox(),
|
mbb = mesh.getBoundingBox(),
|
||||||
|
|
@ -488,7 +490,7 @@ class Widget {
|
||||||
track.top = mbz;
|
track.top = mbz;
|
||||||
}
|
}
|
||||||
// difference between top of stock/bounds and top of widget (cam mode)
|
// difference between top of stock/bounds and top of widget (cam mode)
|
||||||
track.tzoff = mbz - z;
|
track.tzoff = cam ? mbz - z : 0;
|
||||||
this._updateMeshPosition();
|
this._updateMeshPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -503,8 +505,7 @@ class Widget {
|
||||||
}
|
}
|
||||||
|
|
||||||
_move(x, y, z, abs) {
|
_move(x, y, z, abs) {
|
||||||
let mesh = this.mesh,
|
let mat = this.getMaterial(),
|
||||||
mat = this.getMaterial(),
|
|
||||||
pos = this.track.pos;
|
pos = this.track.pos;
|
||||||
// do not allow moves in pure slice view
|
// do not allow moves in pure slice view
|
||||||
if (!mat.visible) return;
|
if (!mat.visible) return;
|
||||||
|
|
@ -528,13 +529,10 @@ class Widget {
|
||||||
track = this.track,
|
track = this.track,
|
||||||
tzoff = track.tzoff,
|
tzoff = track.tzoff,
|
||||||
top = track.top,
|
top = track.top,
|
||||||
tz = tzoff !== top ? -tzoff : 0,
|
tz = -tzoff,
|
||||||
{ x, y, z } = track.pos;
|
{ x, y, z } = track.pos;
|
||||||
if (track.indexed) {
|
if (track.indexed) {
|
||||||
let rad = track.indexRad;
|
let rad = track.indexRad;
|
||||||
let ln = track.box.d / 2;
|
|
||||||
let dx = Math.sin(rad) * ln;
|
|
||||||
let dy = ln - Math.cos(rad) * ln;
|
|
||||||
this.mesh.rotation.x = -rad;
|
this.mesh.rotation.x = -rad;
|
||||||
z = top;
|
z = top;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -782,9 +780,11 @@ class Widget {
|
||||||
}
|
}
|
||||||
if (set) {
|
if (set) {
|
||||||
let dark = this.api.space.is_dark();
|
let dark = this.api.space.is_dark();
|
||||||
|
let cam = this.api.mode.is_cam();
|
||||||
|
let color = dark && !cam ? 0xffffff : 0;
|
||||||
let angle = this.api.conf.get().controller.edgeangle || 20;
|
let angle = this.api.conf.get().controller.edgeangle || 20;
|
||||||
let edges = new THREE.EdgesGeometry(mesh.geometry, angle);
|
let edges = new THREE.EdgesGeometry(mesh.geometry, angle);
|
||||||
let material = new THREE.LineBasicMaterial({ color: 0 });
|
let material = new THREE.LineBasicMaterial({ color });
|
||||||
this.outline = new THREE.LineSegments(edges, material);
|
this.outline = new THREE.LineSegments(edges, material);
|
||||||
this.outline.renderOrder = -20;
|
this.outline.renderOrder = -20;
|
||||||
mesh.add(this.outline);
|
mesh.add(this.outline);
|
||||||
|
|
|
||||||
|
|
@ -5,38 +5,37 @@
|
||||||
import { CSG } from '../../../geo/csg.js';
|
import { CSG } from '../../../geo/csg.js';
|
||||||
import { Tool } from './tool.js';
|
import { Tool } from './tool.js';
|
||||||
|
|
||||||
let nextMeshID = 1;
|
let nextMeshID = 1,
|
||||||
let stock, center, rez;
|
stock,
|
||||||
let path, pathIndex, tool, tools, last;
|
path,
|
||||||
|
pathIndex,
|
||||||
let stockZ;
|
tool,
|
||||||
let stockIndexMsg = false;
|
tools,
|
||||||
let stockSlices;
|
last,
|
||||||
let stockIndex;
|
stockZ,
|
||||||
let startTime;
|
stockIndexMsg = false,
|
||||||
|
stockSlices,
|
||||||
let toolID = -1;
|
stockIndex,
|
||||||
let toolMesh;
|
startTime,
|
||||||
let toolRadius;
|
toolID = -1,
|
||||||
let toolUpdateMsg;
|
toolMesh,
|
||||||
|
toolRadius,
|
||||||
let animateClear = false;
|
toolUpdateMsg,
|
||||||
let animating = false;
|
animateClear = false,
|
||||||
let moveDist = 0;
|
animating = false,
|
||||||
let renderDist = 0;
|
renderDone = false,
|
||||||
let renderDone = false;
|
renderSteps = 0,
|
||||||
let renderPause = 10;
|
renderSpeed = 0,
|
||||||
let renderSteps = 0;
|
indexCount = 0,
|
||||||
let renderSpeed = 0;
|
updates = 0,
|
||||||
let indexCount = 0;
|
maxR;
|
||||||
let updates = 0;
|
|
||||||
|
|
||||||
export function init(worker) {
|
export function init(worker) {
|
||||||
const { dispatch } = worker;
|
const { dispatch } = worker;
|
||||||
|
|
||||||
dispatch.animate_setup2 = function (data, send) {
|
dispatch.animate_setup2 = function (data, send) {
|
||||||
const { settings } = data;
|
const { settings } = data;
|
||||||
const { process } = settings;
|
const { controller, process } = settings;
|
||||||
const print = worker.current.print;
|
const print = worker.current.print;
|
||||||
const density = parseInt(settings.controller.animesh) * 1000;
|
const density = parseInt(settings.controller.animesh) * 1000;
|
||||||
const isIndexed = process.camStockIndexed;
|
const isIndexed = process.camStockIndexed;
|
||||||
|
|
@ -45,8 +44,6 @@ export function init(worker) {
|
||||||
path = print.output.flat();
|
path = print.output.flat();
|
||||||
tools = settings.tools;
|
tools = settings.tools;
|
||||||
stock = settings.stock;
|
stock = settings.stock;
|
||||||
rez = 1 / Math.sqrt(density / (stock.x * stock.y));
|
|
||||||
|
|
||||||
tool = null;
|
tool = null;
|
||||||
last = null;
|
last = null;
|
||||||
animating = false;
|
animating = false;
|
||||||
|
|
@ -56,25 +53,27 @@ export function init(worker) {
|
||||||
startTime = 0;
|
startTime = 0;
|
||||||
updates = 0;
|
updates = 0;
|
||||||
stockZ = isIndexed ? 0 : stock.z;
|
stockZ = isIndexed ? 0 : stock.z;
|
||||||
|
maxR = Math.hypot(stock.y, stock.z);
|
||||||
|
|
||||||
stockSlices = [];
|
stockSlices = [];
|
||||||
const { x, y, z } = stock;
|
const { x, y, z } = stock;
|
||||||
const sliceCount = parseInt(settings.controller.animesh || 2000) / 100;
|
const sliceCount = parseInt(settings.controller.animesh || 2000) / 100;
|
||||||
const sliceWidth = stock.x / sliceCount;
|
const sliceWidth = stock.x / sliceCount;
|
||||||
|
|
||||||
|
if (controller.manifold)
|
||||||
for (let i = 0; i < sliceCount; i++) {
|
for (let i = 0; i < sliceCount; i++) {
|
||||||
let xmin = -(x / 2) + (i * sliceWidth) + sliceWidth / 2;
|
let xmin = -(x / 2) + (i * sliceWidth) + sliceWidth / 2;
|
||||||
let slice = new Stock(sliceWidth, y, z).translate(xmin, 0, 0);
|
let slice = new Stock(sliceWidth, y, z).translate(xmin, 0, 0);
|
||||||
stockSlices.push(slice);
|
stockSlices.push(slice);
|
||||||
slice.updateMesh([]);
|
slice.updateMesh([]);
|
||||||
slice.send(send);
|
slice.send(send);
|
||||||
// send({ mesh_move: { id: slice.id, pos: { x:0, y:0, z: stock.z/2} } });
|
// send.data({ mesh_move: { id: slice.id, pos: { x:0, y:0, z: stock.z/2} } });
|
||||||
}
|
}
|
||||||
|
|
||||||
send.done();
|
send.done();
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch.animate2 = function (data, send) {
|
dispatch.animate2 = function (data, send) {
|
||||||
renderPause = data.pause || renderPause;
|
|
||||||
renderSpeed = data.speed || 0;
|
renderSpeed = data.speed || 0;
|
||||||
if (animating) {
|
if (animating) {
|
||||||
return send.done();
|
return send.done();
|
||||||
|
|
@ -126,7 +125,6 @@ function renderPath(send) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pathIndex++;
|
pathIndex++;
|
||||||
// console.log(next.point.z);
|
|
||||||
|
|
||||||
if (next.tool && (!tool || tool.getID() !== next.tool.getID())) {
|
if (next.tool && (!tool || tool.getID() !== next.tool.getID())) {
|
||||||
// on real tool change, go to safe Z first
|
// on real tool change, go to safe Z first
|
||||||
|
|
@ -143,44 +141,50 @@ function renderPath(send) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = toolID;
|
const id = toolID;
|
||||||
const rezstep = rez * 2;
|
const rezstep = Math.min(tool.maxDiameter(), 1) / 4;
|
||||||
|
// console.log({ rezstep });
|
||||||
|
|
||||||
if (last) {
|
if (last) {
|
||||||
|
// console.log(last.point, next.point);
|
||||||
const lp = last.point, np = next.point;
|
const lp = last.point, np = next.point;
|
||||||
last = next;
|
last = next;
|
||||||
|
|
||||||
|
// render move line on client
|
||||||
|
send.data({ line: [ lp, np ]});
|
||||||
|
|
||||||
// dwell ops have no point
|
// dwell ops have no point
|
||||||
if (!np || !lp) {
|
if (!np || !lp) {
|
||||||
return renderPath(send);
|
return renderPath(send);
|
||||||
}
|
}
|
||||||
|
|
||||||
let dx = np.x - lp.x,
|
let dx = np.x - lp.x,
|
||||||
dy = np.y - lp.y,
|
dy = np.y - lp.y,
|
||||||
dz = np.z - lp.z,
|
dz = np.z - lp.z,
|
||||||
da = Math.abs((np.a || 0) - (lp.a || 0)),
|
da = (np.a ?? 0) - (lp.a ?? 0), // delta rotation degrees
|
||||||
dr = (da / 360) * (2 * Math.PI * Math.max(np.z, lp.z)),
|
AD = maxR * 2, // arc diameter @ max z
|
||||||
dist = Math.sqrt(dx * dx + dy * dy + dz * dz + dr * dr);
|
dr = (Math.abs(da) / 360) * AD, // delta rotation arc
|
||||||
|
dist = Math.max(Math.hypot(dx, dy, dz), dr); // motion distance
|
||||||
moveDist += dist;
|
|
||||||
|
|
||||||
// skip moves that are less than grid resolution
|
|
||||||
if (moveDist < rezstep) {
|
|
||||||
// console.log('skip', moveDist, rezstep, next);
|
|
||||||
renderPath(send);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// console.log({ dz, da, AD, dr, dist });
|
||||||
|
// sub-divide long moves for animation and better boolean ops
|
||||||
const md = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz), dr);
|
const md = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz), dr);
|
||||||
const st = Math.ceil(md / rezstep);
|
const st = Math.ceil(md / rezstep); // steps
|
||||||
const mx = dx / st, my = dy / st, mz = dz / st;
|
const mx = dx / st, my = dy / st, mz = dz / st, ma = da / st;
|
||||||
// const sd = Math.sqrt(mx*mx + my*my + mz*mz + dr*dr);
|
const sd = Math.sqrt(mx * mx + my * my + mz * mz + ma * ma);
|
||||||
const sd = Math.sqrt(mx * mx + my * my + Math.min(1, mz * mz) + dr * dr);
|
const sp = next.speed;
|
||||||
const moves = [];
|
const moves = [];
|
||||||
for (let i = 0, x = lp.x, y = lp.y, z = lp.z; i < st; i++) {
|
for (let i = 0, x = lp.x, y = lp.y, z = lp.z, a = lp.a; i < st; i++) {
|
||||||
moves.push({ x, y, z, a: lp.a, md: sd, dx, dy, dz });
|
moves.push({ x, y, z, a, md: sd, dx, dy, dz, da, sp });
|
||||||
x += mx;
|
x += mx;
|
||||||
y += my;
|
y += my;
|
||||||
z += mz;
|
z += mz;
|
||||||
|
a += ma;
|
||||||
}
|
}
|
||||||
moveDist = 0;
|
// console.log({ dist: md, steps: st, speed: next.speed, moves });
|
||||||
moves.push({ ...next.point, md: sd });
|
|
||||||
|
// push last point
|
||||||
|
moves.push({ ...next.point, md: sd, sp });
|
||||||
|
// console.log(...moves);
|
||||||
renderMoves(id, moves, send);
|
renderMoves(id, moves, send);
|
||||||
} else {
|
} else {
|
||||||
last = next;
|
last = next;
|
||||||
|
|
@ -197,26 +201,25 @@ function renderMoves(id, moves, send, seed = 0) {
|
||||||
if (!pos) {
|
if (!pos) {
|
||||||
throw `no pos @ ${index} of ${moves.length}`;
|
throw `no pos @ ${index} of ${moves.length}`;
|
||||||
}
|
}
|
||||||
const { dx, dy, dz } = pos;
|
const { dx, dy, dz, da, sp, md } = pos;
|
||||||
toolMove(pos);
|
toolMove(pos);
|
||||||
// console.log('renderMoves', {id, moves, seed});
|
// console.log('renderMoves', {id, moves, seed});
|
||||||
let subs = 0;
|
let subs = 0;
|
||||||
if (dx || dy || dz < 0)
|
if (dx || dy || dz < 0 || da)
|
||||||
for (let slice of stockSlices) {
|
for (let slice of stockSlices) {
|
||||||
if (slice.bounds.intersectsBox(toolMesh.bounds)) {
|
if (slice.bounds.intersectsBox(toolMesh.bounds)) {
|
||||||
slice.subtractTool(toolMesh);
|
slice.subtractTool(toolMesh);
|
||||||
subs++;
|
subs++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// console.log({ index, subs });
|
|
||||||
renderDist += pos.md;
|
|
||||||
// pause renderer at specified offsets
|
// pause renderer at specified offsets
|
||||||
if (renderSpeed && renderDist >= renderSpeed) {
|
if (renderSpeed) {
|
||||||
renderDist = 0;
|
let time = sp ? (md / sp) * 60000 : 10;
|
||||||
|
// console.log({ time, ...pos });
|
||||||
renderUpdate(send);
|
renderUpdate(send);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
renderMoves(id, moves, send, index + 1);
|
renderMoves(id, moves, send, index + 1);
|
||||||
}, renderPause);
|
}, time / renderSpeed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,29 +10,35 @@ const { client } = api;
|
||||||
let meshes = {},
|
let meshes = {},
|
||||||
button = {},
|
button = {},
|
||||||
label = {},
|
label = {},
|
||||||
|
lineTracker,
|
||||||
material,
|
material,
|
||||||
speedPauses = [0, 0, 0, 0, 0],
|
|
||||||
speedValues = [1, 2, 4, 8, 32],
|
speedValues = [1, 2, 4, 8, 32],
|
||||||
speedNames = ["1x", "2x", "4x", "8x", "!!"],
|
speedNames = ["1x", "2x", "4x", "8x", "!!"],
|
||||||
speedMax = speedValues.length - 1,
|
speedMax = speedValues.length - 1,
|
||||||
speedIndex = 0,
|
speedIndex = 0,
|
||||||
speed,
|
speed,
|
||||||
origin,
|
origin,
|
||||||
color = 0;
|
color = 0,
|
||||||
|
dark = false,
|
||||||
|
manifold = false,
|
||||||
|
A2R = Math.PI / 180;
|
||||||
|
|
||||||
export function animate_clear2(api) {
|
export function animate_clear2(api) {
|
||||||
let { anim } = api.ui;
|
let { anim } = api.ui;
|
||||||
client.animate_cleanup2();
|
lineTracker?.clear();
|
||||||
Object.keys(meshes).forEach(id => deleteMesh(id));
|
Object.keys(meshes).forEach(id => deleteMesh(id));
|
||||||
api.widgets.setAxisIndex(0);
|
api.widgets.setAxisIndex(0);
|
||||||
api.uc.setVisible(anim.laba, true);
|
api.uc.setVisible(anim.laba, true);
|
||||||
api.uc.setVisible(anim.vala, true);
|
api.uc.setVisible(anim.vala, true);
|
||||||
anim.vala.value = "0.0";
|
anim.vala.value = "0.0";
|
||||||
|
client.animate_cleanup2();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function animate2(api, delay) {
|
export function animate2(api, delay) {
|
||||||
let alert = api.alerts.show("building animation");
|
let alert = api.alerts.show("building animation");
|
||||||
let settings = api.conf.get();
|
let settings = api.conf.get();
|
||||||
|
dark = settings.controller.dark;
|
||||||
|
manifold = settings.controller.manifold;
|
||||||
client.animate_setup2(settings, data => {
|
client.animate_setup2(settings, data => {
|
||||||
handleUpdate(data);
|
handleUpdate(data);
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
@ -48,7 +54,8 @@ export function animate2(api, delay) {
|
||||||
speed: anim.speed,
|
speed: anim.speed,
|
||||||
trans: anim.trans,
|
trans: anim.trans,
|
||||||
model: anim.model,
|
model: anim.model,
|
||||||
shade: anim.shade
|
shade: anim.shade,
|
||||||
|
path: anim.path
|
||||||
});
|
});
|
||||||
Object.assign(label, {
|
Object.assign(label, {
|
||||||
progress: anim.progress,
|
progress: anim.progress,
|
||||||
|
|
@ -59,6 +66,14 @@ export function animate2(api, delay) {
|
||||||
a: anim.vala
|
a: anim.vala
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (manifold) {
|
||||||
|
anim.trans.classList.remove('hide');
|
||||||
|
// anim.shade.classList.remove('hide');
|
||||||
|
} else {
|
||||||
|
anim.trans.classList.add('hide');
|
||||||
|
// anim.shade.classList.add('hide');
|
||||||
|
}
|
||||||
|
|
||||||
updateSpeed(0);
|
updateSpeed(0);
|
||||||
setTimeout(step, delay || 0);
|
setTimeout(step, delay || 0);
|
||||||
toggleTrans(undefined, false);
|
toggleTrans(undefined, false);
|
||||||
|
|
@ -72,6 +87,7 @@ export function animate2(api, delay) {
|
||||||
button.trans.onclick = toggleTrans;
|
button.trans.onclick = toggleTrans;
|
||||||
button.model.onclick = toggleModel;
|
button.model.onclick = toggleModel;
|
||||||
button.shade.onclick = toggleStock;
|
button.shade.onclick = toggleStock;
|
||||||
|
button.path.onclick = togglePath;
|
||||||
button.play.style.display = '';
|
button.play.style.display = '';
|
||||||
button.pause.style.display = 'none';
|
button.pause.style.display = 'none';
|
||||||
|
|
||||||
|
|
@ -86,8 +102,9 @@ Object.assign(client, {
|
||||||
},
|
},
|
||||||
|
|
||||||
animate_setup2(settings, ondone) {
|
animate_setup2(settings, ondone) {
|
||||||
color = settings.controller.dark ? 0x888888 : 0;
|
initPathMesh();
|
||||||
material = new THREE.MeshPhongMaterial({
|
color = dark ? 0x888888 : 0;
|
||||||
|
material = material ?? new THREE.MeshPhongMaterial({
|
||||||
flatShading: true,
|
flatShading: true,
|
||||||
transparent: false,
|
transparent: false,
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
|
|
@ -102,6 +119,73 @@ Object.assign(client, {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function initPathMesh() {
|
||||||
|
if (lineTracker) {
|
||||||
|
return lineTracker.clear();
|
||||||
|
}
|
||||||
|
let count = 0,
|
||||||
|
max = 10000,
|
||||||
|
rot = new THREE.Euler(0,0,0),
|
||||||
|
geo = new THREE.BufferGeometry(),
|
||||||
|
mat = new THREE.LineBasicMaterial({ color: dark ? 0xffff00 : 0x771100 }),
|
||||||
|
pos = new Float32Array(max * 6),
|
||||||
|
lines = new THREE.LineSegments(geo, mat),
|
||||||
|
vec = new THREE.Vector3(0,0,0),
|
||||||
|
ang = new THREE.Vector3(1,0,0),
|
||||||
|
show = api.local.get('cam.anim.path') ?? true;
|
||||||
|
geo.setAttribute('position', new THREE.BufferAttribute(pos, 3));
|
||||||
|
let track = lineTracker = {
|
||||||
|
xro(p) {
|
||||||
|
vec.set(p.x, p.y, p.z);
|
||||||
|
vec.applyAxisAngle(ang, p.a * A2R);
|
||||||
|
},
|
||||||
|
add(p1, p2) {
|
||||||
|
let ind = count * 6;
|
||||||
|
track.xro(p1);
|
||||||
|
pos[ind+0] = vec.x;
|
||||||
|
pos[ind+1] = vec.y;
|
||||||
|
pos[ind+2] = vec.z;
|
||||||
|
track.xro(p2);
|
||||||
|
pos[ind+3] = vec.x;
|
||||||
|
pos[ind+4] = vec.y;
|
||||||
|
pos[ind+5] = vec.z;
|
||||||
|
count++;
|
||||||
|
if (count >= max) {
|
||||||
|
let opos = pos;
|
||||||
|
max += 10000;
|
||||||
|
pos = new Float32Array(max * 6),
|
||||||
|
pos.set(opos);
|
||||||
|
geo.setAttribute('position', new THREE.BufferAttribute(pos, 3));
|
||||||
|
}
|
||||||
|
geo.setDrawRange(0, count * 2);
|
||||||
|
geo.attributes.position.needsUpdate = true;
|
||||||
|
track.visible(true);
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
count = 0;
|
||||||
|
geo.setDrawRange(0,0);
|
||||||
|
track.rotate(0);
|
||||||
|
track.visible(false);
|
||||||
|
},
|
||||||
|
rotate(angle) {
|
||||||
|
rot.x = angle * A2R;
|
||||||
|
lines.setRotationFromEuler(rot);
|
||||||
|
},
|
||||||
|
visible(bool) {
|
||||||
|
if (show && bool !== lines.visible) {
|
||||||
|
lines.visible = bool;
|
||||||
|
} else if (!show) {
|
||||||
|
lines.visible = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
show(bool) {
|
||||||
|
show = bool;
|
||||||
|
lines.visible = bool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
space.world.add(lines);
|
||||||
|
}
|
||||||
|
|
||||||
function meshAdd(id, ind, pos, ilen, plen) {
|
function meshAdd(id, ind, pos, ilen, plen) {
|
||||||
const geo = new THREE.BufferGeometry();
|
const geo = new THREE.BufferGeometry();
|
||||||
const pa = plen ? pos.subarray(0, plen * 3) : pos;
|
const pa = plen ? pos.subarray(0, plen * 3) : pos;
|
||||||
|
|
@ -132,6 +216,7 @@ function meshUpdate(id, ind, pos, ilen, plen) {
|
||||||
|
|
||||||
function deleteMesh(id) {
|
function deleteMesh(id) {
|
||||||
space.world.remove(meshes[id]);
|
space.world.remove(meshes[id]);
|
||||||
|
meshes[id].geometry.dispose();
|
||||||
delete meshes[id];
|
delete meshes[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,8 +226,15 @@ function toggleModel(ev, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleStock(ev, bool, set) {
|
function toggleStock(ev, bool, set) {
|
||||||
set !== false && api.local.toggle('cam.anim.stock', bool);
|
bool = api.local.toggle('cam.anim.stock', bool);
|
||||||
return api.event.emit('cam.stock.toggle', bool ?? undefined);
|
for (let [ id, mesh ] of Object.entries(meshes)) {
|
||||||
|
if (id >= 0) mesh.visible = bool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePath(ev, bool, set) {
|
||||||
|
bool = api.local.toggle('cam.anim.path', bool);
|
||||||
|
lineTracker?.show(bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleTrans(ev, bool) {
|
function toggleTrans(ev, bool) {
|
||||||
|
|
@ -165,8 +257,7 @@ function play(opts) {
|
||||||
}
|
}
|
||||||
client.animate2({
|
client.animate2({
|
||||||
speed,
|
speed,
|
||||||
steps: steps || Infinity,
|
steps: steps || Infinity
|
||||||
pause: speedPauses[speedIndex]
|
|
||||||
}, handleUpdate);
|
}, handleUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,8 +268,7 @@ function fast(opts) {
|
||||||
button.pause.style.display = '';
|
button.pause.style.display = '';
|
||||||
client.animate2({
|
client.animate2({
|
||||||
speed,
|
speed,
|
||||||
steps: steps || Infinity,
|
steps: steps || Infinity
|
||||||
pause: speedPauses[speedIndex]
|
|
||||||
}, handleUpdate);
|
}, handleUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,6 +306,7 @@ function handleUpdate(data) {
|
||||||
if (data.stock_index !== undefined) {
|
if (data.stock_index !== undefined) {
|
||||||
api.widgets.setAxisIndex(data.stock_index);
|
api.widgets.setAxisIndex(data.stock_index);
|
||||||
label.a.value = -data.stock_index.toFixed(1);
|
label.a.value = -data.stock_index.toFixed(1);
|
||||||
|
lineTracker.rotate(-data.stock_index);
|
||||||
}
|
}
|
||||||
if (data.mesh_index) {
|
if (data.mesh_index) {
|
||||||
const { id, index } = data.mesh_index;
|
const { id, index } = data.mesh_index;
|
||||||
|
|
@ -229,9 +320,12 @@ function handleUpdate(data) {
|
||||||
const { id, ind, pos, ilen, plen } = data.mesh_update;
|
const { id, ind, pos, ilen, plen } = data.mesh_update;
|
||||||
meshUpdate(id, ind, pos, ilen, plen);
|
meshUpdate(id, ind, pos, ilen, plen);
|
||||||
}
|
}
|
||||||
if (data && data.progress) {
|
if (data.progress) {
|
||||||
label.progress.value = (data.progress * 100).toFixed(1);
|
label.progress.value = (data.progress * 100).toFixed(1);
|
||||||
}
|
}
|
||||||
|
if (data.line) {
|
||||||
|
lineTracker.add(...data.line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSpeed(inc = 0) {
|
function updateSpeed(inc = 0) {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { selectHoles } from './cl-hole.js';
|
||||||
import { selectHelical } from './cl-helical.js';
|
import { selectHelical } from './cl-helical.js';
|
||||||
import { surfaceAdd } from './cl-surface.js';
|
import { surfaceAdd } from './cl-surface.js';
|
||||||
import { traceAdd } from './cl-trace.js';
|
import { traceAdd } from './cl-trace.js';
|
||||||
|
import { opRender } from './client.js';
|
||||||
|
|
||||||
const { MODES } = api.const;
|
const { MODES } = api.const;
|
||||||
const { uc: UC } = api;
|
const { uc: UC } = api;
|
||||||
|
|
@ -206,7 +207,7 @@ export function createPopOps() {
|
||||||
sep: UC.newBlank({ class: "pop-sep" }),
|
sep: UC.newBlank({ class: "pop-sep" }),
|
||||||
spindle: UC.newInput(LANG.cc_spnd_s, { title: LANG.cc_spnd_l, convert: UC.toInt, show: hasSpindle }),
|
spindle: UC.newInput(LANG.cc_spnd_s, { title: LANG.cc_spnd_l, convert: UC.toInt, show: hasSpindle }),
|
||||||
step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 1.0) }),
|
step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 1.0) }),
|
||||||
stepz: UC.newInput(LANG.cc_sdwn_s, { title: LANG.cc_sdwn_l, convert: UC.toFloat, bound: UC.bound(0, 100.0) }),
|
stepz: UC.newInput(LANG.cc_sdwn_s, { title: LANG.cc_sdwn_l, convert: UC.toFloat, units: true, bound: UC.bound(0, 100.0) }),
|
||||||
rate: UC.newInput(LANG.cc_feed_s, { title: LANG.cc_feed_l, convert: UC.toInt, units: true }),
|
rate: UC.newInput(LANG.cc_feed_s, { title: LANG.cc_feed_l, convert: UC.toInt, units: true }),
|
||||||
down: UC.newInput(LANG.cc_loff_s, { title: LANG.cc_loff_l, convert: UC.toFloat, units: true }),
|
down: UC.newInput(LANG.cc_loff_s, { title: LANG.cc_loff_l, convert: UC.toFloat, units: true }),
|
||||||
inset: UC.newInput(LANG.cc_lxyo_s, { title: LANG.cc_lxyo_l, convert: UC.toFloat, units: true, show: () => !env.popOp.level.rec.stock }),
|
inset: UC.newInput(LANG.cc_lxyo_s, { title: LANG.cc_lxyo_l, convert: UC.toFloat, units: true, show: () => !env.popOp.level.rec.stock }),
|
||||||
|
|
@ -282,7 +283,7 @@ export function createPopOps() {
|
||||||
plunge: UC.newInput(LANG.cc_plng_s, { title: LANG.cc_plng_l, convert: UC.toInt, units: true }),
|
plunge: UC.newInput(LANG.cc_plng_s, { title: LANG.cc_plng_l, convert: UC.toInt, units: true }),
|
||||||
down: UC.newInput(LANG.cc_sdwn_s, { title: LANG.cc_sdwn_l, convert: UC.toFloat, units: true }),
|
down: UC.newInput(LANG.cc_sdwn_s, { title: LANG.cc_sdwn_l, convert: UC.toFloat, units: true }),
|
||||||
step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 1.0), show: () => env.popOp.outline.rec.wide }),
|
step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 1.0), show: () => env.popOp.outline.rec.wide }),
|
||||||
steps: UC.newInput(LANG.cc_sovc_s, { title: LANG.cc_sovc_l, convert: UC.toInt, bound: UC.bound(1, 5), show: () => env.popOp.outline.rec.wide }),
|
steps: UC.newInput(LANG.cc_sovc_s, { title: LANG.cc_sovc_l, convert: UC.toInt, bound: UC.bound(1, 500), show: () => env.popOp.outline.rec.wide }),
|
||||||
sep: UC.newBlank({ class: "pop-sep" }),
|
sep: UC.newBlank({ class: "pop-sep" }),
|
||||||
top: UC.newBoolean(LANG.co_clrt_s, undefined, { title: LANG.co_clrt_l }),
|
top: UC.newBoolean(LANG.co_clrt_s, undefined, { title: LANG.co_clrt_l }),
|
||||||
inside: UC.newBoolean(LANG.co_olin_s, undefined, { title: LANG.co_olin_l, show: (op) => { return !op.inputs.outside.checked } }),
|
inside: UC.newBoolean(LANG.co_olin_s, undefined, { title: LANG.co_olin_l, show: (op) => { return !op.inputs.outside.checked } }),
|
||||||
|
|
@ -349,7 +350,9 @@ export function createPopOps() {
|
||||||
tolerance: 'camTolerance',
|
tolerance: 'camTolerance',
|
||||||
filter: 'camContourFilter',
|
filter: 'camContourFilter',
|
||||||
leave: 'camContourLeave',
|
leave: 'camContourLeave',
|
||||||
linear: 'camLatheLinear'
|
linear: 'camLatheLinear',
|
||||||
|
offStart: 'camLatheOffStart',
|
||||||
|
offEnd: 'camLatheOffEnd'
|
||||||
}).inputs = {
|
}).inputs = {
|
||||||
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
|
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
|
||||||
// axis: UC.newSelect(LANG.cd_axis, {}, "xyaxis"),
|
// axis: UC.newSelect(LANG.cd_axis, {}, "xyaxis"),
|
||||||
|
|
@ -360,6 +363,9 @@ export function createPopOps() {
|
||||||
step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 100.0) }),
|
step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 100.0) }),
|
||||||
angle: UC.newInput(LANG.cc_sang_s, { title: LANG.cc_sang_l, convert: UC.toFloat, bound: UC.bound(0.01, 180.0) }),
|
angle: UC.newInput(LANG.cc_sang_s, { title: LANG.cc_sang_l, convert: UC.toFloat, bound: UC.bound(0.01, 180.0) }),
|
||||||
sep: UC.newBlank({ class: "pop-sep" }),
|
sep: UC.newBlank({ class: "pop-sep" }),
|
||||||
|
offStart: UC.newInput(LANG.ci_laso_s, { title: LANG.ci_laso_l, convert: UC.toFloat}),
|
||||||
|
offEnd: UC.newInput(LANG.ci_laeo_s, { title: LANG.ci_laeo_l, convert: UC.toFloat}),
|
||||||
|
sep: UC.newBlank({ class: "pop-sep" }),
|
||||||
tolerance: UC.newInput(LANG.ou_toll_s, { title: LANG.ou_toll_l, convert: UC.toFloat, bound: UC.bound(0, 10.0), units: true, round: 4 }),
|
tolerance: UC.newInput(LANG.ou_toll_s, { title: LANG.ou_toll_l, convert: UC.toFloat, bound: UC.bound(0, 10.0), units: true, round: 4 }),
|
||||||
leave: UC.newInput(LANG.cf_leav_s, { title: LANG.cf_leav_l, convert: UC.toFloat, bound: UC.bound(0, 100) }),
|
leave: UC.newInput(LANG.cf_leav_s, { title: LANG.cf_leav_l, convert: UC.toFloat, bound: UC.bound(0, 100) }),
|
||||||
sep: UC.newBlank({ class: "pop-sep" }),
|
sep: UC.newBlank({ class: "pop-sep" }),
|
||||||
|
|
|
||||||
|
|
@ -140,8 +140,8 @@ function pointHoverUp(int, ev) {
|
||||||
x += stock.x / 2;
|
x += stock.x / 2;
|
||||||
y += stock.y / 2;
|
y += stock.y / 2;
|
||||||
}
|
}
|
||||||
process.camOriginOffX = x;
|
process.camOriginOffX = x - stock.center.x;
|
||||||
process.camOriginOffY = y;
|
process.camOriginOffY = y - stock.center.y;
|
||||||
process.camOriginOffZ = z;
|
process.camOriginOffZ = z;
|
||||||
api.settings.update_fields(process);
|
api.settings.update_fields(process);
|
||||||
api.platform.update_origin();
|
api.platform.update_origin();
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ export function traceHoverUp(int, ev) {
|
||||||
|
|
||||||
export function traceToggle(obj, skip) {
|
export function traceToggle(obj, skip) {
|
||||||
let material = obj.material[0] || obj.material;
|
let material = obj.material[0] || obj.material;
|
||||||
if (!material) return;
|
if (!(material && obj.trace)) return;
|
||||||
let { color, colorSave } = material;
|
let { color, colorSave } = material;
|
||||||
let { widget, poly } = obj.trace;
|
let { widget, poly } = obj.trace;
|
||||||
let areas = env.poppedRec.areas;
|
let areas = env.poppedRec.areas;
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { Layers } from '../../core/layers.js';
|
||||||
import { Stack } from '../../core/stack.js';
|
import { Stack } from '../../core/stack.js';
|
||||||
import { updateStock } from './cl-stock.js';
|
import { updateStock } from './cl-stock.js';
|
||||||
import { createPopOps } from './cl-ops.js';
|
import { createPopOps } from './cl-ops.js';
|
||||||
import { tabAdd, tabDone, tabClear, restoreTabs, rotateTabs, updateTabs, clearTabs } from './cl-tab.js';
|
import { tabAdd, tabDone, tabClear, restoreTabs, rotateTabs, updateTabs, clearTabs, mirrorTabs } from './cl-tab.js';
|
||||||
import { traceOn, traceDone, unselectTraces } from './cl-trace.js';
|
import { traceOn, traceDone, unselectTraces } from './cl-trace.js';
|
||||||
import { holeSelOn, selectHolesDone, clearHolesRec } from './cl-hole.js';
|
import { holeSelOn, selectHolesDone, clearHolesRec } from './cl-hole.js';
|
||||||
import { surfaceOn, surfaceDone } from './cl-surface.js';
|
import { surfaceOn, surfaceDone } from './cl-surface.js';
|
||||||
|
|
@ -29,6 +29,7 @@ const { widgets: WIDGETS } = api;
|
||||||
class Client {
|
class Client {
|
||||||
animVer = 0;
|
animVer = 0;
|
||||||
camStock;
|
camStock;
|
||||||
|
showStock = true;
|
||||||
current;
|
current;
|
||||||
currentIndex;
|
currentIndex;
|
||||||
flipping;
|
flipping;
|
||||||
|
|
@ -320,6 +321,7 @@ export function opRender() {
|
||||||
el.classList.add("disabled");
|
el.classList.add("disabled");
|
||||||
}
|
}
|
||||||
bounds.push(el);
|
bounds.push(el);
|
||||||
|
let parent = $('camops');
|
||||||
let timer = null;
|
let timer = null;
|
||||||
let inside = true;
|
let inside = true;
|
||||||
let popped = false;
|
let popped = false;
|
||||||
|
|
@ -329,14 +331,14 @@ export function opRender() {
|
||||||
}
|
}
|
||||||
el.rec = rec;
|
el.rec = rec;
|
||||||
el.unpop = () => {
|
el.unpop = () => {
|
||||||
let pos = [...el.childNodes].indexOf(poprec.div);
|
let pos = [...parent.childNodes].indexOf(poprec.div);
|
||||||
if (pos >= 0) {
|
if (pos >= 0) {
|
||||||
el.removeChild(poprec.div);
|
parent.removeChild(poprec.div);
|
||||||
}
|
}
|
||||||
popped = false;
|
popped = false;
|
||||||
};
|
};
|
||||||
function onEnter(ev) {
|
function onEnter(ev) {
|
||||||
if ((surfaceOn || traceOn) && env.poppedRec != rec) {
|
if ((clock || surfaceOn || traceOn) && env.poppedRec != rec) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (popped && env.poppedRec != rec) {
|
if (popped && env.poppedRec != rec) {
|
||||||
|
|
@ -351,22 +353,24 @@ export function opRender() {
|
||||||
popped = true;
|
popped = true;
|
||||||
poprec.use(rec);
|
poprec.use(rec);
|
||||||
env.hoveredOp = el;
|
env.hoveredOp = el;
|
||||||
if (!clock) {
|
// offset Y position of pop div by % of Y screen location of button
|
||||||
// offset Y position of pop div by % of Y screen location of button
|
parent.appendChild(poprec.div);
|
||||||
el.appendChild(poprec.div);
|
poprec.addNote();
|
||||||
poprec.addNote();
|
const { innerHeight } = window;
|
||||||
const { innerHeight } = window;
|
const brect = ev.target.getBoundingClientRect();
|
||||||
const brect = ev.target.getBoundingClientRect();
|
const prect = parent.getBoundingClientRect();
|
||||||
const prect = poprec.div.getBoundingClientRect();
|
const Prect = poprec.div.getBoundingClientRect();
|
||||||
const pcty = (brect.top / innerHeight) * 0.9;
|
const tdiff = prect.top - brect.top;
|
||||||
const offpx = -pcty * prect.height;
|
const botoff = innerHeight - (brect.top + Prect.height);
|
||||||
poprec.div.style.transform = `translateY(${offpx}px)`;
|
const offpx = -tdiff + (botoff < 0 ? botoff : -Prect.height/3);
|
||||||
}
|
poprec.div.style.transform = `translateY(${offpx}px)`;
|
||||||
|
poprec.div.onmouseenter = () => { inside = true };
|
||||||
|
poprec.div.onmouseleave = onLeave;
|
||||||
// option click event appears latent
|
// option click event appears latent
|
||||||
// and overides the sticky settings
|
// and overides the sticky settings
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
UC.setSticky(false);
|
UC.setSticky(false);
|
||||||
}, 0);
|
}, 10);
|
||||||
}
|
}
|
||||||
function onLeave(ev) {
|
function onLeave(ev) {
|
||||||
inside = false;
|
inside = false;
|
||||||
|
|
@ -637,7 +641,8 @@ export function init() {
|
||||||
});
|
});
|
||||||
|
|
||||||
api.event.on("cam.stock.toggle", (bool) => {
|
api.event.on("cam.stock.toggle", (bool) => {
|
||||||
env.camStock && (env.camStock.visible = bool ?? !env.camStock.visible);
|
env.showStock = bool ?? !env.showStock;
|
||||||
|
updateStock();
|
||||||
});
|
});
|
||||||
|
|
||||||
api.event.on("boolean.click", api.platform.update_bounds);
|
api.event.on("boolean.click", api.platform.update_bounds);
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,8 @@ export function cam_export(print, online) {
|
||||||
const widget = print.widgets[0];
|
const widget = print.widgets[0];
|
||||||
if (!widget) return;
|
if (!widget) return;
|
||||||
|
|
||||||
console.log({ cam_export: print });
|
|
||||||
|
|
||||||
const { settings } = print;
|
const { settings } = print;
|
||||||
const { device, tools } = settings;
|
const { device } = settings;
|
||||||
|
|
||||||
let i,
|
let i,
|
||||||
time = 0,
|
time = 0,
|
||||||
|
|
@ -257,17 +255,18 @@ export function cam_export(print, online) {
|
||||||
points--;
|
points--;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let gn;
|
|
||||||
if (out.emit >= 0 && out.emit <= 3) gn = `G${out.emit}`;
|
let dx = opt.dx || newpos.x - pos.x,
|
||||||
let speed = out.speed,
|
|
||||||
arc = out.emit == 2 || out.emit == 3,
|
|
||||||
center = out.center,
|
|
||||||
nl = (compact_output && lastGn === gn) ? [] : [gn],
|
|
||||||
dx = opt.dx || newpos.x - pos.x,
|
|
||||||
dy = opt.dy || newpos.y - pos.y,
|
dy = opt.dy || newpos.y - pos.y,
|
||||||
dz = opt.dz || newpos.z - pos.z,
|
dz = opt.dz || newpos.z - pos.z,
|
||||||
da = newpos.a != pos.a,
|
da = newpos.a != pos.a,
|
||||||
maxf = dz ? maxZd : maxXYd,
|
gv = (dz >= 0 && out.emit >= 0 && out.emit <= 3) ? out.emit : 1,
|
||||||
|
gn = `G${gv}`,
|
||||||
|
speed = out.speed,
|
||||||
|
arc = out.emit == 2 || out.emit == 3,
|
||||||
|
center = out.center,
|
||||||
|
nl = (compact_output && lastGn === gn) ? [] : [gn],
|
||||||
|
maxf = (dz < 0 ? maxZd : maxXYd) || speed,
|
||||||
feed = Math.min(speed || maxf, maxf),
|
feed = Math.min(speed || maxf, maxf),
|
||||||
dist = Math.sqrt(dx * dx + dy * dy + dz * dz),
|
dist = Math.sqrt(dx * dx + dy * dy + dz * dz),
|
||||||
newFeed = feed && feed !== pos.f;
|
newFeed = feed && feed !== pos.f;
|
||||||
|
|
@ -424,7 +423,9 @@ export function cam_export(print, online) {
|
||||||
toolz[out.tool] = ctool;
|
toolz[out.tool] = ctool;
|
||||||
}
|
}
|
||||||
point = out.point;
|
point = out.point;
|
||||||
if (!point || point.mod) return;
|
if (!point || point.mod) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// ensure not point is modified twice
|
// ensure not point is modified twice
|
||||||
point.mod = 1;
|
point.mod = 1;
|
||||||
if (isOffset) {
|
if (isOffset) {
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ export function menu() {
|
||||||
anim.trans = newButton(null,null,{icon:'<i class="fa-solid fa-border-none"></i>',title:"transparency",class:"padleft"}),
|
anim.trans = newButton(null,null,{icon:'<i class="fa-solid fa-border-none"></i>',title:"transparency",class:"padleft"}),
|
||||||
anim.model = newButton(null,null,{icon:'<i class="fa-solid fa-eye"></i>',title:"show model"}),
|
anim.model = newButton(null,null,{icon:'<i class="fa-solid fa-eye"></i>',title:"show model"}),
|
||||||
anim.shade = newButton(null,null,{icon:'<i class="fa-solid fa-cube"></i>',title:"stock box"}),
|
anim.shade = newButton(null,null,{icon:'<i class="fa-solid fa-cube"></i>',title:"stock box"}),
|
||||||
|
anim.path = newButton(null,null,{icon:'<i class="fa-solid fa-draw-polygon"></i>',title:"path"}),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -105,7 +106,7 @@ export function menu() {
|
||||||
separator: newBlank({ class:"set-sep", driven }),
|
separator: newBlank({ class:"set-sep", driven }),
|
||||||
camEaseAngle: newInput(LANG.ou_eang_s, {title:LANG.ou_eang_l, convert:toFloat, bound:bound(0.1,85), show:() => ui.camEaseDown.checked}),
|
camEaseAngle: newInput(LANG.ou_eang_s, {title:LANG.ou_eang_l, convert:toFloat, bound:bound(0.1,85), show:() => ui.camEaseDown.checked}),
|
||||||
camFullEngage: newInput(LANG.ou_feng_s, {title:LANG.ou_feng_l, convert:toFloat, bound:bound(0.1,1.0)}),
|
camFullEngage: newInput(LANG.ou_feng_s, {title:LANG.ou_feng_l, convert:toFloat, bound:bound(0.1,1.0)}),
|
||||||
_____: newGroup(LANG.co_menu, $('cam-origin'), { modes:CAM, driven, separator }),
|
_____: newGroup(LANG.or_menu, $('cam-origin'), { modes:CAM, driven, separator }),
|
||||||
camOriginTop: newBoolean(LANG.or_topp_s, onBooleanClick, {title:LANG.or_topp_l}),
|
camOriginTop: newBoolean(LANG.or_topp_s, onBooleanClick, {title:LANG.or_topp_l}),
|
||||||
camOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, {title:LANG.or_cntr_l}),
|
camOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, {title:LANG.or_cntr_l}),
|
||||||
separator: newBlank({ class:"set-sep", driven }),
|
separator: newBlank({ class:"set-sep", driven }),
|
||||||
|
|
|
||||||
|
|
@ -4,26 +4,70 @@ import { CamOp } from './op.js';
|
||||||
import { Tool } from './tool.js';
|
import { Tool } from './tool.js';
|
||||||
import { generate as Topo } from './topo3.js';
|
import { generate as Topo } from './topo3.js';
|
||||||
import { newPoint } from '../../../geo/point.js';
|
import { newPoint } from '../../../geo/point.js';
|
||||||
|
import { newPolygon } from '../../../geo/polygon.js';
|
||||||
import { tip2tipEmit } from '../../../geo/paths.js';
|
import { tip2tipEmit } from '../../../geo/paths.js';
|
||||||
|
|
||||||
function createFilter(op) {
|
function createFilter(op, origin, axis) {
|
||||||
|
// console.log({ origin, axis });
|
||||||
|
let ok = () => true;
|
||||||
let filter = slices => slices;
|
let filter = slices => slices;
|
||||||
let filterString = op.filter?.map(l => l.trim()).join('\n');
|
let filterString = op.filter?.map(l => l.trim()).join('\n');
|
||||||
if (filterString) {
|
if (filterString) {
|
||||||
try {
|
try {
|
||||||
const obj = eval(`( ${filterString} )`);
|
const obj = eval(`( ${filterString} )`);
|
||||||
let idx = 0;
|
let box = obj?.box;
|
||||||
if (obj && obj.slices) {
|
let slice_fn = obj?.slices;
|
||||||
const nadd = [];
|
let index = 0;
|
||||||
filter = function (slices) {
|
const accept = [];
|
||||||
for (let slice of slices) {
|
filter = function (slices) {
|
||||||
if (obj.slices(slice, idx++)) {
|
for (let slice of slices) {
|
||||||
nadd.push(slice);
|
if (slice_fn && slice_fn(slice, index++)) {
|
||||||
|
accept.push(slice);
|
||||||
|
} else if (box) {
|
||||||
|
// slice.z = x when axis = y
|
||||||
|
// slice.z = y when axis = x
|
||||||
|
let { x, y, z } = box;
|
||||||
|
x = x ?? [ -Infinity, Infinity ];
|
||||||
|
y = y ?? [ -Infinity, Infinity ];
|
||||||
|
z = z ?? [ -Infinity, Infinity ];
|
||||||
|
let ok = false;
|
||||||
|
if (axis === 'x') {
|
||||||
|
let sy = slice.z + origin.y;
|
||||||
|
if (sy >= y[0] && sy <= y[1]) {
|
||||||
|
ok = true;
|
||||||
|
for (let p of slice.camLines) {
|
||||||
|
p.points = p.points.filter(p =>
|
||||||
|
p.x - origin.x >= x[0] && p.x - origin.x <= x[1] &&
|
||||||
|
p.z - origin.z >= z[0] && p.z - origin.z <= z[1]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let sx = slice.z - origin.x;
|
||||||
|
if (sx >= x[0] && sx <= x[1]) {
|
||||||
|
ok = true;
|
||||||
|
for (let p of slice.camLines) {
|
||||||
|
p.points = p.points.filter(p =>
|
||||||
|
p.y + origin.y >= y[0] && p.y + origin.y <= y[1] &&
|
||||||
|
p.z - origin.z >= z[0] && p.z - origin.z <= z[1]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
slice.camLines = slice.camLines.map(p => {
|
||||||
|
if (p.points.length > 1) {
|
||||||
|
return newPolygon(p.points).setOpen(true);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}).filter(p => p);
|
||||||
|
accept.push(slice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nadd;
|
}
|
||||||
};
|
return accept;
|
||||||
}
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('filter parse error', e, op.filter);
|
console.log('filter parse error', e, op.filter);
|
||||||
}
|
}
|
||||||
|
|
@ -38,8 +82,8 @@ class OpContour extends CamOp {
|
||||||
|
|
||||||
async slice(progress) {
|
async slice(progress) {
|
||||||
let { op, state } = this;
|
let { op, state } = this;
|
||||||
let { addSlices, settings, updateToolDiams } = state;
|
let { color, addSlices, settings, updateToolDiams } = state;
|
||||||
let filter = createFilter(op);
|
let filter = createFilter(op, settings.origin, op.axis.toLowerCase());
|
||||||
let toolDiam = this.toolDiam = new Tool(settings, op.tool).fluteDiameter();
|
let toolDiam = this.toolDiam = new Tool(settings, op.tool).fluteDiameter();
|
||||||
updateToolDiams(toolDiam);
|
updateToolDiams(toolDiam);
|
||||||
// we need topo for safe travel moves when roughing and outlining
|
// we need topo for safe travel moves when roughing and outlining
|
||||||
|
|
@ -54,6 +98,11 @@ class OpContour extends CamOp {
|
||||||
slices = filter(slices);
|
slices = filter(slices);
|
||||||
this.sliceOut = slices;
|
this.sliceOut = slices;
|
||||||
addSlices(slices);
|
addSlices(slices);
|
||||||
|
for (let slice of slices) {
|
||||||
|
slice.output()
|
||||||
|
.setLayer(`contour ${op.axis}`, { face: color, line: color })
|
||||||
|
.addPolys(slice.camLines);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
contour: op,
|
contour: op,
|
||||||
state: state
|
state: state
|
||||||
|
|
@ -64,11 +113,11 @@ class OpContour extends CamOp {
|
||||||
|
|
||||||
prepare(ops, progress) {
|
prepare(ops, progress) {
|
||||||
let { op, state, sliceOut } = this;
|
let { op, state, sliceOut } = this;
|
||||||
let { settings, widget } = state;
|
let { settings } = state;
|
||||||
let { process } = settings;
|
let { process } = settings;
|
||||||
|
|
||||||
let { setTolerance, setTool, setSpindle, setPrintPoint } = ops;
|
let { setTolerance, setTool, setSpindle, setPrintPoint } = ops;
|
||||||
let { camOut, polyEmit, newLayer, printPoint, lastPoint } = ops;
|
let { camOut, newLayer, printPoint, lastPoint } = ops;
|
||||||
let { bounds, zmax } = ops;
|
let { bounds, zmax } = ops;
|
||||||
|
|
||||||
let toolDiam = this.toolDiam;
|
let toolDiam = this.toolDiam;
|
||||||
|
|
@ -87,9 +136,9 @@ class OpContour extends CamOp {
|
||||||
if (!slice.camLines) {
|
if (!slice.camLines) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let polys = [], poly, emit;
|
let polys = [], poly;
|
||||||
slice.camLines.forEach(function (poly) {
|
slice.camLines.forEach(function (poly) {
|
||||||
if (depthFirst) poly = poly.clone(true);
|
if (depthFirst) poly = poly.clone(true).annotate({ slice: slice.index + 1 });
|
||||||
polys.push({ first: poly.first(), last: poly.last(), poly: poly });
|
polys.push({ first: poly.first(), last: poly.last(), poly: poly });
|
||||||
});
|
});
|
||||||
if (depthFirst) {
|
if (depthFirst) {
|
||||||
|
|
@ -101,7 +150,7 @@ class OpContour extends CamOp {
|
||||||
poly.reverse();
|
poly.reverse();
|
||||||
}
|
}
|
||||||
poly.forEachPoint(function (point, pidx) {
|
poly.forEachPoint(function (point, pidx) {
|
||||||
camOut(point.clone(), pidx > 0, stepover);
|
camOut(point.clone(), pidx > 0, { moveLen: stepover });
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
newLayer();
|
newLayer();
|
||||||
|
|
@ -115,7 +164,7 @@ class OpContour extends CamOp {
|
||||||
poly.reverse();
|
poly.reverse();
|
||||||
}
|
}
|
||||||
poly.forEachPoint(function (point, pidx) {
|
poly.forEachPoint(function (point, pidx) {
|
||||||
camOut(point.clone(), pidx > 0, stepover);
|
camOut(point.clone().annotate({ slice: poly.slice }), pidx > 0, { moveLen: stepover });
|
||||||
}, false);
|
}, false);
|
||||||
newLayer();
|
newLayer();
|
||||||
return lastPoint();
|
return lastPoint();
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ class OpDrill extends CamOp {
|
||||||
async slice(progress) {
|
async slice(progress) {
|
||||||
let { op, state } = this;
|
let { op, state } = this;
|
||||||
let { settings, addSlices, widget, updateToolDiams } = state;
|
let { settings, addSlices, widget, updateToolDiams } = state;
|
||||||
let { zBottom, zThru, thruHoles, color } = state;
|
let { color } = state;
|
||||||
let { drills, drillThrough } = op
|
let { drills } = op
|
||||||
|
|
||||||
let drillTool = new Tool(settings, op.tool),
|
let drillTool = new Tool(settings, op.tool),
|
||||||
drillToolDiam = drillTool.fluteDiameter(),
|
drillToolDiam = drillTool.fluteDiameter(),
|
||||||
|
|
@ -47,7 +47,7 @@ class OpDrill extends CamOp {
|
||||||
slice.camLines = [poly];
|
slice.camLines = [poly];
|
||||||
|
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("drill", { face: color, line: color })
|
.setLayer(state.layername, { face: color, line: color })
|
||||||
.addPolys(slice.camLines);
|
.addPolys(slice.camLines);
|
||||||
addSlices(slice);
|
addSlices(slice);
|
||||||
sliceOut.push(slice);
|
sliceOut.push(slice);
|
||||||
|
|
@ -55,11 +55,10 @@ class OpDrill extends CamOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare(ops, progress) {
|
prepare(ops, progress) {
|
||||||
let { op, state } = this;
|
let { op } = this;
|
||||||
let { settings, widget, addSlices, updateToolDiams } = state;
|
|
||||||
let { setTool, setSpindle, setDrill, emitDrills } = ops;
|
let { setTool, setSpindle, setDrill, emitDrills } = ops;
|
||||||
setTool(op.tool, undefined, op.rate);
|
setTool(op.tool, undefined, op.rate);
|
||||||
setDrill(op.down, op.lift, op.dwell, op.thru);
|
setDrill(op.down, op.lift, op.dwell);
|
||||||
setSpindle(op.spindle);
|
setSpindle(op.spindle);
|
||||||
emitDrills(this.sliceOut.map(slice => slice.camLines).flat());
|
emitDrills(this.sliceOut.map(slice => slice.camLines).flat());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export class OpIndex extends CamOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare(ops, progress) {
|
prepare(ops, progress) {
|
||||||
const { lastPoint, zmax, zclear, camOut, stock } = ops;
|
const { lastPoint, zmax, zclear, camOut, stock, newLayer } = ops;
|
||||||
let last = lastPoint();
|
let last = lastPoint();
|
||||||
if (last) {
|
if (last) {
|
||||||
// max point of stock corner radius when rotating (safe z when indexing)
|
// max point of stock corner radius when rotating (safe z when indexing)
|
||||||
|
|
@ -34,7 +34,7 @@ export class OpIndex extends CamOp {
|
||||||
// move above rotating stock
|
// move above rotating stock
|
||||||
camOut(last = last.clone().setZ(zmove), 0);
|
camOut(last = last.clone().setZ(zmove), 0);
|
||||||
// issue rotation command
|
// issue rotation command
|
||||||
camOut(last = last.clone().setY(0).setA(this.degrees), 0);
|
camOut(last = last.clone().setZ(zmove).setA(this.degrees), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,22 +6,23 @@ import { generate as topo4_generate } from './topo4.js';
|
||||||
import { newPoint } from '../../../geo/point.js';
|
import { newPoint } from '../../../geo/point.js';
|
||||||
|
|
||||||
function createFilter(op) {
|
function createFilter(op) {
|
||||||
|
let ok = () => true;
|
||||||
let filter = slices => slices;
|
let filter = slices => slices;
|
||||||
if (op.filter) {
|
let filterString = op.filter?.map(l => l.trim()).join('\n');
|
||||||
|
if (filterString) {
|
||||||
try {
|
try {
|
||||||
const obj = eval(`( ${op.filter.join('\n')} )`);
|
const obj = eval(`( ${filterString} )`);
|
||||||
let idx = 0;
|
const accept = [];
|
||||||
if (obj && obj.slices) {
|
let index = 0;
|
||||||
const nadd = [];
|
let sl_fn = obj?.slices ?? ok;
|
||||||
filter = function (slices) {
|
filter = function (slices) {
|
||||||
for (let slice of slices) {
|
for (let slice of slices) {
|
||||||
if (obj.slices(slice, idx++)) {
|
if (sl_fn(slice, index++)) {
|
||||||
nadd.push(slice);
|
accept.push(slice);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nadd;
|
}
|
||||||
};
|
return accept;
|
||||||
}
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('filter parse error', e, op.filter);
|
console.log('filter parse error', e, op.filter);
|
||||||
}
|
}
|
||||||
|
|
@ -36,9 +37,8 @@ class OpLathe extends CamOp {
|
||||||
|
|
||||||
async slice(progress) {
|
async slice(progress) {
|
||||||
let { op, state } = this;
|
let { op, state } = this;
|
||||||
let { addSlices } = state;
|
let { addSlices, color } = state;
|
||||||
let filter = createFilter(op);
|
let filter = createFilter(op);
|
||||||
|
|
||||||
this.topo = await topo4_generate({
|
this.topo = await topo4_generate({
|
||||||
op,
|
op,
|
||||||
state,
|
state,
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,10 @@ class OpLevel extends CamOp {
|
||||||
|
|
||||||
let points = [];
|
let points = [];
|
||||||
let clear = op.stock ?
|
let clear = op.stock ?
|
||||||
[ newPolygon().centerRectangle({x:0,y:0,z:0}, stock.x, stock.y) ] :
|
[ newPolygon().centerRectangle({x:0,y:0,z:0}, stock.x + toolDiam/2, stock.y) ] :
|
||||||
POLY.outer(POLY.offset(tshadow, toolDiam * (op.inset || 0)));
|
POLY.outer(POLY.offset(tshadow, toolDiam * (op.inset || 0)));
|
||||||
|
|
||||||
POLY.fillArea(clear, 90, stepOver, points);
|
POLY.fillArea(clear, 1090, stepOver, points);
|
||||||
|
|
||||||
let layers = this.layers = [];
|
let layers = this.layers = [];
|
||||||
for (let z of zList) {
|
for (let z of zList) {
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@ class OpOutline extends CamOp {
|
||||||
|
|
||||||
async slice(progress) {
|
async slice(progress) {
|
||||||
let { op, state } = this;
|
let { op, state } = this;
|
||||||
let { settings, widget, slicer, addSlices, tshadow, thruHoles, unsafe, color } = state;
|
let { settings, slicer, addSlices, tshadow, thruHoles, unsafe, color, widget } = state;
|
||||||
let { updateToolDiams, tabs, cutTabs, cutPolys, workarea, zMax, shadowAt } = state;
|
let { updateToolDiams, tabs, cutTabs, cutPolys, workarea, zMax, shadowAt } = state;
|
||||||
let { process, stock } = settings;
|
let { process, stock } = settings;
|
||||||
|
let { controller } = settings;
|
||||||
|
let center_off = widget.track.pos ?? { x: 0, y: 0, z: 0};
|
||||||
|
|
||||||
if (op.down <= 0) {
|
if (op.down <= 0) {
|
||||||
throw `invalid step down "${op.down}"`;
|
throw `invalid step down "${op.down}"`;
|
||||||
|
|
@ -39,6 +41,8 @@ class OpOutline extends CamOp {
|
||||||
let indices = slicer.interval(op.down, intopt);
|
let indices = slicer.interval(op.down, intopt);
|
||||||
let trueShadow = process.camTrueShadow === true;
|
let trueShadow = process.camTrueShadow === true;
|
||||||
let lastShadowZ;
|
let lastShadowZ;
|
||||||
|
let stockClip;
|
||||||
|
|
||||||
// shift out first (top-most) slice
|
// shift out first (top-most) slice
|
||||||
indices.shift();
|
indices.shift();
|
||||||
// add flats to shadow
|
// add flats to shadow
|
||||||
|
|
@ -168,16 +172,12 @@ class OpOutline extends CamOp {
|
||||||
let stepover = toolDiam * op.step;
|
let stepover = toolDiam * op.step;
|
||||||
let wideCuts = [] //accumulator for wide cuts
|
let wideCuts = [] //accumulator for wide cuts
|
||||||
for (let c = (op.steps || 1); c > 0; c--){
|
for (let c = (op.steps || 1); c > 0; c--){
|
||||||
offset.slice().forEach(op => {
|
let wideCut = POLY.expand(offset.clone(true), stepover * c, slice.z, [], 1);
|
||||||
// clone removes inners but the real solution is
|
wideCut.forEach(cut =>{ //set order of cuts when wide
|
||||||
// to limit expanded shells to through holes
|
cut.order = c
|
||||||
let wideCut = POLY.expand([op.clone(true)], stepover*c, slice.z, [], 1);
|
if(cut.inner) cut.inner.forEach(inn =>{ inn.order = c })
|
||||||
wideCut.forEach(cut =>{ //set order of cuts when wide
|
|
||||||
cut.order = c
|
|
||||||
if(cut.inner) cut.inner.forEach(inn =>{ inn.order = c })
|
|
||||||
});
|
|
||||||
wideCuts.push(...wideCut)
|
|
||||||
});
|
});
|
||||||
|
wideCuts.push(...wideCut)
|
||||||
}
|
}
|
||||||
offset.appendAll(wideCuts);
|
offset.appendAll(wideCuts);
|
||||||
}
|
}
|
||||||
|
|
@ -187,16 +187,19 @@ class OpOutline extends CamOp {
|
||||||
addDogbones(offset, toolDiam / 5);
|
addDogbones(offset, toolDiam / 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
|
||||||
|
let { center } = stock;
|
||||||
|
let x = center.x - center_off.x;
|
||||||
|
let y = center.y - center_off.y;
|
||||||
|
stockClip = newPolygon().centerRectangle({ x, y }, stock.x + 0.001, stock.y + 0.001);
|
||||||
|
offset = cutPolys([stockClip], offset, slice.z, true);
|
||||||
|
}
|
||||||
|
|
||||||
if (tabs) {
|
if (tabs) {
|
||||||
tabs.forEach(tab => {
|
tabs.forEach(tab => {
|
||||||
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
|
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
|
||||||
});
|
});
|
||||||
offset = cutTabs(tabs, offset, slice.z);
|
offset = cutTabs(tabs, offset);
|
||||||
}
|
|
||||||
|
|
||||||
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
|
|
||||||
let rect = newPolygon().centerRectangle({x:0,y:0}, stock.x, stock.y);
|
|
||||||
offset = cutPolys([rect], offset, slice.z, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// offset.xout(`slice ${slice.z}`);
|
// offset.xout(`slice ${slice.z}`);
|
||||||
|
|
@ -208,11 +211,17 @@ class OpOutline extends CamOp {
|
||||||
|
|
||||||
// project empty up and render
|
// project empty up and render
|
||||||
for (let slice of slices) {
|
for (let slice of slices) {
|
||||||
if (false) slice.output()
|
if (controller.devel) slice.output()
|
||||||
.setLayer("slice", {line: 0xaaaa00}, false)
|
.setLayer("slice", {line: 0xaaaa00}, false)
|
||||||
.addPolys(slice.topPolys())
|
.addPolys(slice.topPolys())
|
||||||
|
if (controller.devel) slice.output()
|
||||||
|
.setLayer("shadow", {line: 0x557799}, false)
|
||||||
|
.addPolys(slice.shadow)
|
||||||
|
if (controller.devel && stockClip) slice.output()
|
||||||
|
.setLayer("stock clip", {line: 0x557799}, false)
|
||||||
|
.addPolys([ stockClip ])
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("outline", {face: color, line: color})
|
.setLayer(state.layername, {face: color, line: color})
|
||||||
.addPolys(slice.camLines);
|
.addPolys(slice.camLines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,9 +233,9 @@ class OpOutline extends CamOp {
|
||||||
let { op, state, sliceOut } = this;
|
let { op, state, sliceOut } = this;
|
||||||
let { setTool, setSpindle, setPrintPoint } = ops;
|
let { setTool, setSpindle, setPrintPoint } = ops;
|
||||||
let { polyEmit, depthOutlinePath } = ops;
|
let { polyEmit, depthOutlinePath } = ops;
|
||||||
let { camOut, newLayer, printPoint } = ops;
|
let { newLayer, printPoint } = ops;
|
||||||
let { settings, widget } = state;
|
let { settings } = state;
|
||||||
let { process, controller } = settings;
|
let { process } = settings;
|
||||||
|
|
||||||
let easeDown = process.camEaseDown;
|
let easeDown = process.camEaseDown;
|
||||||
let toolDiam = this.toolDiam;
|
let toolDiam = this.toolDiam;
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,11 @@ class OpPocket extends CamOp {
|
||||||
|
|
||||||
async slice(progress) {
|
async slice(progress) {
|
||||||
const pocket = this;
|
const pocket = this;
|
||||||
const debug = false;
|
|
||||||
let { op, state } = this;
|
let { op, state } = this;
|
||||||
let { tool, rate, down, plunge, expand, contour, smooth, tolerance } = op;
|
let { tool, rate, down, plunge, expand, contour, smooth, tolerance } = op;
|
||||||
let { ov_botz, ov_conv } = op;
|
let { ov_botz, ov_conv } = op;
|
||||||
let { settings, widget, addSlices, zBottom, zThru, tabs, color } = state;
|
let { settings, widget, addSlices, zBottom, tabs, color } = state;
|
||||||
let { updateToolDiams, cutTabs, healPolys, shadowAt, workarea } = state;
|
let { updateToolDiams, cutTabs, healPolys, shadowAt, workarea } = state;
|
||||||
let { process } = settings;
|
|
||||||
zBottom = ov_botz ? workarea.bottom_stock + ov_botz : zBottom;
|
zBottom = ov_botz ? workarea.bottom_stock + ov_botz : zBottom;
|
||||||
// generate tracing offsets from chosen features
|
// generate tracing offsets from chosen features
|
||||||
let sliceOut;
|
let sliceOut;
|
||||||
|
|
@ -36,24 +34,18 @@ class OpPocket extends CamOp {
|
||||||
let cutdir = ov_conv;
|
let cutdir = ov_conv;
|
||||||
let engrave = contour && op.engrave;
|
let engrave = contour && op.engrave;
|
||||||
let zTop = workarea.top_z;
|
let zTop = workarea.top_z;
|
||||||
|
let devel = settings.controller.devel;
|
||||||
|
let smoothVal = (smooth ?? 0) / 10;
|
||||||
if (contour) {
|
if (contour) {
|
||||||
down = 0;
|
down = 0;
|
||||||
this.topo = await Topo({
|
this.contour = {
|
||||||
// onupdate: (update, msg) => {
|
axis: "-",
|
||||||
onupdate: (index, total, msg) => {
|
inside: true,
|
||||||
progress((index / total) * 0.9, msg);
|
nogpu: true,
|
||||||
},
|
step: toolOver,
|
||||||
ondone: (slices) => {
|
tolerance,
|
||||||
// console.log({ contour: slices });
|
tool,
|
||||||
},
|
};
|
||||||
contour: {
|
|
||||||
tool,
|
|
||||||
tolerance,
|
|
||||||
inside: true,
|
|
||||||
axis: "-"
|
|
||||||
},
|
|
||||||
state: state
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
updateToolDiams(toolDiam);
|
updateToolDiams(toolDiam);
|
||||||
if (tabs) {
|
if (tabs) {
|
||||||
|
|
@ -69,7 +61,7 @@ class OpPocket extends CamOp {
|
||||||
sliceOut.push(slice);
|
sliceOut.push(slice);
|
||||||
return slice;
|
return slice;
|
||||||
}
|
}
|
||||||
function clearZ(polys, z, down) {
|
async function clearZ(polys, z, down) {
|
||||||
if (down) {
|
if (down) {
|
||||||
// adjust step down to a value <= down that
|
// adjust step down to a value <= down that
|
||||||
// ends on the lowest z specified
|
// ends on the lowest z specified
|
||||||
|
|
@ -92,14 +84,14 @@ class OpPocket extends CamOp {
|
||||||
let clip = [], shadow;
|
let clip = [], shadow;
|
||||||
if (contour) {
|
if (contour) {
|
||||||
if (smooth) {
|
if (smooth) {
|
||||||
clip = POLY.offset(POLY.offset([ poly ], smooth), -smooth);
|
clip = POLY.offset(POLY.offset([ poly ], smoothVal), -smoothVal);
|
||||||
} else {
|
} else {
|
||||||
clip = [ poly ];
|
clip = [ poly ];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
shadow = shadowAt(z);
|
shadow = shadowAt(z);
|
||||||
if (smooth) {
|
if (smooth) {
|
||||||
shadow = POLY.setZ(POLY.offset(POLY.offset(shadow, smooth), -smooth), z);
|
shadow = POLY.setZ(POLY.offset(POLY.offset(shadow, smoothVal), -smoothVal), z);
|
||||||
}
|
}
|
||||||
POLY.subtract([ poly ], shadow, clip, undefined, undefined, 0);
|
POLY.subtract([ poly ], shadow, clip, undefined, undefined, 0);
|
||||||
if (op.outline) {
|
if (op.outline) {
|
||||||
|
|
@ -130,16 +122,16 @@ class OpPocket extends CamOp {
|
||||||
}
|
}
|
||||||
POLY.setWinding(slice.camLines, cutdir, false);
|
POLY.setWinding(slice.camLines, cutdir, false);
|
||||||
if (contour) {
|
if (contour) {
|
||||||
slice.camLines = pocket.conform(slice.camLines, op.refine, engrave, pct => {
|
slice.camLines = await pocket.conform(slice.camLines, op.refine, engrave, pct => {
|
||||||
progress(0.9 + (zpro + zinc * pct) * 0.1, "conform");
|
progress(0.9 + (zpro + zinc * pct) * 0.1, "conform");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("pocket", {line: color}, false)
|
.setLayer(state.layername, {line: color}, false)
|
||||||
.addPolys(slice.camLines)
|
.addPolys(slice.camLines)
|
||||||
if (debug && shadow) slice.output()
|
if (devel && shadow) slice.output()
|
||||||
.setLayer("pocket shadow", {line: 0xff8811}, false)
|
.setLayer("pocket shadow", {line: 0xff8811}, false)
|
||||||
.addPolys(shadow)
|
.addPolys(shadow);
|
||||||
if (!contour) {
|
if (!contour) {
|
||||||
progress(zpro, "pocket");
|
progress(zpro, "pocket");
|
||||||
}
|
}
|
||||||
|
|
@ -168,23 +160,37 @@ class OpPocket extends CamOp {
|
||||||
outline = POLY.setWinding(outline, cutdir, false);
|
outline = POLY.setWinding(outline, cutdir, false);
|
||||||
outline = healPolys(outline);
|
outline = healPolys(outline);
|
||||||
if (smooth) {
|
if (smooth) {
|
||||||
outline = POLY.offset(POLY.offset(outline, smooth), -smooth);
|
outline = POLY.offset(POLY.offset(outline, smoothVal), -smoothVal);
|
||||||
}
|
}
|
||||||
if (outline.length) {
|
if (outline.length) {
|
||||||
// option to skip interior features (holes, pillars)
|
// option to skip interior features (holes, pillars)
|
||||||
if (op.outline) {
|
if (op.outline) {
|
||||||
POLY.clearInner(outline);
|
POLY.clearInner(outline);
|
||||||
}
|
}
|
||||||
if (debug) newSliceOut(zmin).output()
|
await clearZ(outline, zmin + 0.0001, down);
|
||||||
|
if (devel && sliceOut?.length) sliceOut[0].output()
|
||||||
.setLayer("pocket area", {line: 0x1188ff}, false)
|
.setLayer("pocket area", {line: 0x1188ff}, false)
|
||||||
.addPolys(outline)
|
.addPolys(outline)
|
||||||
clearZ(outline, zmin + 0.0001, down);
|
|
||||||
progress(1, "pocket");
|
progress(1, "pocket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mold cam output lines to the surface of the topo offset by tool geometry
|
// mold cam output lines to the surface of the topo offset by tool geometry
|
||||||
conform(camLines, refine, engrave, progress) {
|
async conform(camLines, refine, engrave, progress) {
|
||||||
|
if (!this.topo) {
|
||||||
|
console.log('deferred topo');
|
||||||
|
this.topo = await Topo({
|
||||||
|
// onupdate: (update, msg) => {
|
||||||
|
onupdate: (index, total, msg) => {
|
||||||
|
progress((index / total) * 0.9, msg);
|
||||||
|
},
|
||||||
|
ondone: (slices) => {
|
||||||
|
// console.log({ contour: slices });
|
||||||
|
},
|
||||||
|
contour: this.contour,
|
||||||
|
state: this.state
|
||||||
|
});
|
||||||
|
}
|
||||||
const topo = this.topo;
|
const topo = this.topo;
|
||||||
// re-segment polygon to a higher resolution
|
// re-segment polygon to a higher resolution
|
||||||
const hirez = camLines.map(p => p.segment(topo.tolerance * 2));
|
const hirez = camLines.map(p => p.segment(topo.tolerance * 2));
|
||||||
|
|
@ -236,7 +242,7 @@ class OpPocket extends CamOp {
|
||||||
|
|
||||||
prepare(ops, progress) {
|
prepare(ops, progress) {
|
||||||
let { op, state, pockets } = this;
|
let { op, state, pockets } = this;
|
||||||
let { setTool, setSpindle, setTolerance, sliceOutput, getPrintPoint } = ops;
|
let { getPrintPoint , pocket, setTool, setSpindle, setTolerance } = ops;
|
||||||
let { process } = state.settings;
|
let { process } = state.settings;
|
||||||
|
|
||||||
setTool(op.tool, op.rate);
|
setTool(op.tool, op.rate);
|
||||||
|
|
@ -269,11 +275,12 @@ class OpPocket extends CamOp {
|
||||||
}
|
}
|
||||||
if (min.pocket) {
|
if (min.pocket) {
|
||||||
min.pocket.used = true;
|
min.pocket.used = true;
|
||||||
sliceOutput(min.pocket, {
|
pocket({
|
||||||
cutdir: op.ov_conv,
|
cutdir: op.ov_conv,
|
||||||
depthFirst: process.camDepthFirst && !state.isIndexed,
|
depthFirst: process.camDepthFirst && !state.isIndexed,
|
||||||
easeDown: op.down && process.easeDown ? op.down : 0,
|
easeDown: op.down && process.easeDown ? op.down : 0,
|
||||||
progress: (n,m) => progress(n/m, "pocket")
|
progress: (n,m) => progress(n/m, "pocket"),
|
||||||
|
slices: min.pocket
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ class OpRegister extends CamOp {
|
||||||
|
|
||||||
let tool = new Tool(settings, op.tool);
|
let tool = new Tool(settings, op.tool);
|
||||||
let sliceOut = this.sliceOut = [];
|
let sliceOut = this.sliceOut = [];
|
||||||
|
let axis = op.axis.toLowerCase();
|
||||||
|
|
||||||
updateToolDiams(tool.fluteDiameter());
|
updateToolDiams(tool.fluteDiameter());
|
||||||
|
|
||||||
|
|
@ -42,7 +43,7 @@ class OpRegister extends CamOp {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (op.axis.toLowerCase()) {
|
switch (axis) {
|
||||||
case "x":
|
case "x":
|
||||||
if (op.points == 3) {
|
if (op.points == 3) {
|
||||||
pathPoints.push(newPoint(boundMinX - cutOffset, centerY, 0));
|
pathPoints.push(newPoint(boundMinX - cutOffset, centerY, 0));
|
||||||
|
|
@ -64,6 +65,7 @@ class OpRegister extends CamOp {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "-":
|
case "-":
|
||||||
|
case "=":
|
||||||
let halfOffset = toolOffset / 2,
|
let halfOffset = toolOffset / 2,
|
||||||
loopMinX = boundMinX - cutOffset,
|
loopMinX = boundMinX - cutOffset,
|
||||||
loopMaxX = boundMaxX + cutOffset,
|
loopMaxX = boundMaxX + cutOffset,
|
||||||
|
|
@ -84,7 +86,7 @@ class OpRegister extends CamOp {
|
||||||
}
|
}
|
||||||
function rept(count, step, fn) {
|
function rept(count, step, fn) {
|
||||||
while (count-- > 0) {
|
while (count-- > 0) {
|
||||||
fn(step, count === 0);
|
fn(step, count);
|
||||||
step = -step;
|
step = -step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -93,9 +95,16 @@ class OpRegister extends CamOp {
|
||||||
addSlices(slice);
|
addSlices(slice);
|
||||||
sliceOut.push(slice);
|
sliceOut.push(slice);
|
||||||
start(z);
|
start(z);
|
||||||
rept(4, halfOffset, oy => {
|
rept(4, halfOffset, (oy, count) => {
|
||||||
move(0, -oy);
|
move(0, -oy);
|
||||||
move(deltaX, 0);
|
if (axis === '=' && count % 2 === 1) {
|
||||||
|
move(deltaX/2, 0);
|
||||||
|
move(0, oy/2);
|
||||||
|
move(0, -oy/2);
|
||||||
|
move(deltaX/2, 0);
|
||||||
|
} else {
|
||||||
|
move(deltaX, 0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
rept(4, halfOffset, ox => {
|
rept(4, halfOffset, ox => {
|
||||||
move(ox, 0);
|
move(ox, 0);
|
||||||
|
|
@ -136,11 +145,10 @@ class OpRegister extends CamOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare(ops, progress) {
|
prepare(ops, progress) {
|
||||||
let { op, state } = this;
|
let { op } = this;
|
||||||
let { settings, widget, addSlices, updateToolDiams } = state;
|
|
||||||
let { setTool, setSpindle, setDrill, emitDrills } = ops;
|
let { setTool, setSpindle, setDrill, emitDrills } = ops;
|
||||||
|
|
||||||
if (op.axis === '-') {
|
if (op.axis === '-' || op.axis === '=') {
|
||||||
setTool(op.tool, op.feed, op.rate);
|
setTool(op.tool, op.feed, op.rate);
|
||||||
setSpindle(op.spindle);
|
setSpindle(op.spindle);
|
||||||
for (let slice of this.sliceOut) {
|
for (let slice of this.sliceOut) {
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,12 @@ class OpRough extends CamOp {
|
||||||
|
|
||||||
async slice(progress) {
|
async slice(progress) {
|
||||||
let { op, state } = this;
|
let { op, state } = this;
|
||||||
let { settings, slicer, addSlices, unsafe, color } = state;
|
let { settings, slicer, addSlices, unsafe, color, widget } = state;
|
||||||
let { updateToolDiams, thruHoles, tabs, cutTabs, cutPolys } = state;
|
let { updateToolDiams, thruHoles, tabs, cutTabs, cutPolys } = state;
|
||||||
let { ztOff, zMax, shadowAt, isIndexed} = state;
|
let { ztOff, zMax, shadowAt, isIndexed} = state;
|
||||||
let { workarea } = state;
|
let { workarea } = state;
|
||||||
let { process, stock } = settings;
|
let { controller, process, stock } = settings;
|
||||||
|
let center_off = widget.track.pos ?? { x: 0, y: 0, z: 0};
|
||||||
|
|
||||||
if (op.down <= 0) {
|
if (op.down <= 0) {
|
||||||
throw `invalid step down "${op.down}"`;
|
throw `invalid step down "${op.down}"`;
|
||||||
|
|
@ -36,6 +37,12 @@ class OpRough extends CamOp {
|
||||||
|
|
||||||
updateToolDiams(toolDiam);
|
updateToolDiams(toolDiam);
|
||||||
|
|
||||||
|
if (tabs) {
|
||||||
|
tabs.forEach(tab => {
|
||||||
|
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// clear the stock above the area to be roughed out
|
// clear the stock above the area to be roughed out
|
||||||
if (workarea.top_z > workarea.top_part && !cutThruBypass) {
|
if (workarea.top_z > workarea.top_part && !cutThruBypass) {
|
||||||
let shadow = state.shadow.base.clone();
|
let shadow = state.shadow.base.clone();
|
||||||
|
|
@ -58,14 +65,11 @@ class OpRough extends CamOp {
|
||||||
slice.z = z;
|
slice.z = z;
|
||||||
let polys = POLY.setZ(facing.clone(true), slice.z + roughLeaveZ);
|
let polys = POLY.setZ(facing.clone(true), slice.z + roughLeaveZ);
|
||||||
if (tabs) {
|
if (tabs) {
|
||||||
tabs.forEach(tab => {
|
|
||||||
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
|
|
||||||
});
|
|
||||||
polys = cutTabs(tabs, polys, slice.z);
|
polys = cutTabs(tabs, polys, slice.z);
|
||||||
}
|
}
|
||||||
slice.camLines = polys;
|
slice.camLines = polys;
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("roughing", {face: color, line: color})
|
.setLayer(state.layername, {face: color, line: color})
|
||||||
.addPolys(slice.camLines);
|
.addPolys(slice.camLines);
|
||||||
addSlices(slice);
|
addSlices(slice);
|
||||||
camFaces.push(slice);
|
camFaces.push(slice);
|
||||||
|
|
@ -206,20 +210,6 @@ class OpRough extends CamOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tabs) {
|
|
||||||
tabs.forEach(tab => {
|
|
||||||
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
|
|
||||||
});
|
|
||||||
offset = cutTabs(tabs, offset, slice.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!offset) return;
|
|
||||||
|
|
||||||
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
|
|
||||||
let rect = newPolygon().centerRectangle(stock.center, stock.x, stock.y);
|
|
||||||
offset = cutPolys([rect], offset, slice.z, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// elimate double inset on inners
|
// elimate double inset on inners
|
||||||
offset.forEach(op => {
|
offset.forEach(op => {
|
||||||
if (op.inner) {
|
if (op.inner) {
|
||||||
|
|
@ -236,24 +226,38 @@ class OpRough extends CamOp {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
|
||||||
|
let { center } = stock;
|
||||||
|
let x = center.x - center_off.x;
|
||||||
|
let y = center.y - center_off.y;
|
||||||
|
let rect = newPolygon().centerRectangle({ x, y }, stock.x + 0.001, stock.y + 0.001);
|
||||||
|
offset = cutPolys([rect], offset, slice.z, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!offset) return;
|
||||||
|
|
||||||
slice.camLines = offset;
|
slice.camLines = offset;
|
||||||
if (roughLeaveZ) {
|
if (roughLeaveZ) {
|
||||||
// offset roughing in Z as well to minimize
|
// offset roughing in Z as well to minimize tool marks on curved surfaces
|
||||||
// tool marks on curved surfaces
|
|
||||||
// const roughLeaveZ = 1 * Math.min(roughDown, roughLeave / 2);
|
// const roughLeaveZ = 1 * Math.min(roughDown, roughLeave / 2);
|
||||||
slice.camLines.forEach(p => {
|
for (let poly of slice.camLines) {
|
||||||
p.setZ(p.getZ() + roughLeaveZ);
|
for (let point of poly.points) point.z += roughLeaveZ;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
if (controller.devel) {
|
||||||
|
if (tabs) slice.output()
|
||||||
|
.setLayer("tabs clip", {line: 0xaa00aa}, true)
|
||||||
|
.addPolys(tabs.map(tab => tab.off).flat());
|
||||||
|
slice.output()
|
||||||
|
.setLayer("slice", {line: 0xaaaa00}, true)
|
||||||
|
.addPolys(slice.topPolys())
|
||||||
|
// .setLayer("top shadow", {line: 0x0000aa})
|
||||||
|
// .addPolys(tshadow)
|
||||||
|
// .setLayer("rough shadow", {line: 0x00aa00})
|
||||||
|
// .addPolys(shadow)
|
||||||
|
.setLayer("shadow clip", {line: 0xaa0000})
|
||||||
|
.addPolys(shell);
|
||||||
}
|
}
|
||||||
if (false) slice.output()
|
|
||||||
.setLayer("slice", {line: 0xaaaa00}, true)
|
|
||||||
.addPolys(slice.topPolys())
|
|
||||||
// .setLayer("top shadow", {line: 0x0000aa})
|
|
||||||
// .addPolys(tshadow)
|
|
||||||
// .setLayer("rough shadow", {line: 0x00aa00})
|
|
||||||
// .addPolys(shadow)
|
|
||||||
.setLayer("rough shell", {line: 0xaa0000})
|
|
||||||
.addPolys(shell);
|
|
||||||
progress(0.5 + 0.5 * (index / slices.length));
|
progress(0.5 + 0.5 * (index / slices.length));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -278,8 +282,11 @@ class OpRough extends CamOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
slices.forEach(slice => {
|
slices.forEach(slice => {
|
||||||
|
if (slice.camLines && tabs) {
|
||||||
|
slice.camLines = cutTabs(tabs, slice.camLines);
|
||||||
|
}
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("roughing", {face: color, line: color})
|
.setLayer(state.layername, {face: color, line: color})
|
||||||
.addPolys(slice.camLines);
|
.addPolys(slice.camLines);
|
||||||
});
|
});
|
||||||
this.sliceOut = slices.filter(slice => slice.camLines);
|
this.sliceOut = slices.filter(slice => slice.camLines);
|
||||||
|
|
@ -289,15 +296,13 @@ class OpRough extends CamOp {
|
||||||
|
|
||||||
prepare(ops, progress) {
|
prepare(ops, progress) {
|
||||||
let { op, state, sliceOut, camFaces } = this;
|
let { op, state, sliceOut, camFaces } = this;
|
||||||
let { setTool, setSpindle, setPrintPoint, sliceOutput, polyEmit } = ops;
|
let { setTool, setSpindle, setPrintPoint, pocket, polyEmit, newLayer, printPoint } = ops;
|
||||||
let { camOut, newLayer, printPoint } = ops;
|
|
||||||
let { settings } = state;
|
let { settings } = state;
|
||||||
let { process } = settings;
|
let { process } = settings;
|
||||||
|
|
||||||
let easeDown = process.camEaseDown;
|
let easeDown = process.camEaseDown;
|
||||||
let cutdir = op.ov_conv;
|
let cutdir = op.ov_conv;
|
||||||
let depthFirst = process.camDepthFirst && !state.isIndexed;
|
let depthFirst = process.camDepthFirst && !state.isIndexed;
|
||||||
let depthData = [];
|
|
||||||
|
|
||||||
setTool(op.tool, op.rate, op.plunge);
|
setTool(op.tool, op.rate, op.plunge);
|
||||||
setSpindle(op.spindle);
|
setSpindle(op.spindle);
|
||||||
|
|
@ -325,11 +330,12 @@ class OpRough extends CamOp {
|
||||||
|
|
||||||
// output the roughing passes
|
// output the roughing passes
|
||||||
setPrintPoint(printPoint);
|
setPrintPoint(printPoint);
|
||||||
sliceOutput(sliceOut, {
|
pocket({
|
||||||
cutdir,
|
cutdir,
|
||||||
depthFirst,
|
depthFirst,
|
||||||
easeDown: op.down && easeDown ? 0.001 : 0,
|
easeDown: op.down && easeDown ? 0.001 : 0,
|
||||||
progress: (n,m) => progress(n/m, "routing")
|
progress: (n,m) => progress(n/m, "routing"),
|
||||||
|
slices: sliceOut
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ class OpTrace extends CamOp {
|
||||||
}
|
}
|
||||||
POLY.setWinding(slice.camLines, offset === "outside" ? !cutdir : cutdir, false);
|
POLY.setWinding(slice.camLines, offset === "outside" ? !cutdir : cutdir, false);
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("trace follow", {line: color}, false)
|
.setLayer(state.layername, {line: color}, false)
|
||||||
.addPolys(slice.camLines)
|
.addPolys(slice.camLines)
|
||||||
}
|
}
|
||||||
function clearZnew(polys, z, down) {
|
function clearZnew(polys, z, down) {
|
||||||
|
|
@ -154,7 +154,7 @@ class OpTrace extends CamOp {
|
||||||
.setLayer("trace poly", {line: 0x1188ff}, false)
|
.setLayer("trace poly", {line: 0x1188ff}, false)
|
||||||
.addPolys([ poly ])
|
.addPolys([ poly ])
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("trace", {line: color}, false)
|
.setLayer(state.layername, {line: color}, false)
|
||||||
.addPolys(slice.camLines)
|
.addPolys(slice.camLines)
|
||||||
progress(zpro, "trace");
|
progress(zpro, "trace");
|
||||||
zpro += zinc;
|
zpro += zinc;
|
||||||
|
|
@ -295,7 +295,6 @@ class OpTrace extends CamOp {
|
||||||
|
|
||||||
prepare(ops, progress) {
|
prepare(ops, progress) {
|
||||||
let { op, state } = this;
|
let { op, state } = this;
|
||||||
let { settings } = state;
|
|
||||||
let { setTool, setSpindle } = ops;
|
let { setTool, setSpindle } = ops;
|
||||||
|
|
||||||
setTool(op.tool, op.rate);
|
setTool(op.tool, op.rate);
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
|
|
||||||
if (widget.camops.length === 0 || widget.meta.disabled) return;
|
if (widget.camops.length === 0 || widget.meta.disabled) return;
|
||||||
|
|
||||||
let device = settings.device,
|
let { device, process } = settings,
|
||||||
process = settings.process,
|
|
||||||
isIndexed = process.camStockIndexed,
|
isIndexed = process.camStockIndexed,
|
||||||
startCenter = process.camOriginCenter,
|
startCenter = process.camOriginCenter,
|
||||||
alignTop = settings.controller.alignTop,
|
alignTop = settings.controller.alignTop,
|
||||||
|
|
@ -65,7 +64,6 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
stockz = stock.z * (isIndexed ? 0.5 : 1),
|
stockz = stock.z * (isIndexed ? 0.5 : 1),
|
||||||
outer = settings.bounds || widget.getPositionBox(),
|
outer = settings.bounds || widget.getPositionBox(),
|
||||||
outerz = outer.max.z,
|
outerz = outer.max.z,
|
||||||
slices = widget.slices,
|
|
||||||
zclear = (process.camZClearance || 1),
|
zclear = (process.camZClearance || 1),
|
||||||
zmax_force = process.camForceZMax || false,
|
zmax_force = process.camForceZMax || false,
|
||||||
zmax_outer = stockz + zclear,
|
zmax_outer = stockz + zclear,
|
||||||
|
|
@ -75,13 +73,13 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
boundsz = isIndexed ? stock.z / 2 : bounds.max.z + ztOff,
|
boundsz = isIndexed ? stock.z / 2 : bounds.max.z + ztOff,
|
||||||
zadd = !isIndexed ? stock.z - boundsz : alignTop ? outerz - boundsz : 0,
|
zadd = !isIndexed ? stock.z - boundsz : alignTop ? outerz - boundsz : 0,
|
||||||
zmax = outerz + zclear + (process.camOriginOffZ || 0),
|
zmax = outerz + zclear + (process.camOriginOffZ || 0),
|
||||||
|
zsafe = isIndexed ? Math.hypot(bounds.dim.y, bounds.dim.z) / 2 + zclear : zmax,
|
||||||
wmpos = widget.track.pos,
|
wmpos = widget.track.pos,
|
||||||
wmx = wmpos.x,
|
wmx = wmpos.x,
|
||||||
wmy = wmpos.y,
|
wmy = wmpos.y,
|
||||||
originx = (startCenter ? 0 : -stock.x / 2) + (process.camOriginOffX || 0),
|
originx = (startCenter ? 0 : -stock.x / 2) + (process.camOriginOffX || 0),
|
||||||
originy = (startCenter ? 0 : -stock.y / 2) + (process.camOriginOffY || 0),
|
originy = (startCenter ? 0 : -stock.y / 2) + (process.camOriginOffY || 0),
|
||||||
origin = newPoint(originx + wmx, originy + wmy, zmax),
|
origin = newPoint(originx + wmx, originy + wmy, zmax),
|
||||||
output = print.output,
|
|
||||||
easeDown = process.camEaseDown,
|
easeDown = process.camEaseDown,
|
||||||
easeAngle = process.camEaseAngle,
|
easeAngle = process.camEaseAngle,
|
||||||
depthFirst = process.camDepthFirst,
|
depthFirst = process.camDepthFirst,
|
||||||
|
|
@ -94,13 +92,11 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
drillDown = 0,
|
drillDown = 0,
|
||||||
drillLift = 0,
|
drillLift = 0,
|
||||||
drillDwell = 0,
|
drillDwell = 0,
|
||||||
drillThru = 0,
|
|
||||||
lasering = false,
|
lasering = false,
|
||||||
laserPower = 0,
|
laserPower = 0,
|
||||||
newOutput = print.output || [],
|
newOutput = print.output || [],
|
||||||
layerOut = [],
|
layerOut = [],
|
||||||
printPoint,
|
printPoint,
|
||||||
isNewMode,
|
|
||||||
isPocket,
|
isPocket,
|
||||||
isContour,
|
isContour,
|
||||||
isRough,
|
isRough,
|
||||||
|
|
@ -112,6 +108,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
toolDiamMove,
|
toolDiamMove,
|
||||||
plungeRate = process.camFastFeedZ,
|
plungeRate = process.camFastFeedZ,
|
||||||
feedRate,
|
feedRate,
|
||||||
|
lastOp,
|
||||||
lastTool,
|
lastTool,
|
||||||
lastPush,
|
lastPush,
|
||||||
lastPoint,
|
lastPoint,
|
||||||
|
|
@ -187,11 +184,10 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
laserPower = power;
|
laserPower = power;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDrill(down, lift, dwell, thru) {
|
function setDrill(down, lift, dwell) {
|
||||||
drillDown = down;
|
drillDown = down;
|
||||||
drillLift = lift;
|
drillLift = lift;
|
||||||
drillDwell = dwell;
|
drillDwell = dwell;
|
||||||
drillThru = thru;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitDrills(polys) {
|
function emitDrills(polys) {
|
||||||
|
|
@ -264,10 +260,11 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
*/
|
*/
|
||||||
function layerPush(point, emit, speed, tool, options) {
|
function layerPush(point, emit, speed, tool, options) {
|
||||||
const { type, center, arcPoints } = options ?? {};
|
const { type, center, arcPoints } = options ?? {};
|
||||||
const dz = (point && lastPush && lastPush.point) ? point.z - lastPush.point.z : 0;
|
const dz = (point && lastPush?.point) ? point.z - lastPush.point.z : 0;
|
||||||
if (dz < 0 && speed > plungeRate) {
|
if (dz < -0.05 && speed > plungeRate) {
|
||||||
speed = plungeRate;
|
speed = plungeRate;
|
||||||
}
|
}
|
||||||
|
// if (options?.type !== 'lerp') console.log( point, currentOp.type );
|
||||||
layerOut.mode = currentOp;
|
layerOut.mode = currentOp;
|
||||||
if (lasering) {
|
if (lasering) {
|
||||||
let power = emit ? laserPower : 0;
|
let power = emit ? laserPower : 0;
|
||||||
|
|
@ -320,7 +317,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
p.x + wmx,
|
p.x + wmx,
|
||||||
p.y + wmy,
|
p.y + wmy,
|
||||||
p.z + zadd
|
p.z + zadd
|
||||||
).setA(p.a ?? lastPoint?.a);
|
).setA(p.a ?? lastPoint?.a).annotate({ slice: p.slice });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -333,7 +330,26 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
* @param {number} opts.factor speed scale factor
|
* @param {number} opts.factor speed scale factor
|
||||||
*/
|
*/
|
||||||
function camOut(point, emit = 1, opts) {
|
function camOut(point, emit = 1, opts) {
|
||||||
// console.trace({point, emit, opts})
|
let lop = lastOp;
|
||||||
|
lastOp = currentOp;
|
||||||
|
|
||||||
|
// on operation changes:
|
||||||
|
// 1. move to safe z of current point preserving angle
|
||||||
|
// 2. move to safe z of new point preserving old angle
|
||||||
|
// 3. move to safe z of new point with new angle
|
||||||
|
if (lop && lop !== currentOp && lastPoint) {
|
||||||
|
// compensate for applyWidgetMovement() applied to lastPoint
|
||||||
|
let lpo = lastPoint.clone().move({ x: -wmx, y: -wmy, z: -zadd });
|
||||||
|
camOut(lpo.clone().setZ(zsafe).setA(lastPoint.a), 0);
|
||||||
|
camOut(point.clone().setZ(zsafe).setA(lastPoint.a), 0);
|
||||||
|
camOut(point.clone().setZ(zsafe), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lop?.type === 'index' && lop !== currentOp ) {
|
||||||
|
// console.log('post index first point', point);
|
||||||
|
camOut(point.clone().setZ(lastPoint.z).setA(lastPoint.a), 1);
|
||||||
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
center = {},
|
center = {},
|
||||||
clockwise = true,
|
clockwise = true,
|
||||||
|
|
@ -342,14 +358,12 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
factor = 1,
|
factor = 1,
|
||||||
} = opts ?? {}
|
} = opts ?? {}
|
||||||
|
|
||||||
// console.log({radius})
|
|
||||||
const isArc = emit == 2 || emit == 3;
|
const isArc = emit == 2 || emit == 3;
|
||||||
|
const pointA = point.a;
|
||||||
|
|
||||||
// apply widget movement pos
|
// apply widget movement pos
|
||||||
const pointA = point.a;
|
|
||||||
point = applyWidgetMovement(point);
|
point = applyWidgetMovement(point);
|
||||||
|
|
||||||
// console.log(point.z);
|
|
||||||
if (nextIsMove) {
|
if (nextIsMove) {
|
||||||
emit = 0;
|
emit = 0;
|
||||||
nextIsMove = false;
|
nextIsMove = false;
|
||||||
|
|
@ -370,7 +384,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
for (let a of lerp) {
|
for (let a of lerp) {
|
||||||
let lp = point.clone().setA(a);
|
let lp = point.clone().setA(a);
|
||||||
// console.log(lp.a, lp.x, lp.y, lp.z);
|
// console.log(lp.a, lp.x, lp.y, lp.z);
|
||||||
lastPoint = layerPush(
|
layerPush(
|
||||||
lp,
|
lp,
|
||||||
emit,
|
emit,
|
||||||
rate,
|
rate,
|
||||||
|
|
@ -403,16 +417,20 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
layerPush(point.clone().setZ(stockz + 1), 0, 0, tool);
|
layerPush(point.clone().setZ(stockz + 1), 0, 0, tool);
|
||||||
// change to cutting move for remainder of plunge
|
// change to cutting move for remainder of plunge
|
||||||
emit = 1;
|
emit = 1;
|
||||||
|
isMove = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop points too close together
|
// drop points too close together
|
||||||
if (!isLathe && !isArc && deltaXY < 0.001 && point.z === lastPoint.z) {
|
if (!isLathe && !isArc && deltaXY < 0.001 && point.z === lastPoint.z && point.a === lastPoint.a) {
|
||||||
// console.trace(["drop dup",lastPoint,point]);
|
// console.trace(["drop dup",lastPoint,point]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// no jump moves in contour mode to adjacent slice points
|
||||||
|
let steady = lastPoint && currentOp.type === 'contour' && Math.abs(point.slice - lastPoint.slice) < 4;
|
||||||
|
|
||||||
// convert short planar moves to cuts in some cases
|
// convert short planar moves to cuts in some cases
|
||||||
if (hasDelta && !isRough && !isArc && isMove && deltaXY <= moveLen && deltaZ <= 0 && !lasering) {
|
if (hasDelta && !isArc && isMove && deltaXY <= moveLen && deltaZ <= 0 && !lasering) {
|
||||||
let iscontour = tolerance > 0;
|
let iscontour = tolerance > 0;
|
||||||
let isflat = absDeltaZ < 0.001;
|
let isflat = absDeltaZ < 0.001;
|
||||||
// restrict this to contouring
|
// restrict this to contouring
|
||||||
|
|
@ -431,7 +449,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
} else if (point.z < lastPoint.z) {
|
} else if (point.z < lastPoint.z) {
|
||||||
layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool);
|
layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool);
|
||||||
}
|
}
|
||||||
} else if (isMove) {
|
} else if (isMove && !steady) {
|
||||||
// for longer moves, check the terrain to see if we need to go up and over
|
// for longer moves, check the terrain to see if we need to go up and over
|
||||||
const bigXY = (deltaXY > moveLen && !lasering);
|
const bigXY = (deltaXY > moveLen && !lasering);
|
||||||
const bigZ = (deltaZ > toolDiam / 2 && deltaXY > tolerance);
|
const bigZ = (deltaZ > toolDiam / 2 && deltaXY > tolerance);
|
||||||
|
|
@ -478,13 +496,15 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// set new plunge rate
|
// set new plunge rate
|
||||||
|
let tmprate;
|
||||||
|
if (false)
|
||||||
if (!lasering && !isLathe && deltaZ < -tolerance) {
|
if (!lasering && !isLathe && deltaZ < -tolerance) {
|
||||||
let threshold = Math.min(deltaXY / 2, absDeltaZ),
|
let threshold = Math.min(deltaXY / 2, absDeltaZ),
|
||||||
modifier = threshold / absDeltaZ;
|
modifier = threshold / absDeltaZ;
|
||||||
if (synthPlunge && threshold && modifier && deltaXY > tolerance) {
|
if (synthPlunge && threshold && modifier && deltaXY > tolerance) {
|
||||||
// use modifier to speed up long XY move plunge rates
|
// use modifier to speed up long XY move plunge rates
|
||||||
// console.log('modifier', modifier);
|
// console.log('modifier', modifier);
|
||||||
rate = Math.max(
|
tmprate = Math.max(
|
||||||
plungeRate,
|
plungeRate,
|
||||||
Math.round(plungeRate + ((feedRate - plungeRate) * modifier))
|
Math.round(plungeRate + ((feedRate - plungeRate) * modifier))
|
||||||
);
|
);
|
||||||
|
|
@ -492,7 +512,8 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
let L = Math.hypot(deltaXY, absDeltaZ);
|
let L = Math.hypot(deltaXY, absDeltaZ);
|
||||||
let limXY = feedRate * L / deltaXY;
|
let limXY = feedRate * L / deltaXY;
|
||||||
let limZ = plungeRate * L / Math.abs(deltaZ);
|
let limZ = plungeRate * L / Math.abs(deltaZ);
|
||||||
rate = Math.min(feedRate, limXY, limZ);
|
tmprate = Math.min(feedRate, limXY, limZ);
|
||||||
|
console.log({ rate_override: rate, was: rate });
|
||||||
// let zps = len / absDeltaZ;
|
// let zps = len / absDeltaZ;
|
||||||
// rate = Math.max(
|
// rate = Math.max(
|
||||||
// plungeRate,
|
// plungeRate,
|
||||||
|
|
@ -508,7 +529,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
lastPoint = layerPush(
|
lastPoint = layerPush(
|
||||||
point,
|
point,
|
||||||
clockwise ? 2 : 3,
|
clockwise ? 2 : 3,
|
||||||
rate,
|
tmprate ?? rate,
|
||||||
tool,
|
tool,
|
||||||
{
|
{
|
||||||
center,
|
center,
|
||||||
|
|
@ -523,7 +544,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
lastPoint = layerPush(
|
lastPoint = layerPush(
|
||||||
point,
|
point,
|
||||||
emit,
|
emit,
|
||||||
rate,
|
tmprate ?? rate,
|
||||||
tool
|
tool
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -533,16 +554,15 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
* output an array of slices that form a pocket
|
* output an array of slices that form a pocket
|
||||||
* used by rough and pocket ops, does not support arcs
|
* used by rough and pocket ops, does not support arcs
|
||||||
*
|
*
|
||||||
* @param {Slice[]} top-down Z stack of slices
|
* @param {Slice[]} slices top-down Z stack of slices
|
||||||
* @param {*} opts
|
* @param {boolean} cutdir true=CW false=CCW
|
||||||
|
* @param {boolean} depthFirst prioritize cut depth in pockets by nesting
|
||||||
*/
|
*/
|
||||||
function sliceOutput(sliceOut, opts = {}) {
|
function pocket({ slices, cutdir, depthFirst, easeDown, progress }) {
|
||||||
const { cutdir, depthFirst, easeDown, progress } = opts;
|
|
||||||
|
|
||||||
let total = 0;
|
let total = 0;
|
||||||
let depthData = [];
|
let depthData = [];
|
||||||
|
|
||||||
for (let slice of sliceOut) {
|
for (let slice of slices) {
|
||||||
let polys = [], t = [], c = [];
|
let polys = [], t = [], c = [];
|
||||||
POLY.flatten(slice.camLines).forEach(function (poly) {
|
POLY.flatten(slice.camLines).forEach(function (poly) {
|
||||||
let child = poly.parent;
|
let child = poly.parent;
|
||||||
|
|
@ -569,7 +589,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
}, { swapdir: false });
|
}, { swapdir: false });
|
||||||
newLayer();
|
newLayer();
|
||||||
}
|
}
|
||||||
progress(++total, sliceOut.length);
|
progress(++total, slices.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// crucially returns true for -0 as well as other negative #s
|
// crucially returns true for -0 as well as other negative #s
|
||||||
|
|
@ -663,44 +683,32 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
// make top start offset configurable
|
// make top start offset configurable
|
||||||
printPoint = firstPoint || origin;
|
printPoint = firstPoint || origin;
|
||||||
|
|
||||||
// accumulated data for depth-first optimizations
|
|
||||||
// let depthData = {
|
|
||||||
// rough: [],
|
|
||||||
// outline: [],
|
|
||||||
// roughDiam: 0,
|
|
||||||
// outlineDiam: 0,
|
|
||||||
// contourx: [],
|
|
||||||
// contoury: [],
|
|
||||||
// trace: [],
|
|
||||||
// drill: [],
|
|
||||||
// layer: 0,
|
|
||||||
// };
|
|
||||||
|
|
||||||
let ops = {
|
let ops = {
|
||||||
stock,
|
|
||||||
setTool,
|
|
||||||
setDrill,
|
|
||||||
setSpindle,
|
|
||||||
setTolerance,
|
|
||||||
setPrintPoint,
|
|
||||||
setLasering,
|
|
||||||
getPrintPoint() { return printPoint },
|
|
||||||
printPoint,
|
|
||||||
newLayer,
|
|
||||||
addGCode,
|
addGCode,
|
||||||
|
bounds,
|
||||||
camOut,
|
camOut,
|
||||||
polyEmit,
|
|
||||||
poly2polyEmit,
|
|
||||||
tip2tipEmit,
|
|
||||||
depthRoughPath,
|
|
||||||
depthOutlinePath,
|
depthOutlinePath,
|
||||||
sliceOutput,
|
depthRoughPath,
|
||||||
emitDrills,
|
emitDrills,
|
||||||
emitTrace,
|
emitTrace,
|
||||||
bounds,
|
getPrintPoint() { return printPoint },
|
||||||
|
lastPoint: () => { return lastPoint },
|
||||||
|
newLayer,
|
||||||
|
pocket,
|
||||||
|
poly2polyEmit,
|
||||||
|
polyEmit,
|
||||||
|
printPoint,
|
||||||
|
setDrill,
|
||||||
|
setLasering,
|
||||||
|
setPrintPoint,
|
||||||
|
setSpindle,
|
||||||
|
setTolerance,
|
||||||
|
setTool,
|
||||||
|
stock,
|
||||||
|
tip2tipEmit,
|
||||||
zclear,
|
zclear,
|
||||||
zmax,
|
zmax,
|
||||||
lastPoint: () => { return lastPoint }
|
zsafe,
|
||||||
};
|
};
|
||||||
|
|
||||||
let opSum = 0;
|
let opSum = 0;
|
||||||
|
|
@ -973,7 +981,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
|
|
||||||
|
|
||||||
let arcPoints = arcToPath(from, to, arcPreviewRes, { clockwise, center })
|
let arcPoints = arcToPath(from, to, arcPreviewRes, { clockwise, center })
|
||||||
.map(applyWidgetMovement);
|
.map(applyWidgetMovement);
|
||||||
|
|
||||||
// console.log({arcPoints})
|
// console.log({arcPoints})
|
||||||
|
|
||||||
|
|
@ -1096,7 +1104,10 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
if (lastPoint && newOutput.length) {
|
if (lastPoint && newOutput.length) {
|
||||||
let lastLayer = newOutput.filter(layer => Array.isArray(layer)).peek();
|
let lastLayer = newOutput.filter(layer => Array.isArray(layer)).peek();
|
||||||
if (Array.isArray(lastLayer)) {
|
if (Array.isArray(lastLayer)) {
|
||||||
print.addOutput(lastLayer, printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool);
|
printPoint = lastPoint.clone();
|
||||||
|
if (printPoint.z < zmax_outer) printPoint.setZ(zmax_outer);
|
||||||
|
print.addOutput(lastLayer, printPoint, 0, 0, tool);
|
||||||
|
// print.addOutput(lastLayer, printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// console.log("prepare output", newOutput);
|
// console.log("prepare output", newOutput);
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,10 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
||||||
ondone(msg);
|
ondone(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function alert(msg) {
|
||||||
|
onupdate(null,null,msg);
|
||||||
|
}
|
||||||
|
|
||||||
if (unsafe) {
|
if (unsafe) {
|
||||||
console.log("disabling overhang safeties");
|
console.log("disabling overhang safeties");
|
||||||
}
|
}
|
||||||
|
|
@ -135,20 +139,20 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
||||||
let maxDelta = 1e-3;
|
let maxDelta = 1e-3;
|
||||||
|
|
||||||
if (stock.x + maxDelta < (bounds.max.x - bounds.min.x)) {
|
if (stock.x + maxDelta < (bounds.max.x - bounds.min.x)) {
|
||||||
return error('stock X too small for part. resize stock or use offset stock');
|
alert('stock X too small for part');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stock.y + maxDelta < (bounds.max.y - bounds.min.y)) {
|
if (stock.y + maxDelta < (bounds.max.y - bounds.min.y)) {
|
||||||
return error('stock Y too small for part. resize stock or use offset stock');
|
alert('stock Y too small for part');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stock.z + maxDelta < (bounds.max.z - bounds.min.z)) {
|
if (stock.z + maxDelta < (bounds.max.z - bounds.min.z)) {
|
||||||
return error('stock Z too small for part. resize stock or use offset stock');
|
alert('stock Z too small for part');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zMin >= bounds.max.z) {
|
if (zMin >= bounds.max.z) {
|
||||||
return error(`invalid z bottom ${(zMin / units).round(3)} >= bounds z max ${(zMax / units).round(3)}`);
|
alert(`invalid z bottom ${(zMin / units).round(3)} >= bounds z max ${(zMax / units).round(3)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = Date.now();
|
let mark = Date.now();
|
||||||
|
|
@ -158,31 +162,31 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
||||||
let shadows = {};
|
let shadows = {};
|
||||||
let slicer;
|
let slicer;
|
||||||
let state = {
|
let state = {
|
||||||
settings,
|
addSlices,
|
||||||
widget,
|
|
||||||
bounds,
|
bounds,
|
||||||
tabs,
|
color,
|
||||||
cutTabs,
|
computeShadows,
|
||||||
cutPolys,
|
|
||||||
contourPolys,
|
contourPolys,
|
||||||
|
cutPolys,
|
||||||
|
cutTabs,
|
||||||
|
dark,
|
||||||
healPolys,
|
healPolys,
|
||||||
|
isIndexed,
|
||||||
|
ops: opList,
|
||||||
|
setAxisIndex,
|
||||||
|
settings,
|
||||||
shadowAt,
|
shadowAt,
|
||||||
slicer,
|
slicer,
|
||||||
addSlices,
|
tabs,
|
||||||
isIndexed,
|
unsafe,
|
||||||
setAxisIndex,
|
|
||||||
updateToolDiams,
|
|
||||||
updateSlicer,
|
updateSlicer,
|
||||||
computeShadows,
|
updateToolDiams,
|
||||||
|
widget,
|
||||||
zBottom,
|
zBottom,
|
||||||
|
zMax,
|
||||||
zThru,
|
zThru,
|
||||||
ztOff,
|
ztOff,
|
||||||
zMax,
|
zTop
|
||||||
zTop,
|
|
||||||
unsafe,
|
|
||||||
color,
|
|
||||||
dark,
|
|
||||||
ops: opList
|
|
||||||
};
|
};
|
||||||
let tracker = setSliceTracker({ rotation: 0 });
|
let tracker = setSliceTracker({ rotation: 0 });
|
||||||
|
|
||||||
|
|
@ -310,8 +314,11 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
||||||
workover.bottom_z = isIndexed ? valz.ov_botz : bottom_stock + valz.ov_botz;
|
workover.bottom_z = isIndexed ? valz.ov_botz : bottom_stock + valz.ov_botz;
|
||||||
workover.bottom_cut = Math.max(workover.bottom_z, -zThru);
|
workover.bottom_cut = Math.max(workover.bottom_z, -zThru);
|
||||||
}
|
}
|
||||||
// state.workarea = workover;
|
let { note, type } = op.op;
|
||||||
|
let named = note ? note.split(' ').filter(v => v.charAt(0) === '#') : [];
|
||||||
|
let layername = named.length ? named : (note ? `${type} (${note})` : type);
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
|
layername,
|
||||||
zBottom,
|
zBottom,
|
||||||
zThru,
|
zThru,
|
||||||
ztOff,
|
ztOff,
|
||||||
|
|
@ -688,7 +695,7 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
||||||
let opts = { each: onEach, progress: (num, total) => onProgress(num / total * 0.5, "slice") };
|
let opts = { each: onEach, progress: (num, total) => onProgress(num / total * 0.5, "slice") };
|
||||||
await slicer.slice(indices, opts);
|
await slicer.slice(indices, opts);
|
||||||
let shadowedDrills = false;
|
let shadowedDrills = false;
|
||||||
// console.log("slices",slices)
|
|
||||||
for (let [i, slice] of slices.entries()) {
|
for (let [i, slice] of slices.entries()) {
|
||||||
for (let top of slice.tops) {
|
for (let top of slice.tops) {
|
||||||
// console.log("slicing",slice.z,top)
|
// console.log("slicing",slice.z,top)
|
||||||
|
|
@ -698,13 +705,13 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (let poly of inner) {
|
for (let poly of inner) {
|
||||||
if (poly.points.length < 7) continue;
|
if (poly.points.length < 7) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let center = poly.calcCircleCenter();
|
let center = poly.calcCircleCenter();
|
||||||
center.area = poly.area();
|
center.area = poly.area();
|
||||||
center.overlapping = [center];
|
center.overlapping = [center];
|
||||||
center.depth = 0;
|
center.depth = 0;
|
||||||
// console.log("center",center)
|
|
||||||
if (poly.circularity() < 0.98) {
|
if (poly.circularity() < 0.98) {
|
||||||
// if not circular, don't add to holes
|
// if not circular, don't add to holes
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -717,8 +724,7 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
||||||
let overlap = false;
|
let overlap = false;
|
||||||
for (let [i, circle] of circles.entries()) {
|
for (let [i, circle] of circles.entries()) {
|
||||||
let dist = circle.distTo2D(center);
|
let dist = circle.distTo2D(center);
|
||||||
|
// if on the same xy point,
|
||||||
// //if on the same xy point,
|
|
||||||
if (dist <= centerDiff) {
|
if (dist <= centerDiff) {
|
||||||
// console.log("overlap",center,circle);
|
// console.log("overlap",center,circle);
|
||||||
circle.overlapping.push(center);
|
circle.overlapping.push(center);
|
||||||
|
|
@ -732,12 +738,10 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
||||||
}
|
}
|
||||||
onProgress(0.5 + (i / slices.length * 0.25), "recognize circles");
|
onProgress(0.5 + (i / slices.length * 0.25), "recognize circles");
|
||||||
}
|
}
|
||||||
|
|
||||||
let drills = [];
|
let drills = [];
|
||||||
|
|
||||||
for (let [i, c] of circles.entries()) {
|
for (let [i, c] of circles.entries()) {
|
||||||
let overlapping = c.overlapping;
|
let overlapping = c.overlapping;
|
||||||
|
|
||||||
let last = overlapping.shift();
|
let last = overlapping.shift();
|
||||||
while (overlapping.length) {
|
while (overlapping.length) {
|
||||||
let circ = overlapping.shift();
|
let circ = overlapping.shift();
|
||||||
|
|
@ -775,14 +779,33 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
||||||
return drills;
|
return drills;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cutTabs(tabs, offset, z, inter) {
|
function cutTabs(tabs, offset) {
|
||||||
tabs = tabs.filter(tab => z < tab.pos.z + tab.dim.z / 2).map(tab => tab.off).flat();
|
let out = [];
|
||||||
return cutPolys(tabs, offset, z, false);
|
for (let tab of tabs) {
|
||||||
|
tab.top = tab.pos.z + tab.dim.z / 2;
|
||||||
|
}
|
||||||
|
for (let poly of offset) {
|
||||||
|
let polyZ = poly.getZ();
|
||||||
|
let cut = tabs.filter(tab => tab.top >= polyZ);
|
||||||
|
let lo = poly.cut(cut.map(tab => tab.off).flat(), false);
|
||||||
|
let hi = [];
|
||||||
|
for (let tab of cut) {
|
||||||
|
hi.appendAll(POLY.setZ(poly.cut(tab.off, true), tab.top));
|
||||||
|
}
|
||||||
|
if (hi.length) {
|
||||||
|
let heal = healPolys([ ...lo, ...hi ], false);
|
||||||
|
POLY.setWinding(heal, poly.isClockwise());
|
||||||
|
out.push(...heal);
|
||||||
|
} else {
|
||||||
|
out.push(poly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cutPolys(polys, offset, z, inter) {
|
function cutPolys(polys, offset) {
|
||||||
let noff = [];
|
let noff = [];
|
||||||
offset.forEach(op => noff.appendAll(op.cut(POLY.union(polys, 0, true), inter)));
|
offset.forEach(op => noff.appendAll( op.cut(POLY.union(polys, 0, true), true) ));
|
||||||
return healPolys(noff);
|
return healPolys(noff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -799,7 +822,17 @@ function contourPolys(widget, polys) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function healPolys(noff) {
|
function dedup(arr, same) {
|
||||||
|
const out = [];
|
||||||
|
let prev;
|
||||||
|
for (const x of arr) {
|
||||||
|
if (!prev || !same(prev, x)) out.push(x);
|
||||||
|
prev = x;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function healPolys(noff, sameZ = true) {
|
||||||
for (let p of noff) {
|
for (let p of noff) {
|
||||||
if (p.appearsClosed()) {
|
if (p.appearsClosed()) {
|
||||||
p.points.pop();
|
p.points.pop();
|
||||||
|
|
@ -818,29 +851,29 @@ function healPolys(noff) {
|
||||||
let s2 = ntmp[j];
|
let s2 = ntmp[j];
|
||||||
if (!s2) continue;
|
if (!s2) continue;
|
||||||
// require polys at same Z to heal
|
// require polys at same Z to heal
|
||||||
if (Math.abs(s1.getZ() - s2.getZ()) > 0.01) {
|
if (sameZ && Math.abs(s1.getZ() - s2.getZ()) > 0.01) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!(s1.open && s2.open)) continue;
|
if (!(s1.open && s2.open)) continue;
|
||||||
if (s1.last().isMergable2D(s2.first())) {
|
if (s1.last().isMergable2D(s2.first())) {
|
||||||
s1.addPoints(s2.points.slice(1));
|
s1.addPoints(s2.points);
|
||||||
ntmp[j] = null;
|
ntmp[j] = null;
|
||||||
continue outer;
|
continue outer;
|
||||||
}
|
}
|
||||||
if (s2.last().isMergable2D(s1.first())) {
|
if (s2.last().isMergable2D(s1.first())) {
|
||||||
s2.addPoints(s1.points.slice(1));
|
s2.addPoints(s1.points);
|
||||||
ntmp[i] = null;
|
ntmp[i] = null;
|
||||||
continue outer;
|
continue outer;
|
||||||
}
|
}
|
||||||
if (s1.first().isMergable2D(s2.first())) {
|
if (s1.first().isMergable2D(s2.first())) {
|
||||||
s1.reverse();
|
s1.reverse();
|
||||||
s1.addPoints(s2.points.slice(1));
|
s1.addPoints(s2.points);
|
||||||
ntmp[j] = null;
|
ntmp[j] = null;
|
||||||
continue outer;
|
continue outer;
|
||||||
}
|
}
|
||||||
if (s1.last().isMergable2D(s2.last())) {
|
if (s1.last().isMergable2D(s2.last())) {
|
||||||
s2.reverse();
|
s2.reverse();
|
||||||
s1.addPoints(s2.points.slice(1));
|
s1.addPoints(s2.points);
|
||||||
ntmp[j] = null;
|
ntmp[j] = null;
|
||||||
continue outer;
|
continue outer;
|
||||||
}
|
}
|
||||||
|
|
@ -854,9 +887,14 @@ function healPolys(noff) {
|
||||||
}
|
}
|
||||||
// close poly if head meets tail
|
// close poly if head meets tail
|
||||||
for (let poly of noff) {
|
for (let poly of noff) {
|
||||||
if (poly.open && poly.first().isMergable2D(poly.last())) {
|
poly.points = dedup(poly.points, (a,b) => a.isMergable3D(b));
|
||||||
poly.points.pop();
|
if (poly.open) {
|
||||||
poly.open = false;
|
if (poly.first().isMergable3D(poly.last())) {
|
||||||
|
poly.points.pop();
|
||||||
|
poly.open = false;
|
||||||
|
} else if (poly.first().isMergable2D(poly.last())) {
|
||||||
|
poly.open = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ const { sliceConnect, sliceDedup } = slicer;
|
||||||
const { config } = base;
|
const { config } = base;
|
||||||
const timing = false;
|
const timing = false;
|
||||||
const zDecimal = 3;
|
const zDecimal = 3;
|
||||||
|
const epsilon = 10e-5;
|
||||||
|
|
||||||
const begin = function () {
|
const begin = function () {
|
||||||
if (timing) console.time(...arguments);
|
if (timing) console.time(...arguments);
|
||||||
|
|
@ -94,7 +95,7 @@ export class Slicer {
|
||||||
countZ(p3.z);
|
countZ(p3.z);
|
||||||
}
|
}
|
||||||
// use co-flat and co-line detection to adjust slice Z
|
// use co-flat and co-line detection to adjust slice Z
|
||||||
if (p1.z === p2.z && p2.z === p3.z) {
|
if (Math.abs(p1.z - p2.z) < epsilon && Math.abs(p2.z - p3.z) < epsilon) {
|
||||||
// detect zFlat faces to avoid slicing directly on them
|
// detect zFlat faces to avoid slicing directly on them
|
||||||
let zkey = p1.z.toFixed(zDecimal),
|
let zkey = p1.z.toFixed(zDecimal),
|
||||||
area = Math.abs(util.area2(p1, p2, p3)) / 2;
|
area = Math.abs(util.area2(p1, p2, p3)) / 2;
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,10 @@ export class Topo {
|
||||||
|
|
||||||
async generate(opt = {}) {
|
async generate(opt = {}) {
|
||||||
let { state, contour, onupdate, ondone } = opt;
|
let { state, contour, onupdate, ondone } = opt;
|
||||||
let { widget, settings, tshadow, center, tabs, color } = opt.state;
|
let { widget, settings, tshadow, tabs } = opt.state;
|
||||||
|
|
||||||
let { controller, process } = settings,
|
let { controller, process } = settings,
|
||||||
|
{ webGPU } = controller,
|
||||||
animesh = parseInt(controller.animesh || 100) * 2500,
|
animesh = parseInt(controller.animesh || 100) * 2500,
|
||||||
axis = contour.axis.toLowerCase(),
|
axis = contour.axis.toLowerCase(),
|
||||||
contourX = axis === "x",
|
contourX = axis === "x",
|
||||||
|
|
@ -37,16 +38,15 @@ export class Topo {
|
||||||
boundsY = maxY - minY,
|
boundsY = maxY - minY,
|
||||||
inside = contour.inside,
|
inside = contour.inside,
|
||||||
density = 1 + (contour.reduction || 0),
|
density = 1 + (contour.reduction || 0),
|
||||||
resolution = tolerance ? tolerance : 1 / Math.sqrt(animesh / (boundsX * boundsY)),
|
resolution = (tolerance ? tolerance : 1 / Math.sqrt(animesh / (boundsX * boundsY))).round(5),
|
||||||
tool = new Tool(settings, contour.tool),
|
tool = new Tool(settings, contour.tool),
|
||||||
toolOffset = tool.generateProfile(resolution).profile,
|
toolOffset = tool.generateProfile(resolution).profile,
|
||||||
toolDiameter = tool.fluteDiameter(),
|
toolDiameter = tool.fluteDiameter(),
|
||||||
toolStep = toolDiameter * contour.step, //tool.contourOffset(contour.step),
|
toolStep = toolDiameter * contour.step,
|
||||||
leave = contour.leave || 0,
|
leave = contour.leave || 0,
|
||||||
maxangle = contour.angle,
|
maxangle = contour.angle,
|
||||||
curvesOnly = contour.curves,
|
curvesOnly = contour.curves,
|
||||||
bridge = contour.bridging || 0,
|
bridge = contour.bridging || 0,
|
||||||
// R2A = 180 / Math.PI,
|
|
||||||
stepsX = Math.ceil(boundsX / resolution),
|
stepsX = Math.ceil(boundsX / resolution),
|
||||||
stepsY = Math.ceil(boundsY / resolution),
|
stepsY = Math.ceil(boundsY / resolution),
|
||||||
widtopo = widget.topo,
|
widtopo = widget.topo,
|
||||||
|
|
@ -75,12 +75,7 @@ export class Topo {
|
||||||
clipTo = inside ? shadow : POLY.expand(shadow, toolDiameter / 2 + resolution * 3),
|
clipTo = inside ? shadow : POLY.expand(shadow, toolDiameter / 2 + resolution * 3),
|
||||||
partOff = inside ? 0 : toolDiameter / 2 + resolution,
|
partOff = inside ? 0 : toolDiameter / 2 + resolution,
|
||||||
gridDelta = Math.floor(partOff / resolution),
|
gridDelta = Math.floor(partOff / resolution),
|
||||||
debug = false,
|
debug_clips = false;
|
||||||
debug_clips = debug && true,
|
|
||||||
debug_topo = debug && true,
|
|
||||||
debug_topo_lines = debug && true,
|
|
||||||
debug_topo_shells = debug && true,
|
|
||||||
time = Date.now;
|
|
||||||
|
|
||||||
if (tolerance === 0 && !topoCache) {
|
if (tolerance === 0 && !topoCache) {
|
||||||
console.log(widget.id, 'topo auto tolerance', resolution.round(4));
|
console.log(widget.id, 'topo auto tolerance', resolution.round(4));
|
||||||
|
|
@ -90,7 +85,7 @@ export class Topo {
|
||||||
// used by Pocket -> Contour
|
// used by Pocket -> Contour
|
||||||
this.tolerance = resolution;
|
this.tolerance = resolution;
|
||||||
|
|
||||||
if (tabs) {
|
if (tabsOn) {
|
||||||
clipTab.appendAll(tabs.map(tab => {
|
clipTab.appendAll(tabs.map(tab => {
|
||||||
let ctab = POLY.expand([tab.poly], toolDiameter / 2);
|
let ctab = POLY.expand([tab.poly], toolDiameter / 2);
|
||||||
ctab.forEach(ct => ct.z = tab.dim.z / 2 + tab.pos.z);
|
ctab.forEach(ct => ct.z = tab.dim.z / 2 + tab.pos.z);
|
||||||
|
|
@ -107,6 +102,190 @@ export class Topo {
|
||||||
newslices.push(debug);
|
newslices.push(debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (webGPU && !contour.nogpu) {
|
||||||
|
// invert tool Z offset for gpu code
|
||||||
|
let toolBounds = new THREE.Box3()
|
||||||
|
.expandByPoint({ x: -toolDiameter/2, y: -toolDiameter/2, z: 0 })
|
||||||
|
.expandByPoint({ x: toolDiameter/2, y: toolDiameter/2, z: 0 });
|
||||||
|
let toolPos = tool.profile.slice();
|
||||||
|
for (let i=0; i<toolPos.length; i+= 3) {
|
||||||
|
// toolPos[i+2] = -toolPos[i+2];
|
||||||
|
toolBounds.expandByPoint({ x: toolPos[i], y: toolPos[i+1], z: toolPos[i+2] });
|
||||||
|
}
|
||||||
|
let toolData = { positions: toolPos, bounds: toolBounds };
|
||||||
|
|
||||||
|
const vertices = widget.getGeoVertices({ unroll: true, translate: true });
|
||||||
|
const wbounds = widget.getBoundingBox();
|
||||||
|
if (!inside) {
|
||||||
|
wbounds.expandByVector({ x: toolDiameter/2, y: toolDiameter/2, z: 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contourY) {
|
||||||
|
// console.time('swap XY vertices');
|
||||||
|
// swap XY vertices
|
||||||
|
for (let i=0; i<vertices.length; i+= 3) {
|
||||||
|
let tmp = vertices[i+1];
|
||||||
|
vertices[i+1] = vertices[i+0];
|
||||||
|
vertices[i+0] = tmp;
|
||||||
|
}
|
||||||
|
['min','max'].forEach(ext => {
|
||||||
|
ext = wbounds[ext];
|
||||||
|
let tmp = ext.x;
|
||||||
|
ext.x = ext.y;
|
||||||
|
ext.y = tmp;
|
||||||
|
});
|
||||||
|
// console.timeEnd('swap XY vertices');
|
||||||
|
}
|
||||||
|
|
||||||
|
let trace = contour.trace;
|
||||||
|
let gpu = await self.get_raster_gpu({
|
||||||
|
mode: trace ? "tracing" : "planar",
|
||||||
|
resolution
|
||||||
|
});
|
||||||
|
let xStep = density;
|
||||||
|
let yStep = Math.ceil(toolStep / resolution);
|
||||||
|
let epsilon = 10e-4;
|
||||||
|
await gpu.loadTool({
|
||||||
|
sparseData: toolData
|
||||||
|
});
|
||||||
|
let terrain = await gpu.loadTerrain({
|
||||||
|
triangles: vertices,
|
||||||
|
boundsOverride: wbounds
|
||||||
|
});
|
||||||
|
let { gridWidth, positions } = terrain;
|
||||||
|
let output = await gpu.generateToolpaths({
|
||||||
|
xStep,
|
||||||
|
yStep,
|
||||||
|
zFloor: zBottom - 1,
|
||||||
|
onProgress(pct) { console.log({ pct }); onupdate(pct/100, 100) }
|
||||||
|
});
|
||||||
|
gpu.terminate();
|
||||||
|
|
||||||
|
let { numScanlines, pointsPerLine, pathData } = output;
|
||||||
|
let xmult = xStep * resolution;
|
||||||
|
let ymult = yStep * resolution;
|
||||||
|
let xoff = wbounds.min.x;
|
||||||
|
let yoff = wbounds.min.y;
|
||||||
|
let slices = [];
|
||||||
|
function calcPoint(x, y, z, tx, ty) {
|
||||||
|
let p = newPoint(x, y, z);
|
||||||
|
p.tx = tx;
|
||||||
|
p.ty = ty;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
for (let i=0; i<numScanlines; i++) {
|
||||||
|
let lineStart = i * pointsPerLine;
|
||||||
|
let lineData = pathData.slice(lineStart, lineStart + pointsPerLine);
|
||||||
|
let points = contourY
|
||||||
|
? Array.from(lineData).map((v,j) => calcPoint(i * ymult + yoff, j * xmult + xoff, v, i * xStep, j * yStep))
|
||||||
|
: Array.from(lineData).map((v,j) => calcPoint(j * xmult + xoff, i * ymult + yoff, v, j * xStep, i * yStep))
|
||||||
|
;
|
||||||
|
let slice = newSlice(i);
|
||||||
|
let poly = newPolygon().setOpen();
|
||||||
|
let lines = [ ];
|
||||||
|
let skip = 0;
|
||||||
|
let lp;
|
||||||
|
for (let p of points) {
|
||||||
|
if (inside && positions[p.ty * gridWidth + p.tx] < zBottom - epsilon) {
|
||||||
|
if (poly.length > 1) {
|
||||||
|
lines.push(poly);
|
||||||
|
poly = newPolygon().setOpen();
|
||||||
|
} else if (poly.length === 1) {
|
||||||
|
poly = newPolygon().setOpen();
|
||||||
|
}
|
||||||
|
lp = undefined;
|
||||||
|
skip = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (p.z < zBottom - epsilon) {
|
||||||
|
lp = p;
|
||||||
|
if (poly.length === 0) {
|
||||||
|
skip++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (poly.length > 1) {
|
||||||
|
skip = 0;
|
||||||
|
p.z = zBottom;
|
||||||
|
poly.push(p);
|
||||||
|
lines.push(poly);
|
||||||
|
poly = newPolygon().setOpen();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (lp && skip && poly.length === 0) {
|
||||||
|
lp.z = zBottom;
|
||||||
|
poly.push(lp);
|
||||||
|
}
|
||||||
|
poly.push(lp = p);
|
||||||
|
skip = 0;
|
||||||
|
}
|
||||||
|
// require two points or more
|
||||||
|
if (poly.length > 1) {
|
||||||
|
lines.push(poly);
|
||||||
|
}
|
||||||
|
// raize output points when inside tab boundaries
|
||||||
|
if (!inside && tabsOn && clipTab.length)
|
||||||
|
for (let poly of lines) {
|
||||||
|
for (let p of poly.points) {
|
||||||
|
for (let clip of clipTab) {
|
||||||
|
if (p.z < clip.z && p.isInPolygon(clip)) {
|
||||||
|
p.z = clip.z;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// drop interior points along a continuous slope
|
||||||
|
for (let poly of lines) {
|
||||||
|
let { points } = poly;
|
||||||
|
let merged = [ points[0] ];
|
||||||
|
let lp = points[1];
|
||||||
|
let dz = points[0].z - lp.z;
|
||||||
|
for (let i=2; i<points.length; i++) {
|
||||||
|
let np = points[i];
|
||||||
|
let ndz = np.z - lp.z;
|
||||||
|
if (Math.abs(dz - ndz) > epsilon) {
|
||||||
|
// slope changed
|
||||||
|
merged.push(lp);
|
||||||
|
dz = ndz;
|
||||||
|
} else if (curvesOnly && Math.abs(ndz) < epsilon) {
|
||||||
|
// add empty points as path separators
|
||||||
|
merged.push(undefined);
|
||||||
|
}
|
||||||
|
lp = np;
|
||||||
|
}
|
||||||
|
if (merged.peek() !== lp) {
|
||||||
|
merged.push(lp);
|
||||||
|
}
|
||||||
|
poly.points = merged;
|
||||||
|
}
|
||||||
|
// drop flat regions when enabled
|
||||||
|
if (curvesOnly) {
|
||||||
|
let nulines = [];
|
||||||
|
for (let poly of lines) {
|
||||||
|
let { points } = poly;
|
||||||
|
let newp = [];
|
||||||
|
for (let p of points) {
|
||||||
|
if (p) {
|
||||||
|
newp.push(p);
|
||||||
|
} else if (newp.length) {
|
||||||
|
nulines.push(newPolygon(newp).setOpen());
|
||||||
|
newp = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nulines.push(newPolygon(newp).setOpen());
|
||||||
|
}
|
||||||
|
lines = nulines.filter(p => p.perimeter() > toolStep);
|
||||||
|
}
|
||||||
|
slice.camLines = lines;
|
||||||
|
slices.push(slice);
|
||||||
|
if (inside) {
|
||||||
|
onupdate(i, numScanlines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ondone(slices);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
const probe = this.probe = new Probe({
|
const probe = this.probe = new Probe({
|
||||||
profile: toolOffset,
|
profile: toolOffset,
|
||||||
data,
|
data,
|
||||||
|
|
@ -177,8 +356,7 @@ export class Topo {
|
||||||
clipTabZ: clipTab ? clipTab.map(t => t.z) : undefined,
|
clipTabZ: clipTab ? clipTab.map(t => t.z) : undefined,
|
||||||
tabHeight,
|
tabHeight,
|
||||||
newslices,
|
newslices,
|
||||||
leave,
|
leave
|
||||||
color
|
|
||||||
}, (i, l, p) => {
|
}, (i, l, p) => {
|
||||||
onupdate(l / 2 + i / 2, l, p);
|
onupdate(l / 2 + i / 2, l, p);
|
||||||
});
|
});
|
||||||
|
|
@ -304,7 +482,7 @@ export class Topo {
|
||||||
|
|
||||||
const { minX, maxX, minY, maxY, boundsX, boundsY, stepsX, stepsY } = params;
|
const { minX, maxX, minY, maxY, boundsX, boundsY, stepsX, stepsY } = params;
|
||||||
const { gridDelta, resolution, density, partOff, toolStep, contourX, contourY } = params;
|
const { gridDelta, resolution, density, partOff, toolStep, contourX, contourY } = params;
|
||||||
const { clipTo, clipTab, clipTabZ, tabHeight, newslices, color, leave } = params;
|
const { clipTo, clipTab, clipTabZ, tabHeight, newslices, leave } = params;
|
||||||
|
|
||||||
let stepsTaken = 0,
|
let stepsTaken = 0,
|
||||||
stepsTotal = 0;
|
stepsTotal = 0;
|
||||||
|
|
@ -370,11 +548,8 @@ export class Topo {
|
||||||
gridy
|
gridy
|
||||||
}, segments => {
|
}, segments => {
|
||||||
if (segments.length > 0) {
|
if (segments.length > 0) {
|
||||||
let slice = newSlice(gridx);
|
let slice = newSlice(x);
|
||||||
slice.camLines = segments;
|
slice.camLines = segments;
|
||||||
slice.output()
|
|
||||||
.setLayer("contour y", { face: color, line: color })
|
|
||||||
.addPolys(segments);
|
|
||||||
slicesY.push(slice);
|
slicesY.push(slice);
|
||||||
}
|
}
|
||||||
onupdate(++stepsTaken, stepsTotal, "contour y");
|
onupdate(++stepsTaken, stepsTotal, "contour y");
|
||||||
|
|
@ -399,11 +574,8 @@ export class Topo {
|
||||||
gridy
|
gridy
|
||||||
}, segments => {
|
}, segments => {
|
||||||
if (segments.length > 0) {
|
if (segments.length > 0) {
|
||||||
let slice = newSlice(gridy);
|
let slice = newSlice(y);
|
||||||
slice.camLines = segments;
|
slice.camLines = segments;
|
||||||
slice.output()
|
|
||||||
.setLayer("contour x", { face: color, line: color })
|
|
||||||
.addPolys(segments);
|
|
||||||
slicesX.push(slice);
|
slicesX.push(slice);
|
||||||
}
|
}
|
||||||
onupdate(++stepsTaken, stepsTotal, "contour x");
|
onupdate(++stepsTaken, stepsTotal, "contour x");
|
||||||
|
|
@ -534,9 +706,9 @@ export class Trace {
|
||||||
console.log(...arguments);
|
console.log(...arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
const push_point = this.push_point = function (x, y, z) {
|
this.push_point = function (x, y, z) {
|
||||||
const newP = newPoint(x, y, z);
|
const newP = newPoint(x, y, z);
|
||||||
const lastP = lastPP;//trace.last();
|
const lastP = lastPP;
|
||||||
|
|
||||||
if (lastP) {
|
if (lastP) {
|
||||||
const dl = (x - lastP.x) || (y - lastP.y);
|
const dl = (x - lastP.x) || (y - lastP.y);
|
||||||
|
|
@ -553,7 +725,6 @@ export class Trace {
|
||||||
}
|
}
|
||||||
if (curvesOnly) {
|
if (curvesOnly) {
|
||||||
const dv = contourX ? Math.abs(lastP.x - x) : Math.abs(lastP.y - y);
|
const dv = contourX ? Math.abs(lastP.x - x) : Math.abs(lastP.y - y);
|
||||||
// const dz = lastPP.z - z;
|
|
||||||
const angle = Math.atan2(Math.abs(dz), dv) * RAD2DEG;
|
const angle = Math.atan2(Math.abs(dz), dv) * RAD2DEG;
|
||||||
if (angle > maxangle) {
|
if (angle > maxangle) {
|
||||||
end_poly();
|
end_poly();
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,9 @@ export class Topo {
|
||||||
|
|
||||||
async generate(opt = {}) {
|
async generate(opt = {}) {
|
||||||
let { state, op, onupdate, ondone } = opt;
|
let { state, op, onupdate, ondone } = opt;
|
||||||
let { widget, settings, tabs } = opt.state;
|
let { widget, settings, tabs, color } = state;
|
||||||
let { controller, process } = settings;
|
let { controller, process } = settings;
|
||||||
|
let { webGPU } = controller;
|
||||||
|
|
||||||
let axis = op.axis.toLowerCase(),
|
let axis = op.axis.toLowerCase(),
|
||||||
tool = new Tool(settings, op.tool),
|
tool = new Tool(settings, op.tool),
|
||||||
|
|
@ -38,9 +39,8 @@ export class Topo {
|
||||||
x: axis === "x",
|
x: axis === "x",
|
||||||
y: axis === "y"
|
y: axis === "y"
|
||||||
},
|
},
|
||||||
zMin = min.z + 0.0001,
|
|
||||||
tolerance = op.tolerance,
|
tolerance = op.tolerance,
|
||||||
resolution = tolerance ? tolerance : 1 / Math.sqrt(density / (span.x * span.y)),
|
resolution = (tolerance ? tolerance : 1 / Math.sqrt(density / (span.x * span.y))).round(5),
|
||||||
step = this.step = (tool.traceOffset() * 2) * op.step,
|
step = this.step = (tool.traceOffset() * 2) * op.step,
|
||||||
angle = this.angle = op.angle || 1;
|
angle = this.angle = op.angle || 1;
|
||||||
|
|
||||||
|
|
@ -52,31 +52,36 @@ export class Topo {
|
||||||
console.log(widget.id, 'topo4 auto tolerance', resolution.round(4));
|
console.log(widget.id, 'topo4 auto tolerance', resolution.round(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.zBottom = state.zBottom;
|
this.zBottom = state.zBottom ?? 0;
|
||||||
this.resolution = resolution;
|
this.resolution = resolution;
|
||||||
this.vertices = widget.getGeoVertices({ unroll: true, translate: true });
|
this.vertices = widget.getGeoVertices({ unroll: true, translate: true });
|
||||||
this.tabverts = widget.getTabVertices();
|
this.tabverts = widget.getTabVertices();
|
||||||
this.tool = tool.generateProfile(resolution).profile;
|
this.tool = tool.generateProfile(resolution).profile;
|
||||||
this.maxo = tool.profileDim.maxo * resolution;
|
this.maxo = tool.profileDim.maxo * resolution;
|
||||||
this.diam = tool.fluteDiameter();
|
this.diam = tool.fluteDiameter();
|
||||||
|
this.unit = tool.unitScale();
|
||||||
|
this.units = controller.units === 'in' ? 25.4 : 1
|
||||||
this.zoff = widget.track.top || 0;
|
this.zoff = widget.track.top || 0;
|
||||||
this.leave = op.leave || 0;
|
this.leave = op.leave || 0;
|
||||||
this.linear = op.linear || false;
|
this.linear = op.linear || false;
|
||||||
this.lineColor = state.settings.controller.dark ? 0xffff00 : 0x555500;
|
this.lineColor = color;//controller.dark ? 0xffff00 : 0x555500;
|
||||||
|
this.offStart = op.offStart ?? 0;
|
||||||
|
this.offEnd = op.offEnd ?? 0;
|
||||||
|
this.bounds = bounds;
|
||||||
|
this.gpu = webGPU ?? false;
|
||||||
|
|
||||||
onupdate(0, "lathe");
|
onupdate(0, "lathe");
|
||||||
|
|
||||||
|
const parts = webGPU ? [ 0.8, 0.2 ] : [ 0.25, 0.75 ];
|
||||||
const range = this.range = { min: Infinity, max: -Infinity };
|
const range = this.range = { min: Infinity, max: -Infinity };
|
||||||
const slices = this.sliced = await this.slice(scale(onupdate, 0.25, 0));
|
const slices = this.sliced = await this.slice(scale(onupdate, parts[0], 0));
|
||||||
|
|
||||||
for (let slice of slices) {
|
for (let slice of slices) {
|
||||||
range.min = Math.min(range.min, slice.z);
|
range.min = Math.min(range.min, slice.z);
|
||||||
range.max = Math.max(range.max, slice.z);
|
range.max = Math.max(range.max, slice.z);
|
||||||
slice.output()
|
|
||||||
.setLayer("lathe", { line: 0x888888 })
|
|
||||||
.addPolys(slice.topPolys());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const lathe = await this.lathe(scale(onupdate, 0.75, 0.25));
|
const lathe = await this.lathe(scale(onupdate, parts[1], parts[0]));
|
||||||
|
|
||||||
onupdate(1, "lathe");
|
onupdate(1, "lathe");
|
||||||
ondone(lathe);
|
ondone(lathe);
|
||||||
|
|
@ -86,10 +91,9 @@ export class Topo {
|
||||||
}
|
}
|
||||||
|
|
||||||
async slice(onupdate) {
|
async slice(onupdate) {
|
||||||
const { vertices, resolution, tabverts, zoff } = this;
|
const { vertices, resolution, tabverts, zoff, offStart, offEnd, units } = this;
|
||||||
|
const { minions } = self.kiri_worker;
|
||||||
const range = this.range = { min: Infinity, max: -Infinity };
|
const range = this.range = { min: Infinity, max: -Infinity };
|
||||||
const box = this.box = new THREE.Box2();
|
|
||||||
|
|
||||||
// swap XZ in shared array
|
// swap XZ in shared array
|
||||||
for (let i = 0, l = vertices.length; i < l; i += 3) {
|
for (let i = 0, l = vertices.length; i < l; i += 3) {
|
||||||
|
|
@ -101,9 +105,10 @@ export class Topo {
|
||||||
range.max = Math.max(range.max, x);
|
range.max = Math.max(range.max, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add tool radius to slice min/max range ot fully carve part
|
// add tool diameter to slice min/max range to fully carve part
|
||||||
range.min -= this.diam / 2;
|
range.min += offStart * units;
|
||||||
range.max += this.diam / 2;
|
range.max -= offEnd * units;
|
||||||
|
range.max += resolution * 2;
|
||||||
|
|
||||||
// merge in tab vertices here so they don't affect slice range / dimensions
|
// merge in tab vertices here so they don't affect slice range / dimensions
|
||||||
for (let i = 0, l = tabverts.length; i < l; i += 3) {
|
for (let i = 0, l = tabverts.length; i < l; i += 3) {
|
||||||
|
|
@ -116,24 +121,36 @@ export class Topo {
|
||||||
// re-create shared vertex array for workers
|
// re-create shared vertex array for workers
|
||||||
this.vertices = [].appendAll(vertices).appendAll(tabverts).toFloat32().toShared();
|
this.vertices = [].appendAll(vertices).appendAll(tabverts).toFloat32().toShared();
|
||||||
|
|
||||||
const shards = Math.ceil(Math.min(25, vertices.length / 27000));
|
// rp.rasterizeMesh(vertices, resolution).then(rpo => console.log({ rpo }));
|
||||||
const step = (range.max - range.min) / shards;
|
if (this.gpu) {
|
||||||
|
return await this.sliceGPU(onupdate);
|
||||||
let index = 0;
|
|
||||||
let slices = this.slices = [];
|
|
||||||
let slice = { min: range.min, max: range.min + step, index };
|
|
||||||
|
|
||||||
for (let z = range.min; z < range.max; z += resolution) {
|
|
||||||
if (z > slice.max) {
|
|
||||||
slices.push(slice);
|
|
||||||
slice = { min: z, max: z + step, index };
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
}
|
||||||
slices.push(slice);
|
|
||||||
// console.log({ shards, range, step, slices });
|
|
||||||
|
|
||||||
const { minions } = self.kiri_worker;
|
const zSpan = range.max - range.min;
|
||||||
|
const shards = Math.ceil(Math.min(25, vertices.length / 27000));
|
||||||
|
const totalSteps = Math.ceil(zSpan / resolution);
|
||||||
|
const stardSteps = Math.ceil(totalSteps / shards);
|
||||||
|
const stepWidth = stardSteps * resolution;
|
||||||
|
|
||||||
|
let slices = this.slices = [];
|
||||||
|
for (let i=0; i<shards; i++) {
|
||||||
|
let s;
|
||||||
|
slices.push(s = {
|
||||||
|
min: range.min + i * stepWidth,
|
||||||
|
max: Math.min(range.max, range.min + (i+1) * stepWidth),
|
||||||
|
index: i * stardSteps
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log({
|
||||||
|
// shards,
|
||||||
|
// step: stepWidth,
|
||||||
|
// range,
|
||||||
|
// resolution,
|
||||||
|
// slices: slices.slice(),
|
||||||
|
// minions
|
||||||
|
// });
|
||||||
|
|
||||||
if (minions?.running > 1) {
|
if (minions?.running > 1) {
|
||||||
return await this.sliceMinions(onupdate);
|
return await this.sliceMinions(onupdate);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -141,6 +158,96 @@ export class Topo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sliceGPU(onupdate) {
|
||||||
|
const { angle, diam, linear, offStart, offEnd, resolution, tool, units, vertices, zBottom } = this;
|
||||||
|
|
||||||
|
// invert tool Z offset for gpu code
|
||||||
|
let toolBounds = new THREE.Box3()
|
||||||
|
.expandByPoint({ x: -this.diam/2, y: -diam/2, z: 0 })
|
||||||
|
.expandByPoint({ x: this.diam/2, y: diam/2, z: 0 });
|
||||||
|
let toolPos = tool.slice();
|
||||||
|
let minz = Infinity;
|
||||||
|
for (let i=0; i<toolPos.length; i += 3) {
|
||||||
|
// toolPos[i+2] = -toolPos[i+2];
|
||||||
|
toolBounds.expandByPoint({ x: 0, y: 0, z: toolPos[i+2] });
|
||||||
|
minz = Math.min(minz,toolPos[i+2]);
|
||||||
|
}
|
||||||
|
let toolData = { positions: toolPos, bounds: toolBounds };
|
||||||
|
|
||||||
|
// console.time('swap XZ vertices');
|
||||||
|
// swap XZ vertices for gpu code
|
||||||
|
for (let i=0; i<vertices.length; i+= 3) {
|
||||||
|
let tmp = vertices[i+2];
|
||||||
|
vertices[i+2] = vertices[i+0];
|
||||||
|
vertices[i+1] = -vertices[i+1];
|
||||||
|
vertices[i+0] = tmp;
|
||||||
|
}
|
||||||
|
// console.timeEnd('swap XZ vertices');
|
||||||
|
|
||||||
|
let gpu = await self.get_raster_gpu({
|
||||||
|
mode: "radial",
|
||||||
|
resolution,
|
||||||
|
rotationStep: angle,
|
||||||
|
radialRotationOffset: 90
|
||||||
|
});
|
||||||
|
let xStep = Math.max(1, Math.round(this.step / resolution));
|
||||||
|
let boundsOverride = this.bounds.clone();
|
||||||
|
boundsOverride.min.x += offStart * units;
|
||||||
|
boundsOverride.max.x -= offEnd * units;
|
||||||
|
await gpu.loadTool({
|
||||||
|
sparseData: toolData
|
||||||
|
});
|
||||||
|
await gpu.loadTerrain({
|
||||||
|
triangles: vertices,
|
||||||
|
boundsOverride,
|
||||||
|
onProgress(pct) { onupdate(pct/100) }
|
||||||
|
});
|
||||||
|
let output = await gpu.generateToolpaths({
|
||||||
|
xStep,
|
||||||
|
yStep: 1,
|
||||||
|
zFloor: zBottom,
|
||||||
|
onProgress(i,j) { onupdate(i/j) }
|
||||||
|
});
|
||||||
|
gpu.terminate();
|
||||||
|
|
||||||
|
let { numStrips, strips } = output;
|
||||||
|
let degPerRow = 360 / numStrips;
|
||||||
|
let slices = this.gpu_slices = [];
|
||||||
|
let xmult = resolution * xStep;
|
||||||
|
let xoff = boundsOverride.min.x;
|
||||||
|
let rows = [];
|
||||||
|
for (let i=0; i<numStrips; i++) {
|
||||||
|
let points = Array.from(strips[i].pathData).map((v,j) => newPoint(j * xmult + xoff, 0, v).setA(-i * degPerRow));
|
||||||
|
rows.push(points);
|
||||||
|
}
|
||||||
|
if (linear) {
|
||||||
|
for (let i=0; i<rows.length; i++) {
|
||||||
|
let slice = newSlice(i);
|
||||||
|
slice.index = i;
|
||||||
|
slice.camLines = [ newPolygon(rows[i]).setOpen() ];
|
||||||
|
if (i % 2 === 1) slice.camLines[0].reverse();
|
||||||
|
slices.push(slice);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let { pointsPerLine } = strips[0];
|
||||||
|
for (let i=0; i<pointsPerLine; i++) {
|
||||||
|
let slice = newSlice(i);
|
||||||
|
let points = rows.map(row => row[i]);
|
||||||
|
if (i % 2 === 1) points.reverse();
|
||||||
|
slice.index = i;
|
||||||
|
slice.camLines = [ newPolygon(points).setOpen() ];
|
||||||
|
slices.push(slice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let slice of slices) {
|
||||||
|
slice.output()
|
||||||
|
.setLayer("lathe", { line: this.lineColor })
|
||||||
|
.addPoly(slice.camLines[0].clone().applyRotations());
|
||||||
|
}
|
||||||
|
// console.log({ webGPU: output, slices });
|
||||||
|
return slices;
|
||||||
|
}
|
||||||
|
|
||||||
async sliceWorker(onupdate) {
|
async sliceWorker(onupdate) {
|
||||||
const { vertices, slices, resolution } = this;
|
const { vertices, slices, resolution } = this;
|
||||||
|
|
||||||
|
|
@ -205,22 +312,28 @@ export class Topo {
|
||||||
|
|
||||||
async lathe(onupdate) {
|
async lathe(onupdate) {
|
||||||
const { minions } = self.kiri_worker;
|
const { minions } = self.kiri_worker;
|
||||||
if (minions?.running > 1) {
|
if (this.gpu) {
|
||||||
|
return await this.latheGPU(onupdate);
|
||||||
|
} else if (minions?.running > 1) {
|
||||||
return await this.latheMinions(onupdate);
|
return await this.latheMinions(onupdate);
|
||||||
} else {
|
} else {
|
||||||
return await this.latheWorker(onupdate);
|
return await this.latheWorker(onupdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async latheGPU(onupdate) {
|
||||||
|
return this.gpu_slices;
|
||||||
|
}
|
||||||
|
|
||||||
lathePath(slices, tool) {
|
lathePath(slices, tool) {
|
||||||
const { resolution, step, zBottom, maxo } = this;
|
const { resolution, step, zBottom, maxo } = this;
|
||||||
|
|
||||||
const tlen = tool.length;
|
const tlen = tool.length;
|
||||||
const slen = slices.length;
|
const slen = slices.length;
|
||||||
const sinc = Math.max(1, Math.ceil(step / resolution));
|
|
||||||
const heights = [];
|
const heights = [];
|
||||||
|
|
||||||
// cull slice lines to only the ones in range (5x faster)
|
// console.log({ tlen, slen, sinc, slices: slices.map(s => s.z) });
|
||||||
|
// cull slice lines to only the ones in range (~5x faster)
|
||||||
const oslices = [];
|
const oslices = [];
|
||||||
for (let slice of slices) {
|
for (let slice of slices) {
|
||||||
const lines = slice.lines;
|
const lines = slice.lines;
|
||||||
|
|
@ -244,7 +357,9 @@ export class Topo {
|
||||||
// iterate over all slices (real x = z)
|
// iterate over all slices (real x = z)
|
||||||
// find max real z using z ray intersect from tool point to slice lines + offset
|
// find max real z using z ray intersect from tool point to slice lines + offset
|
||||||
let lz = 0;
|
let lz = 0;
|
||||||
for (let si = 0; si < slen; si += sinc) {
|
for (let sz = 0; ; sz += step) {
|
||||||
|
let si = Math.ceil(sz / resolution);
|
||||||
|
if (si >= oslices.length) break;
|
||||||
const rx = oslices[si].z;
|
const rx = oslices[si].z;
|
||||||
let mz = -Infinity;
|
let mz = -Infinity;
|
||||||
// iterate over tool offsets
|
// iterate over tool offsets
|
||||||
|
|
@ -281,12 +396,12 @@ export class Topo {
|
||||||
}
|
}
|
||||||
if (mz === -Infinity && xo === 0 && yo === 0) {
|
if (mz === -Infinity && xo === 0 && yo === 0) {
|
||||||
// tool tip is off the model
|
// tool tip is off the model
|
||||||
continue;
|
// continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mz === -Infinity) {
|
if (mz === -Infinity) {
|
||||||
mz = zBottom; // Math.min(0, lz)
|
mz = zBottom;
|
||||||
} else if (zBottom && mz < zBottom) {
|
} else if (mz < zBottom) {
|
||||||
mz = zBottom;
|
mz = zBottom;
|
||||||
}
|
}
|
||||||
heights.push(rx, 0, lz = mz);
|
heights.push(rx, 0, lz = mz);
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,11 @@ export function init() {
|
||||||
"sliceFillType": LANG.fi_type,
|
"sliceFillType": LANG.fi_type,
|
||||||
"sliceFillWidth": LANG.fi_wdth_s,
|
"sliceFillWidth": LANG.fi_wdth_s,
|
||||||
"sliceFillSparse": LANG.fi_pcnt_s,
|
"sliceFillSparse": LANG.fi_pcnt_s,
|
||||||
"sliceFillRate": LANG.ou_feed_s,
|
|
||||||
"sliceFillGrow": LANG.fi_grow_s,
|
"sliceFillGrow": LANG.fi_grow_s,
|
||||||
"sliceSolidRate": LANG.ou_fini_s,
|
|
||||||
"sliceSolidMinArea": LANG.ad_msol_s,
|
"sliceSolidMinArea": LANG.ad_msol_s,
|
||||||
"sliceTopLayers": LANG.sl_ltop_s,
|
"sliceTopLayers": LANG.sl_ltop_s,
|
||||||
"sliceBottomLayers": LANG.sl_lbot_s,
|
"sliceBottomLayers": LANG.sl_lbot_s,
|
||||||
"firstLayerRate": LANG.fl_rate_s,
|
"firstLayerRate": LANG.fl_rate_s,
|
||||||
"firstLayerPrintMult": LANG.fl_mult_s,
|
|
||||||
"firstLayerFanSpeed": LANG.ou_fans_s,
|
"firstLayerFanSpeed": LANG.ou_fans_s,
|
||||||
// prepare
|
// prepare
|
||||||
"sliceShellOrder": LANG.sl_ordr_s,
|
"sliceShellOrder": LANG.sl_ordr_s,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { fdm_export } from './export.js';
|
||||||
// fil = filament diameter
|
// fil = filament diameter
|
||||||
// slice = slice height
|
// slice = slice height
|
||||||
function extrudePerMM(noz, fil, slice) {
|
function extrudePerMM(noz, fil, slice) {
|
||||||
|
// (ratio of nozzle to filament) * (ratio of slice to nozzle)
|
||||||
return ((Math.PI * util.sqr(noz / 2)) / (Math.PI * util.sqr(fil / 2))) * (slice / noz);
|
return ((Math.PI * util.sqr(noz / 2)) / (Math.PI * util.sqr(fil / 2))) * (slice / noz);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
{ extrudeAbs } = device,
|
{ extrudeAbs } = device,
|
||||||
{ exportThumb } = controller,
|
{ exportThumb } = controller,
|
||||||
{ extrudeMM, extrudePerMM } = FDM,
|
{ extrudeMM, extrudePerMM } = FDM,
|
||||||
extused = Object.keys(print.extruders).map(v => parseInt(v)),
|
|
||||||
timeFactor = (device.gcodeTime || 1) * 1.5,
|
timeFactor = (device.gcodeTime || 1) * 1.5,
|
||||||
decimals = config.gcode_decimals || 4,
|
decimals = config.gcode_decimals || 4,
|
||||||
zMoveMax = device.deviceZMax || 0,
|
zMoveMax = device.deviceZMax || 0,
|
||||||
|
|
@ -49,7 +48,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
last = null,
|
last = null,
|
||||||
zpos = 0,
|
zpos = 0,
|
||||||
bmax = 0,
|
bmax = 0,
|
||||||
blastz = 0,
|
|
||||||
belt_add_y = (process.firstLayerYOffset || 0) - (belty || 0),
|
belt_add_y = (process.firstLayerYOffset || 0) - (belty || 0),
|
||||||
oloops = process.outputLoops || 0,
|
oloops = process.outputLoops || 0,
|
||||||
zhop = process.zHopDistance || 0, // range
|
zhop = process.zHopDistance || 0, // range
|
||||||
|
|
@ -58,10 +56,8 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
retDist = process.outputRetractDist || 0, // range
|
retDist = process.outputRetractDist || 0, // range
|
||||||
retSpeed = process.outputRetractSpeed * 60 || 1, // range
|
retSpeed = process.outputRetractSpeed * 60 || 1, // range
|
||||||
retDwell = process.outputRetractDwell || 0, // range
|
retDwell = process.outputRetractDwell || 0, // range
|
||||||
timeDwell = retDwell / 1000,
|
|
||||||
scarfZ = process.outputScarfLength ? true : false,
|
scarfZ = process.outputScarfLength ? true : false,
|
||||||
arcDist = isBelt || scarfZ ? 0 : (process.arcTolerance || 0),
|
arcDist = isBelt || scarfZ ? 0 : (process.arcTolerance || 0),
|
||||||
arcMin = 1,
|
|
||||||
arcRes = 20,
|
arcRes = 20,
|
||||||
arcDev = 0.5,
|
arcDev = 0.5,
|
||||||
arcMax = 40,
|
arcMax = 40,
|
||||||
|
|
@ -85,7 +81,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
fanSpeedSave = undefined,
|
fanSpeedSave = undefined,
|
||||||
fanSpeedBase = 0,
|
fanSpeedBase = 0,
|
||||||
fanSpeed = 0,
|
fanSpeed = 0,
|
||||||
lastType = undefined,
|
|
||||||
lastNozzleTemp = nozzleTemp,
|
lastNozzleTemp = nozzleTemp,
|
||||||
lastBedTemp = bedTemp,
|
lastBedTemp = bedTemp,
|
||||||
lastFanSpeed = fanSpeed,
|
lastFanSpeed = fanSpeed,
|
||||||
|
|
@ -169,7 +164,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
retDist = params.outputRetractDist || 0; // range
|
retDist = params.outputRetractDist || 0; // range
|
||||||
retSpeed = params.outputRetractSpeed * 60 || 1; // range
|
retSpeed = params.outputRetractSpeed * 60 || 1; // range
|
||||||
retDwell = params.outputRetractDwell || 0; // range
|
retDwell = params.outputRetractDwell || 0; // range
|
||||||
timeDwell = retDwell / 1000;
|
|
||||||
nozzleTemp = layer === 0 ?
|
nozzleTemp = layer === 0 ?
|
||||||
params.firstLayerNozzleTemp || params.outputTemp :
|
params.firstLayerNozzleTemp || params.outputTemp :
|
||||||
params.outputTemp || params.firstLayerNozzleTemp;
|
params.outputTemp || params.firstLayerNozzleTemp;
|
||||||
|
|
@ -297,8 +291,9 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
append(`; Bed type: ${bedType}`);
|
append(`; Bed type: ${bedType}`);
|
||||||
append(`; Target: ${filter[mode]}`);
|
append(`; Target: ${filter[mode]}`);
|
||||||
// inject thumbnail preview
|
// inject thumbnail preview
|
||||||
if (exportThumb && worker.snap) {
|
let { current } = self.kiri_worker ?? {};
|
||||||
let { width, height, url } = worker.snap;
|
if (exportThumb && current?.snap) {
|
||||||
|
let { width, height, url } = current.snap;
|
||||||
let data = url.substring(url.indexOf(',') + 1);
|
let data = url.substring(url.indexOf(',') + 1);
|
||||||
append(`; thumbnail begin ${width} ${height} ${data.length}`);
|
append(`; thumbnail begin ${width} ${height} ${data.length}`);
|
||||||
for (let i=0; i<data.length; i += 78) {
|
for (let i=0; i<data.length; i += 78) {
|
||||||
|
|
@ -470,7 +465,7 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
if (isBelt) {
|
if (isBelt) {
|
||||||
let zheight = path ? path.height || 0 : 0;
|
let zheight = path ? path.height || 0 : 0;
|
||||||
epos.x = originCenter ? -pos.x : device.bedWidth - pos.x;
|
epos.x = originCenter ? -pos.x : device.bedWidth - pos.x;
|
||||||
epos.z = blastz = pos.z * icos;
|
epos.z = pos.z * icos;
|
||||||
epos.y = -pos.y + epos.z * bcos + belt_add_y;
|
epos.y = -pos.y + epos.z * bcos + belt_add_y;
|
||||||
lout = epos;
|
lout = epos;
|
||||||
}
|
}
|
||||||
|
|
@ -613,6 +608,9 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
for (pidx=0; pidx<path.length; pidx++) {
|
for (pidx=0; pidx<path.length; pidx++) {
|
||||||
out = path[pidx];
|
out = path[pidx];
|
||||||
speedMMM = (out.speed || process.outputFeedrate) * 60; // range
|
speedMMM = (out.speed || process.outputFeedrate) * 60; // range
|
||||||
|
if (!isBelt) {
|
||||||
|
fanSpeed = out.fan ?? fanSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
// hint to controller that we're working on a specific object
|
// hint to controller that we're working on a specific object
|
||||||
// so that gcode between start/stop comments can be cancelled
|
// so that gcode between start/stop comments can be cancelled
|
||||||
|
|
@ -634,7 +632,7 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
|
|
||||||
// emit comment on output type chage
|
// emit comment on output type chage
|
||||||
if (last && out.type !== last.type) {
|
if (last && out.type !== last.type) {
|
||||||
lastType = subst.feature = out.type;
|
subst.feature = out.type;
|
||||||
if (gcodeFeature && gcodeFeature.length) {
|
if (gcodeFeature && gcodeFeature.length) {
|
||||||
appendAllSub(gcodeFeature);
|
appendAllSub(gcodeFeature);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -687,10 +685,10 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
// in belt mode, fan can change per segment (for base)
|
// in belt mode, fan can change per segment (for base)
|
||||||
if (isBelt) {
|
if (isBelt) {
|
||||||
setTempFanSpeed(out.fan);
|
setTempFanSpeed(out.fan);
|
||||||
if (fanSpeed !== lastFanSpeed) {
|
}
|
||||||
appendAllSub(gcodeFan);
|
if (fanSpeed !== lastFanSpeed) {
|
||||||
lastFanSpeed = fanSpeed;
|
appendAllSub(gcodeFan);
|
||||||
}
|
lastFanSpeed = fanSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastp && out.emit) {
|
if (lastp && out.emit) {
|
||||||
|
|
@ -751,7 +749,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if new point is off the arc
|
// if new point is off the arc
|
||||||
// if (deem || depm || desp || dc > arcDist || cc.r < arcMin || cc.r > arcMax || dist > cc.r) {
|
|
||||||
if (deem || depm || desp || dc * arcQ.center.length / arcQ.rSum > arcDist || dist > cc.r || cc.r > arcMax || radFault || !arcValid()) {
|
if (deem || depm || desp || dc * arcQ.center.length / arcQ.rSum > arcDist || dist > cc.r || cc.r > arcMax || radFault || !arcValid()) {
|
||||||
// let debug = [deem, depm, desp, dc * arcQ.center.length / arcQ.rSum > arcDist, dist > cc.r, cc.r > arcMax, radFault];
|
// let debug = [deem, depm, desp, dc * arcQ.center.length / arcQ.rSum > arcDist, dist > cc.r, cc.r > arcMax, radFault];
|
||||||
if (arcQ.length === 4) {
|
if (arcQ.length === 4) {
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ export function menu() {
|
||||||
_____: newGroup(LANG.sw_menu, $('fdm-walls'), { modes:FDM, driven, hideable, separator, group:"fdm-walls" }),
|
_____: newGroup(LANG.sw_menu, $('fdm-walls'), { modes:FDM, driven, hideable, separator, group:"fdm-walls" }),
|
||||||
sliceShells: newInput(LANG.sl_shel_s, { title:LANG.sl_shel_l, convert:toFloat }),
|
sliceShells: newInput(LANG.sl_shel_s, { title:LANG.sl_shel_l, convert:toFloat }),
|
||||||
sliceLineWidth: newInput(LANG.sl_line_s, { title:LANG.sl_line_l, convert:toFloat, bound:bound(0,5) }),
|
sliceLineWidth: newInput(LANG.sl_line_s, { title:LANG.sl_line_l, convert:toFloat, bound:bound(0,5) }),
|
||||||
|
sliceFillOverlap: newInput(LANG.fi_over_s, {title:LANG.fi_over_l, convert:toFloat, bound:bound(0.0,1.0)}),
|
||||||
separator: newBlank({ class:"set-sep", driven }),
|
separator: newBlank({ class:"set-sep", driven }),
|
||||||
sliceShellOrder: newSelect(LANG.sl_ordr_s, { title:LANG.sl_ordr_l}, "shell"),
|
sliceShellOrder: newSelect(LANG.sl_ordr_s, { title:LANG.sl_ordr_l}, "shell"),
|
||||||
sliceDetectThin: newSelect(LANG.ad_thin_s, { title: LANG.ad_thin_l, action: thinWallSave }, "thin"),
|
sliceDetectThin: newSelect(LANG.ad_thin_s, { title: LANG.ad_thin_l, action: thinWallSave }, "thin"),
|
||||||
|
|
@ -86,19 +87,17 @@ export function menu() {
|
||||||
sliceZInterleave: newBoolean(LANG.ad_zint_s, onBooleanClick, {title:LANG.ad_zint_l, show:zIntShow}),
|
sliceZInterleave: newBoolean(LANG.ad_zint_s, onBooleanClick, {title:LANG.ad_zint_l, show:zIntShow}),
|
||||||
_____: newGroup(LANG.fs_menu, $('fdm-solid'), { modes:FDM, driven, hideable, separator, group:"fdm-solid" }),
|
_____: newGroup(LANG.fs_menu, $('fdm-solid'), { modes:FDM, driven, hideable, separator, group:"fdm-solid" }),
|
||||||
sliceFillAngle: newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:toFloat}),
|
sliceFillAngle: newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:toFloat}),
|
||||||
sliceFillOverlap: newInput(LANG.fi_over_s, {title:LANG.fi_over_l, convert:toFloat, bound:bound(0.0,2.0)}),
|
|
||||||
sliceFillGrow: newInput(LANG.fi_grow_s, {title:LANG.fi_grow_l, convert:toFloat}),
|
sliceFillGrow: newInput(LANG.fi_grow_s, {title:LANG.fi_grow_l, convert:toFloat}),
|
||||||
sliceSolidMinArea: newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:toFloat}),
|
sliceSolidMinArea: newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:toFloat}),
|
||||||
_____: newGroup(LANG.fi_menu, $('fdm-fill'), { modes:FDM, driven, hideable, separator, group:"fdm-fill" }),
|
_____: newGroup(LANG.fi_menu, $('fdm-fill'), { modes:FDM, driven, hideable, separator, group:"fdm-fill" }),
|
||||||
sliceFillType: newSelect(LANG.fi_type, {trigger}, "infill"),
|
sliceFillType: newSelect(LANG.fi_type, {trigger}, "infill"),
|
||||||
sliceFillSparse: newInput(LANG.fi_pcnt_s, {title:LANG.fi_pcnt_l, convert:toFloat, bound:bound(0.0,1.0), show:hasInfill}),
|
sliceFillSparse: newInput(LANG.fi_pcnt_s, {title:LANG.fi_pcnt_l, convert:toFloat, bound:bound(0.0,1.0), show:hasInfill}),
|
||||||
sliceFillRepeat: newInput(LANG.fi_rept_s, {title:LANG.fi_rept_l, convert:toInt, bound:bound(1,10), show:fillIsLinear}),
|
sliceFillRepeat: newInput(LANG.fi_rept_s, {title:LANG.fi_rept_l, convert:toInt, bound:bound(1,10), show:fillIsLinear}),
|
||||||
separator: newBlank({ class:"set-sep", driven }),
|
|
||||||
sliceFillRate: newInput(LANG.ou_feed_s, {title:LANG.ou_feed_l, convert:toInt, bound:bound(0,500)}),
|
|
||||||
sliceSolidRate: newInput(LANG.ou_fini_s, {title:LANG.ou_fini_l, convert:toInt, bound:bound(0,500)}),
|
|
||||||
_____: newGroup(LANG.fh_menu, $('fdm-heat'), { modes:FDM, driven, hideable, separator, group:"fdm-heat" }),
|
_____: newGroup(LANG.fh_menu, $('fdm-heat'), { modes:FDM, driven, hideable, separator, group:"fdm-heat" }),
|
||||||
outputTemp: newInput(LANG.ou_nozl_s, {title:LANG.ou_nozl_l, convert:toInt}),
|
outputTemp: newInput(LANG.ou_nozl_s, {title:LANG.ou_nozl_l, convert:toInt}),
|
||||||
outputBedTemp: newInput(LANG.ou_bedd_s, {title:LANG.ou_bedd_l, convert:toInt}),
|
outputBedTemp: newInput(LANG.ou_bedd_s, {title:LANG.ou_bedd_l, convert:toInt}),
|
||||||
|
separator: newBlank({ class:"set-sep", driven }),
|
||||||
|
outputDraftShield: newBoolean(LANG.fr_draf_s, onBooleanClick, {title:LANG.fr_draf_l, trigger, show:() => isNotBelt()}),
|
||||||
_____: newGroup(LANG.fc_menu, $('fdm-cool'), { modes:FDM, driven, hideable, separator, group:"fdm-cool" }),
|
_____: newGroup(LANG.fc_menu, $('fdm-cool'), { modes:FDM, driven, hideable, separator, group:"fdm-cool" }),
|
||||||
outputFanLayer: newInput(LANG.ou_fanl_s, { title:LANG.ou_fanl_l, convert:toInt, bound:bound(0,255) }),
|
outputFanLayer: newInput(LANG.ou_fanl_s, { title:LANG.ou_fanl_l, convert:toInt, bound:bound(0,255) }),
|
||||||
outputFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255)}),
|
outputFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255)}),
|
||||||
|
|
@ -135,11 +134,9 @@ export function menu() {
|
||||||
firstLayerFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255), show:isBelt}),
|
firstLayerFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255), show:isBelt}),
|
||||||
firstLayerYOffset: newInput(LANG.fl_zoff_s, {title:LANG.fl_zoff_l, convert:toFloat, show:isBelt}),
|
firstLayerYOffset: newInput(LANG.fl_zoff_s, {title:LANG.fl_zoff_l, convert:toFloat, show:isBelt}),
|
||||||
firstLayerFlatten: newInput(LANG.fl_flat_s, {title:LANG.fl_flat_l, convert:toFloat, show:isBelt}),
|
firstLayerFlatten: newInput(LANG.fl_flat_s, {title:LANG.fl_flat_l, convert:toFloat, show:isBelt}),
|
||||||
|
firstLayerBeltFact: newInput(LANG.fl_mult_s, {title:LANG.fl_mult_l, convert:toFloat, bound:bound(0, 2), show:isBelt}),
|
||||||
firstLayerRate: newInput(LANG.fl_rate_s, {title:LANG.fl_rate_l, convert:toFloat}),
|
firstLayerRate: newInput(LANG.fl_rate_s, {title:LANG.fl_rate_l, convert:toFloat}),
|
||||||
firstLayerFillRate: newInput(LANG.fl_frat_s, {title:LANG.fl_frat_l, convert:toFloat, show:isNotBelt}),
|
firstLayerFillRate: newInput(LANG.fl_frat_s, {title:LANG.fl_frat_l, convert:toFloat, show:isNotBelt}),
|
||||||
separator: newBlank({ class:"set-sep", driven, show:isNotBelt }),
|
|
||||||
firstLayerLineMult: newInput(LANG.fl_sfac_s, {title:LANG.fl_sfac_l, convert:toFloat, bound:bound(0.5,2), show:isNotBelt}),
|
|
||||||
firstLayerPrintMult: newInput(LANG.fl_mult_s, {title:LANG.fl_mult_l, convert:toFloat}),
|
|
||||||
separator: newBlank({ class:"set-sep", driven, show:isBelt }),
|
separator: newBlank({ class:"set-sep", driven, show:isBelt }),
|
||||||
firstLayerBrim: newInput(LANG.fl_brim_s, {title:LANG.fl_brim_l, convert:toInt, show:isBelt}),
|
firstLayerBrim: newInput(LANG.fl_brim_s, {title:LANG.fl_brim_l, convert:toInt, show:isBelt}),
|
||||||
firstLayerBrimIn: newInput(LANG.fl_brin_s, {title:LANG.fl_brin_l, convert:toInt, show:isBelt}),
|
firstLayerBrimIn: newInput(LANG.fl_brin_s, {title:LANG.fl_brin_l, convert:toInt, show:isBelt}),
|
||||||
|
|
@ -154,7 +151,6 @@ export function menu() {
|
||||||
outputRaftSpacing: newInput(LANG.fr_spac_s, {title:LANG.fr_spac_l, convert:toFloat, bound:bound(0.0,3.0), show: () => ui.outputRaft.checked && isNotBelt() }),
|
outputRaftSpacing: newInput(LANG.fr_spac_s, {title:LANG.fr_spac_l, convert:toFloat, bound:bound(0.0,3.0), show: () => ui.outputRaft.checked && isNotBelt() }),
|
||||||
separator: newBlank({ class:"set-sep", driven, show:isNotBelt }),
|
separator: newBlank({ class:"set-sep", driven, show:isNotBelt }),
|
||||||
outputRaft: newBoolean(LANG.fr_nabl_s, onBooleanClick, {title:LANG.fr_nabl_l, trigger, show:() => isNotBelt()}),
|
outputRaft: newBoolean(LANG.fr_nabl_s, onBooleanClick, {title:LANG.fr_nabl_l, trigger, show:() => isNotBelt()}),
|
||||||
outputDraftShield: newBoolean(LANG.fr_draf_s, onBooleanClick, {title:LANG.fr_draf_l, trigger, show:() => isNotBelt()}),
|
|
||||||
_____: newGroup(LANG.ou_menu, $('fdm-output'), { modes:FDM, driven, hideable, separator, group:"fdm-out" }),
|
_____: newGroup(LANG.ou_menu, $('fdm-output'), { modes:FDM, driven, hideable, separator, group:"fdm-out" }),
|
||||||
outputFeedrate: newInput(LANG.ou_feed_s, {title:LANG.ou_feed_l, convert:toInt}),
|
outputFeedrate: newInput(LANG.ou_feed_s, {title:LANG.ou_feed_l, convert:toInt}),
|
||||||
outputFinishrate: newInput(LANG.ou_fini_s, {title:LANG.ou_fini_l, convert:toInt}),
|
outputFinishrate: newInput(LANG.ou_fini_s, {title:LANG.ou_fini_l, convert:toInt}),
|
||||||
|
|
@ -177,10 +173,13 @@ export function menu() {
|
||||||
arcTolerance: newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, convert:toFloat, bound:bound(0,1.0), show:() => { return isNotBelt() }}),
|
arcTolerance: newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, convert:toFloat, bound:bound(0,1.0), show:() => { return isNotBelt() }}),
|
||||||
outputRetractDwell: newInput(LANG.ad_rdwl_s, {title:LANG.ad_rdwl_l, convert:toInt}),
|
outputRetractDwell: newInput(LANG.ad_rdwl_s, {title:LANG.ad_rdwl_l, convert:toInt}),
|
||||||
outputMinSpeed: newInput(LANG.ad_mins_s, {title:LANG.ad_mins_l, convert:toFloat, bound:bound(1,200)}),
|
outputMinSpeed: newInput(LANG.ad_mins_s, {title:LANG.ad_mins_l, convert:toFloat, bound:bound(1,200)}),
|
||||||
|
outputMaxFlowrate: newInput(LANG.ad_maxf_s, {title:LANG.ad_maxf_l, convert:toFloat, bound:bound(1,200)}),
|
||||||
outputPurgeTower: newInput(LANG.ad_purg_s, {title:LANG.ad_purg_l, convert:toInt, bound:bound(0,1000)}),
|
outputPurgeTower: newInput(LANG.ad_purg_s, {title:LANG.ad_purg_l, convert:toInt, bound:bound(0,1000)}),
|
||||||
outputScarfLength: newInput(LANG.ad_scar_s, {title:LANG.ad_scar_l, convert:toFloat, bound:bound(0,1000)}),
|
outputScarfLength: newInput(LANG.ad_scar_s, {title:LANG.ad_scar_l, convert:toFloat, bound:bound(0,1000)}),
|
||||||
outputShortPoly: newInput(LANG.ad_spol_s, {title:LANG.ad_spol_l, convert:toFloat, bound:bound(0,10000)}),
|
outputShortPoly: newInput(LANG.ad_spol_s, {title:LANG.ad_spol_l, convert:toFloat, bound:bound(0,10000)}),
|
||||||
outputCoastDist: newInput(LANG.ad_scst_s, {title:LANG.ad_scst_l, convert:toFloat, bound:bound(0.0,10)}),
|
outputCoastDist: newInput(LANG.ad_scst_s, {title:LANG.ad_scst_l, convert:toFloat, bound:bound(0.0,10)}),
|
||||||
|
sliceCompInner: newInput(LANG.sl_ofin_s, {title:LANG.sl_ofin_l, convert:toFloat, bound:bound(-10.0,10.0)}),
|
||||||
|
sliceCompOuter: newInput(LANG.sl_ofot_s, {title:LANG.sl_ofot_l, convert:toFloat, bound:bound(-10.0,10.0)}),
|
||||||
zHopDistance: newInput(LANG.ad_zhop_s, {title:LANG.ad_zhop_l, convert:toFloat, bound:bound(0,3.0)}),
|
zHopDistance: newInput(LANG.ad_zhop_s, {title:LANG.ad_zhop_l, convert:toFloat, bound:bound(0,3.0)}),
|
||||||
outputLoops: newInput(LANG.ag_loop_s, {title:LANG.ag_loop_l, convert:toInt, bound:bound(-1,1000), show:isBelt}),
|
outputLoops: newInput(LANG.ag_loop_s, {title:LANG.ag_loop_l, convert:toInt, bound:bound(-1,1000), show:isBelt}),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,13 @@ slicer.slicePost.FDM = slicePost;
|
||||||
* data object. return is ignored.
|
* data object. return is ignored.
|
||||||
*/
|
*/
|
||||||
export function slicePost(data, options) {
|
export function slicePost(data, options) {
|
||||||
const { z, lines, groups } = data;
|
const { groups, z } = data;
|
||||||
const { useAssembly, post_args, zIndexes } = options;
|
const { post_args, useAssembly, zIndexes } = options;
|
||||||
const { process, isSynth, vaseMode } = post_args;
|
const { isSynth, process, vaseMode } = post_args;
|
||||||
const { shellOffset, fillOffset, clipOffset } = post_args;
|
const { compInner, compOuter, pump } = post_args;
|
||||||
|
const { clipOffset, fillOffset, shellOffset } = post_args;
|
||||||
|
|
||||||
if (options.pump) {
|
if (pump) {
|
||||||
let nugroups = [];
|
let nugroups = [];
|
||||||
let pump = shellOffset / 10;
|
let pump = shellOffset / 10;
|
||||||
for (let p of groups) {
|
for (let p of groups) {
|
||||||
|
|
@ -41,6 +42,19 @@ export function slicePost(data, options) {
|
||||||
data.tops = POLY.nest(groups);
|
data.tops = POLY.nest(groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// perimeter and inner hole compensation offsets
|
||||||
|
if (compInner || compOuter) {
|
||||||
|
let inner = POLY.inner(data.tops).flat();
|
||||||
|
let outer = POLY.outer(data.tops);
|
||||||
|
if (compInner) {
|
||||||
|
inner = POLY.flatten(POLY.offset(inner, compInner/2));
|
||||||
|
}
|
||||||
|
if (compOuter) {
|
||||||
|
outer = POLY.flatten(POLY.offset(outer, -compOuter/2));
|
||||||
|
}
|
||||||
|
data.tops = POLY.nest([...inner, ...outer]);
|
||||||
|
}
|
||||||
|
|
||||||
if (isSynth) {
|
if (isSynth) {
|
||||||
const process = post_args.process;
|
const process = post_args.process;
|
||||||
if (process.sliceSupportGrow > 0) {
|
if (process.sliceSupportGrow > 0) {
|
||||||
|
|
@ -54,6 +68,7 @@ export function slicePost(data, options) {
|
||||||
delete data.groups;
|
delete data.groups;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = zIndexes.indexOf(z);
|
const index = zIndexes.indexOf(z);
|
||||||
const range = getRangeParameters(process, index);
|
const range = getRangeParameters(process, index);
|
||||||
// calculate fractional shells
|
// calculate fractional shells
|
||||||
|
|
@ -67,12 +82,9 @@ export function slicePost(data, options) {
|
||||||
let trg = shellFrac > 0.5 ? 1 : parts - 1;
|
let trg = shellFrac > 0.5 ? 1 : parts - 1;
|
||||||
sliceShells += rem >= trg ? 1 : 0;
|
sliceShells += rem >= trg ? 1 : 0;
|
||||||
}
|
}
|
||||||
const isFirst = index === 0;
|
|
||||||
const height = process.sliceHeight;
|
|
||||||
const spaceMult = isFirst ? process.firstLayerLineMult || 1 : 1;
|
|
||||||
const count = isSynth ? 1 : sliceShells;
|
const count = isSynth ? 1 : sliceShells;
|
||||||
const offset = shellOffset * spaceMult;
|
const offset = shellOffset;
|
||||||
const fillOff = fillOffset * spaceMult;
|
const fillOff = fillOffset;
|
||||||
const thinType = isSynth ? undefined : process.sliceDetectThin;
|
const thinType = isSynth ? undefined : process.sliceDetectThin;
|
||||||
const nutops = [];
|
const nutops = [];
|
||||||
// co-locate shell processing with top generation in slicer
|
// co-locate shell processing with top generation in slicer
|
||||||
|
|
@ -129,7 +141,7 @@ function offset_default(params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const deltaBig = 160;
|
const deltaBig = 160;
|
||||||
const deltSmall = 20;
|
const deltaSmall = 20;
|
||||||
|
|
||||||
// look for close points on adjacent segmented polys and merge
|
// look for close points on adjacent segmented polys and merge
|
||||||
let test_polys = first !== last ? [ ...first, ...last ] : last;
|
let test_polys = first !== last ? [ ...first, ...last ] : last;
|
||||||
|
|
@ -139,6 +151,8 @@ function offset_default(params) {
|
||||||
for (let poly of test_flats)
|
for (let poly of test_flats)
|
||||||
poly.forEachSegment((p0, p1) => {
|
poly.forEachSegment((p0, p1) => {
|
||||||
p0.angle = new Slope(p0, p1);
|
p0.angle = new Slope(p0, p1);
|
||||||
|
// todo calc vertex angle
|
||||||
|
// exclude segment vertex from merge when angle < deltaSmall
|
||||||
});
|
});
|
||||||
|
|
||||||
// segment each poly to be tested
|
// segment each poly to be tested
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
import { FDM } from './driver-be.js';
|
||||||
import { base, util } from '../../../geo/base.js';
|
import { base, util } from '../../../geo/base.js';
|
||||||
import { poly2polyEmit, tip2tipEmit } from '../../../geo/paths.js';
|
import { poly2polyEmit, tip2tipEmit } from '../../../geo/paths.js';
|
||||||
import { newBounds } from '../../../geo/bounds.js';
|
import { newBounds } from '../../../geo/bounds.js';
|
||||||
|
|
@ -46,8 +47,6 @@ export async function fdm_prepare(widgets, settings, update) {
|
||||||
output = [],
|
output = [],
|
||||||
layerout = [];
|
layerout = [];
|
||||||
|
|
||||||
let lastLayerStart = null;
|
|
||||||
|
|
||||||
// compute bounds if missing
|
// compute bounds if missing
|
||||||
if (!bounds) {
|
if (!bounds) {
|
||||||
bounds = new THREE.Box3();
|
bounds = new THREE.Box3();
|
||||||
|
|
@ -553,12 +552,9 @@ export async function fdm_prepare(widgets, settings, update) {
|
||||||
offset,
|
offset,
|
||||||
layerout,
|
layerout,
|
||||||
{
|
{
|
||||||
routeAround: process.outputAvoidGaps,
|
|
||||||
seedPoint: printPoint.sub(offset),
|
|
||||||
params, // range parameters
|
|
||||||
first: slice.index === 0,
|
first: slice.index === 0,
|
||||||
support: slice.widget.support,
|
|
||||||
onBelt: beltStart,
|
onBelt: beltStart,
|
||||||
|
params, // range parameters
|
||||||
pretract: (wipeDist) => {
|
pretract: (wipeDist) => {
|
||||||
if (!(lastLayer && lastLayer.length)) {
|
if (!(lastLayer && lastLayer.length)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -573,7 +569,11 @@ export async function fdm_prepare(widgets, settings, update) {
|
||||||
print.addOutput(lastLayer, endpoint, null, null, lastOut.tool);
|
print.addOutput(lastLayer, endpoint, null, null, lastOut.tool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
routeAround: process.outputAvoidGaps,
|
||||||
|
seedPoint: printPoint.sub(offset),
|
||||||
|
support: slice.widget.support,
|
||||||
|
zmax,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
layerout.print_time = printPoint.layer_time;
|
layerout.print_time = printPoint.layer_time;
|
||||||
|
|
@ -682,8 +682,8 @@ export async function fdm_prepare(widgets, settings, update) {
|
||||||
|
|
||||||
for (let rec of layer) {
|
for (let rec of layer) {
|
||||||
overate = rec.overate >= 0 ? rec.overate : overate;
|
overate = rec.overate >= 0 ? rec.overate : overate;
|
||||||
|
let bmult = params.firstLayerBeltFact ?? 1;
|
||||||
let brate = params.firstLayerRate || firstLayerRate;
|
let brate = params.firstLayerRate || firstLayerRate;
|
||||||
let bmult = params.firstLayerPrintMult || params.firstLayerPrintMult;
|
|
||||||
let point = rec.point;
|
let point = rec.point;
|
||||||
let belty = rec.belty = -point.y + (point.z * belt.slope);
|
let belty = rec.belty = -point.y + (point.z * belt.slope);
|
||||||
let lowrate = belty <= thresh ? brate : overate || brate;
|
let lowrate = belty <= thresh ? brate : overate || brate;
|
||||||
|
|
@ -695,8 +695,9 @@ export async function fdm_prepare(widgets, settings, update) {
|
||||||
if (rec.emit && belty <= thresh && lastout && Math.abs(lastout.belty - belty) < 0.01) {
|
if (rec.emit && belty <= thresh && lastout && Math.abs(lastout.belty - belty) < 0.01) {
|
||||||
// apply base speed to segments touching belt
|
// apply base speed to segments touching belt
|
||||||
rec.speed = params.firstLayerRate || Math.min(rec.speed, lowrate);
|
rec.speed = params.firstLayerRate || Math.min(rec.speed, lowrate);
|
||||||
rec.emit *= bmult;
|
|
||||||
rec.fan = params.firstLayerFanSpeed;
|
rec.fan = params.firstLayerFanSpeed;
|
||||||
|
// apply base flowrate multiplier to segments touching belt
|
||||||
|
rec.emit *= bmult;
|
||||||
minx = Math.min(minx, point.x, lastout.point.x);
|
minx = Math.min(minx, point.x, lastout.point.x);
|
||||||
maxx = Math.max(maxx, point.x, lastout.point.x);
|
maxx = Math.max(maxx, point.x, lastout.point.x);
|
||||||
maxy = Math.max(maxy, point.y);
|
maxy = Math.max(maxy, point.y);
|
||||||
|
|
@ -795,10 +796,22 @@ export async function fdm_prepare(widgets, settings, update) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output and route Slice (shell,fill) paths for a single Slice
|
||||||
|
*
|
||||||
|
* @param {*} print Print output Object
|
||||||
|
* @param {*} slice current slice being Output
|
||||||
|
* @param {*} startPoint starting point for this layer
|
||||||
|
* @param {*} offset start point offset
|
||||||
|
* @param {*} output output prebuffer
|
||||||
|
* @param {*} opt options
|
||||||
|
*/
|
||||||
function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
const { settings } = print;
|
const { settings } = print;
|
||||||
const { device } = settings;
|
const { device } = settings;
|
||||||
const { bedWidth, bedDepth, bedRound } = device;
|
const { bedWidth, bedDepth, bedRound } = device;
|
||||||
|
const { extrudePerMM } = FDM;
|
||||||
|
|
||||||
let preout = [],
|
let preout = [],
|
||||||
process = opt.params || settings.process,
|
process = opt.params || settings.process,
|
||||||
|
|
@ -807,6 +820,7 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
scarfLength = process.outputScarfLength || 0,
|
scarfLength = process.outputScarfLength || 0,
|
||||||
minLayerTime = process.outputMinLayerTime || 0,
|
minLayerTime = process.outputMinLayerTime || 0,
|
||||||
nozzleSize = process.sliceLineWidth || device.extruders[extruder].extNozzle,
|
nozzleSize = process.sliceLineWidth || device.extruders[extruder].extNozzle,
|
||||||
|
nozzles = device.extruders.map(e => e.extNozzle),
|
||||||
firstLayer = (opt.first || false) && !opt.support,
|
firstLayer = (opt.first || false) && !opt.support,
|
||||||
thinWall = nozzleSize * (opt.thinWall || 1.75),
|
thinWall = nozzleSize * (opt.thinWall || 1.75),
|
||||||
retractDist = opt.retractOver || (nozzleSize * 5),
|
retractDist = opt.retractOver || (nozzleSize * 5),
|
||||||
|
|
@ -815,17 +829,20 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
shellOrder = {"out-in":-1,"in-out":1,"alternate":-2}[process.sliceShellOrder] || -1,
|
shellOrder = {"out-in":-1,"in-out":1,"alternate":-2}[process.sliceShellOrder] || -1,
|
||||||
sparseMult = process.outputSparseMult,
|
sparseMult = process.outputSparseMult,
|
||||||
coastDist = process.outputCoastDist || 0,
|
coastDist = process.outputCoastDist || 0,
|
||||||
|
fanSpeed = process.outputFanSpeed,
|
||||||
firstShellSpeed = process.firstLayerRate,
|
firstShellSpeed = process.firstLayerRate,
|
||||||
firstFillSpeed = process.firstLayerFillRate,
|
firstFillSpeed = process.firstLayerFillRate,
|
||||||
firstPrintMult = process.firstLayerPrintMult,
|
|
||||||
finishSpeed = firstLayer ? firstShellSpeed : (opt.speed || process.outputFinishrate),
|
finishSpeed = firstLayer ? firstShellSpeed : (opt.speed || process.outputFinishrate),
|
||||||
printSpeed = opt.speed || (firstLayer ? firstShellSpeed : process.outputFeedrate),
|
printSpeed = opt.speed || (firstLayer ? firstShellSpeed : process.outputFeedrate),
|
||||||
fillSpeed = opt.speed || opt.fillSpeed || (firstLayer ? firstFillSpeed || firstShellSpeed : process.outputFeedrate),
|
fillSpeed = opt.speed || opt.fillSpeed || (firstLayer ? firstFillSpeed || firstShellSpeed : process.outputFeedrate),
|
||||||
infillSpeed = process.sliceFillRate || opt.infillSpeed || fillSpeed || printSpeed,
|
infillSpeed = opt.infillSpeed || fillSpeed || printSpeed,
|
||||||
moveSpeed = process.outputSeekrate,
|
moveSpeed = process.outputSeekrate,
|
||||||
|
minSpeed = process.outputMinSpeed,
|
||||||
origin = startPoint.add(offset),
|
origin = startPoint.add(offset),
|
||||||
zhop = process.zHopDistance || 0,
|
zhop = process.zHopDistance || 0,
|
||||||
|
zmax = opt.zmax,
|
||||||
antiBacklash = process.antiBacklash,
|
antiBacklash = process.antiBacklash,
|
||||||
|
maxFlowRate = process.outputMaxFlowrate,
|
||||||
wipeDist = process.outputRetractWipe || 0,
|
wipeDist = process.outputRetractWipe || 0,
|
||||||
isBelt = device.bedBelt,
|
isBelt = device.bedBelt,
|
||||||
bedOffset = originCenter ? {
|
bedOffset = originCenter ? {
|
||||||
|
|
@ -849,11 +866,6 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
shellOrder = -shellOrder;
|
shellOrder = -shellOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update fill speed when solids detected
|
|
||||||
if (slice.finishSolids) {
|
|
||||||
fillSpeed = process.sliceSolidRate || finishSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// on flats layers also slow down shell print speed
|
// on flats layers also slow down shell print speed
|
||||||
if (slice.isFlatsLayer) {
|
if (slice.isFlatsLayer) {
|
||||||
printSpeed = finishSpeed || printSpeed;
|
printSpeed = finishSpeed || printSpeed;
|
||||||
|
|
@ -862,13 +874,7 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
// override: adapt bridge layer speed
|
// override: adapt bridge layer speed
|
||||||
if (slice.isBridgeLayer) {
|
if (slice.isBridgeLayer) {
|
||||||
fillSpeed /= 2;
|
fillSpeed /= 2;
|
||||||
}
|
printSpeed /= 2;
|
||||||
|
|
||||||
// apply first layer extrusion multipliers
|
|
||||||
if (firstLayer) {
|
|
||||||
fillMult *= firstPrintMult;
|
|
||||||
shellMult *= firstPrintMult;
|
|
||||||
sparseMult *= firstPrintMult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function retract() {
|
function retract() {
|
||||||
|
|
@ -1100,9 +1106,6 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
coast: firstLayer ? 0 : coastDist,
|
coast: firstLayer ? 0 : coastDist,
|
||||||
extrude: numOrDefault(opt.extrude, shellMult),
|
extrude: numOrDefault(opt.extrude, shellMult),
|
||||||
nozzleSize,
|
nozzleSize,
|
||||||
rate: finishShell ? finishSpeed : printSpeed,
|
|
||||||
scarf,
|
|
||||||
tool: extruder,
|
|
||||||
onfirst: (firstPoint, preout) => {
|
onfirst: (firstPoint, preout) => {
|
||||||
let from = seedPoint || startPoint;
|
let from = seedPoint || startPoint;
|
||||||
if (from.distTo2D(firstPoint) > retractDist) {
|
if (from.distTo2D(firstPoint) > retractDist) {
|
||||||
|
|
@ -1111,7 +1114,11 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
seedPoint = null;
|
seedPoint = null;
|
||||||
}
|
},
|
||||||
|
rate: finishShell ? finishSpeed : printSpeed,
|
||||||
|
scarf,
|
||||||
|
tool: extruder,
|
||||||
|
zmax
|
||||||
});
|
});
|
||||||
lastPoly = slice.lastPoly = poly;
|
lastPoly = slice.lastPoly = poly;
|
||||||
}
|
}
|
||||||
|
|
@ -1591,16 +1598,34 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
if (!p.emit) continue;
|
if (!p.emit) continue;
|
||||||
let mms = p.speed ?? 1;
|
let mms = p.speed ?? 1;
|
||||||
let dst = lp?.distTo2D(p.point) ?? 0;
|
let dst = lp?.distTo2D(p.point) ?? 0;
|
||||||
|
lp = p.point;
|
||||||
if (dst) {
|
if (dst) {
|
||||||
tt += (dst/mms);
|
tt += (dst/mms);
|
||||||
td += dst;
|
td += dst;
|
||||||
|
p.dst = dst;
|
||||||
}
|
}
|
||||||
lp = p.point;
|
|
||||||
}
|
}
|
||||||
if (minLayerTime && tt < minLayerTime) {
|
if (minLayerTime && tt < minLayerTime) {
|
||||||
let fact = tt / minLayerTime;
|
let fact = tt / minLayerTime;
|
||||||
for (let p of preout) {
|
for (let p of preout) {
|
||||||
if (p.emit) p.speed *= fact;
|
if (p.emit) {
|
||||||
|
p.speed = Math.max(p.speed * fact, minSpeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply speed damping when output exceeds volumetric flowrate cap
|
||||||
|
if (maxFlowRate) {
|
||||||
|
for (let p of preout) {
|
||||||
|
if (p.emit && p.dst) {
|
||||||
|
let vfr = nozzles[p.tool] * slice.height * p.speed;
|
||||||
|
if (vfr > maxFlowRate) {
|
||||||
|
let old = p.speed;
|
||||||
|
let damp = maxFlowRate / vfr;
|
||||||
|
p.speed = p.speed * damp;
|
||||||
|
// console.log({ vfr, damp, old, new: p.speed });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1612,6 +1637,12 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
// add offset points to total print
|
// add offset points to total print
|
||||||
print.addPrintPoints(preout, output);
|
print.addPrintPoints(preout, output);
|
||||||
|
|
||||||
|
// bridge layer fan change
|
||||||
|
if (preout.length && slice.isBridgeLayer) {
|
||||||
|
preout[0].fan = Math.round(fanSpeed / 3);
|
||||||
|
preout.peek().fan = fanSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
// create next layer start point and annotate
|
// create next layer start point and annotate
|
||||||
// with print time for previous layer (hack to
|
// with print time for previous layer (hack to
|
||||||
// avoid changing return signature rn)
|
// avoid changing return signature rn)
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,8 @@ export function fdm_slice(settings, widget, onupdate, ondone) {
|
||||||
fillOffset = lineWidth * fillOffsetMult,
|
fillOffset = lineWidth * fillOffsetMult,
|
||||||
clipOffset = process.sliceSupportOffset,
|
clipOffset = process.sliceSupportOffset,
|
||||||
sliceFillAngle = process.sliceFillAngle,
|
sliceFillAngle = process.sliceFillAngle,
|
||||||
supportDensity = process.sliceSupportDensity;
|
supportDensity = process.sliceSupportDensity,
|
||||||
|
{ sliceCompInner, sliceCompOuter } = process;
|
||||||
|
|
||||||
// override globals used by vopt()
|
// override globals used by vopt()
|
||||||
isFlat = controller.lineType === "flat";
|
isFlat = controller.lineType === "flat";
|
||||||
|
|
@ -230,6 +231,8 @@ export function fdm_slice(settings, widget, onupdate, ondone) {
|
||||||
useAssembly,
|
useAssembly,
|
||||||
post: 'FDM',
|
post: 'FDM',
|
||||||
post_args: {
|
post_args: {
|
||||||
|
compInner: sliceCompInner,
|
||||||
|
compOuter: sliceCompOuter,
|
||||||
shellOffset,
|
shellOffset,
|
||||||
fillOffset,
|
fillOffset,
|
||||||
clipOffset,
|
clipOffset,
|
||||||
|
|
@ -495,7 +498,6 @@ export function fdm_slice(settings, widget, onupdate, ondone) {
|
||||||
// just the top/bottom special solid layers or range defined solid layers
|
// just the top/bottom special solid layers or range defined solid layers
|
||||||
forSlices(0.15, 0.2, slice => {
|
forSlices(0.15, 0.2, slice => {
|
||||||
let range = slice.params;
|
let range = slice.params;
|
||||||
let spaceMult = slice.index === 0 ? process.firstLayerLineMult || 1 : 1;
|
|
||||||
let isBottom = slice.index < bottomLayers;
|
let isBottom = slice.index < bottomLayers;
|
||||||
let isTop = topLayers && slice.index > slices.length - topLayers - 1;
|
let isTop = topLayers && slice.index > slices.length - topLayers - 1;
|
||||||
let isTopBase = isTop && slice.index === slices.length - topLayers;
|
let isTopBase = isTop && slice.index === slices.length - topLayers;
|
||||||
|
|
@ -503,7 +505,7 @@ export function fdm_slice(settings, widget, onupdate, ondone) {
|
||||||
let isSolid = (isBottom || ((isTop || isDense) && !vaseMode)) && !isSynth;
|
let isSolid = (isBottom || ((isTop || isDense) && !vaseMode)) && !isSynth;
|
||||||
let solidWidth = isSolid ? range.sliceFillWidth || 1 : 0;
|
let solidWidth = isSolid ? range.sliceFillWidth || 1 : 0;
|
||||||
if (solidWidth) {
|
if (solidWidth) {
|
||||||
let fillSpace = fillSpacing * spaceMult * solidWidth;
|
let fillSpace = fillSpacing * solidWidth;
|
||||||
doSolidLayerFill(slice, fillSpace, sliceFillAngle);
|
doSolidLayerFill(slice, fillSpace, sliceFillAngle);
|
||||||
}
|
}
|
||||||
if (slice.index === slices.length - 1) {
|
if (slice.index === slices.length - 1) {
|
||||||
|
|
@ -752,8 +754,7 @@ export function fdm_slice(settings, widget, onupdate, ondone) {
|
||||||
let params = slice.params || process;
|
let params = slice.params || process;
|
||||||
let first = slice.index === 0;
|
let first = slice.index === 0;
|
||||||
let solidWidth = params.sliceFillWidth || 1;
|
let solidWidth = params.sliceFillWidth || 1;
|
||||||
let spaceMult = first ? params.firstLayerLineMult || 1 : 1;
|
let fillSpace = fillSpacing * solidWidth;
|
||||||
let fillSpace = fillSpacing * spaceMult * solidWidth;
|
|
||||||
let solidMinArea = params.sliceSolidMinArea;
|
let solidMinArea = params.sliceSolidMinArea;
|
||||||
doSolidsFill(slice, fillSpace, sliceFillAngle, solidMinArea, promises);
|
doSolidsFill(slice, fillSpace, sliceFillAngle, solidMinArea, promises);
|
||||||
sliceFillAngle = (sliceFillAngle + 90.0) % 360;
|
sliceFillAngle = (sliceFillAngle + 90.0) % 360;
|
||||||
|
|
|
||||||