Merge branch 'rel-4.3' into helical-op

This commit is contained in:
bakedpotatolord 2025-07-17 13:50:34 -06:00
commit 45ba762d1e
60 changed files with 1116 additions and 639 deletions

1
.gitignore vendored
View file

@ -5,6 +5,7 @@ app.json
build build
data data
dist dist
jszip-esm.js
logs logs
mod mod
node_modules node_modules

147
app.js
View file

@ -36,6 +36,7 @@ let http;
let util; let util;
let dir; let dir;
let log; let log;
let pre;
const EventEmitter = require('events'); const EventEmitter = require('events');
class AppEmitter extends EventEmitter {} class AppEmitter extends EventEmitter {}
@ -46,9 +47,13 @@ netdb.create = async function(map = {}) {
Object.assign(map, JSON.parse(fs.readFileSync(map.conf))); Object.assign(map, JSON.parse(fs.readFileSync(map.conf)));
} }
const client = new netdb(); const client = new netdb();
try {
if (map.host && map.port) await client.open(map.host, map.port); if (map.host && map.port) await client.open(map.host, map.port);
if (map.user && map.pass) await client.auth(map.user, map.pass); if (map.user && map.pass) await client.auth(map.user, map.pass);
if (map.base) await client.use(map.base); if (map.base) await client.use(map.base);
} catch (e) {
logger.log({ netdb_setup_error: e });
}
logger.log({ netdb: map.host, user: map.user, base: map.base }); logger.log({ netdb: map.host, user: map.user, base: map.base });
return client; return client;
}; };
@ -59,6 +64,7 @@ function init(mod) {
startTime = time(); startTime = time();
lastmod = mod.util.lastmod; lastmod = mod.util.lastmod;
logger = mod.log; logger = mod.log;
pre = ENV.pre || mod.meta.pre;
debug = ENV.debug || mod.meta.debug; debug = ENV.debug || mod.meta.debug;
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;
@ -72,17 +78,24 @@ function init(mod) {
dversion = debug ? `_${version}` : version; dversion = debug ? `_${version}` : version;
forceUseCache = ENV.cache ? true : false; forceUseCache = ENV.cache ? true : false;
const approot = PATH.join("main","gapp");
const refcache = {};
const callstack = [];
let xxxx = false;
if (!ENV.electron) { if (!ENV.electron) {
generateDevices(); generateDevices();
} }
if (pre) {
for (let [k,v] of Object.entries(productionMap)) {
productionMap[`${pre}${k}`] = `${pre}${v}`;
}
}
mod.on.test((req) => { mod.on.test((req) => {
let cookie = cookieValue(req.headers.cookie, "version") || undefined; if (req.app?.path?.startsWith(pre)) {
req.url = req.url.substring(pre.length);
req.app.path = req.app.path.substring(pre.length);
req.app.ispre = true;
return true;
}
let cookie = cookieValue(req.headers.cookie, "version") || '';
let vmatch = mod.meta.version || "*"; let vmatch = mod.meta.version || "*";
if (!Array.isArray(vmatch)) { if (!Array.isArray(vmatch)) {
vmatch = [ vmatch ]; vmatch = [ vmatch ];
@ -93,17 +106,28 @@ function init(mod) {
return vmatch.indexOf(cookie) >= 0; return vmatch.indexOf(cookie) >= 0;
}); });
mod.on.testv((version) => {
let vmatch = mod.meta.version || "*";
if (!Array.isArray(vmatch)) {
vmatch = [ vmatch ];
}
if (vmatch.indexOf("*") >= 0) {
return true;
}
return vmatch.indexOf(version) >= 0;
});
mod.add(handleSetup); mod.add(handleSetup);
mod.add(handleOptions); mod.add(handleOptions);
mod.add(serveWasm); mod.add(serveWasm);
mod.add(serveCode); mod.add(serveCode);
mod.add(fullpath({ mod.add(fullpath({
"/kiri" : redir("/kiri/", 301), "/kiri" : redir((pre??"") + "/kiri/", 301),
"/mesh" : redir("/mesh/", 301), "/mesh" : redir((pre??"") + "/mesh/", 301),
"/meta" : redir("/meta/", 301), "/meta" : redir((pre??"") + "/meta/", 301),
"/kiri/index.html" : redir("/kiri/", 301), "/kiri/index.html" : redir((pre??"") + "/kiri/", 301),
"/mesh/index.html" : redir("/mesh/", 301), "/mesh/index.html" : redir((pre??"") + "/mesh/", 301),
"/meta/index.html" : redir("/meta/", 301) "/meta/index.html" : redir((pre??"") + "/meta/", 301)
})); }));
mod.add(handleVersion); mod.add(handleVersion);
mod.add(fixedmap("/api/", api)); mod.add(fixedmap("/api/", api));
@ -116,8 +140,6 @@ function init(mod) {
}); });
} }
mod.add(rewriteHtmlVersion); mod.add(rewriteHtmlVersion);
// example of how to use the new middleware
// mod.add(appendContent('/kiri/index.html', '<script>console.log("hello")</script>'));
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");
@ -126,8 +148,8 @@ function init(mod) {
mod.static("/moto/", "web/moto"); mod.static("/moto/", "web/moto");
mod.static("/kiri/", "web/kiri"); mod.static("/kiri/", "web/kiri");
// module loader
function load_modules(root, force) { function load_modules(root, force) {
// load modules
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) === '.' && !ENV.single) return;
@ -152,7 +174,7 @@ function init(mod) {
// load optional local modules // load optional local modules
load_modules('mods'); load_modules('mods');
// run loads injected by modules // run load functions injected by modules
while (load.length) { while (load.length) {
try { try {
load.shift()(); load.shift()();
@ -161,7 +183,7 @@ function init(mod) {
} }
} }
// minify appends // minify appends in production mode
if (!debug) { if (!debug) {
for (let key of Object.keys(append)) { for (let key of Object.keys(append)) {
append[key] = minify(append[key]); append[key] = minify(append[key]);
@ -177,6 +199,9 @@ function loadModule(mod, dir) {
if (lastmod(`${mod.dir}/${dir}/.ignore`)) { if (lastmod(`${mod.dir}/${dir}/.ignore`)) {
return; return;
} }
// either load the module, or if it's not a module
// directory, serve the module as static content
// bound to the root
lastmod(`${mod.dir}/${dir}/init.js`) ? lastmod(`${mod.dir}/${dir}/init.js`) ?
initModule(mod, `./${dir}/init.js`, dir) : initModule(mod, `./${dir}/init.js`, dir) :
mod.static("/", `${mod.dir}/${dir}`); mod.static("/", `${mod.dir}/${dir}`);
@ -189,14 +214,14 @@ function initModule(mod, file, dir) {
// express functions added here show up at "/api/" url root // express functions added here show up at "/api/" url root
api: api, api: api,
adm: { adm: {
setver: (ver) => { oversion = ver }, setver(ver) { oversion = ver },
crossOrigin: (bool) => { crossOrigin = bool } crossOrigin(bool) { crossOrigin = bool }
}, },
events, events,
const: { const: {
args: {}, args: {},
meta: mod.meta, meta: mod.meta,
debug: debug, debug,
moddir: dir, moddir: dir,
rootdir: mod.dir, rootdir: mod.dir,
version: oversion || version version: oversion || version
@ -209,35 +234,21 @@ function initModule(mod, file, dir) {
}, },
mod: mods, mod: mods,
util: { util: {
guid,
time,
log: logger.log, log: logger.log,
time: time, logger: log.new,
guid: guid,
mkdirs: util.mkdir, mkdirs: util.mkdir,
isfile: util.isfile,
confdir: util.confdir, confdir: util.confdir,
datadir: util.datadir, datadir: util.datadir,
lastmod: lastmod, lastmod: lastmod,
obj2string: obj2string, isfile: util.isfile,
string2obj: string2obj,
getCookieValue: cookieValue, getCookieValue: cookieValue,
logger: log.new obj2string(o) { return JSON.stringify(o) },
}, string2obj(s) { return JSON.parse(s) },
inject: (code, file, opt = {}) => {
const path = mod.dir + '/' + dir + '/' + file;
try {
const body = fs.readFileSync(path);
// console.log({ inject: code, file, opt });
if (opt.first) {
append[code] = body.toString() + '\n' + append[code];
} else {
append[code] += body.toString() + '\n';
}
} catch (e) {
console.log({ missing_file: path, dir, mod });
}
}, },
path: { path: {
any: arg => { mod.add(arg) }, any(arg) { mod.add(arg) },
code() { code() {
const [ path, file ] = [ ...arguments ]; const [ path, file ] = [ ...arguments ];
if (lastmod(file)) { if (lastmod(file)) {
@ -251,13 +262,13 @@ function initModule(mod, file, dir) {
console.log({ MISSING_CODE: path, file }); console.log({ MISSING_CODE: path, file });
} }
}, },
full: arg => { mod.add(fullpath(arg)) }, full(arg) { mod.add(fullpath(arg)) },
map: arg => { mod.add(fixedmap(arg)) }, map(arg) { mod.add(fixedmap(arg)) },
pre: arg => { mod.add(prepath(arg)) }, pre(arg) { mod.add(prepath(arg)) },
redir: redir, redir: redir,
remap: remap, remap: remap,
setup: fn => { setupFn = fn }, setup(fn) { setupFn = fn },
static: (root, pre) => { static(root, pre) {
mod.static(pre || "/", root); mod.static(pre || "/", root);
}, },
}, },
@ -271,22 +282,29 @@ function initModule(mod, file, dir) {
ws: { ws: {
register: mod.wss register: mod.wss
}, },
onload: (fn) => { inject(code, file, opt = {}) {
const path = mod.dir + '/' + dir + '/' + file;
try {
const body = fs.readFileSync(path);
// console.log({ inject: code, file, opt });
if (opt.first) {
append[code] = body.toString() + '\n' + append[code];
} else {
append[code] += body.toString() + '\n';
}
} catch (e) {
console.log({ missing_file: path, dir, mod });
}
},
onload(fn) {
load.push(fn); load.push(fn);
}, },
onexit: (fn) => { onexit(fn) {
mod.on.exit(fn); mod.on.exit(fn);
} }
}); });
} }
// prevent caching of specified modules
const cachever = {};
function promise(resolve, reject) {
return new Promise(resolve, reject);
}
function rval() { function rval() {
return (Math.round(Math.random()*0xffffffff)).toString(36); return (Math.round(Math.random()*0xffffffff)).toString(36);
} }
@ -299,14 +317,6 @@ function time() {
return Date.now(); return Date.now();
} }
function obj2string(o) {
return JSON.stringify(o);
}
function string2obj(s) {
return JSON.parse(s);
}
function handleSetup(req, res, next) { function handleSetup(req, res, next) {
if (setupFn) { if (setupFn) {
setupFn(req, res, next); setupFn(req, res, next);
@ -322,12 +332,16 @@ const productionMap = {
'/lib/kiri/run/engine.js' : '/lib/pack/kiri-eng.js', '/lib/kiri/run/engine.js' : '/lib/pack/kiri-eng.js',
'/lib/kiri/run/minion.js' : '/lib/pack/kiri-pool.js', '/lib/kiri/run/minion.js' : '/lib/pack/kiri-pool.js',
'/lib/kiri/run/worker.js' : '/lib/pack/kiri-work.js', '/lib/kiri/run/worker.js' : '/lib/pack/kiri-work.js',
// '/lib/kiri/run/frame.js' : '/lib/pack/kiri-frame.js',
}; };
const redirList = [
"/kiri/",
"/mesh/"
];
function handleVersion(req, res, next) { function handleVersion(req, res, next) {
let vstr = oversion || dversion || version; let vstr = oversion || dversion || version;
if (["/kiri/","/mesh/"].indexOf(req.app.path) >= 0 && req.url.indexOf(vstr) < 0) { if (!req.app.ispre && redirList.indexOf(req.app.path) === 0 && req.url.indexOf(vstr) < 0) {
if (req.url.indexOf("?") > 0) { if (req.url.indexOf("?") > 0) {
return http.redirect(res, `${req.url},ver:${vstr}`); return http.redirect(res, `${req.url},ver:${vstr}`);
} else { } else {
@ -550,7 +564,8 @@ function rewriteHtmlVersion(req, res, next) {
"/lib/main/mesh.js" "/lib/main/mesh.js"
].indexOf(req.app.path) >= 0) { ].indexOf(req.app.path) >= 0) {
addCorsHeaders(req, res); addCorsHeaders(req, res);
const data = append[req.app.path.split('/')[3].split('.')[0]]; // 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 (!data) return next();
if (debug) logger.log({ append: req.app.path, data: data.length }); if (debug) logger.log({ append: req.app.path, data: data.length });

View file

@ -0,0 +1,7 @@
import * as JSZip from "../node_modules/jszip/dist/jszip.js";
export default JSZip;
export {
JSZip
};

38
bin/webpack-jszip-esm.js Normal file
View file

@ -0,0 +1,38 @@
const path = require('path');
module.exports = {
mode: 'production',
entry: path.resolve(__dirname, './webpack-jszip-bundle.js'),
output: {
path: path.resolve(__dirname, '../src/ext'),
filename: 'jszip-esm.js',
library: {
type: 'module'
},
module: true
},
experiments: {
outputModule: true
},
resolve: {
extensions: ['.mjs', '.js'],
},
module: {
rules: [
{
test: /\.m?js$/,
resolve: {
fullySpecified: false,
},
},
],
},
optimization: {
minimize: false
},
performance: {
hints: false,
maxAssetSize: 2 * 1024 * 1024,
maxEntrypointSize: 2 * 1024 * 1024,
},
};

View file

@ -2,8 +2,6 @@
## `C` cosmetic, `F` functional, `P` performance, `B` bug fix ## `C` cosmetic, `F` functional, `P` performance, `B` bug fix
* `F` experiment with structuredClone(), SharedArrayBuffer, createImageBitmap()
* `F` test #private fields, array.at() for negative indices
* `F` option to hide platform when viewing from below * `F` option to hide platform when viewing from below
# Kiri:Moto # Kiri:Moto
@ -14,7 +12,6 @@
* `P` refactor vertex replacement and widget update matrix tracking * `P` refactor vertex replacement and widget update matrix tracking
* `P` duplicate objects should share same slice data unless rotated or scaled * `P` duplicate objects should share same slice data unless rotated or scaled
* `P` allow selection to be decimated on demand (context menu?)
* `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
@ -26,7 +23,7 @@
* `F` complete and expose grouping feature * `F` complete and expose grouping feature
* `F` add svgnest-like arrange algorithm * `F` add svgnest-like arrange algorithm
* `F` date column and sorting in recent files list * `F` date column and sorting in recent files list
* `F` warn if part hanging in negative Z space or off bed in general * `F` highlight part outside workspace bounds (like can neg Z shader)
# FDM # FDM
@ -34,6 +31,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` better thin-wall (like arachne)
* `F` add scarf seams * `F` add scarf seams
* `F` add 'random' and 'seam' start point options * `F` add 'random' and 'seam' start point options
* `F` convert ranges to z offsets while continuing to show layer # * `F` convert ranges to z offsets while continuing to show layer #
@ -52,12 +50,6 @@
* `P` extruder + filament max flow rate cap in planner * `P` extruder + filament max flow rate cap in planner
* `P` solid fill the tops of supports for down facing flats * `P` solid fill the tops of supports for down facing flats
# FDM - SLA
# `P` common support area detection (fork new fdm code)
* `P` prioritize supports by length of unsupported span. mandatory when circularity > X
* or % area of inner to outer poly is high (making it a thin shell)
# FDM - BELT # FDM - BELT
* `B` auto bed resizing leaves the origin in the wrong place at first * `B` auto bed resizing leaves the origin in the wrong place at first
@ -69,7 +61,6 @@
# CAM # CAM
* `B` roughing leave stock should also increase Z slightly (sqrt(2) * leave)
* `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` tab cuts cause loss of direction, parenting, depth, and it's hard to fix
@ -84,18 +75,14 @@
* `F` add `match faces` option in `outline` operation * `F` add `match faces` option in `outline` operation
* `F` add {progress} substitution and maybe {time-remaining} if can be calc'd * `F` add {progress} substitution and maybe {time-remaining} if can be calc'd
* `F` import and follow 2D paths (conformed like pocket contours) * `F` import and follow 2D paths (conformed like pocket contours)
* `F` add user-defined origin (issue #28)
* `F` intelligently turn circle hole pocket clear into spiral down
* `F` add `plunge max` to contouring that can override z feed limit * `F` add `plunge max` to contouring that can override z feed limit
* `F` add lead-in milling (requires adding clamp / no go areas) * `F` add lead-in milling (requires adding clamp / no go areas)
* `F` add linear clearing strategy * `F` add linear clearing strategy
* `F` add adaptive clearing strategy * `F` add adaptive clearing strategy
* `F` add ease-down support to trace op
* `F` add support for tapered ball mills * `F` add support for tapered ball mills
* `F` change color of line selection in trace op when not a closed poly * `F` change color of line selection in trace op when not a closed poly
* `F` limit cut depth to flute length of selected tool (or warn) * `F` limit cut depth to flute length of selected tool (or warn)
* `F` validate muti-part layout and spacing exceeds largest outside tool diameter * `F` validate muti-part layout and spacing exceeds largest outside tool diameter
* `F` parameterize dropping close points in prep.js. ensure long segments remain straight
* `F` trapezoidal tabs in Z * `F` trapezoidal tabs in Z
* `F` ease-in and ease-out especially on tab cut-out start/stop * `F` ease-in and ease-out especially on tab cut-out start/stop
* `F` maintain several part orientations + op chains in a single profile * `F` maintain several part orientations + op chains in a single profile
@ -104,14 +91,8 @@
* `P` outer corner arc moves * `P` outer corner arc moves
* `P` log Z interpolation for contour XYZ moves * `P` log Z interpolation for contour XYZ moves
* `P` option to start with the smallest poly by area on layer change * `P` option to start with the smallest poly by area on layer change
* `P` refactor slicing engine to add minion threading
* `P` port arc code from FDM export to CAM export
* `P` redo all path route / planning in prepare to account for terrain before camOut * `P` redo all path route / planning in prepare to account for terrain before camOut
# SLA
* `P` blit layers w/ 3JS ortho camera to canvas
# Laser # Laser
* `F` add PLT / HP-GL output format (https://en.wikipedia.org/wiki/HP-GL) * `F` add PLT / HP-GL output format (https://en.wikipedia.org/wiki/HP-GL)
@ -124,8 +105,9 @@
# Mesh:Tool # Mesh:Tool
* font to path with https://github.com/paulzi/svg-text-to-path
* add undo/redo * add undo/redo
* add TextGeometry
* font to path with https://github.com/paulzi/svg-text-to-path
* click to repair normals based on a known good (selected) face * click to repair normals based on a known good (selected) face
* send to Kiri:Moto workspace (or update model vertices in place) * send to Kiri:Moto workspace (or update model vertices in place)
* better z snap using just vertexes from face intersected * better z snap using just vertexes from face intersected
@ -134,6 +116,5 @@
* add flatten/crush op: for z bottoms (or surfaces?) * add flatten/crush op: for z bottoms (or surfaces?)
* allow setting model/group origin for scale/rotate * allow setting model/group origin for scale/rotate
* fix mirror to work with groups (just models currently) * fix mirror to work with groups (just models currently)
* bounding box toggle should be global, not selection
* add analyze results dialog * add analyze results dialog
* remove/hide auto-repair function * remove/hide auto-repair function

View file

@ -36,7 +36,7 @@
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^6.1.1", "@fortawesome/fontawesome-free": "^6.1.1",
"@gridspace/app-server": "^0.0.17", "@gridspace/app-server": "^0.0.18",
"@gridspace/basic-ftp": "^5.0.5", "@gridspace/basic-ftp": "^5.0.5",
"@gridspace/net-level-client": "^0.2.3", "@gridspace/net-level-client": "^0.2.3",
"@tracespace/parser": "^5.0.0-next.0", "@tracespace/parser": "^5.0.0-next.0",
@ -49,9 +49,11 @@
"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",
"i": "^0.3.7",
"jszip": "^3.7.1", "jszip": "^3.7.1",
"manifold-3d": "^3.1.1", "manifold-3d": "^3.1.1",
"moment": "^2.29.4", "moment": "^2.29.4",
"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": "^1.14.1",
@ -79,7 +81,7 @@
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"
}, },
"scripts": { "scripts": {
"setup": "npm i && npm run clean && npm run dev -- --dryrun && npm run webpack3", "setup": "npm i && npm run clean && npm run dev -- --dryrun && npm run webpack-ext",
"build": "npm run prebuild && electron-builder", "build": "npm run prebuild && electron-builder",
"build-nopublish": "npm run prebuild && electron-builder --publish never", "build-nopublish": "npm run prebuild && electron-builder --publish never",
"build-debug": "npm run prebuild && DEBUG=electron-builder electron-builder", "build-debug": "npm run prebuild && DEBUG=electron-builder electron-builder",
@ -94,8 +96,9 @@
"docs-build": "docusaurus build --config conf/docusaurus.config.js", "docs-build": "docusaurus build --config conf/docusaurus.config.js",
"docs-serve": "docusaurus serve --config conf/docusaurus.config.js", "docs-serve": "docusaurus serve --config conf/docusaurus.config.js",
"docs-check": "prettier --config ./conf/prettier.config.js ./docs --check", "docs-check": "prettier --config ./conf/prettier.config.js ./docs --check",
"dev": "npm run webpack3 && gs-app-server --debug --single", "dev": "npm run webpack-ext && gs-app-server --debug --single",
"prod": "npm run webpack prod && gs-app-server", "prod": "npm run webpack-ext && npm run webpack-src prod && gs-app-server",
"prod-pack": "npm run webpack-ext && npm run webpack-src prod",
"prod-dryrun": "gs-app-server --dryrun", "prod-dryrun": "gs-app-server --dryrun",
"start": "npm run prebuild prod && electron .", "start": "npm run prebuild prod && electron .",
"start-dev": "npm run prebuild prod && electron . --devel", "start-dev": "npm run prebuild prod && electron . --devel",
@ -103,11 +106,13 @@
"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", "mk-links": "find src web -type l | xargs -I{} sh -c 'echo \"{},$(readlink {})\"' > conf/links.csv",
"mac-verify": "spctl --assess -vv --type install dist/*/*.app", "mac-verify": "spctl --assess -vv --type install dist/*/*.app",
"prebuild": "npm run setup && npm run webpack prod && node bin/electron-pre.js", "prebuild": "npm run setup && npm run webpack-src prod && node bin/electron-pre.js",
"postbuild": "node bin/electron-post.js", "postbuild": "node bin/electron-post.js",
"preinstall": "node bin/install-pre.js", "preinstall": "node bin/install-pre.js",
"webpack": "npm run webpack3 && node bin/esbuild.config.mjs", "webpack-ext": "npm run webpack-three && npm run webpack-zip",
"webpack3": "npx webpack --config bin/webpack-three-esm.js" "webpack-src": "node bin/esbuild.config.mjs",
"webpack-three": "npx webpack --config bin/webpack-three-esm.js",
"webpack-zip": "npx webpack --config bin/webpack-jszip-esm.js"
}, },
"main": "app-el.js", "main": "app-el.js",
"build": { "build": {

View file

@ -1,7 +0,0 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
//import original library
import * as JSZip from "./jszip.js";
//export object attached to the window object
export default self.JSZip;

View file

@ -117,7 +117,7 @@ function indexVertices(pos) {
let Instance; let Instance;
manifold({ manifold({
locateFile() { return "/wasm/manifold.wasm" } locateFile() { return "../wasm/manifold.wasm" }
}).then(inst => { }).then(inst => {
inst.setup(); inst.setup();
Instance = inst; Instance = inst;

View file

@ -843,6 +843,7 @@ export class Polygon {
* scale polygon around origin * scale polygon around origin
*/ */
scale(scale, round) { scale(scale, round) {
this.area2 = undefined;
let x, y, z; let x, y, z;
if (typeof(scale) === 'number') { if (typeof(scale) === 'number') {
x = y = z = scale; x = y = z = scale;
@ -1073,6 +1074,7 @@ export class Polygon {
* append point to polygon and return point * append point to polygon and return point
*/ */
push(p) { push(p) {
this.area2 = undefined;
// clone any point belonging to another polygon // clone any point belonging to another polygon
if (p.poly) p = p.clone(); if (p.poly) p = p.clone();
p.poly = this; p.poly = this;
@ -1117,6 +1119,14 @@ export class Polygon {
return this.first().isEqual(this.last()); return this.first().isEqual(this.last());
} }
fixClosed() {
if (this.appearsClosed()) {
this.points.pop();
this.open = false;
}
return this;
}
setClockwise() { setClockwise() {
if (!this.isClockwise()) this.reverse(); if (!this.isClockwise()) this.reverse();
return this; return this;
@ -1552,7 +1562,12 @@ export class Polygon {
* @returns {number} 0.0 - 1.0 from flat to perfectly circular * @returns {number} 0.0 - 1.0 from flat to perfectly circular
*/ */
circularity() { circularity() {
try {
return (4 * Math.PI * this.area()) / util.sqr(this.perimeter()); return (4 * Math.PI * this.area()) / util.sqr(this.perimeter());
} catch (e) {
console.log(this.perimeter(), e);
return 0;
}
} }
circularityDeep() { circularityDeep() {

View file

@ -359,7 +359,7 @@ export function subtract(setA, setB, outA, outB, z, minArea, opt = {}) {
function filter(from, to = []) { function filter(from, to = []) {
from.forEach(function(poly) { from.forEach(function(poly) {
if (poly.area() >= min) { if (poly.area() >= min && poly.length > 2) {
to.push(poly); to.push(poly);
out.push(poly); out.push(poly);
} }

View file

@ -1,9 +1,9 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */ /** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { base } from '../geo/base.js'; import { base } from '../geo/base.js';
import { newPolygon } from './polygon.js';
const { config } = base; const { config } = base;
const factor = config.clipper; const factor = config.clipper;
export const wasm_ctrl = { export const wasm_ctrl = {
@ -54,7 +54,7 @@ function writePoly(view, poly, inner) {
function readPoly(view, z) { function readPoly(view, z) {
let points = view.readU16(true); let points = view.readU16(true);
if (points === 0) return; if (points === 0) return;
let poly = base.newPolygon(); let poly = newPolygon();
while (points-- > 0) { while (points-- > 0) {
poly.add(view.readI32(true)/factor, view.readI32(true)/factor, z || 0); poly.add(view.readI32(true)/factor, view.readI32(true)/factor, z || 0);
} }

View file

@ -6,7 +6,7 @@ import STACKS from './stacks.js';
import { broker } from '../../moto/broker.js'; import { broker } from '../../moto/broker.js';
import { client as work } from './client.js'; import { client as work } from './client.js';
import { consts, COLOR as color, LISTS as lists, MODES, VIEWS, PATHS } 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, probe, process, show, space, util, view } from './main.js';

View file

@ -5,6 +5,10 @@ const boxes = [];
let lastBox; let lastBox;
export function clearboxes() {
boxes.length = 0;
}
export function getlastbox() { export function getlastbox() {
return lastBox; return lastBox;
} }
@ -19,7 +23,7 @@ export function delbox(name) {
export function addbox(point, color, name, dim = {x:1,y:1,z:1,rz:0}, opt = {}) { export function addbox(point, color, name, dim = {x:1,y:1,z:1,rz:0}, opt = {}) {
delbox(name); delbox(name);
const box = boxes[name] = new Mesh( const box = new Mesh(
new BoxGeometry(dim.x, dim.y, dim.z), new BoxGeometry(dim.x, dim.y, dim.z),
new MeshPhongMaterial({ new MeshPhongMaterial({
transparent: true, transparent: true,
@ -28,6 +32,10 @@ export function addbox(point, color, name, dim = {x:1,y:1,z:1,rz:0}, opt = {}) {
}) })
); );
if (name) {
boxes[name] = box;
}
box.position.x = point.x; box.position.x = point.x;
box.position.y = point.y; box.position.y = point.y;
box.position.z = point.z; box.position.z = point.z;

View file

@ -2,6 +2,9 @@
import { api } from './api.js'; import { api } from './api.js';
import { noop } from './utils.js'; import { noop } from './utils.js';
import { codec } from './codec.js';
import { Widget } from './widget.js';
import { newPrint } from './print.js';
// this code runs in kiri's main loop // this code runs in kiri's main loop
let debug = self.debug === true, let debug = self.debug === true,
@ -199,7 +202,7 @@ export const client = {
send("rotate", { settings }, reply => { send("rotate", { settings }, reply => {
if (reply.group) { if (reply.group) {
// collect post-rotation data for slice/preview renders // collect post-rotation data for slice/preview renders
for (let widget of kiri.Widget.Groups.forid(reply.group)) { for (let widget of Widget.Groups.forid(reply.group)) {
widget.belt = reply.belt; widget.belt = reply.belt;
} }
} else if (callback) { } else if (callback) {
@ -280,10 +283,10 @@ export const client = {
y: -origin.y, y: -origin.y,
z: origin.z z: origin.z
}; };
const output = kiri.newPrint().parseSVG(code, offset); const output = newPrint().parseSVG(code, offset);
send("parse_svg", output, reply => { send("parse_svg", output, reply => {
if (reply.parsed) { if (reply.parsed) {
done(kiri.codec.decode(reply.parsed)); done(codec.decode(reply.parsed));
} }
}); });
return; return;
@ -293,7 +296,7 @@ export const client = {
progress(reply.progress); progress(reply.progress);
} }
if (reply.parsed) { if (reply.parsed) {
done(kiri.codec.decode(reply.parsed), reply.maxSpeed, reply.minSpeed); done(codec.decode(reply.parsed), reply.maxSpeed, reply.minSpeed);
} }
}); });
}, },

View file

@ -581,7 +581,8 @@ export const conf = {
outputInvertY: false, outputInvertY: false,
camExpertFast: false, camExpertFast: false,
camTrueShadow: false, camTrueShadow: false,
camArcTolerance: 0.006, camArcEnabled: false,
camArcTolerance: 0.15,
camArcResolution: 5, camArcResolution: 5,
camForceZMax: false, camForceZMax: false,
camFirstZMax: false, camFirstZMax: false,

View file

@ -147,8 +147,8 @@ const PMODES = {
}; };
const PATHS = { const PATHS = {
work: "/lib/kiri/run/worker.js", work: "../lib/kiri/run/worker.js",
pool: "/lib/kiri/run/minion.js" pool: "./minion.js"
}; };
const SEED = 'kiri-seed'; const SEED = 'kiri-seed';

View file

@ -133,7 +133,7 @@ function prepareSlices(callback, scale = 1, offset = 0) {
if (msg && msg !== lastMsg) { if (msg && msg !== lastMsg) {
let mark = Date.now(); let mark = Date.now();
if (lastMsg) { if (lastMsg) {
let key = widgets.length > 1 ? let key = slicing.length > 1 ?
`${widget.id}_${segNumber++}_${lastMsg}` : `${widget.id}_${segNumber++}_${lastMsg}` :
`${segNumber++}_${lastMsg}` `${segNumber++}_${lastMsg}`
segtimes[key] = mark - startTime; segtimes[key] = mark - startTime;
@ -158,7 +158,10 @@ function prepareSlices(callback, scale = 1, offset = 0) {
if (sliced) { if (sliced) {
// update segment time // update segment time
if (lastMsg) { if (lastMsg) {
segtimes[`${widget.id}_${segNumber++}_${lastMsg}`] = mark - startTime; let key = slicing.length > 1 ?
`${widget.id}_${segNumber++}_${lastMsg}` :
`${segNumber++}_${lastMsg}`
segtimes[`${key}`] = mark - startTime;
} }
event.emit('slice', settings.mode); event.emit('slice', settings.mode);
} }
@ -237,7 +240,10 @@ function prepareSlices(callback, scale = 1, offset = 0) {
}; };
for (let widget of slicing) { for (let widget of slicing) {
// on done // on done
segtimes[`${widget.id}_${segNumber++}_draw`] = widget.render(widget.stack); let key = slicing.length > 1 ?
`${widget.id}_${segNumber++}_draw` :
`${segNumber++}_draw`
segtimes[`${key}`] = widget.render(widget.stack);
// rotate stack for belt beds // rotate stack for belt beds
if (widget.belt) { if (widget.belt) {
widget.stack.obj.rotate(widget.belt); widget.stack.obj.rotate(widget.belt);
@ -452,8 +458,7 @@ function cancelWorker() {
} }
function parseCode(code, type) { function parseCode(code, type) {
const { conf, event, show, widgets, view } = api; const { conf, event, show, stacks, widgets, view } = api;
const { stacks } = kiri;
const settings = conf.get(); const settings = conf.get();
event.emit("code.load", {code, type}); event.emit("code.load", {code, type});

View file

@ -16,6 +16,10 @@ import { init as initLaser } from '../mode/laser/client.js';
import { init as initSLA } from '../mode/sla/client.js'; import { init as initSLA } from '../mode/sla/client.js';
import { init as initWEDM } from '../mode/wedm/client.js'; import { init as initWEDM } from '../mode/wedm/client.js';
import { init as initWJET } from '../mode/wjet/client.js'; import { init as initWJET } from '../mode/wjet/client.js';
import { menu as menuCAM } from '../mode/cam/init-menu.js';
import { menu as menuFDM } from '../mode/fdm/init-menu.js';
import { menu as menuLaser } from '../mode/laser/init-menu.js';
import { menu as menuSLA } from '../mode/sla/init-menu.js';
let { CAM, SLA, FDM, LASER, DRAG, WJET, WEDM } = MODES, let { CAM, SLA, FDM, LASER, DRAG, WJET, WEDM } = MODES,
{ client, catalog, platform, selection, stats } = api, { client, catalog, platform, selection, stats } = api,
@ -90,15 +94,6 @@ function lineTypeSave() {
} }
} }
function thinWallSave() {
let opt = ui.sliceDetectThin;
let level = opt.options[opt.selectedIndex];
if (level) {
settings().process.sliceDetectThin = level.value;
api.conf.save();
}
}
function detailSave() { function detailSave() {
let level = ui.detail.options[ui.detail.selectedIndex]; let level = ui.detail.options[ui.detail.selectedIndex];
if (level) { if (level) {
@ -123,11 +118,6 @@ function speedSave() {
api.view.update_speeds(); api.view.update_speeds();
} }
function zAnchorSave() {
api.conf.update();
api.platform.update_top_z();
}
function setThreaded(bool) { function setThreaded(bool) {
if (bool) { if (bool) {
client.pool.start(); client.pool.start();
@ -234,6 +224,8 @@ function onBooleanClick(el) {
api.devices.update_laser_state(); api.devices.update_laser_state();
} }
api.event.on('click.boolean', onBooleanClick);
function onButtonClick(ev) { function onButtonClick(ev) {
let target = ev.target; let target = ev.target;
while (target && target.tagName !== 'BUTTON') { while (target && target.tagName !== 'BUTTON') {
@ -242,6 +234,8 @@ function onButtonClick(ev) {
api.event.emit("button.click", target); api.event.emit("button.click", target);
} }
api.event.on('click.button', onButtonClick);
function inputHasFocus() { function inputHasFocus() {
let active = DOC.activeElement; let active = DOC.activeElement;
return active && (active.nodeName === "INPUT" || active.nodeName === "TEXTAREA"); return active && (active.nodeName === "INPUT" || active.nodeName === "TEXTAREA");
@ -769,11 +763,6 @@ function updateCatalog(files) {
} }
} }
function isMultiHead() {
let dev = api.conf.get().device;
return isNotBelt() && dev.extruders && dev.extruders.length > 1;
}
function isBelt() { function isBelt() {
return api.device.isBelt(); return api.device.isBelt();
} }
@ -845,7 +834,6 @@ function init_one() {
// api augmentation with local functions // api augmentation with local functions
api.device.export = deviceExport; api.device.export = deviceExport;
let anim = ui.anim = {};
Object.assign(ui, { Object.assign(ui, {
tracker: tracker, tracker: tracker,
@ -982,33 +970,6 @@ function init_one() {
stockDepth: $('stock-width'), stockDepth: $('stock-width'),
stockHeight: $('stock-width'), stockHeight: $('stock-width'),
/** CAM Animation Bar */
_____: {
_____: newDiv({ addto: $('layer-animate'), set:true }),
row: newRow([
anim.replay = newButton(null,"anim.replay",{icon:'<i class="fas fa-fast-backward"></i>',title:"restart"}),
anim.play = newButton(null,"anim.play",{icon:'<i class="fas fa-play"></i>',title:"play"}),
anim.pause = newButton(null,"anim.pause",{icon:'<i class="fas fa-pause"></i>',title:"pause"}),
anim.step = newButton(null,"anim.step",{icon:'<i class="fas fa-step-forward"></i>',title:"single step"}),
anim.speed = newButton(null,"anim.fast",{icon:'<i class="fas fa-forward"></i>',title:"toggle speed"}),
anim.labspd = newValue(3, {class:"center padleft"}),
anim.labx = newLabel("X", {class:"padleft"}),
anim.valx = newValue(7, {class:"center"}),
anim.laby = newLabel("Y", {class:"padleft"}),
anim.valy = newValue(7, {class:"center"}),
anim.labz = newLabel("Z", {class:"padleft"}),
anim.valz = newValue(6, {class:"center"}),
anim.laba = newLabel("A", {class:"padleft hide"}),
anim.vala = newValue(6, {class:"center hide"}),
anim.labpro = newLabel("%", {class:"padleft"}),
anim.progress = newValue(5, {class:"center"}),
anim.trans = newButton(null,"anim.trans",{icon:'<i class="fa-solid fa-border-none"></i>',title:"transparency",class:"padleft"}),
anim.model = newButton(null,"anim.model",{icon:'<i class="fa-solid fa-eye"></i>',title:"show model"}),
anim.shade = newButton(null,"anim.stock",{icon:'<i class="fa-solid fa-cube"></i>',title:"stock box"}),
])
},
/** Device Browser / Editor */ /** Device Browser / Editor */
_____: newDiv({ class: "f-col t-body t-inset", addto: $('dev-config'), set:true }), _____: newDiv({ class: "f-col t-body t-inset", addto: $('dev-config'), set:true }),
@ -1110,255 +1071,16 @@ function init_one() {
prefadd: uc.checkpoint($('prefs-add')), prefadd: uc.checkpoint($('prefs-add')),
/** FDM Settings */ /** FDM Settings */
...menuFDM(),
_____: newGroup(LANG.sl_menu, $('fdm-layers'), { modes:FDM, driven, hideable, separator, group:"fdm-layers" }),
sliceHeight: newInput(LANG.sl_lahi_s, { title:LANG.sl_lahi_l, convert:toFloat }),
sliceMinHeight: newInput(LANG.ad_minl_s, { title:LANG.ad_minl_l, convert:toFloat, bound:bound(0,3.0), show:() => ui.sliceAdaptive.checked }),
sliceTopLayers: newInput(LANG.sl_ltop_s, { title:LANG.sl_ltop_l, convert:toInt }),
sliceBottomLayers: newInput(LANG.sl_lbot_s, { title:LANG.sl_lbot_l, convert:toInt }),
separator: newBlank({ class:"set-sep", driven }),
sliceAdaptive: newBoolean(LANG.ad_adap_s, onBooleanClick, { title: LANG.ad_adap_l }),
_____: 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 }),
sliceLineWidth: newInput(LANG.sl_line_s, { title:LANG.sl_line_l, convert:toFloat, bound:bound(0,5) }),
separator: newBlank({ class:"set-sep", driven }),
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"),
outputAlternating: newBoolean(LANG.ad_altr_s, onBooleanClick, {title:LANG.ad_altr_l}),
sliceZInterleave: newBoolean(LANG.ad_zint_s, onBooleanClick, {title:LANG.ad_zint_l, show:zIntShow}),
_____: newGroup(LANG.fi_menu, $('fdm-fill'), { modes:FDM, driven, hideable, separator, group:"fdm-fill" }),
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}),
sliceFillRepeat: newInput(LANG.fi_rept_s, {title:LANG.fi_rept_l, convert:toInt, bound:bound(1,10), show:fillIsLinear}),
sliceFillOverlap: newInput(LANG.fi_over_s, {title:LANG.fi_over_l, convert:toFloat, bound:bound(0.0,2.0), show:hasInfill}),
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)}),
separator: newBlank({ class:"set-sep", driven }),
sliceFillGrow: newInput(LANG.fi_grow_s, {title:LANG.fi_grow_l, convert:toFloat}),
sliceFillAngle: newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:toFloat}),
_____: 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}),
outputBedTemp: newInput(LANG.ou_bedd_s, {title:LANG.ou_bedd_l, convert:toInt}),
_____: 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) }),
outputFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255)}),
_____: newGroup(LANG.sp_menu, $('fdm-support'), { modes:FDM, driven, hideable, separator, group:"fdm-supp" }),
sliceSupportNozzle: newSelect(LANG.sp_nozl_s, {title:LANG.sp_nozl_l, show:isMultiHead}, "extruders"),
sliceSupportDensity: newInput(LANG.sp_dens_s, {title:LANG.sp_dens_l, convert:toFloat, bound:bound(0.0,1.0)}),
sliceSupportSize: newInput(LANG.sp_size_s, {title:LANG.sp_size_l, convert:toFloat, bound:bound(1.0,200.0)}),
sliceSupportOffset: newInput(LANG.sp_offs_s, {title:LANG.sp_offs_l, convert:toFloat, bound:bound(0.0,200.0)}),
sliceSupportGap: newInput(LANG.sp_gaps_s, {title:LANG.sp_gaps_l, convert:toInt, bound:bound(0,5)}),
sliceSupportArea: newInput(LANG.sp_area_s, {title:LANG.sp_area_l, convert:toFloat, bound:bound(0.0,200.0)}),
sliceSupportExtra: newInput(LANG.sp_xpnd_s, {title:LANG.sp_xpnd_l, convert:toFloat, bound:bound(0.0,10.0)}),
sliceSupportGrow: newInput(LANG.sp_grow_s, {title:LANG.sp_grow_l, convert:toFloat, bound:bound(0.0,10.0)}),
sliceSupportAngle: newInput(LANG.sp_angl_s, {title:LANG.sp_angl_l, convert:toFloat, bound:bound(0.0,90.0)}),
sliceSupportSpan: newInput(LANG.sp_span_s, {title:LANG.sp_span_l, convert:toFloat, bound:bound(0.0,200.0), show:() => ui.sliceSupportEnable.checked }),
separator: newBlank({ class:"set-sep", driven }),
sliceSupportEnable: newBoolean(LANG.sp_auto_s, onBooleanClick, {title:LANG.sp_auto_l, show:isNotBelt}),
sliceSupportOutline: newBoolean(LANG.sp_outl_s, onBooleanClick, {title:LANG.sp_outl_l}),
separator: newBlank({ class:"set-sep", driven }),
sliceSupportGen: newRow([
ui.ssaGen = newButton(LANG.sp_detect, onButtonClick, {class: "f-col grow a-center"})
], { modes: FDM, class: "ext-buttons f-row grow" }),
separator: newBlank({ class:"set-sep", driven }),
sliceSupportManual: newRow([
(ui.ssmAdd = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
(ui.ssmDun = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
(ui.ssmClr = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
], {class:"ext-buttons f-row"}),
_____: newGroup(LANG.fl_menu, $('fdm-base'), { modes:FDM, driven, hideable, separator, group:"fdm-base" }),
firstSliceHeight: newInput(LANG.fl_lahi_s, {title:LANG.fl_lahi_l, convert:toFloat, show:isNotBelt}),
firstLayerNozzleTemp:newInput(LANG.fl_nozl_s, {title:LANG.fl_nozl_l, convert:toInt, show:isNotBelt}),
firstLayerBedTemp: newInput(LANG.fl_bedd_s, {title:LANG.fl_bedd_l, convert:toInt, show:isNotBelt}),
separator: newBlank({ class:"set-sep", driven }),
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}),
firstLayerFlatten: newInput(LANG.fl_flat_s, {title:LANG.fl_flat_l, convert:toFloat, show:isBelt}),
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}),
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 }),
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}),
firstLayerBrimTrig: newInput(LANG.fl_brmn_s, {title:LANG.fl_brmn_l, convert:toInt, show:isBelt}),
firstLayerBrimGap: newInput(LANG.fl_brgp_s, {title:LANG.fl_brgp_l, convert:toFloat, show:isBelt}),
separator: newBlank({ class:"set-sep", driven, show:isBelt }),
firstLayerBeltLead: newInput(LANG.fl_bled_s, {title:LANG.fl_bled_l, convert:toFloat, show:isBelt}),
firstLayerBeltBump: newInput(LANG.fl_blmp_s, {title:LANG.fl_blmp_l, convert:toFloat, bound:bound(0, 10), show:isBelt}),
separator: newBlank({ class:"set-sep", driven, show:isNotBelt }),
outputBrimCount: newInput(LANG.fl_skrt_s, {title:LANG.fl_skrt_l, convert:toInt, show:isNotBelt}),
outputBrimOffset: newInput(LANG.fl_skro_s, {title:LANG.fl_skro_l, convert:toFloat, show: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 }),
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" }),
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}),
outputSeekrate: newInput(LANG.ou_move_s, {title:LANG.ou_move_l, convert:toInt}),
separator: newBlank({ class:"set-sep", driven }),
outputShellMult: newInput(LANG.ou_shml_s, {title:LANG.ou_exml_l, convert:toFloat, bound:bound(0.0,2.0)}),
outputFillMult: newInput(LANG.ou_flml_s, {title:LANG.ou_exml_l, convert:toFloat, bound:bound(0.0,2.0)}),
outputSparseMult: newInput(LANG.ou_spml_s, {title:LANG.ou_exml_l, convert:toFloat, bound:bound(0.0,2.0)}),
separator: newBlank({ class:"set-sep", driven }),
outputRetractDist: newInput(LANG.ad_rdst_s, {title:LANG.ad_rdst_l, convert:toFloat}),
outputRetractSpeed: newInput(LANG.ad_rrat_s, {title:LANG.ad_rrat_l, convert:toInt}),
outputRetractWipe: newInput(LANG.ad_wpln_s, {title:LANG.ad_wpln_l, convert:toFloat, bound:bound(0.0,10)}),
separator: newBlank({ class:"set-sep", driven }),
sliceLayerStart: newSelect(LANG.sl_strt_s, {title:LANG.sl_strt_l}, "start"),
outputLayerRetract: newBoolean(LANG.ad_lret_s, onBooleanClick, {title:LANG.ad_lret_l}),
outputAvoidGaps: newBoolean(LANG.ad_agap_s, onBooleanClick, {title:LANG.ad_agap_l}),
separator: newBlank({ class:"set-sep", driven, show:isBelt }),
outputBeltFirst: newBoolean(LANG.ad_lbir_s, onBooleanClick, {title:LANG.ad_lbir_l, show:isBelt}),
_____: newGroup(LANG.ad_menu, $('fdm-expert'), { modes:FDM, driven, hideable, separator, group:"fdm-xprt" }),
sliceAngle: newInput(LANG.sl_angl_s, {title:LANG.sl_angl_l, convert:toFloat, show:isBelt}),
outputRetractDwell: newInput(LANG.ad_rdwl_s, {title:LANG.ad_rdwl_l, convert:toInt}),
sliceSolidMinArea: newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:toFloat}),
outputMinSpeed: newInput(LANG.ad_mins_s, {title:LANG.ad_mins_l, convert:toFloat, bound:bound(1,200)}),
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)}),
zHopDistance: newInput(LANG.ad_zhop_s, {title:LANG.ad_zhop_l, convert:toFloat, bound:bound(0,3.0)}),
arcTolerance: newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, convert:toFloat, bound:bound(0,1.0), show:() => { return isNotBelt() }}),
antiBacklash: newInput(LANG.ad_abkl_s, {title:LANG.ad_abkl_l, convert:toInt, bound:bound(0,3)}),
outputLoops: newInput(LANG.ag_loop_s, {title:LANG.ag_loop_l, convert:toInt, bound:bound(-1,1000), show:isBelt}),
outputPurgeTower: newInput(LANG.ad_purg_s, {title:LANG.ad_purg_l, convert:toInt, bound:bound(0,1000)}),
fdmRanges: $('fdm-ranges'),
/** CAM Settings */ /** CAM Settings */
...menuCAM(),
_____: newGroup(LANG.ct_menu, $('cam-tabs'), { modes:CAM, marker:true, driven, separator }),
camTabsWidth: newInput(LANG.ct_wdth_s, {title:LANG.ct_wdth_l, convert:toFloat, bound:bound(0.005,100), units}),
camTabsHeight: newInput(LANG.ct_hght_s, {title:LANG.ct_hght_l, convert:toFloat, bound:bound(0.005,100), units}),
camTabsDepth: newInput(LANG.ct_dpth_s, {title:LANG.ct_dpth_l, convert:toFloat, bound:bound(0.005,100), units}),
separator: newBlank({ class:"set-sep", driven }),
camTabsMidline: newBoolean(LANG.ct_midl_s, onBooleanClick, {title:LANG.ct_midl_l}),
separator: newBlank({ class:"set-sep", driven }),
camTabsManual: newRow([
(ui.tabAdd = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
(ui.tabDun = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
(ui.tabClr = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
], {class:"ext-buttons f-row"}),
_____: newGroup(LANG.cs_menu, $('cam-stock'), { modes:CAM, driven, separator }),
camStockX: newInput(LANG.cs_wdth_s, {title:LANG.cs_wdth_l, convert:toFloat, bound:bound(0,9999), units}),
camStockY: newInput(LANG.cs_dpth_s, {title:LANG.cs_dpth_l, convert:toFloat, bound:bound(0,9999), units}),
camStockZ: newInput(LANG.cs_hght_s, {title:LANG.cs_hght_l, convert:toFloat, bound:bound(0,9999), units}),
separator: newBlank({ class:"set-sep", driven }),
camStockOffset: newBoolean(LANG.cs_offs_s, onBooleanClick, {title:LANG.cs_offs_l}),
camStockClipTo: newBoolean(LANG.cs_clip_s, onBooleanClick, {title:LANG.cs_clip_l}),
camStockIndexed: newBoolean(LANG.cs_indx_s, onBooleanClick, {title:LANG.cs_indx_l}),
camStockIndexGrid: newBoolean(LANG.cs_ishg_s, onBooleanClick, {title:LANG.cs_ishg_l, show:() => ui.camStockIndexed.checked}),
_____: newGroup(LANG.cc_menu, $('cam-limits'), { modes:CAM, driven, separator }),
camZAnchor: newSelect(LANG.ou_zanc_s, {title: LANG.ou_zanc_l, action:zAnchorSave, show:() => !ui.camStockIndexed.checked}, "zanchor"),
camZOffset: newInput(LANG.ou_ztof_s, {title:LANG.ou_ztof_l, convert:toFloat, units}),
camZTop: newInput(LANG.ou_ztop_s, {title:LANG.ou_ztop_l, convert:toFloat, units, trigger}),
camZBottom: newInput(LANG.ou_zbot_s, {title:LANG.ou_zbot_l, convert:toFloat, units, trigger}),
camZThru: newInput(LANG.ou_ztru_s, {title:LANG.ou_ztru_l, convert:toFloat, bound:bound(0.0,100), units }),
camZClearance: newInput(LANG.ou_zclr_s, {title:LANG.ou_zclr_l, convert:toFloat, bound:bound(0.01,100), units }),
camFastFeedZ: newInput(LANG.cc_rzpd_s, {title:LANG.cc_rzpd_l, convert:toFloat, units}),
camFastFeed: newInput(LANG.cc_rapd_s, {title:LANG.cc_rapd_l, convert:toFloat, units}),
_____: newGroup(LANG.ou_menu, $('cam-output'), { modes:CAM, driven, separator, group:"cam-output" }),
camConventional: newBoolean(LANG.ou_conv_s, onBooleanClick, {title:LANG.ou_conv_l}),
camEaseDown: newBoolean(LANG.cr_ease_s, onBooleanClick, {title:LANG.cr_ease_l}),
camDepthFirst: newBoolean(LANG.ou_depf_s, onBooleanClick, {title:LANG.ou_depf_l}),
camToolInit: newBoolean(LANG.ou_toin_s, onBooleanClick, {title:LANG.ou_toin_l}),
separator: newBlank({ class:"set-sep", driven }),
camFirstZMax: newBoolean(LANG.ou_z1st_s, onBooleanClick, {title:LANG.ou_z1st_l}),
camForceZMax: newBoolean(LANG.ou_forz_s, onBooleanClick, {title:LANG.ou_forz_l}),
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}),
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 }),
camOriginTop: newBoolean(LANG.or_topp_s, onBooleanClick, {title:LANG.or_topp_l}),
camOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, {title:LANG.or_cntr_l}),
separator: newBlank({ class:"set-sep", driven }),
camOriginOffX: newInput(LANG.co_offx_s, {title:LANG.co_offx_l, convert:toFloat, units}),
camOriginOffY: newInput(LANG.co_offy_s, {title:LANG.co_offy_l, convert:toFloat, units}),
camOriginOffZ: newInput(LANG.co_offz_s, {title:LANG.co_offz_l, convert:toFloat, units}),
_____: newGroup(LANG.op_xprt_s, $('cam-expert'), { group:"cam_expert", modes:CAM, marker: false, driven, separator }),
camExpertFast: newBoolean(LANG.cx_fast_s, onBooleanClick, {title:LANG.cx_fast_l, show: () => !ui.camTrueShadow.checked }),
camTrueShadow: newBoolean(LANG.cx_true_s, onBooleanClick, {title:LANG.cx_true_l, show: () => !ui.camExpertFast.checked }),
separator: newBlank({ class:"set-sep", driven }),
camArcTolerance: newInput(LANG.cx_arct_s, {title:LANG.cx_arct_l, convert:toFloat, units, bound:bound(0,100)}),
camArcResolution: newInput(LANG.cx_arcr_s, {title:LANG.cx_arcr_l, convert:toFloat, bound:bound(0,180), show:() => ui.camArcTolerance.value > 0}),
/** LASER/DRAG/WJET/WEDM cut tool Settings */ /** LASER/DRAG/WJET/WEDM cut tool Settings */
...menuLaser(),
_____: newGroup(LANG.sl_menu, $('lzr-slice'), { modes:TWOD, driven, separator }),
ctSliceKerf: newInput(LANG.ls_offs_s, {title:LANG.ls_offs_l, convert:toFloat}),
ctSliceHeight: newInput(LANG.ls_lahi_s, {title:LANG.ls_lahi_l, convert:toFloat, trigger}),
ctSliceHeightMin: newInput(LANG.ls_lahm_s, {title:LANG.ls_lahm_l, convert:toFloat, show:() => ui.ctSliceHeight.value == 0 && !ui.ctSliceSingle.checked }),
separator: newBlank({ class:"set-sep", driven }),
ctSliceSingle: newBoolean(LANG.ls_sngl_s, onBooleanClick, {title:LANG.ls_sngl_l}),
ctOmitInner: newBoolean(LANG.we_omit_s, onBooleanClick, {title:LANG.we_omit_l, modes:WEDM}),
_____: newGroup('surfaces', $('lzr-surface'), { modes:[-1], driven, separator }),
ctSurfaces: newRow([
(ui.faceAdd = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
(ui.faceDun = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
(ui.faceClr = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
], {class:"ext-buttons f-row", modes:WEDM}),
_____: newGroup(LANG.dk_menu, $('lzr-knife'), { modes:DRAG, marker:true, driven, separator }),
ctOutKnifeDepth: newInput(LANG.dk_dpth_s, { title:LANG.dk_dpth_l, convert:toFloat, bound:bound(0.0,5.0) }),
ctOutKnifePasses: newInput(LANG.dk_pass_s, { title:LANG.dk_pass_l, convert:toInt, bound:bound(0,5) }),
ctOutKnifeTip: newInput(LANG.dk_offs_s, { title:LANG.dk_offs_l, convert:toFloat, bound:bound(0.0,10.0) }),
_____: newGroup(LANG.lo_menu, $('lzr-layout'), { modes:TWOD, driven, separator }),
ctOutTileSpacing: newInput(LANG.ou_spac_s, { title:LANG.ou_spac_l, convert:toInt }),
ctOutMerged: newBoolean(LANG.ou_mrgd_s, onBooleanClick, {title:LANG.ou_mrgd_l, modes:TWONED, show:() => !ui.ctOutStack.checked }),
ctOutGroup: newBoolean(LANG.ou_grpd_s, onBooleanClick, {title:LANG.ou_grpd_l, show:() => !(ui.ctOutMark.checked || ui.ctOutStack.checked) }),
_____: newGroup(LANG.ou_menu, $('lzr-output'), { modes:TWOD, driven, separator, group:"lzr-output" }),
ctOutPower: newInput(LANG.ou_powr_s, {title:LANG.ou_powr_l, convert:toInt, bound:bound(1,100), modes:TWONED }),
ctOutSpeed: newInput(LANG.ou_sped_s, {title:LANG.ou_sped_l, convert:toInt }),
ctAdaptive: newBoolean('adaptive speed', onBooleanClick, {modes:WEDM, title:'controller determines best cutting speed based on material feedback at runtime'}),
separator: newBlank({ class:"set-sep", driven }),
ctOriginBounds: newBoolean(LANG.or_bnds_s, onBooleanClick, { title:LANG.or_bnds_l, show:() => !ui.ctOriginCenter.checked }),
ctOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, { title:LANG.or_cntr_l, show:() => !ui.ctOriginBounds.checked }),
separator: newBlank({ class:"set-sep", driven, modes:WEDM, show:() => ui.ctOriginBounds.checked }),
ctOriginOffX: newInput(LANG.or_offx_s, { title:LANG.or_offx_l, convert:toFloat, modes:WEDM, show:() => ui.ctOriginBounds.checked }),
ctOriginOffY: newInput(LANG.or_offy_s, { title:LANG.or_offy_l, convert:toFloat, modes:WEDM, show:() => ui.ctOriginBounds.checked }),
separator: newBlank({ class:"set-sep", driven, modes:TWONED }),
ctOutZColor: newBoolean(LANG.ou_layo_s, onBooleanClick, { title:LANG.ou_layo_l, modes:TWONED, show:() => !ui.ctOutMerged.checked }),
ctOutLayer: newBoolean(LANG.ou_layr_s, onBooleanClick, { title:LANG.ou_layr_l, modes:TWONED, show:() => !ui.ctOutStack.checked }),
ctOutMark: newBoolean(LANG.ou_lays_s, onBooleanClick, { title:LANG.ou_lays_l, modes:TWONED, show:() => !ui.ctOutStack.checked }),
separator: newBlank({ class:"set-sep", driven, modes:LASER }),
ctOutInches: newBoolean(LANG.ou_inch_s, onBooleanClick, { title:LANG.ou_inch_l, modes:LASER }),
ctOutStack: newBoolean(LANG.ou_stak_s, onBooleanClick, { title:LANG.ou_stak_l, modes:LASER }),
ctOutShaper: newBoolean(LANG.ou_shap_s, onBooleanClick, { title:LANG.ou_shap_l, modes:LASER, show:() => ui.ctOutStack.checked }),
/** SLA SETTINGS */ /** SLA SETTINGS */
...menuSLA(),
slaProc: newGroup(LANG.sa_menu, $('sla-slice'), { modes:SLA, group:"sla-slice", driven, separator }),
slaSlice: newInput(LANG.sa_lahe_s, {title:LANG.sa_lahe_l, convert:toFloat}),
slaShell: newInput(LANG.sa_shel_s, {title:LANG.sa_shel_l, convert:toFloat}),
slaOpenTop: newBoolean(LANG.sa_otop_s, onBooleanClick, {title:LANG.sa_otop_l}),
slaOpenBase: newBoolean(LANG.sa_obas_s, onBooleanClick, {title:LANG.sa_obas_l}),
slaLayers: newGroup(LANG.sa_layr_m, $('sla-layers'), { modes:SLA, group:"sla-layers", driven, separator }),
slaLayerOn: newInput(LANG.sa_lton_s, {title:LANG.sa_lton_l, convert:toFloat}),
slaLayerOff: newInput(LANG.sa_ltof_s, {title:LANG.sa_ltof_l, convert:toFloat}),
slaPeelDist: newInput(LANG.sa_pldi_s, {title:LANG.sa_pldi_l, convert:toFloat}),
slaPeelLiftRate: newInput(LANG.sa_pllr_s, {title:LANG.sa_pllr_l, convert:toFloat}),
slaPeelDropRate: newInput(LANG.sa_pldr_s, {title:LANG.sa_pldr_l, convert:toFloat}),
slaBase: newGroup(LANG.sa_base_m, $('sla-base'), { modes:SLA, group:"sla-base", driven, separator }),
slaBaseLayers: newInput(LANG.sa_balc_s, {title:LANG.sa_balc_l, convert:toInt}),
slaBaseOn: newInput(LANG.sa_lton_s, {title:LANG.sa_bltn_l, convert:toFloat}),
slaBaseOff: newInput(LANG.sa_ltof_s, {title:LANG.sa_bltf_l, convert:toFloat}),
slaBasePeelDist: newInput(LANG.sa_pldi_s, {title:LANG.sa_pldi_l, convert:toFloat}),
slaBasePeelLiftRate: newInput(LANG.sa_pllr_s, {title:LANG.sa_pllr_l, convert:toFloat}),
slaFill: newGroup(LANG.sa_infl_m, $('sla-fill'), { modes:SLA, group:"sla-infill", driven, separator }),
slaFillDensity: newInput(LANG.sa_ifdn_s, {title:LANG.sa_ifdn_l, convert:toFloat, bound:bound(0,1)}),
slaFillLine: newInput(LANG.sa_iflw_s, {title:LANG.sa_iflw_l, convert:toFloat, bound:bound(0,5)}),
slaSupport: newGroup(LANG.sa_supp_m, $('sla-support'), { modes:SLA, group:"sla-support", driven, separator }),
slaSupportLayers: newInput(LANG.sa_slyr_s, {title:LANG.sa_slyr_l, convert:toInt, bound:bound(5,100)}),
slaSupportGap: newInput(LANG.sa_slgp_s, {title:LANG.sa_slgp_l, convert:toInt, bound:bound(3,30)}),
slaSupportDensity: newInput(LANG.sa_sldn_s, {title:LANG.sa_sldn_l, convert:toFloat, bound:bound(0.01,0.9)}),
slaSupportSize: newInput(LANG.sa_slsz_s, {title:LANG.sa_slsz_l, convert:toFloat, bound:bound(0.1,1)}),
slaSupportPoints: newInput(LANG.sa_slpt_s, {title:LANG.sa_slpt_l, convert:toInt, bound:bound(3,10)}),
slaSupportEnable: newBoolean(LANG.enable, onBooleanClick, {title:LANG.sl_slen_l}),
slaOutput: newGroup(LANG.sa_outp_m, $('sla-output'), { modes:SLA, driven, separator, group:"sla-output" }),
slaFirstOffset: newInput(LANG.sa_opzo_s, {title:LANG.sa_opzo_l, convert:toFloat, bound:bound(0,1)}),
slaAntiAlias: newSelect(LANG.sa_opaa_s, {title:LANG.sa_opaa_l}, "antialias"),
layers: uc.setGroup($("layers")), layers: uc.setGroup($("layers")),
@ -1367,6 +1089,8 @@ function init_one() {
}); });
// run client mode initializations right after UI is bound // run client mode initializations right after UI is bound
// MUST run after main ui menu inits since they rely on passed state
// that createPopOps disrupts (should be refactored)
initCAM(); initCAM();
initDRAG(); initDRAG();
initFDM(); initFDM();
@ -1380,27 +1104,6 @@ function init_one() {
settingsSave(undefined, ui.settingsName.value); settingsSave(undefined, ui.settingsName.value);
}; };
function optSelected(sel) {
let opt = sel.options[sel.selectedIndex];
return opt ? opt.value : undefined;
}
function hasInfill() {
return optSelected(ui.sliceFillType) !== 'none'
}
function fillIsLinear() {
return hasInfill() && optSelected(ui.sliceFillType) === 'linear';
}
function spindleShow() {
return settings().device.spindleMax > 0;
}
function zIntShow() {
return settings().controller.devel;
}
// slider setup // slider setup
const mobile = space.info.mob; const mobile = space.info.mob;
const slbar = mobile ? 80 : 30; const slbar = mobile ? 80 : 30;
@ -2099,8 +1802,12 @@ function init_two() {
sdb.kiri_beta = beta; sdb.kiri_beta = beta;
} }
// hide url params // hide url params but preserve version root (when present)
history.replaceState({}, '', '/kiri/'); let wlp = WIN.location.pathname;
let kio = wlp.indexOf('/kiri/');
if (kio >= 0) {
history.replaceState({}, '', wlp.substring(0,kio + 6));
}
// lift curtain // lift curtain
$('curtain').style.display = 'none'; $('curtain').style.display = 'none';
@ -2165,7 +1872,7 @@ function init_lang() {
} }
export function run() { export function run() {
init_lang();
client.start(); client.start();
init_lang();
return api; return api;
} }

View file

@ -613,10 +613,15 @@ class Print {
scope.belt = belt; scope.belt = belt;
if (scope.debugE) { if (scope.debugE) {
console.log({ bounds, print_time: time.round(2) }); console.log({
bounds,
minf,
maxf,
print_time: time.round(2),
output: scope.output
});
} }
console.log(scope.output)
done({ output: scope.output }); done({ output: scope.output });
} }
} }

View file

@ -9,6 +9,7 @@ import { space } from '../../moto/space.js';
import { local } from '../../data/local.js'; import { local } from '../../data/local.js';
import { utils } from './utils.js'; import { utils } from './utils.js';
import { version } from '../../moto/license.js'; import { version } from '../../moto/license.js';
import { JSZip } from '../../ext/jszip-esm.js';
const { areEqual, ls2o, js2o } = utils; const { areEqual, ls2o, js2o } = utils;
const { COLOR } = consts; const { COLOR } = consts;
@ -221,7 +222,7 @@ function updateSettingsFromFields(setrec, uirec = api.ui, changes) {
} }
// given a settings region, update values of matching bound UI fields // given a settings region, update values of matching bound UI fields
function updateFieldsFromSettings(setrec, uirec = api.ui, trace) { function updateFieldsFromSettings(setrec, uirec = api.ui, opt = {}) {
if (!setrec) { if (!setrec) {
updateFieldsFromSettings(settings.device); updateFieldsFromSettings(settings.device);
updateFieldsFromSettings(settings.process); updateFieldsFromSettings(settings.process);
@ -229,6 +230,9 @@ function updateFieldsFromSettings(setrec, uirec = api.ui, trace) {
updateExtruderFields(settings.device); updateExtruderFields(settings.device);
return; return;
} }
if (opt.trace) {
console.log({ setrec, uirec });
}
for (let key in setrec) { for (let key in setrec) {
if (!setrec.hasOwnProperty(key)) { if (!setrec.hasOwnProperty(key)) {
continue; continue;
@ -238,6 +242,9 @@ function updateFieldsFromSettings(setrec, uirec = api.ui, trace) {
continue; continue;
} }
let uie = uirec[key], typ = uie ? uie.type : null; let uie = uirec[key], typ = uie ? uie.type : null;
if (opt.trace) {
console.log({ key, uie, typ, val });
}
if (typ === 'text') { if (typ === 'text') {
if (uie.setv) { if (uie.setv) {
uie.setv(val); uie.setv(val);
@ -271,6 +278,8 @@ function updateFieldsFromSettings(setrec, uirec = api.ui, trace) {
} else { } else {
uie.value = ''; uie.value = '';
} }
} else if (opt.trace) {
console.log('skipped', { key, val, typ });
} }
} }
} }
@ -651,7 +660,7 @@ function settingsImportUrl(url, ask) {
function settingsImportZip(data, ask) { function settingsImportZip(data, ask) {
let alert = api.show.alert("Importing Workspace"); let alert = api.show.alert("Importing Workspace");
JSZip.loadAsync(data).then(zip => { new JSZip().loadAsync(data).then(zip => {
for (let [key,value] of Object.entries(zip.files)) { for (let [key,value] of Object.entries(zip.files)) {
if (key === "workspace.json") { if (key === "workspace.json") {
value.async("string").then(json => { value.async("string").then(json => {

View file

@ -434,8 +434,13 @@ function newDiv(opt = {}) {
(opt.addto || addTo).appendChild(div); (opt.addto || addTo).appendChild(div);
if (opt.addto) lastDiv = addTo = div; if (opt.addto) lastDiv = addTo = div;
if (opt.class) div.setAttribute('class', opt.class); if (opt.class) div.setAttribute('class', opt.class);
if (opt.group !== false) {
lastGroup?.push(div); lastGroup?.push(div);
div._group = groupName; div._group = groupName;
} else {
lastGroup = undefined;
groupName = undefined;
}
return div; return div;
} }
@ -685,6 +690,7 @@ function addUnits(input, round) {
} else { } else {
input.value = value; input.value = value;
} }
// console.log({ value }, input.real, input.value );
return input.real; return input.real;
}; };
return input; return input;

View file

@ -104,16 +104,16 @@
"sliceFillWidth": 1, "sliceFillWidth": 1,
"sliceFillRate": 0, "sliceFillRate": 0,
"sliceSupportEnable": false, "sliceSupportEnable": false,
"firstSliceHeight": 0.25, "firstSliceHeight": 0.5,
"firstLayerFillRate": 20, "firstLayerFillRate": 20,
"firstLayerLineMult": 1, "firstLayerLineMult": 1,
"firstLayerNozzleTemp": 0, "firstLayerNozzleTemp": 0,
"firstLayerBedTemp": 0, "firstLayerBedTemp": 0,
"firstLayerBrimTrig": 0, "firstLayerBrimTrig": 0,
"firstLayerBrimGap": 0, "firstLayerBrimGap": 0,
"outputRaft": true, "outputRaft": false,
"outputRaftSpacing": 0.2, "outputRaftSpacing": 0.2,
"outputBrimCount": 5, "outputBrimCount": 0,
"outputBrimOffset": 2, "outputBrimOffset": 2,
"outputPurgeTower": 0, "outputPurgeTower": 0,
"outputInvertX": false, "outputInvertX": false,

View file

@ -104,16 +104,16 @@
"sliceFillWidth": 1, "sliceFillWidth": 1,
"sliceFillRate": 0, "sliceFillRate": 0,
"sliceSupportEnable": false, "sliceSupportEnable": false,
"firstSliceHeight": 0.25, "firstSliceHeight": 0.5,
"firstLayerFillRate": 20, "firstLayerFillRate": 20,
"firstLayerLineMult": 1, "firstLayerLineMult": 1,
"firstLayerNozzleTemp": 0, "firstLayerNozzleTemp": 0,
"firstLayerBedTemp": 0, "firstLayerBedTemp": 0,
"firstLayerBrimTrig": 0, "firstLayerBrimTrig": 0,
"firstLayerBrimGap": 0, "firstLayerBrimGap": 0,
"outputRaft": true, "outputRaft": false,
"outputRaftSpacing": 0.2, "outputRaftSpacing": 0.2,
"outputBrimCount": 5, "outputBrimCount": 0,
"outputBrimOffset": 2, "outputBrimOffset": 2,
"outputPurgeTower": 0, "outputPurgeTower": 0,
"outputInvertX": false, "outputInvertX": false,

View file

@ -87,10 +87,10 @@ Object.assign(client, {
animate_setup2(settings, ondone) { animate_setup2(settings, ondone) {
color = settings.controller.dark ? 0x888888 : 0; color = settings.controller.dark ? 0x888888 : 0;
material = new THREE.MeshMatcapMaterial({ material = new THREE.MeshPhongMaterial({
flatShading: true, flatShading: true,
transparent: false, transparent: false,
opacity: 0.9, opacity: 0.5,
color: 0x888888, color: 0x888888,
side: THREE.DoubleSide side: THREE.DoubleSide
}); });

View file

@ -0,0 +1,140 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { api } from '../../core/api.js';
import { env, clearPops } from './client.js';
import { CAM } from './driver-fe.js';
import { addbox, clearboxes } from '../../core/boxes.js';
export let originSelectOn = false;
let { world } = api.SPACE;
let alert, lastHover, lastPoints;
export function originSelect() {
if (originSelectOn) {
return originSelectDone();
}
clearPops();
alert = api.show.alert("analyzing parts...", 1000);
originSelectOn = true;
api.feature.hover = true;
api.feature.hoverAdds = true;
env.hover = pointHover;
env.hoverUp = pointHoverUp;
CAM.traces((ids) => {
api.hide.alert(alert);
alert = api.show.alert("select origin<br>[esc] to cancel", 1000);
let ends = {};
let points = {};
let color = new THREE.Color( 0xff0000 );
function incpoint(widget, point) {
let rec = ends[point.key];
if (rec && rec.count++ === 2) {
addpoint(widget, point);
} else if (!rec) {
rec = ends[point.key] = { count: 1 };
}
}
function addpoint(widget, center) {
let box = addbox(center, color, undefined, undefined, { opacity: 1 });
widget.adds.push(box);
world.add(box);
box._origin = center;
points[center.key] = { box, center };
}
api.widgets.for(widget => {
let { pos, tzoff } = widget.track;
let polys = widget.traces.map(trace => {
return trace.fixClosed().clone().move(pos).move({ x:0, y:0, z: -tzoff });
});
for (let poly of polys) {
if (poly.circularity() >= 0.99) {
let center = poly.calcCircleCenter();
if (points[center.key]) {
continue;
}
addpoint(widget, center);
} else if (poly.isOpen()) {
incpoint(widget, poly.first());
incpoint(widget, poly.last());
}
}
});
lastPoints = Object.values(points);
});
}
export function originReset() {
let { process} = api.conf.get();
process.camOriginOffX = 0;
process.camOriginOffY = 0;
process.camOriginOffZ = 0;
api.settings.update_fields(process);
api.platform.update_origin();
api.conf.save();
originSelectDone();
}
export function originSelectDone() {
if (!originSelectOn) {
return;
}
api.hide.alert(alert);
api.feature.hover = false;
api.feature.hoverAdds = false;
api.widgets.for(widget => {
for (let box of widget.adds) {
world.remove(box);
}
widget.adds.length = 0;
});
clearboxes();
originSelectOn = false;
}
function pointHover(data) {
if (lastHover) {
let { color, colorSave } = lastHover.material[0] || lastHover.material;
color.r = colorSave.r;
color.g = colorSave.g;
color.b = colorSave.b;
}
lastHover = null;
if (data.type === 'platform') {
return;
}
if (!data.int.object._origin) {
return;
}
lastHover = data.int.object;
let material = lastHover.material[0] || lastHover.material;
let color = material.color;
material.colorSave = color.clone();
color.setHex(0x00ff00);
}
function pointHoverUp(int, ev) {
if (!int?.object?._origin) return;
let { _origin } = int.object;
let { process, stock } = api.conf.get();
let { x, y, z } = _origin;
let { camOriginTop, camOriginCenter } = process;
if (camOriginTop) {
z = z - stock.z;
}
if (!camOriginCenter) {
x += stock.x / 2;
y += stock.y / 2;
}
process.camOriginOffX = x;
process.camOriginOffY = y;
process.camOriginOffZ = z;
api.settings.update_fields(process);
api.platform.update_origin();
api.conf.save();
originSelectDone();
}

View file

@ -52,7 +52,6 @@ export function updateStock() {
}); });
env.camStock = new THREE.Mesh(geo, mat); env.camStock = new THREE.Mesh(geo, mat);
env.camStock.renderOrder = 2; env.camStock.renderOrder = 2;
let lo = 0.5; let lo = 0.5;
let lidat = [ let lidat = [
lo, lo, lo, lo, lo, -lo, lo, lo, lo, lo, lo, -lo,

View file

@ -2,8 +2,9 @@
import { api } from '../../core/api.js'; import { api } from '../../core/api.js';
import { env, isDark } from './client.js'; import { env, isDark } from './client.js';
import { addbox, delbox } from '../../core/boxes.js'; import { addbox, delbox, clearboxes } from '../../core/boxes.js';
import { traceDone } from './cl-trace.js'; import { traceDone } from './cl-trace.js';
import { space as SPACE } from '../../../moto/space.js';
let showTab, lastTab, tab, iw, ic; let showTab, lastTab, tab, iw, ic;
@ -107,6 +108,7 @@ export function clearTabs(widget, skiprec) {
if (!skiprec) { if (!skiprec) {
delete api.widgets.annotate(widget.id).tab; delete api.widgets.annotate(widget.id).tab;
} }
clearboxes();
} }
export function updateTabs() { export function updateTabs() {
@ -198,6 +200,7 @@ export function tabAdd() {
export function tabDone() { export function tabDone() {
delbox('tabb'); delbox('tabb');
clearboxes();
api.hide.alert(alert); api.hide.alert(alert);
api.feature.hover = false; api.feature.hover = false;
if (lastTab) { if (lastTab) {

View file

@ -1,7 +1,7 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */ /** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { api } from '../../core/api.js'; import { api } from '../../core/api.js';
import { env, clearPops } from './client.js'; import { env, clearPops, isDark } from './client.js';
import { CAM } from './driver-fe.js'; import { CAM } from './driver-fe.js';
import { Layers } from '../../core/layers.js'; import { Layers } from '../../core/layers.js';
import { Stack } from '../../core/stack.js'; import { Stack } from '../../core/stack.js';
@ -123,7 +123,7 @@ export function traceHover(data) {
let material = lastTrace.material[0] || lastTrace.material; let material = lastTrace.material[0] || lastTrace.material;
let color = material.color; let color = material.color;
material.colorSave = color.clone(); material.colorSave = color.clone();
color.setHex(env.isDark() ? 0x0066ff : 0x0000ff); color.setHex(isDark() ? 0x0066ff : 0x0000ff);
} }
export function traceHoverUp(int, ev) { export function traceHoverUp(int, ev) {
@ -157,7 +157,7 @@ export function traceToggle(obj, skip) {
colorSave = material.colorSave = color.clone(); colorSave = material.colorSave = color.clone();
} }
if (obj.selected) { if (obj.selected) {
color.setHex(env.isDark() ? 0xdd0011 : 0xff0033); color.setHex(isDark() ? 0xdd0011 : 0xff0033);
colorSave.r = color.r; colorSave.r = color.r;
colorSave.g = color.g; colorSave.g = color.g;
colorSave.b = color.b; colorSave.b = color.b;

View file

@ -1,6 +1,6 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */ /** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { $, h } from '../../../moto/webui.js'; import { $ } from '../../../moto/webui.js';
import { api } from '../../core/api.js'; import { api } from '../../core/api.js';
import { load } from '../../../load/file.js'; import { load } from '../../../load/file.js';
import { animate as anim_2d, animate_clear as anim_2d_clear } from './anim-2d-fe.js'; import { animate as anim_2d, animate_clear as anim_2d_clear } from './anim-2d-fe.js';
@ -10,18 +10,19 @@ 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, updateTabs } from './cl-tab.js'; import { tabAdd, tabDone, tabClear, restoreTabs, rotateTabs, updateTabs } 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';
import { helicalOn, helicalDone } from './cl-helical.js'; import { helicalOn, helicalDone } from './cl-helical.js';
import { originSelectDone } from './cl-origin.js';
const DEG2RAD = Math.PI / 180; const DEG2RAD = Math.PI / 180;
const RAD2DEG = 180 / Math.PI; const RAD2DEG = 180 / Math.PI;
const hasSharedArrays = self.SharedArrayBuffer ? true : false; const hasSharedArrays = self.SharedArrayBuffer ? true : false;
const { VIEWS, STACKS } = api.const; const { VIEWS, STACKS } = api.const;
const { noop, space } = api; const { noop } = api;
const { ui: UI, uc: UC } = api; const { ui: UI, uc: UC } = api;
const { widgets: WIDGETS } = api; const { widgets: WIDGETS } = api;
@ -55,6 +56,9 @@ export function isDark() {
export function clearPops() { export function clearPops() {
if (env.func.unpop) env.func.unpop(); if (env.func.unpop) env.func.unpop();
originSelectDone();
selectHolesDone();
surfaceDone();
tabDone(); tabDone();
traceDone(); traceDone();
surfaceDone(); surfaceDone();

View file

@ -0,0 +1,128 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { api } from '../../core/api.js';
import { originReset, originSelect } from './cl-origin.js';
let LANG = api.language.current;
let { CAM } = api.const.MODES,
{ $ } = api.web,
{ uc, ui } = api,
{ bound, toInt, toFloat } = uc,
{ newBlank, newButton, newBoolean, newGroup, newInput } = uc,
{ newSelect, newLabel, newValue, newRow, newDiv } = uc,
driven = true,
separator = true,
trigger = true,
units = true
;
function onBooleanClick(el) {
api.event.emit('click.boolean', el);
}
function onButtonClick(el) {
api.event.emit('click.button', el);
}
function zAnchorSave() {
api.conf.update();
api.platform.update_top_z();
}
export function menu() {
let anim = ui.anim = {};
return {
/** Animation Bar */
_____: {
_____: newDiv({ addto: $('layer-animate'), set:true, group:false }),
row: newRow([
anim.replay = newButton(null,"anim.replay",{icon:'<i class="fas fa-fast-backward"></i>',title:"restart"}),
anim.play = newButton(null,"anim.play",{icon:'<i class="fas fa-play"></i>',title:"play"}),
anim.pause = newButton(null,"anim.pause",{icon:'<i class="fas fa-pause"></i>',title:"pause"}),
anim.step = newButton(null,"anim.step",{icon:'<i class="fas fa-step-forward"></i>',title:"single step"}),
anim.speed = newButton(null,"anim.fast",{icon:'<i class="fas fa-forward"></i>',title:"toggle speed"}),
anim.labspd = newValue(3, {class:"center padleft"}),
anim.labx = newLabel("X", {class:"padleft"}),
anim.valx = newValue(7, {class:"center"}),
anim.laby = newLabel("Y", {class:"padleft"}),
anim.valy = newValue(7, {class:"center"}),
anim.labz = newLabel("Z", {class:"padleft"}),
anim.valz = newValue(6, {class:"center"}),
anim.laba = newLabel("A", {class:"padleft hide"}),
anim.vala = newValue(6, {class:"center hide"}),
anim.labpro = newLabel("%", {class:"padleft"}),
anim.progress = newValue(5, {class:"center"}),
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.shade = newButton(null,null,{icon:'<i class="fa-solid fa-cube"></i>',title:"stock box"}),
])
},
/** Left Side Menu */
_____: newGroup(LANG.ct_menu, $('cam-tabs'), { modes:CAM, marker:true, driven, separator }),
camTabsWidth: newInput(LANG.ct_wdth_s, {title:LANG.ct_wdth_l, convert:toFloat, bound:bound(0.005,100), units}),
camTabsHeight: newInput(LANG.ct_hght_s, {title:LANG.ct_hght_l, convert:toFloat, bound:bound(0.005,100), units}),
camTabsDepth: newInput(LANG.ct_dpth_s, {title:LANG.ct_dpth_l, convert:toFloat, bound:bound(0.005,100), units}),
separator: newBlank({ class:"set-sep", driven }),
camTabsMidline: newBoolean(LANG.ct_midl_s, onBooleanClick, {title:LANG.ct_midl_l}),
separator: newBlank({ class:"set-sep", driven }),
camTabsManual: newRow([
(ui.tabAdd = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
(ui.tabDun = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
(ui.tabClr = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
], {class:"ext-buttons f-row"}),
_____: newGroup(LANG.cs_menu, $('cam-stock'), { modes:CAM, driven, separator }),
camStockX: newInput(LANG.cs_wdth_s, {title:LANG.cs_wdth_l, convert:toFloat, bound:bound(0,9999), units}),
camStockY: newInput(LANG.cs_dpth_s, {title:LANG.cs_dpth_l, convert:toFloat, bound:bound(0,9999), units}),
camStockZ: newInput(LANG.cs_hght_s, {title:LANG.cs_hght_l, convert:toFloat, bound:bound(0,9999), units}),
separator: newBlank({ class:"set-sep", driven }),
camStockOffset: newBoolean(LANG.cs_offs_s, onBooleanClick, {title:LANG.cs_offs_l}),
camStockClipTo: newBoolean(LANG.cs_clip_s, onBooleanClick, {title:LANG.cs_clip_l}),
camStockIndexed: newBoolean(LANG.cs_indx_s, onBooleanClick, {title:LANG.cs_indx_l}),
camStockIndexGrid: newBoolean(LANG.cs_ishg_s, onBooleanClick, {title:LANG.cs_ishg_l, show:() => ui.camStockIndexed.checked}),
_____: newGroup(LANG.cc_menu, $('cam-limits'), { modes:CAM, driven, separator }),
camZAnchor: newSelect(LANG.ou_zanc_s, {title: LANG.ou_zanc_l, action:zAnchorSave, show:() => !ui.camStockIndexed.checked}, "zanchor"),
camZOffset: newInput(LANG.ou_ztof_s, {title:LANG.ou_ztof_l, convert:toFloat, units}),
camZTop: newInput(LANG.ou_ztop_s, {title:LANG.ou_ztop_l, convert:toFloat, units, trigger}),
camZBottom: newInput(LANG.ou_zbot_s, {title:LANG.ou_zbot_l, convert:toFloat, units, trigger}),
camZThru: newInput(LANG.ou_ztru_s, {title:LANG.ou_ztru_l, convert:toFloat, bound:bound(0.0,100), units }),
camZClearance: newInput(LANG.ou_zclr_s, {title:LANG.ou_zclr_l, convert:toFloat, bound:bound(0.01,100), units }),
camFastFeedZ: newInput(LANG.cc_rzpd_s, {title:LANG.cc_rzpd_l, convert:toFloat, units}),
camFastFeed: newInput(LANG.cc_rapd_s, {title:LANG.cc_rapd_l, convert:toFloat, units}),
_____: newGroup(LANG.ou_menu, $('cam-output'), { modes:CAM, driven, separator, group:"cam-output" }),
camConventional: newBoolean(LANG.ou_conv_s, onBooleanClick, {title:LANG.ou_conv_l}),
camEaseDown: newBoolean(LANG.cr_ease_s, onBooleanClick, {title:LANG.cr_ease_l}),
camDepthFirst: newBoolean(LANG.ou_depf_s, onBooleanClick, {title:LANG.ou_depf_l}),
camToolInit: newBoolean(LANG.ou_toin_s, onBooleanClick, {title:LANG.ou_toin_l}),
separator: newBlank({ class:"set-sep", driven }),
camFirstZMax: newBoolean(LANG.ou_z1st_s, onBooleanClick, {title:LANG.ou_z1st_l}),
camForceZMax: newBoolean(LANG.ou_forz_s, onBooleanClick, {title:LANG.ou_forz_l}),
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}),
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 }),
camOriginTop: newBoolean(LANG.or_topp_s, onBooleanClick, {title:LANG.or_topp_l}),
camOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, {title:LANG.or_cntr_l}),
separator: newBlank({ class:"set-sep", driven }),
camOriginOffX: newInput(LANG.co_offx_s, {title:LANG.co_offx_l, convert:toFloat, units}),
camOriginOffY: newInput(LANG.co_offy_s, {title:LANG.co_offy_l, convert:toFloat, units}),
camOriginOffZ: newInput(LANG.co_offz_s, {title:LANG.co_offz_l, convert:toFloat, units}),
separator: newBlank({ class:"set-sep", driven }),
camOriginSelect: newRow([
newButton("select", originSelect),
newButton("reset", originReset),
], { class: "ext-buttons f-row" }),
_____: newGroup(LANG.op_xprt_s, $('cam-expert'), { group:"cam_expert", modes:CAM, marker: false, driven, separator }),
camExpertFast: newBoolean(LANG.cx_fast_s, onBooleanClick, {title:LANG.cx_fast_l, show: () => !ui.camTrueShadow.checked }),
camTrueShadow: newBoolean(LANG.cx_true_s, onBooleanClick, {title:LANG.cx_true_l, show: () => !ui.camExpertFast.checked }),
separator: newBlank({ class:"set-sep", driven }),
camArcEnabled: newBoolean(LANG.cx_arce_s, onBooleanClick, {title:LANG.cx_arce_l}),
camArcTolerance: newInput(LANG.cx_arct_s, {title:LANG.cx_arct_l, convert:toFloat, bound:bound(0,100), units, trigger, show:() => ui.camArcEnabled.checked}),
camArcResolution: newInput(LANG.cx_arcr_s, {title:LANG.cx_arcr_l, convert:toFloat, bound:bound(0,180), trigger, show:() => ui.camArcEnabled.checked}),
};
};

View file

@ -15,7 +15,7 @@ function createFilter(op) {
let idx = 0; let idx = 0;
if (obj && obj.slices) { if (obj && obj.slices) {
const nadd = []; const nadd = [];
filter = function(slices) { filter = function (slices) {
for (let slice of slices) { for (let slice of slices) {
if (obj.slices(slice, idx++)) { if (obj.slices(slice, idx++)) {
nadd.push(slice); nadd.push(slice);
@ -38,8 +38,10 @@ class OpContour extends CamOp {
async slice(progress) { async slice(progress) {
let { op, state } = this; let { op, state } = this;
let { addSlices } = state; let { addSlices, settings, updateToolDiams } = state;
let filter = createFilter(op); let filter = createFilter(op);
let toolDiam = this.toolDiam = new Tool(settings, op.tool).fluteDiameter();
updateToolDiams(toolDiam);
// we need topo for safe travel moves when roughing and outlining // we need topo for safe travel moves when roughing and outlining
// not generated when drilling-only. then all z moves use bounds max. // not generated when drilling-only. then all z moves use bounds max.
// also generates x and y contouring when selected // also generates x and y contouring when selected
@ -69,7 +71,7 @@ class OpContour extends CamOp {
let { camOut, polyEmit, newLayer, printPoint, lastPoint } = ops; let { camOut, polyEmit, newLayer, printPoint, lastPoint } = ops;
let { bounds, zmax } = ops; let { bounds, zmax } = ops;
let toolDiam = this.toolDiam = new Tool(settings, op.tool).fluteDiameter(); let toolDiam = this.toolDiam;
let stepover = toolDiam * op.step * 2; let stepover = toolDiam * op.step * 2;
let depthFirst = process.camDepthFirst; let depthFirst = process.camDepthFirst;
let depthData = []; let depthData = [];
@ -78,7 +80,7 @@ class OpContour extends CamOp {
setSpindle(op.spindle); setSpindle(op.spindle);
setTolerance(this.tolerance); setTolerance(this.tolerance);
printPoint = newPoint(bounds.min.x,bounds.min.y,zmax); printPoint = newPoint(bounds.min.x, bounds.min.y, zmax);
for (let slice of sliceOut) { for (let slice of sliceOut) {
// ignore debug slices // ignore debug slices
@ -88,17 +90,17 @@ class OpContour extends CamOp {
let polys = [], poly, emit; let polys = [], poly, emit;
slice.camLines.forEach(function (poly) { slice.camLines.forEach(function (poly) {
if (depthFirst) poly = poly.clone(true); if (depthFirst) poly = poly.clone(true);
polys.push({first:poly.first(), last:poly.last(), poly:poly}); polys.push({ first: poly.first(), last: poly.last(), poly: poly });
}); });
if (depthFirst) { if (depthFirst) {
depthData.appendAll(polys); depthData.appendAll(polys);
} else { } else {
printPoint = tip2tipEmit(polys, printPoint, function(el, point, count) { printPoint = tip2tipEmit(polys, printPoint, function (el, point, count) {
poly = el.poly; poly = el.poly;
if (poly.last() === point) { if (poly.last() === point) {
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, stepover);
}, false); }, false);
}); });
@ -107,12 +109,12 @@ class OpContour extends CamOp {
} }
if (depthFirst) { if (depthFirst) {
printPoint = tip2tipEmit(depthData, printPoint, function(el, point, count) { printPoint = tip2tipEmit(depthData, printPoint, function (el, point, count) {
let poly = el.poly; let poly = el.poly;
if (poly.last() === point) { if (poly.last() === point) {
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, stepover);
}, false); }, false);
newLayer(); newLayer();

View file

@ -15,7 +15,7 @@ class OpDrill extends CamOp {
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 { zBottom, zThru, thruHoles, color } = state;
let { drills, drillThrough} = op let { drills, drillThrough } = op
let drillTool = new Tool(settings, op.tool), let drillTool = new Tool(settings, op.tool),
drillToolDiam = drillTool.fluteDiameter(), drillToolDiam = drillTool.fluteDiameter(),
@ -25,8 +25,8 @@ class OpDrill extends CamOp {
const allDrills = drills[widget.id] ?? [] const allDrills = drills[widget.id] ?? []
// drill points to use center (average of all points) of the polygon // drill points to use center (average of all points) of the polygon
allDrills.forEach((drill)=> { allDrills.forEach((drill) => {
if(!drill.selected){ if (!drill.selected) {
return return
} }
let slice = newSlice(0); let slice = newSlice(0);
@ -36,7 +36,7 @@ class OpDrill extends CamOp {
} }
drill.zBottom = drill.z - drill.depth; drill.zBottom = drill.z - drill.depth;
// for thru holes, follow z thru when set // for thru holes, follow z thru when set
if ((op.thru>0) ) { if ((op.thru > 0)) {
drill.zBottom -= op.thru; drill.zBottom -= op.thru;
} }
const poly = newPolygon() const poly = newPolygon()
@ -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("drill", { face: color, line: color })
.addPolys(slice.camLines); .addPolys(slice.camLines);
addSlices(slice); addSlices(slice);
sliceOut.push(slice); sliceOut.push(slice);
@ -59,7 +59,7 @@ class OpDrill extends CamOp {
let { settings, widget, addSlices, updateToolDiams } = state; 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, op.thru);
setSpindle(op.spindle); setSpindle(op.spindle);
emitDrills(this.sliceOut.map(slice => slice.camLines).flat()); emitDrills(this.sliceOut.map(slice => slice.camLines).flat());
} }

View file

@ -7,6 +7,10 @@ export class OpIndex extends CamOp {
super(state, op); super(state, op);
} }
weight() {
return 0.1;
}
async slice() { async slice() {
let { op, state } = this; let { op, state } = this;
if (!state.isIndexed) { if (!state.isIndexed) {

View file

@ -2,6 +2,7 @@
import { CamOp } from './op.js'; import { CamOp } from './op.js';
import { Tool } from './tool.js'; import { Tool } from './tool.js';
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) {
@ -12,7 +13,7 @@ function createFilter(op) {
let idx = 0; let idx = 0;
if (obj && obj.slices) { if (obj && obj.slices) {
const nadd = []; const nadd = [];
filter = function(slices) { filter = function (slices) {
for (let slice of slices) { for (let slice of slices) {
if (obj.slices(slice, idx++)) { if (obj.slices(slice, idx++)) {
nadd.push(slice); nadd.push(slice);
@ -38,7 +39,7 @@ class OpLathe extends CamOp {
let { addSlices } = state; let { addSlices } = state;
let filter = createFilter(op); let filter = createFilter(op);
this.topo = await Topo4({ this.topo = await topo4_generate({
op, op,
state, state,
onupdate: (pct, msg) => { onupdate: (pct, msg) => {
@ -109,7 +110,7 @@ class OpLathe extends CamOp {
// camOut(last = last.clone().setZ(zmax), 0); // camOut(last = last.clone().setZ(zmax), 0);
// camOut(last = last.clone().setA(amax), 0); // camOut(last = last.clone().setA(amax), 0);
newLayer(); newLayer();
ops.addGCode([`G0 Z${zmax.round(2)}`,`G0 A${amax}`,"G92 A0"]); ops.addGCode([`G0 Z${zmax.round(2)}`, `G0 A${amax}`, "G92 A0"]);
setPrintPoint(last); setPrintPoint(last);
} }

View file

@ -17,7 +17,7 @@ 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, widget, slicer, addSlices, tshadow, thruHoles, unsafe, color } = state;
let { updateToolDiams, tabs, cutTabs, cutPolys, workarea, zMax } = state; let { updateToolDiams, tabs, cutTabs, cutPolys, workarea, zMax, shadowAt } = state;
let { process, stock } = settings; let { process, stock } = settings;
if (op.down <= 0) { if (op.down <= 0) {
@ -37,6 +37,7 @@ 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;
// shift out first (top-most) slice // shift out first (top-most) slice
indices.shift(); indices.shift();
// add flats to shadow // add flats to shadow
@ -44,7 +45,6 @@ class OpOutline extends CamOp {
.map(v => (parseFloat(v) - 0.01).round(5)) .map(v => (parseFloat(v) - 0.01).round(5))
.filter(v => v > 0 && indices.indexOf(v) < 0); .filter(v => v > 0 && indices.indexOf(v) < 0);
indices = indices.appendAll(flats).sort((a,b) => b-a); indices = indices.appendAll(flats).sort((a,b) => b-a);
let cnt = 0; let cnt = 0;
let tot = 0; let tot = 0;
if (op.outside && !op.inside) { if (op.outside && !op.inside) {
@ -65,7 +65,7 @@ class OpOutline extends CamOp {
// exclude flats injected to complete shadow // exclude flats injected to complete shadow
return; return;
} }
data.shadow = trueShadow ? shadowAt(widget, data.z) : shadow.clone(true); data.shadow = trueShadow ? shadowAt(data.z, lastShadowZ) : shadow.clone(true);
data.slice.shadow = data.shadow; data.slice.shadow = data.shadow;
// data.slice.tops[0].inner = data.shadow; // data.slice.tops[0].inner = data.shadow;
// data.slice.tops[0].inner = POLY.setZ(tshadow.clone(true), data.z); // data.slice.tops[0].inner = POLY.setZ(tshadow.clone(true), data.z);
@ -73,6 +73,7 @@ class OpOutline extends CamOp {
// data.slice.xray(); // data.slice.xray();
// onupdate(0.2 + (index/total) * 0.1, "outlines"); // onupdate(0.2 + (index/total) * 0.1, "outlines");
progress(0.5 + 0.5 * (++cnt / tot)); progress(0.5 + 0.5 * (++cnt / tot));
lastShadowZ = data.z;
}, progress: (index, total) => { }, progress: (index, total) => {
tot = total; tot = total;
progress((index / total) * 0.5); progress((index / total) * 0.5);

View file

@ -78,7 +78,7 @@ class OpRegister extends CamOp {
poly, cp, cz; poly, cp, cz;
function start(z) { function start(z) {
cz = z; cz = z;
cp = {x:x0 + o2 * 0.5, y:y0 + o2 * 1.5}; cp = { x: x0 + o2 * 0.5, y: y0 + o2 * 1.5 };
poly = newPolygon().add(cp.x, cp.y, z); poly = newPolygon().add(cp.x, cp.y, z);
} }
function move(dx, dy) { function move(dx, dy) {
@ -115,16 +115,16 @@ class OpRegister extends CamOp {
}); });
poly.points.pop(); poly.points.pop();
slice.camTrace = { tool: tool.getID(), rate: op.feed, plunge: op.rate }; slice.camTrace = { tool: tool.getID(), rate: op.feed, plunge: op.rate };
slice.camLines = [ poly ]; slice.camLines = [poly];
slice.output() slice.output()
.setLayer("register", {line: color}, false) .setLayer("register", { line: color }, false)
.addPolys(slice.camLines) .addPolys(slice.camLines)
} }
break; break;
} }
if (points.length) { if (points.length) {
let slice = newSlice(0,null), polys = []; let slice = newSlice(0, null), polys = [];
points.forEach(point => { points.forEach(point => {
polys.push(newPolygon() polys.push(newPolygon()
.append(point.clone().setZ(z1)) .append(point.clone().setZ(z1))
@ -132,7 +132,7 @@ class OpRegister extends CamOp {
}); });
slice.camLines = polys; slice.camLines = polys;
slice.output() slice.output()
.setLayer("register", {face: color, line: color}) .setLayer("register", { face: color, line: color })
.addPolys(polys); .addPolys(polys);
addSlices(slice); addSlices(slice);
sliceOut.push(slice); sliceOut.push(slice);

View file

@ -114,6 +114,7 @@ class OpRough extends CamOp {
indices = indices.filter(v => v >= workarea.bottom_z); indices = indices.filter(v => v >= workarea.bottom_z);
// console.log('indices', ...indices, {zBottom}); // console.log('indices', ...indices, {zBottom});
let lsz;
let cnt = 0; let cnt = 0;
let tot = 0; let tot = 0;
await slicer.slice(indices, { each: data => { await slicer.slice(indices, { each: data => {
@ -125,9 +126,10 @@ class OpRough extends CamOp {
if (data.z > workarea.top_z) { if (data.z > workarea.top_z) {
return; return;
} }
data.shadow = trueShadow ? shadowAt(data.z) : shadow.clone(true); data.shadow = trueShadow ? shadowAt(data.z, lsz) : shadow.clone(true);
data.slice.shadow = data.shadow; data.slice.shadow = data.shadow;
slices.push(data.slice); slices.push(data.slice);
lsz = data.z;
progress(0.25 + 0.25 * (++cnt / tot)); progress(0.25 + 0.25 * (++cnt / tot));
}, progress: (index, total) => { }, progress: (index, total) => {
tot = total; tot = total;

View file

@ -20,6 +20,10 @@ class OpShadow extends CamOp {
super(state, op); super(state, op);
} }
weight() {
return 0.1;
}
async slice(progress) { async slice(progress) {
let state = this.state; let state = this.state;
let { ops, slicer, widget, unsafe, addSlices, shadowAt } = state; let { ops, slicer, widget, unsafe, addSlices, shadowAt } = state;

View file

@ -107,15 +107,28 @@ class OpTrace extends CamOp {
for (let z of zs) { for (let z of zs) {
let clip = [], shadow; let clip = [], shadow;
shadow = shadowAt(z); shadow = shadowAt(z);
// for cases where the shadow IS the poly like
// with lettering without a bounding frame, clip
// will fail and we need to restore the matching poly
let subshadow = true;
for (let spo of shadow) {
if (poly.isInside(spo, 0.01)) {
subshadow = false;
clip = [ poly ];
break;
}
}
if (subshadow) {
POLY.subtract([ poly ], shadow, clip, undefined, undefined, 0); POLY.subtract([ poly ], shadow, clip, undefined, undefined, 0);
}
if (op.outline) { if (op.outline) {
POLY.clearInner(clip); POLY.clearInner(clip);
} }
if (clip.length === 0) { if (clip.length === 0) {
continue; continue;
} }
let slice = newSliceOut(z);
let count = 999; let count = 999;
let slice = newSliceOut(z);
slice.camTrace = { tool, rate, plunge }; slice.camTrace = { tool, rate, plunge };
if (toolDiam) { if (toolDiam) {
const offs = [ -toolDiam / 2, -toolOver ]; const offs = [ -toolDiam / 2, -toolOver ];
@ -132,12 +145,15 @@ class OpTrace extends CamOp {
slice.camLines = POLY.flatten(slice.camLines, null, true); slice.camLines = POLY.flatten(slice.camLines, null, true);
} }
POLY.setWinding(slice.camLines, cutdir, false); POLY.setWinding(slice.camLines, cutdir, false);
slice.output()
.setLayer("trace", {line: color}, false)
.addPolys(slice.camLines)
if (debug && shadow) slice.output() if (debug && shadow) slice.output()
.setLayer("trace shadow", {line: 0xff8811}, false) .setLayer("trace shadow", {line: 0xff8811}, false)
.addPolys(shadow) .addPolys(shadow)
if (debug) slice.output()
.setLayer("trace poly", {line: 0x1188ff}, false)
.addPolys([ poly ])
slice.output()
.setLayer("trace", {line: color}, false)
.addPolys(slice.camLines)
progress(zpro, "trace"); progress(zpro, "trace");
zpro += zinc; zpro += zinc;
addSlices(slice); addSlices(slice);
@ -254,6 +270,7 @@ class OpTrace extends CamOp {
case "clear": case "clear":
const zbo = widget.track.top - widget.track.box.d; const zbo = widget.track.top - widget.track.box.d;
let zmap = {}; let zmap = {};
polys = POLY.nest(polys);
for (let poly of polys) { for (let poly of polys) {
let z = minZ(poly.minZ()); let z = minZ(poly.minZ());
if (offover) { if (offover) {

View file

@ -88,6 +88,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
engageFactor = process.camFullEngage, engageFactor = process.camFullEngage,
arcTolerance = process.camArcTolerance, arcTolerance = process.camArcTolerance,
arcRes = toRadians(process.camArcResolution), arcRes = toRadians(process.camArcResolution),
arcEnabled = process.camArcEnabled && arcTolerance > 0 && arcRes > 0,
tolerance = 0, tolerance = 0,
drillDown = 0, drillDown = 0,
drillLift = 0, drillLift = 0,
@ -177,6 +178,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
} }
feedRate = feed || feedRate || plunge; feedRate = feed || feedRate || plunge;
plungeRate = Math.min(feedRate || plunge, plunge || plungeRate || feedRate); plungeRate = Math.min(feedRate || plunge, plunge || plungeRate || feedRate);
// console.log({ setTool: toolID, feed, plunge, plungeRate });
} }
function setLasering(bool, power = 0) { function setLasering(bool, power = 0) {
@ -241,7 +243,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
break; break;
} }
} }
camOut(point.clone().setZ(zmax)); camOut(point.clone().setZ(zmax_outer));
points.forEach(function (point, index) { points.forEach(function (point, index) {
camOut(point, 1); camOut(point, 1);
if (index > 0 && index < points.length - 1) { if (index > 0 && index < points.length - 1) {
@ -249,7 +251,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
if (lift) camOut(point.clone().setZ(point.z + lift), 0); if (lift) camOut(point.clone().setZ(point.z + lift), 0);
} }
}) })
camOut(point.clone().setZ(zmax), 0); camOut(point.clone().setZ(zmax_outer), 0);
newLayer(); newLayer();
} }
@ -317,7 +319,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);
} }
/** /**
@ -342,7 +344,8 @@ export function prepEach(widget, settings, print, firstPoint, update) {
// console.log({radius}) // console.log({radius})
const isArc = emit == 2 || emit == 3; const isArc = emit == 2 || emit == 3;
//apply widget movement pos // apply widget movement pos
const pointA = point.a;
point = applyWidgetMovement(point); point = applyWidgetMovement(point);
// console.log(point.z); // console.log(point.z);
@ -354,15 +357,13 @@ export function prepEach(widget, settings, print, firstPoint, update) {
let rate = feedRate * factor; let rate = feedRate * factor;
// carry rotation forward when not overridden // carry rotation forward when not overridden
if (point.a === undefined && lastPoint) { if (lastPoint && pointA !== undefined && lastPoint.a !== undefined) {
point.a = lastPoint.a; let DA = lastPoint.a - pointA;
} else if (lastPoint && point.a !== undefined && lastPoint.a !== undefined) {
let DA = lastPoint.a - point.a;
let MZ = Math.max(lastPoint.z, point.z) let MZ = Math.max(lastPoint.z, point.z)
// find arc length // find arc length
let AL = (Math.abs(DA) / 360) * (2 * Math.PI * MZ); let AL = (Math.abs(DA) / 360) * (2 * Math.PI * MZ);
if (AL >= 1) { if (AL >= 1) {
let lerp = base.util.lerp(lastPoint.a, point.a, 1); let lerp = base.util.lerp(lastPoint.a, pointA, 1);
// create interpolated point set for rendering and animation // create interpolated point set for rendering and animation
// console.log({ DA, MZ, AL }, lerp.length); // console.log({ DA, MZ, AL }, lerp.length);
for (let a of lerp) { for (let a of lerp) {
@ -461,16 +462,27 @@ export function prepEach(widget, settings, print, firstPoint, update) {
} }
// set new plunge rate // set new plunge rate
if (!lasering && deltaZ < -tolerance && !isLathe) { 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.round(plungeRate + ((feedRate - plungeRate) * modifier)); rate = Math.max(
cut = 1; plungeRate,
Math.round(plungeRate + ((feedRate - plungeRate) * modifier))
);
} else { } else {
rate = 1 / Math.hypot(deltaXY / feedRate, absDeltaZ / plungeRate); let L = Math.hypot(deltaXY, absDeltaZ);
let limXY = feedRate * L / deltaXY;
let limZ = plungeRate * L / Math.abs(deltaZ);
rate = Math.min(feedRate, limXY, limZ);
// let zps = len / absDeltaZ;
// rate = Math.max(
// plungeRate,
// 1 / Math.hypot(deltaXY / feedRate, absDeltaZ / plungeRate)
// );
// console.log({ rate, deltaXY, deltaZ });
} }
} }
@ -765,7 +777,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
if (indexA == startIndex) { if (indexA == startIndex) {
camOut(pointA.clone(), 0, { factor: engageFactor }); camOut(pointA.clone(), 0, { factor: engageFactor });
// if first point, move to and call export function // if first point, move to and call export function
arcQ.push(pointA); if (arcEnabled) arcQ.push(pointA);
} }
lastPoint = arcExport(pointB, pointA); lastPoint = arcExport(pointB, pointA);
}, !poly.isClosed(), startIndex); }, !poly.isClosed(), startIndex);
@ -782,7 +794,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
function arcExport(point, lastp) { function arcExport(point, lastp) {
let dist = lastp ? point.distTo2D(lastp) : 0; let dist = lastp ? point.distTo2D(lastp) : 0;
if (lastp) { if (lastp) {
if (dist > lineTolerance && lastp) { if (arcEnabled && dist > lineTolerance && lastp) {
let rec = Object.assign(point, { dist }); let rec = Object.assign(point, { dist });
arcQ.push(rec); arcQ.push(rec);

View file

@ -34,6 +34,15 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
top_stock, top_part, top_gap, top_z, top_stock, top_part, top_gap, top_z,
workarea; workarea;
axisRotation = axisIndex = undefined;
dark = settings.controller.dark;
color = dark ? 0xbbbbbb : 0;
minToolDiam = Infinity;
maxToolDiam = -Infinity;
tabs = widget.anno.tab;
unsafe = proc.camExpertFast;
units = settings.controller.units === 'in' ? 25.4 : 1;
// allow recomputing later if widget or settings changes // allow recomputing later if widget or settings changes
const var_compute = () => { const var_compute = () => {
let { camStockX, camStockY, camStockZ, camStockOffset } = proc; let { camStockX, camStockY, camStockZ, camStockOffset } = proc;
@ -46,9 +55,9 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
x: camStockX, x: camStockX,
y: camStockY, y: camStockY,
z: camStockZ z: camStockZ
} };
track = widget.track;
({ camZTop, camZBottom, camZThru } = proc); ({ camZTop, camZBottom, camZThru } = proc);
track = widget.track;
wztop = track.top; wztop = track.top;
ztOff = isIndexed ? (stock.z - bounds.dim.z) / 2 : (stock.z - wztop); ztOff = isIndexed ? (stock.z - bounds.dim.z) / 2 : (stock.z - wztop);
zbOff = isIndexed ? (stock.z - bounds.dim.z) / 2 : (wztop - track.box.d); zbOff = isIndexed ? (stock.z - bounds.dim.z) / 2 : (wztop - track.box.d);
@ -57,14 +66,6 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
zMax = bounds.max.z; zMax = bounds.max.z;
zThru = camZThru; zThru = camZThru;
zTop = zMax + ztOff; zTop = zMax + ztOff;
minToolDiam = Infinity;
maxToolDiam = -Infinity;
dark = !!settings.controller.dark;
color = dark ? 0xbbbbbb : 0;
tabs = widget.anno.tab;
unsafe = proc.camExpertFast;
units = settings.controller.units === 'in' ? 25.4 : 1;
axisRotation = axisIndex = undefined;
part_size = bounds.dim; part_size = bounds.dim;
bottom_gap = zbOff; bottom_gap = zbOff;
bottom_part = 0; bottom_part = 0;
@ -223,7 +224,7 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
} }
function setAxisIndex(degrees = 0, absolute = true) { function setAxisIndex(degrees = 0, absolute = true) {
axisIndex = absolute ? degrees : axisIndex + degrees; axisIndex = absolute ? degrees : (axisIndex || 0) + degrees;
axisRotation = (Math.PI / 180) * axisIndex; axisRotation = (Math.PI / 180) * axisIndex;
widget.setAxisIndex(isIndexed ? -axisIndex : 0); widget.setAxisIndex(isIndexed ? -axisIndex : 0);
return isIndexed ? -axisIndex : 0; return isIndexed ? -axisIndex : 0;
@ -266,10 +267,14 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
// silently preface op list with OpShadow // silently preface op list with OpShadow
if (isIndexed) { if (isIndexed) {
if (activeOps.length === 0 || activeOps[0].type !== 'index') if (activeOps.length === 0 || activeOps[0].type !== 'index') {
opList.push(new OPS.index(state, { type: "index", index: 0 })); opList.push(new OPS.index(state, { type: "index", index: 0 }));
opTot += opList.peek().weight();
}
} else { } else {
opList.push(new OPS.shadow(state, { type: "shadow", silent: true })); opList.push(new OPS.shadow(state, { type: "shadow", silent: true }));
opTot += opList.peek().weight();
} }
// determing # of steps and step weighting for progress bar // determing # of steps and step weighting for progress bar
@ -399,7 +404,7 @@ export function addDogbones(poly, dist, reverse) {
} }
}; };
export async function traces(settings, widget, single) { export async function traces(settings, widget) {
if (widget.traces) { if (widget.traces) {
return false; return false;
} }
@ -652,7 +657,6 @@ export async function holes(settings, widget, individual, rec, onProgress) {
// keeps the z bottom relative to the part when z align changes // keeps the z bottom relative to the part when z align changes
zBottom = isIndexed ? camZBottom : camZBottom - zbOff; zBottom = isIndexed ? camZBottom : camZBottom - zbOff;
let slicerOpts = { flatoff: 0.001 } let slicerOpts = { flatoff: 0.001 }
let slicer = new cam_slicer(widget, slicerOpts); let slicer = new cam_slicer(widget, slicerOpts);
let zFlats = Object.keys(slicer.zFlat).map(Number).map(z => [z, z - 0.002]).flat() let zFlats = Object.keys(slicer.zFlat).map(Number).map(z => [z, z - 0.002]).flat()
@ -920,7 +924,7 @@ export function computeShadowAt(widget, z, ztop) {
.add(a[2].x, a[2].y, a[2].z); .add(a[2].x, a[2].y, a[2].z);
}); });
polys = POLY.union(polys, 0.001, true); polys = POLY.union(polys, 0, true);
return polys; return polys;
} }

View file

@ -26,7 +26,7 @@ export class Slicer {
const max = options.max || Infinity; const max = options.max || Infinity;
const len = position.length; const len = position.length;
const pts = this.points = []; const pts = this.points = [];
for (let i=0; i<len; ) { for (let i = 0; i < len;) {
const x0 = position[i++]; const x0 = position[i++];
const y0 = position[i++]; const y0 = position[i++];
const z0 = position[i++]; const z0 = position[i++];
@ -57,7 +57,7 @@ export class Slicer {
const len = points.length; const len = points.length;
const min = options.min || -Infinity; const min = options.min || -Infinity;
const max = options.max || Infinity; const max = options.max || Infinity;
for (let i=0; i<len; ) { for (let i = 0; i < len;) {
const p0 = points[i++]; const p0 = points[i++];
const p1 = points[i++]; const p1 = points[i++];
const p2 = points[i++]; const p2 = points[i++];
@ -95,11 +95,11 @@ export class Slicer {
if (!under && !both) over = true; if (!under && !both) over = true;
// iterate over matching buckets for this z offset // iterate over matching buckets for this z offset
for (let i = 0; i < points.length; ) { for (let i = 0; i < points.length;) {
p1 = points[i++]; p1 = points[i++];
p2 = points[i++]; p2 = points[i++];
p3 = points[i++]; p3 = points[i++];
let where = {under: [], over: [], on: []}; let where = { under: [], over: [], on: [] };
checkOverUnderOn(p1, z, where); checkOverUnderOn(p1, z, where);
checkOverUnderOn(p2, z, where); checkOverUnderOn(p2, z, where);
checkOverUnderOn(p3, z, where); checkOverUnderOn(p3, z, where);
@ -130,7 +130,7 @@ export class Slicer {
if (line.length === 2) { if (line.length === 2) {
lines.push(makeZLine(phash, line[0], line[1])); lines.push(makeZLine(phash, line[0], line[1]));
} else { } else {
console.log({msg: "invalid ips", line: line, where: where}); console.log({ msg: "invalid ips", line: line, where: where });
} }
} }
} }
@ -210,7 +210,7 @@ function getCachedPoint(phash, p) {
function makeZLine(phash, p1, p2, coplanar, edge) { function makeZLine(phash, p1, p2, coplanar, edge) {
p1 = getCachedPoint(phash, p1); p1 = getCachedPoint(phash, p1);
p2 = getCachedPoint(phash, p2); p2 = getCachedPoint(phash, p2);
let line = newOrderedLine(p1,p2); let line = newOrderedLine(p1, p2);
line.coplanar = coplanar || false; line.coplanar = coplanar || false;
line.edge = edge || false; line.edge = edge || false;
return line; return line;
@ -243,7 +243,7 @@ function removeDuplicateLines(lines, debug) {
function addLinesToPoint(point, line) { function addLinesToPoint(point, line) {
cachePoint(point); cachePoint(point);
if (!point.group) point.group = [ line ]; if (!point.group) point.group = [line];
else point.group.push(line); else point.group.push(line);
} }
@ -261,7 +261,7 @@ function removeDuplicateLines(lines, debug) {
}); });
// associate points with their lines, cull deleted // associate points with their lines, cull deleted
lines.forEach(function(line) { lines.forEach(function (line) {
if (!line.del) { if (!line.del) {
tmplines.push(line); tmplines.push(line);
addLinesToPoint(line.p1, line); addLinesToPoint(line.p1, line);
@ -270,7 +270,7 @@ function removeDuplicateLines(lines, debug) {
}); });
// merge collinear lines // merge collinear lines
points.forEach(function(point) { points.forEach(function (point) {
if (point.group.length != 2) return; if (point.group.length != 2) return;
let l1 = point.group[0], let l1 = point.group[0],
l2 = point.group[1]; l2 = point.group[1];
@ -280,7 +280,7 @@ function removeDuplicateLines(lines, debug) {
// find new endpoints that are not shared point // find new endpoints that are not shared point
let p1 = l1.p1 != point ? l1.p1 : l1.p2, let p1 = l1.p1 != point ? l1.p1 : l1.p2,
p2 = l2.p1 != point ? l2.p1 : l2.p2, p2 = l2.p1 != point ? l2.p1 : l2.p2,
newline = newOrderedLine(p1,p2); newline = newOrderedLine(p1, p2);
// remove deleted lines from associated points // remove deleted lines from associated points
p1.group.remove(l1); p1.group.remove(l1);
p1.group.remove(l2); p1.group.remove(l2);
@ -307,7 +307,7 @@ function removeDuplicateLines(lines, debug) {
}); });
// create new line array culling deleted // create new line array culling deleted
tmplines.forEach(function(line) { tmplines.forEach(function (line) {
if (!line.del) { if (!line.del) {
output.push(line); output.push(line);
line.p1.group = null; line.p1.group = null;

View file

@ -1,7 +1,7 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */ /** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
const HPI = Math.PI/2; const HPI = Math.PI / 2;
const RAD2DEG = 180/Math.PI; const RAD2DEG = 180 / Math.PI;
class Tool { class Tool {
constructor(settings, id, number) { constructor(settings, id, number) {
@ -102,7 +102,7 @@ class Tool {
return this.tool.type === "tapermill"; return this.tool.type === "tapermill";
} }
isDrill(){ isDrill() {
return this.tool.type === "drill"; return this.tool.type === "drill";
} }
@ -162,8 +162,8 @@ class Tool {
// z_offset = (1 - Math.cos((dist_from_center / flute_radius_pix_float) * HPI)) * -flute_radius; // z_offset = (1 - Math.cos((dist_from_center / flute_radius_pix_float) * HPI)) * -flute_radius;
} else if (taper && dist_from_center >= tip_radius_pix_float) {// if tapered and not in the flat tip radius } else if (taper && dist_from_center >= tip_radius_pix_float) {// if tapered and not in the flat tip radius
z_offset = ((dist_from_center - tip_radius_pix_float) / tip_max_radius_offset) * -shaft_offset; z_offset = ((dist_from_center - tip_radius_pix_float) / tip_max_radius_offset) * -shaft_offset;
}else if(drill){ } else if (drill) {
z_offset = -dist_from_center /45; z_offset = -dist_from_center / 45;
} }
toolOffset.push(dx, dy, z_offset); toolOffset.push(dx, dy, z_offset);
} else if (shaft_offset && larger_shaft && dist_from_center <= shaft_radius_pix_float) { } else if (shaft_offset && larger_shaft && dist_from_center <= shaft_radius_pix_float) {

View file

@ -11,7 +11,7 @@ import { Tool } from './tool.js';
const RAD2DEG = 180 / Math.PI; const RAD2DEG = 180 / Math.PI;
const DEG2RAD = Math.PI / 180; const DEG2RAD = Math.PI / 180;
class Topo { export class Topo {
constructor() { } constructor() { }
async generate(opt = {}) { async generate(opt = {}) {
@ -37,7 +37,7 @@ 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)),
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(),
@ -68,11 +68,11 @@ class Topo {
}, },
data = topo.data, data = topo.data,
newslices = [], newslices = [],
tabsMax = tabs ? Math.max(...tabs.map(tab => tab.dim.z/2 + tab.pos.z)) : 0, tabsMax = tabs ? Math.max(...tabs.map(tab => tab.dim.z / 2 + tab.pos.z)) : 0,
tabsOn = tabs, tabsOn = tabs,
tabHeight = Math.max(process.camTabsHeight + zBottom, tabsMax), tabHeight = Math.max(process.camTabsHeight + zBottom, tabsMax),
clipTab = tabsOn ? [] : null, clipTab = tabsOn ? [] : null,
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 = false,
@ -92,8 +92,8 @@ class Topo {
if (tabs) { if (tabs) {
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);
return ctab; return ctab;
}).flat()); }).flat());
} }
@ -197,7 +197,7 @@ class Topo {
const range = { min: Infinity, max: -Infinity }; const range = { min: Infinity, max: -Infinity };
// 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) {
const x = vertices[i]; const x = vertices[i];
const z = vertices[i + 2]; const z = vertices[i + 2];
vertices[i] = z; vertices[i] = z;
@ -226,9 +226,11 @@ class Topo {
// define sharded ranges // define sharded ranges
if (minions.running > 1) { if (minions.running > 1) {
dispatch.putCache({ key: widget.id, data: vertices }, { done: data => { dispatch.putCache({ key: widget.id, data: vertices }, {
done: data => {
// console.log({ put_cache_done: data }); // console.log({ put_cache_done: data });
}}); }
});
let promises = slices.map(slice => { let promises = slices.map(slice => {
return new Promise(resolve => { return new Promise(resolve => {
@ -256,9 +258,11 @@ class Topo {
return box2; return box2;
}); });
dispatch.clearCache({}, { done: data => { dispatch.clearCache({}, {
done: data => {
// console.log({ clear_cache_done: data }); // console.log({ clear_cache_done: data });
}}); }
});
} else { } else {
@ -340,8 +344,8 @@ class Topo {
let promise = new Promise(resolve => { let promise = new Promise(resolve => {
resolver = () => { resolver = () => {
// sort output slices (required for async) // sort output slices (required for async)
slicesY.sort((a,b) => a.z - b.z); slicesY.sort((a, b) => a.z - b.z);
slicesX.sort((a,b) => a.z - b.z); slicesX.sort((a, b) => a.z - b.z);
newslices.appendAll(slicesY); newslices.appendAll(slicesY);
newslices.appendAll(slicesX); newslices.appendAll(slicesX);
resolve(); resolve();
@ -369,7 +373,7 @@ class Topo {
let slice = newSlice(gridx); let slice = newSlice(gridx);
slice.camLines = segments; slice.camLines = segments;
slice.output() slice.output()
.setLayer("contour y", {face: color, line: color}) .setLayer("contour y", { face: color, line: color })
.addPolys(segments); .addPolys(segments);
slicesY.push(slice); slicesY.push(slice);
} }
@ -398,7 +402,7 @@ class Topo {
let slice = newSlice(gridy); let slice = newSlice(gridy);
slice.camLines = segments; slice.camLines = segments;
slice.output() slice.output()
.setLayer("contour x", {face: color, line: color}) .setLayer("contour x", { face: color, line: color })
.addPolys(segments); .addPolys(segments);
slicesX.push(slice); slicesX.push(slice);
} }
@ -431,7 +435,7 @@ export class Probe {
this.params = params; this.params = params;
// return the touching z given topo x,y and a tool profile // return the touching z given topo x,y and a tool profile
const toolAtZ = this.toolAtZ = function(x,y) { const toolAtZ = this.toolAtZ = function (x, y) {
let sx = stepsX, let sx = stepsX,
sy = stepsY, sy = stepsY,
xl = sx - 1, xl = sx - 1,
@ -461,13 +465,13 @@ export class Probe {
// export z probe function // export z probe function
const rx = stepsX / boundsX; const rx = stepsX / boundsX;
const ry = stepsX / boundsX; const ry = stepsX / boundsX;
const toolAtXY = this.toolAtXY = function(px, py) { const toolAtXY = this.toolAtXY = function (px, py) {
px = Math.round(rx * (px - minX)); px = Math.round(rx * (px - minX));
py = Math.round(ry * (py - minY)); py = Math.round(ry * (py - minY));
return toolAtZ(px, py); return toolAtZ(px, py);
}; };
const zAtXY = this.zAtXY = function(px, py) { const zAtXY = this.zAtXY = function (px, py) {
let ix = Math.round(rx * (px - minX)); let ix = Math.round(rx * (px - minX));
let iy = Math.round(ry * (py - minY)); let iy = Math.round(ry * (py - minY));
return data[ix * stepsY + iy] || zMin; return data[ix * stepsY + iy] || zMin;
@ -496,11 +500,11 @@ export class Trace {
this.slice = slice = []; this.slice = slice = [];
} }
const newtrace = this.newtrace = function() { const newtrace = this.newtrace = function () {
trace = newPolygon().setOpen(); trace = newPolygon().setOpen();
} }
const end_poly = this.end_poly = function(point) { const end_poly = this.end_poly = function (point) {
if (latent) { if (latent) {
trace.push(latent); trace.push(latent);
} }
@ -520,7 +524,7 @@ export class Trace {
} }
} }
const log = function(map) { const log = function (map) {
for (let key in map) { for (let key in map) {
const val = map[key]; const val = map[key];
if (typeof val === 'number') { if (typeof val === 'number') {
@ -530,7 +534,7 @@ export class Trace {
console.log(...arguments); console.log(...arguments);
} }
const push_point = this.push_point = function(x, y, z) { const push_point = 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;//trace.last();
@ -550,7 +554,7 @@ 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 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();
} }
@ -568,7 +572,7 @@ export class Trace {
const object = this.object = this; const object = this.object = this;
this.inClip = function (clips, checkZ, point) { this.inClip = function (clips, checkZ, point) {
for (let i=0; i<clips.length; i++) { for (let i = 0; i < clips.length; i++) {
let poly = clips[i]; let poly = clips[i];
let zok = checkZ ? checkZ <= poly.z : true; let zok = checkZ ? checkZ <= poly.z : true;
object.tabZ = poly.z; object.tabZ = poly.z;
@ -590,7 +594,7 @@ export class Trace {
// in this case, z is appended to clip tabs in topo constructor // in this case, z is appended to clip tabs in topo constructor
// we pass it as a side-channel and re-consitute here // we pass it as a side-channel and re-consitute here
if (clipTab) if (clipTab)
for (let i=0, l=clipTab.length; i<l; i++) { for (let i = 0, l = clipTab.length; i < l; i++) {
clipTab[i].z = clipTabZ[i]; clipTab[i].z = clipTabZ[i];
} }
@ -649,7 +653,7 @@ export class Trace {
let { from, to, x, gridx, gridy } = params; let { from, to, x, gridx, gridy } = params;
const step = resolution * density; const step = resolution * density;
const checkr = newPoint(0,0); const checkr = newPoint(0, 0);
newslice(); newslice();
newtrace(); newtrace();
for (let y = from; y < to; y += step) { for (let y = from; y < to; y += step) {
@ -687,7 +691,7 @@ export class Trace {
let { from, to, y, gridx, gridy } = params; let { from, to, y, gridx, gridy } = params;
const step = resolution * density; const step = resolution * density;
const checkr = newPoint(0,0); const checkr = newPoint(0, 0);
newslice(); newslice();
newtrace(); newtrace();
for (let x = from; x < to; x += step) { for (let x = from; x < to; x += step) {
@ -734,7 +738,7 @@ export function raster_slice(inputs) {
// emit an array of valid line-pairs // emit an array of valid line-pairs
const len = lines.length; const len = lines.length;
outer: for (let i=0; i<len; i++) { outer: for (let i = 0; i < len; i++) {
let l1 = lines[i], p1 = l1.p1, p2 = l1.p2; let l1 = lines[i], p1 = l1.p1, p2 = l1.p2;
// eliminate vertical // eliminate vertical
if (Math.abs(p1.y - p2.y) < flatness) continue; if (Math.abs(p1.y - p2.y) < flatness) continue;
@ -743,14 +747,14 @@ export function raster_slice(inputs) {
// sort p1,p2 by y for comparison // sort p1,p2 by y for comparison
if (p1.y > p2.y) { const tp = p1; p1 = p2; p2 = tp }; if (p1.y > p2.y) { const tp = p1; p1 = p2; p2 = tp };
// eliminate if points "under" other lines // eliminate if points "under" other lines
for (let j=0; j<len; j++) { for (let j = 0; j < len; j++) {
// skip self and adjacent // skip self and adjacent
if (j >= i-1 && j <= i+1) continue; if (j >= i - 1 && j <= i + 1) continue;
let l2 = lines[j], p3 = l2.p1, p4 = l2.p2; let l2 = lines[j], p3 = l2.p1, p4 = l2.p2;
// sort p3,p4 by y for comparison // sort p3,p4 by y for comparison
if (p3.y > p4.y) { const tp = p3; p3 = p4; p4 = tp }; if (p3.y > p4.y) { const tp = p3; p3 = p4; p4 = tp };
// it's under the other line // it's under the other line
if (Math.max(p1.z,p2.z) < Math.min(p3.z,p4.z)) { if (Math.max(p1.z, p2.z) < Math.min(p3.z, p4.z)) {
// it's inside the other line, too, so skip // it's inside the other line, too, so skip
if (p1.y >= p3.y && p2.y <= p4.y) continue outer; if (p1.y >= p3.y && p2.y <= p4.y) continue outer;
} }
@ -766,8 +770,8 @@ export function raster_slice(inputs) {
gridi = gridx * stepsY + gridy; gridi = gridx * stepsY + gridy;
gridv = data[gridi] || zMin; gridv = data[gridi] || zMin;
// strategy using raw lines (faster slice, but more lines) // strategy using raw lines (faster slice, but more lines)
for (i=0, il=points.length; i<il; i += 2) { for (i = 0, il = points.length; i < il; i += 2) {
const p1 = points[i], p2 = points[i+1]; const p1 = points[i], p2 = points[i + 1];
// one endpoint above grid // one endpoint above grid
const crossz = (p1.z > gridv || p2.z > gridv); const crossz = (p1.z > gridv || p2.z > gridv);
// segment crosses grid y // segment crosses grid y
@ -783,7 +787,7 @@ export function raster_slice(inputs) {
if (nz > gridv) { if (nz > gridv) {
gridv = data[gridi] = Math.max(nz, zMin); gridv = data[gridi] = Math.max(nz, zMin);
if (slice) slice.output() if (slice) slice.output()
.setLayer("heights", {face: 0, line: 0}) .setLayer("heights", { face: 0, line: 0 })
.addLine( .addLine(
newPoint(p1.x, y, 0), newPoint(p1.x, y, 0),
newPoint(p1.x, y, gridv) newPoint(p1.x, y, gridv)
@ -797,9 +801,9 @@ export function raster_slice(inputs) {
// remove flat lines when curvesOnly // remove flat lines when curvesOnly
if (curvesOnly) { if (curvesOnly) {
let nup = []; let nup = [];
for (let i=0, p=points, l=p.length; i<l; i += 2) { for (let i = 0, p = points, l = p.length; i < l; i += 2) {
const p1 = p[i]; const p1 = p[i];
const p2 = p[i+1]; const p2 = p[i + 1];
if (Math.abs(p1.z - p2.z) >= flatness) { if (Math.abs(p1.z - p2.z) >= flatness) {
nup.push(p1, p2); nup.push(p1, p2);
} }

View file

@ -4,6 +4,7 @@ import { codec } from '../../core/codec.js';
import { newPoint } from '../../../geo/point.js'; import { newPoint } from '../../../geo/point.js';
import { newPolygon } from '../../../geo/polygon.js'; import { newPolygon } from '../../../geo/polygon.js';
import { sliceConnect } from '../../../geo/slicer.js'; import { sliceConnect } from '../../../geo/slicer.js';
import { newSlice } from '../../core/slice.js';
import { Tool } from './tool.js'; import { Tool } from './tool.js';
import { Slicer as topo_slicer } from './slicer_topo.js'; import { Slicer as topo_slicer } from './slicer_topo.js';
@ -16,7 +17,7 @@ function scale(fn, factor = 1, base = 0) {
} }
} }
class Topo4 { export class Topo {
constructor() { } constructor() { }
async generate(opt = {}) { async generate(opt = {}) {
@ -91,7 +92,7 @@ class Topo4 {
const box = this.box = new THREE.Box2(); 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) {
const x = vertices[i]; const x = vertices[i];
const z = vertices[i + 2] + zoff; const z = vertices[i + 2] + zoff;
vertices[i] = z; vertices[i] = z;
@ -105,7 +106,7 @@ class Topo4 {
range.max += this.diam / 2; range.max += this.diam / 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) {
const x = tabverts[i]; const x = tabverts[i];
const z = tabverts[i + 2] + zoff; const z = tabverts[i + 2] + zoff;
tabverts[i] = z; tabverts[i] = z;
@ -132,7 +133,8 @@ class Topo4 {
slices.push(slice); slices.push(slice);
// console.log({ shards, range, step, slices }); // console.log({ shards, range, step, slices });
if (kiri.minions.running > 1) { const { minions } = self.kiri_worker;
if (minions?.running > 1) {
return await this.sliceMinions(onupdate); return await this.sliceMinions(onupdate);
} else { } else {
return await this.sliceWorker(onupdate); return await this.sliceWorker(onupdate);
@ -158,7 +160,7 @@ class Topo4 {
slice.index = rec.index; slice.index = rec.index;
slice.addTops(sliceConnect(rec.lines)); slice.addTops(sliceConnect(rec.lines));
const points = codec.encodePointArray(rec.lines.map(l => [ l.p1, l.p2 ]).flat()); const points = codec.encodePointArray(rec.lines.map(l => [l.p1, l.p2]).flat());
const shared = new Float32Array(new SharedArrayBuffer(points.length * 4)); const shared = new Float32Array(new SharedArrayBuffer(points.length * 4));
shared.set(points); shared.set(points);
slice.shared = shared; slice.shared = shared;
@ -166,7 +168,7 @@ class Topo4 {
return slice; return slice;
}); });
output.appendAll(recs); output.appendAll(recs);
onupdate(++complete /slices.length); onupdate(++complete / slices.length);
} }
return output; return output;
@ -195,14 +197,15 @@ class Topo4 {
.map(rec => newSlice(rec.z) .map(rec => newSlice(rec.z)
.addTops(rec.polys) .addTops(rec.polys)
.setFields({ shared: rec.shared })) .setFields({ shared: rec.shared }))
.sort((a,b) => a.z - b.z); .sort((a, b) => a.z - b.z);
clearCache(); clearCache();
return output; return output;
} }
async lathe(onupdate) { async lathe(onupdate) {
if (kiri.minions.running > 1) { const { minions } = self.kiri_worker;
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);
@ -223,7 +226,7 @@ class Topo4 {
const lines = slice.lines; const lines = slice.lines;
const plen = lines.length; const plen = lines.length;
const rec = { z: slice.z, lines: [] }; const rec = { z: slice.z, lines: [] };
for (let i = 0; i < plen; ) { for (let i = 0; i < plen;) {
++i; // skip x which should match slice.z ++i; // skip x which should match slice.z
let py0 = lines[i++]; let py0 = lines[i++];
const pz0 = lines[i++]; const pz0 = lines[i++];
@ -245,7 +248,7 @@ class Topo4 {
const rx = oslices[si].z; const rx = oslices[si].z;
let mz = -Infinity; let mz = -Infinity;
// iterate over tool offsets // iterate over tool offsets
for (let ti = 0; ti < tlen; ) { for (let ti = 0; ti < tlen;) {
// tool offset in grid units from present x (si) // tool offset in grid units from present x (si)
const xo = tool[ti++]; // x grid offset (slice) const xo = tool[ti++]; // x grid offset (slice)
const yo = tool[ti++] * resolution; // y grid offset (mult rez to get real y) const yo = tool[ti++] * resolution; // y grid offset (mult rez to get real y)
@ -259,7 +262,7 @@ class Topo4 {
const slice = oslices[ts]; const slice = oslices[ts];
const lines = slice.lines; const lines = slice.lines;
const plen = lines.length; const plen = lines.length;
for (let i = 0; i < plen; ) { for (let i = 0; i < plen;) {
++i; // skip x which should match slice.z ++i; // skip x which should match slice.z
let py0 = lines[i++]; let py0 = lines[i++];
const pz0 = lines[i++]; const pz0 = lines[i++];
@ -293,7 +296,7 @@ class Topo4 {
} }
async latheWorker(onupdate) { async latheWorker(onupdate) {
const { sliced, tool, zoff, leave } = this; const { sliced, tool, zoff, leave, linear } = this;
const rota = this.angle * DEG2RAD; const rota = this.angle * DEG2RAD;
const steps = (Math.PI * 2) / rota; const steps = (Math.PI * 2) / rota;
@ -316,20 +319,33 @@ class Topo4 {
onupdate(count / steps); onupdate(count / steps);
} }
count = recs[0].heights.length / 3; count = linear ? recs.length : recs[0].heights.length / 3;
// count = recs[0].heights.length / 3;
while (count-- > 0) { while (count-- > 0) {
let slice = newSlice(count); let slice = newSlice(count);
slice.camLines = [ newPolygon().setOpen() ]; slice.camLines = [newPolygon().setOpen()];
paths.push(slice); paths.push(slice);
} }
if (linear) {
recs.forEach((rec, i) => {
const { degrees, heights } = rec;
[...heights].group(3).forEach((a) => {
paths[i].camLines[0].push(newPoint(a[0], a[1], a[2] + leave).setA(degrees));
});
if (i % 2 === 1) {
paths[i].camLines[0].reverse();
}
});
} else {
for (let rec of recs) { for (let rec of recs) {
const { degrees, heights } = rec; const { degrees, heights } = rec;
[...heights].group(3).forEach((a,i) => { [...heights].group(3).forEach((a, i) => {
// progress each path 360 degrees to prevent A rolling backwards // progress each path 360 degrees to prevent A rolling backwards
paths[i].camLines[0].push( newPoint(a[0], a[1], a[2] + leave).setA(degrees + i * -360) ); paths[i].camLines[0].push(newPoint(a[0], a[1], a[2] + leave).setA(degrees + i * -360));
}); });
} }
}
for (let slice of paths) { for (let slice of paths) {
const poly = slice.camLines[0]; const poly = slice.camLines[0];
@ -342,7 +358,7 @@ class Topo4 {
slice.camLines[0].push(repeat.clone().setA(repeat.a - 360)); slice.camLines[0].push(repeat.clone().setA(repeat.a - 360));
slice.output() slice.output()
.setLayer("lathe", { line: this.lineColor }) .setLayer("lathe", { line: this.lineColor })
.addPoly(poly.clone().applyRotations().move({ z: -zoff, x:0, y:0 })); .addPoly(poly.clone().applyRotations().move({ z: -zoff, x: 0, y: 0 }));
} }
// console.log({ tool, slices, paths }); // console.log({ tool, slices, paths });
@ -392,20 +408,18 @@ class Topo4 {
await Promise.all(promises); await Promise.all(promises);
recs.sort((a, b) => { return b.angle - a.angle }); recs.sort((a, b) => { return b.angle - a.angle });
// let linear = true;
count = linear ? recs.length : recs[0].heights.length / 3; count = linear ? recs.length : recs[0].heights.length / 3;
while (count-- > 0) { while (count-- > 0) {
let slice = newSlice(count); let slice = newSlice(count);
slice.camLines = [ newPolygon().setOpen() ]; slice.camLines = [newPolygon().setOpen()];
paths.push(slice); paths.push(slice);
} }
if (linear) { if (linear) {
recs.forEach((rec,i) => { recs.forEach((rec, i) => {
const { degrees, heights } = rec; const { degrees, heights } = rec;
[...heights].group(3).forEach((a) => { [...heights].group(3).forEach((a) => {
paths[i].camLines[0].push( newPoint(a[0], a[1], a[2] + leave).setA(degrees) ); paths[i].camLines[0].push(newPoint(a[0], a[1], a[2] + leave).setA(degrees));
}); });
if (i % 2 === 1) { if (i % 2 === 1) {
paths[i].camLines[0].reverse(); paths[i].camLines[0].reverse();
@ -414,9 +428,9 @@ class Topo4 {
} else { } else {
for (let rec of recs) { for (let rec of recs) {
const { degrees, heights } = rec; const { degrees, heights } = rec;
[...heights].group(3).forEach((a,i) => { [...heights].group(3).forEach((a, i) => {
// progress each path 360 degrees to prevent A rolling backwards // progress each path 360 degrees to prevent A rolling backwards
paths[i].camLines[0].push( newPoint(a[0], a[1], a[2] + leave).setA(degrees + i * -360) ); paths[i].camLines[0].push(newPoint(a[0], a[1], a[2] + leave).setA(degrees + i * -360));
}); });
} }
} }
@ -434,7 +448,7 @@ class Topo4 {
} }
slice.output() slice.output()
.setLayer("lathe", { line: this.lineColor }) .setLayer("lathe", { line: this.lineColor })
.addPoly(poly.clone().applyRotations().move({ z: -zoff, x:0, y:0 })); .addPoly(poly.clone().applyRotations().move({ z: -zoff, x: 0, y: 0 }));
} }
// console.log({ tool, slices, paths }); // console.log({ tool, slices, paths });
@ -444,24 +458,27 @@ class Topo4 {
} }
putCache(key, data) { putCache(key, data) {
kiri.worker.putCache({ key, data }, { done: data => { } }); const { dispatch } = self.kiri_worker;
dispatch.putCache({ key, data }, { done: data => { } });
} }
clearCache() { clearCache() {
kiri.worker.clearCache({}, { done: data => { } }); const { dispatch } = self.kiri_worker;
dispatch.clearCache({}, { done: data => { } });
} }
queue(cmd, params) { queue(cmd, params) {
const { minions } = self.kiri_worker;
return new Promise(resolve => { return new Promise(resolve => {
kiri.minions.queue({ cmd, ...params }, resolve); minions.queue({ cmd, ...params }, resolve);
}); });
} }
} }
function rotatePoints(lines, rot) { export function rotatePoints(lines, rot) {
new THREE.BufferAttribute(lines, 3).applyMatrix4(rot); new THREE.BufferAttribute(lines, 3).applyMatrix4(rot);
} }
export function Topo4(opt) { export async function generate(opt) {
return new Topo4().generate(opt); return new Topo().generate(opt);
}; };

View file

@ -0,0 +1,188 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { api } from '../../core/api.js';
let LANG = api.language.current;
let { FDM } = api.const.MODES,
{ $ } = api.web,
{ uc, ui } = api,
{ bound, toInt, toFloat } = uc,
{ newBlank, newButton, newBoolean, newGroup, newInput, newSelect, newRow } = uc,
driven = true,
hideable = true,
separator = true,
trigger = true
;
function onBooleanClick(el) {
api.event.emit('click.boolean', el);
}
function onButtonClick(el) {
api.event.emit('click.button', el);
}
function thinWallSave() {
let opt = ui.sliceDetectThin;
let level = opt.options[opt.selectedIndex];
if (level) {
api.conf.get().process.sliceDetectThin = level.value;
api.conf.save();
}
}
function isMultiHead() {
let dev = api.conf.get().device;
return isNotBelt() && dev.extruders && dev.extruders.length > 1;
}
function optSelected(sel) {
let opt = sel.options[sel.selectedIndex];
return opt ? opt.value : undefined;
}
function hasInfill() {
return optSelected(ui.sliceFillType) !== 'none'
}
function fillIsLinear() {
return hasInfill() && optSelected(ui.sliceFillType) === 'linear';
}
function zIntShow() {
return settings().controller.devel;
}
function isBelt() {
return api.device.isBelt();
}
function isNotBelt() {
return !isBelt();
}
export function menu() {
return {
/** Left Side Menu */
_____: newGroup(LANG.sl_menu, $('fdm-layers'), { modes:FDM, driven, hideable, separator, group:"fdm-layers" }),
sliceHeight: newInput(LANG.sl_lahi_s, { title:LANG.sl_lahi_l, convert:toFloat }),
sliceMinHeight: newInput(LANG.ad_minl_s, { title:LANG.ad_minl_l, convert:toFloat, bound:bound(0,3.0), show:() => ui.sliceAdaptive.checked }),
sliceTopLayers: newInput(LANG.sl_ltop_s, { title:LANG.sl_ltop_l, convert:toInt }),
sliceBottomLayers: newInput(LANG.sl_lbot_s, { title:LANG.sl_lbot_l, convert:toInt }),
separator: newBlank({ class:"set-sep", driven }),
sliceAdaptive: newBoolean(LANG.ad_adap_s, onBooleanClick, { title: LANG.ad_adap_l }),
_____: 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 }),
sliceLineWidth: newInput(LANG.sl_line_s, { title:LANG.sl_line_l, convert:toFloat, bound:bound(0,5) }),
separator: newBlank({ class:"set-sep", driven }),
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"),
outputAlternating: newBoolean(LANG.ad_altr_s, onBooleanClick, {title:LANG.ad_altr_l}),
sliceZInterleave: newBoolean(LANG.ad_zint_s, onBooleanClick, {title:LANG.ad_zint_l, show:zIntShow}),
_____: newGroup(LANG.fi_menu, $('fdm-fill'), { modes:FDM, driven, hideable, separator, group:"fdm-fill" }),
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}),
sliceFillRepeat: newInput(LANG.fi_rept_s, {title:LANG.fi_rept_l, convert:toInt, bound:bound(1,10), show:fillIsLinear}),
sliceFillOverlap: newInput(LANG.fi_over_s, {title:LANG.fi_over_l, convert:toFloat, bound:bound(0.0,2.0), show:hasInfill}),
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)}),
separator: newBlank({ class:"set-sep", driven }),
sliceFillGrow: newInput(LANG.fi_grow_s, {title:LANG.fi_grow_l, convert:toFloat}),
sliceFillAngle: newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:toFloat}),
_____: 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}),
outputBedTemp: newInput(LANG.ou_bedd_s, {title:LANG.ou_bedd_l, convert:toInt}),
_____: 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) }),
outputFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255)}),
_____: newGroup(LANG.sp_menu, $('fdm-support'), { modes:FDM, driven, hideable, separator, group:"fdm-supp" }),
sliceSupportNozzle: newSelect(LANG.sp_nozl_s, {title:LANG.sp_nozl_l, show:isMultiHead}, "extruders"),
sliceSupportDensity: newInput(LANG.sp_dens_s, {title:LANG.sp_dens_l, convert:toFloat, bound:bound(0.0,1.0)}),
sliceSupportSize: newInput(LANG.sp_size_s, {title:LANG.sp_size_l, convert:toFloat, bound:bound(1.0,200.0)}),
sliceSupportOffset: newInput(LANG.sp_offs_s, {title:LANG.sp_offs_l, convert:toFloat, bound:bound(0.0,200.0)}),
sliceSupportGap: newInput(LANG.sp_gaps_s, {title:LANG.sp_gaps_l, convert:toInt, bound:bound(0,5)}),
sliceSupportArea: newInput(LANG.sp_area_s, {title:LANG.sp_area_l, convert:toFloat, bound:bound(0.0,200.0)}),
sliceSupportExtra: newInput(LANG.sp_xpnd_s, {title:LANG.sp_xpnd_l, convert:toFloat, bound:bound(0.0,10.0)}),
sliceSupportGrow: newInput(LANG.sp_grow_s, {title:LANG.sp_grow_l, convert:toFloat, bound:bound(0.0,10.0)}),
sliceSupportAngle: newInput(LANG.sp_angl_s, {title:LANG.sp_angl_l, convert:toFloat, bound:bound(0.0,90.0)}),
sliceSupportSpan: newInput(LANG.sp_span_s, {title:LANG.sp_span_l, convert:toFloat, bound:bound(0.0,200.0), show:() => ui.sliceSupportEnable.checked }),
separator: newBlank({ class:"set-sep", driven }),
sliceSupportEnable: newBoolean(LANG.sp_auto_s, onBooleanClick, {title:LANG.sp_auto_l, show:isNotBelt}),
sliceSupportOutline: newBoolean(LANG.sp_outl_s, onBooleanClick, {title:LANG.sp_outl_l}),
separator: newBlank({ class:"set-sep", driven }),
sliceSupportGen: newRow([
ui.ssaGen = newButton(LANG.sp_detect, onButtonClick, {class: "f-col grow a-center"})
], { modes: FDM, class: "ext-buttons f-row grow" }),
separator: newBlank({ class:"set-sep", driven }),
sliceSupportManual: newRow([
(ui.ssmAdd = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
(ui.ssmDun = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
(ui.ssmClr = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
], {class:"ext-buttons f-row"}),
_____: newGroup(LANG.fl_menu, $('fdm-base'), { modes:FDM, driven, hideable, separator, group:"fdm-base" }),
firstSliceHeight: newInput(LANG.fl_lahi_s, {title:LANG.fl_lahi_l, convert:toFloat, show:isNotBelt}),
firstLayerNozzleTemp:newInput(LANG.fl_nozl_s, {title:LANG.fl_nozl_l, convert:toInt, show:isNotBelt}),
firstLayerBedTemp: newInput(LANG.fl_bedd_s, {title:LANG.fl_bedd_l, convert:toInt, show:isNotBelt}),
separator: newBlank({ class:"set-sep", driven }),
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}),
firstLayerFlatten: newInput(LANG.fl_flat_s, {title:LANG.fl_flat_l, convert:toFloat, show:isBelt}),
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}),
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 }),
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}),
firstLayerBrimTrig: newInput(LANG.fl_brmn_s, {title:LANG.fl_brmn_l, convert:toInt, show:isBelt}),
firstLayerBrimGap: newInput(LANG.fl_brgp_s, {title:LANG.fl_brgp_l, convert:toFloat, show:isBelt}),
separator: newBlank({ class:"set-sep", driven, show:isBelt }),
firstLayerBeltLead: newInput(LANG.fl_bled_s, {title:LANG.fl_bled_l, convert:toFloat, show:isBelt}),
firstLayerBeltBump: newInput(LANG.fl_blmp_s, {title:LANG.fl_blmp_l, convert:toFloat, bound:bound(0, 10), show:isBelt}),
separator: newBlank({ class:"set-sep", driven, show:isNotBelt }),
outputBrimCount: newInput(LANG.fl_skrt_s, {title:LANG.fl_skrt_l, convert:toInt, show:isNotBelt}),
outputBrimOffset: newInput(LANG.fl_skro_s, {title:LANG.fl_skro_l, convert:toFloat, show: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 }),
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" }),
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}),
outputSeekrate: newInput(LANG.ou_move_s, {title:LANG.ou_move_l, convert:toInt}),
separator: newBlank({ class:"set-sep", driven }),
outputShellMult: newInput(LANG.ou_shml_s, {title:LANG.ou_exml_l, convert:toFloat, bound:bound(0.0,2.0)}),
outputFillMult: newInput(LANG.ou_flml_s, {title:LANG.ou_exml_l, convert:toFloat, bound:bound(0.0,2.0)}),
outputSparseMult: newInput(LANG.ou_spml_s, {title:LANG.ou_exml_l, convert:toFloat, bound:bound(0.0,2.0)}),
separator: newBlank({ class:"set-sep", driven }),
outputRetractDist: newInput(LANG.ad_rdst_s, {title:LANG.ad_rdst_l, convert:toFloat}),
outputRetractSpeed: newInput(LANG.ad_rrat_s, {title:LANG.ad_rrat_l, convert:toInt}),
outputRetractWipe: newInput(LANG.ad_wpln_s, {title:LANG.ad_wpln_l, convert:toFloat, bound:bound(0.0,10)}),
separator: newBlank({ class:"set-sep", driven }),
sliceLayerStart: newSelect(LANG.sl_strt_s, {title:LANG.sl_strt_l}, "start"),
outputLayerRetract: newBoolean(LANG.ad_lret_s, onBooleanClick, {title:LANG.ad_lret_l}),
outputAvoidGaps: newBoolean(LANG.ad_agap_s, onBooleanClick, {title:LANG.ad_agap_l}),
separator: newBlank({ class:"set-sep", driven, show:isBelt }),
outputBeltFirst: newBoolean(LANG.ad_lbir_s, onBooleanClick, {title:LANG.ad_lbir_l, show:isBelt}),
_____: newGroup(LANG.ad_menu, $('fdm-expert'), { modes:FDM, driven, hideable, separator, group:"fdm-xprt" }),
sliceAngle: newInput(LANG.sl_angl_s, {title:LANG.sl_angl_l, convert:toFloat, show:isBelt}),
outputRetractDwell: newInput(LANG.ad_rdwl_s, {title:LANG.ad_rdwl_l, convert:toInt}),
sliceSolidMinArea: newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:toFloat}),
outputMinSpeed: newInput(LANG.ad_mins_s, {title:LANG.ad_mins_l, convert:toFloat, bound:bound(1,200)}),
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)}),
zHopDistance: newInput(LANG.ad_zhop_s, {title:LANG.ad_zhop_l, convert:toFloat, bound:bound(0,3.0)}),
arcTolerance: newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, convert:toFloat, bound:bound(0,1.0), show:() => { return isNotBelt() }}),
antiBacklash: newInput(LANG.ad_abkl_s, {title:LANG.ad_abkl_l, convert:toInt, bound:bound(0,3)}),
outputLoops: newInput(LANG.ag_loop_s, {title:LANG.ag_loop_l, convert:toInt, bound:bound(-1,1000), show:isBelt}),
outputPurgeTower: newInput(LANG.ad_purg_s, {title:LANG.ad_purg_l, convert:toInt, bound:bound(0,1000)}),
fdmRanges: $('fdm-ranges'),
};
}

View file

@ -0,0 +1,74 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { api } from '../../core/api.js';
let LANG = api.language.current;
let { LASER, DRAG, WJET, WEDM } = api.const.MODES,
TWOD = [ LASER, DRAG, WJET, WEDM ],
TWONED = [ LASER, DRAG, WJET ],
{ $ } = api.web,
{ uc, ui } = api,
{ bound, toInt, toFloat } = uc,
{ newBlank, newButton, newBoolean, newGroup, newInput, newRow } = uc,
driven = true,
separator = true,
trigger = true
;
function onBooleanClick(el) {
api.event.emit('click.boolean', el);
}
function onButtonClick(el) {
api.event.emit('click.button', el);
}
export function menu() {
return {
/** Left Side Menu */
_____: newGroup(LANG.sl_menu, $('lzr-slice'), { modes:TWOD, driven, separator }),
ctSliceKerf: newInput(LANG.ls_offs_s, {title:LANG.ls_offs_l, convert:toFloat}),
ctSliceHeight: newInput(LANG.ls_lahi_s, {title:LANG.ls_lahi_l, convert:toFloat, trigger}),
ctSliceHeightMin: newInput(LANG.ls_lahm_s, {title:LANG.ls_lahm_l, convert:toFloat, show:() => ui.ctSliceHeight.value == 0 && !ui.ctSliceSingle.checked }),
separator: newBlank({ class:"set-sep", driven }),
ctSliceSingle: newBoolean(LANG.ls_sngl_s, onBooleanClick, {title:LANG.ls_sngl_l}),
ctOmitInner: newBoolean(LANG.we_omit_s, onBooleanClick, {title:LANG.we_omit_l, modes:WEDM}),
_____: newGroup('surfaces', $('lzr-surface'), { modes:[-1], driven, separator }),
ctSurfaces: newRow([
(ui.faceAdd = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
(ui.faceDun = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
(ui.faceClr = newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
], {class:"ext-buttons f-row", modes:WEDM}),
_____: newGroup(LANG.dk_menu, $('lzr-knife'), { modes:DRAG, marker:true, driven, separator }),
ctOutKnifeDepth: newInput(LANG.dk_dpth_s, { title:LANG.dk_dpth_l, convert:toFloat, bound:bound(0.0,5.0) }),
ctOutKnifePasses: newInput(LANG.dk_pass_s, { title:LANG.dk_pass_l, convert:toInt, bound:bound(0,5) }),
ctOutKnifeTip: newInput(LANG.dk_offs_s, { title:LANG.dk_offs_l, convert:toFloat, bound:bound(0.0,10.0) }),
_____: newGroup(LANG.lo_menu, $('lzr-layout'), { modes:TWOD, driven, separator }),
ctOutTileSpacing: newInput(LANG.ou_spac_s, { title:LANG.ou_spac_l, convert:toInt }),
ctOutMerged: newBoolean(LANG.ou_mrgd_s, onBooleanClick, {title:LANG.ou_mrgd_l, modes:TWONED, show:() => !ui.ctOutStack.checked }),
ctOutGroup: newBoolean(LANG.ou_grpd_s, onBooleanClick, {title:LANG.ou_grpd_l, show:() => !(ui.ctOutMark.checked || ui.ctOutStack.checked) }),
_____: newGroup(LANG.ou_menu, $('lzr-output'), { modes:TWOD, driven, separator, group:"lzr-output" }),
ctOutPower: newInput(LANG.ou_powr_s, {title:LANG.ou_powr_l, convert:toInt, bound:bound(1,100), modes:TWONED }),
ctOutSpeed: newInput(LANG.ou_sped_s, {title:LANG.ou_sped_l, convert:toInt }),
ctAdaptive: newBoolean('adaptive speed', onBooleanClick, {modes:WEDM, title:'controller determines best cutting speed based on material feedback at runtime'}),
separator: newBlank({ class:"set-sep", driven }),
ctOriginBounds: newBoolean(LANG.or_bnds_s, onBooleanClick, { title:LANG.or_bnds_l, show:() => !ui.ctOriginCenter.checked }),
ctOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, { title:LANG.or_cntr_l, show:() => !ui.ctOriginBounds.checked }),
separator: newBlank({ class:"set-sep", driven, modes:WEDM, show:() => ui.ctOriginBounds.checked }),
ctOriginOffX: newInput(LANG.or_offx_s, { title:LANG.or_offx_l, convert:toFloat, modes:WEDM, show:() => ui.ctOriginBounds.checked }),
ctOriginOffY: newInput(LANG.or_offy_s, { title:LANG.or_offy_l, convert:toFloat, modes:WEDM, show:() => ui.ctOriginBounds.checked }),
separator: newBlank({ class:"set-sep", driven, modes:TWONED }),
ctOutZColor: newBoolean(LANG.ou_layo_s, onBooleanClick, { title:LANG.ou_layo_l, modes:TWONED, show:() => !ui.ctOutMerged.checked }),
ctOutLayer: newBoolean(LANG.ou_layr_s, onBooleanClick, { title:LANG.ou_layr_l, modes:TWONED, show:() => !ui.ctOutStack.checked }),
ctOutMark: newBoolean(LANG.ou_lays_s, onBooleanClick, { title:LANG.ou_lays_l, modes:TWONED, show:() => !ui.ctOutStack.checked }),
separator: newBlank({ class:"set-sep", driven, modes:LASER }),
ctOutInches: newBoolean(LANG.ou_inch_s, onBooleanClick, { title:LANG.ou_inch_l, modes:LASER }),
ctOutStack: newBoolean(LANG.ou_stak_s, onBooleanClick, { title:LANG.ou_stak_l, modes:LASER }),
ctOutShaper: newBoolean(LANG.ou_shap_s, onBooleanClick, { title:LANG.ou_shap_l, modes:LASER, show:() => ui.ctOutStack.checked }),
};
}

View file

@ -0,0 +1,58 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { api } from '../../core/api.js';
let LANG = api.language.current;
let { SLA } = api.const.MODES,
{ $ } = api.web,
{ uc, ui } = api,
{ bound, toInt, toFloat } = uc,
{ newBoolean, newGroup, newInput, newSelect } = uc,
driven = true,
separator = true
;
function onBooleanClick(el) {
api.event.emit('click.boolean', el);
}
export function menu() {
return {
/** Left Side Menu */
slaProc: newGroup(LANG.sa_menu, $('sla-slice'), { modes:SLA, group:"sla-slice", driven, separator }),
slaSlice: newInput(LANG.sa_lahe_s, {title:LANG.sa_lahe_l, convert:toFloat}),
slaShell: newInput(LANG.sa_shel_s, {title:LANG.sa_shel_l, convert:toFloat}),
slaOpenTop: newBoolean(LANG.sa_otop_s, onBooleanClick, {title:LANG.sa_otop_l}),
slaOpenBase: newBoolean(LANG.sa_obas_s, onBooleanClick, {title:LANG.sa_obas_l}),
slaLayers: newGroup(LANG.sa_layr_m, $('sla-layers'), { modes:SLA, group:"sla-layers", driven, separator }),
slaLayerOn: newInput(LANG.sa_lton_s, {title:LANG.sa_lton_l, convert:toFloat}),
slaLayerOff: newInput(LANG.sa_ltof_s, {title:LANG.sa_ltof_l, convert:toFloat}),
slaPeelDist: newInput(LANG.sa_pldi_s, {title:LANG.sa_pldi_l, convert:toFloat}),
slaPeelLiftRate: newInput(LANG.sa_pllr_s, {title:LANG.sa_pllr_l, convert:toFloat}),
slaPeelDropRate: newInput(LANG.sa_pldr_s, {title:LANG.sa_pldr_l, convert:toFloat}),
slaBase: newGroup(LANG.sa_base_m, $('sla-base'), { modes:SLA, group:"sla-base", driven, separator }),
slaBaseLayers: newInput(LANG.sa_balc_s, {title:LANG.sa_balc_l, convert:toInt}),
slaBaseOn: newInput(LANG.sa_lton_s, {title:LANG.sa_bltn_l, convert:toFloat}),
slaBaseOff: newInput(LANG.sa_ltof_s, {title:LANG.sa_bltf_l, convert:toFloat}),
slaBasePeelDist: newInput(LANG.sa_pldi_s, {title:LANG.sa_pldi_l, convert:toFloat}),
slaBasePeelLiftRate: newInput(LANG.sa_pllr_s, {title:LANG.sa_pllr_l, convert:toFloat}),
slaFill: newGroup(LANG.sa_infl_m, $('sla-fill'), { modes:SLA, group:"sla-infill", driven, separator }),
slaFillDensity: newInput(LANG.sa_ifdn_s, {title:LANG.sa_ifdn_l, convert:toFloat, bound:bound(0,1)}),
slaFillLine: newInput(LANG.sa_iflw_s, {title:LANG.sa_iflw_l, convert:toFloat, bound:bound(0,5)}),
slaSupport: newGroup(LANG.sa_supp_m, $('sla-support'), { modes:SLA, group:"sla-support", driven, separator }),
slaSupportLayers: newInput(LANG.sa_slyr_s, {title:LANG.sa_slyr_l, convert:toInt, bound:bound(5,100)}),
slaSupportGap: newInput(LANG.sa_slgp_s, {title:LANG.sa_slgp_l, convert:toInt, bound:bound(3,30)}),
slaSupportDensity: newInput(LANG.sa_sldn_s, {title:LANG.sa_sldn_l, convert:toFloat, bound:bound(0.01,0.9)}),
slaSupportSize: newInput(LANG.sa_slsz_s, {title:LANG.sa_slsz_l, convert:toFloat, bound:bound(0.1,1)}),
slaSupportPoints: newInput(LANG.sa_slpt_s, {title:LANG.sa_slpt_l, convert:toInt, bound:bound(3,10)}),
slaSupportEnable: newBoolean(LANG.enable, onBooleanClick, {title:LANG.sl_slen_l}),
slaOutput: newGroup(LANG.sa_outp_m, $('sla-output'), { modes:SLA, driven, separator, group:"sla-output" }),
slaFirstOffset: newInput(LANG.sa_opzo_s, {title:LANG.sa_opzo_l, convert:toFloat, bound:bound(0,1)}),
slaAntiAlias: newSelect(LANG.sa_opaa_s, {title:LANG.sa_opaa_l}, "antialias"),
};
}

View file

@ -10,7 +10,7 @@ import { load } from '../../load/file.js';
import { newWidget } from '../core/widget.js'; import { newWidget } from '../core/widget.js';
class Engine { class Engine {
constructor() { constructor({ workURL, poolURL } = {}) {
this.widget = newWidget(); this.widget = newWidget();
this.settings = { this.settings = {
mode: "FDM", mode: "FDM",
@ -22,7 +22,13 @@ class Engine {
widget: { [this.widget.id]: {} } widget: { [this.widget.id]: {} }
}; };
this.listener = () => { }; this.listener = () => { };
client.start(); try {
client.setWorkPath(workURL);
client.setPoolPath(poolURL);
client.restart();
} catch (error) {
console.log({ error });
}
} }
load(url) { load(url) {
@ -190,7 +196,7 @@ class Engine {
} }
export function newEngine() { export function newEngine() {
return new Engine(); return new Engine(...arguments);
} }
export { Engine }; export { Engine };

View file

@ -11,10 +11,11 @@ import { codec, encode, encodePointArray } from '../core/codec.js';
import { doTopShells } from '../mode/fdm/post.js'; import { doTopShells } from '../mode/fdm/post.js';
import { newPoint } from '../../geo/point.js'; import { newPoint } from '../../geo/point.js';
import { polygons as POLY } from '../../geo/polygons.js'; import { polygons as POLY } from '../../geo/polygons.js';
import { sliceZ } from '../../geo/slicer.js'; import { sliceZ, sliceConnect } from '../../geo/slicer.js';
import { Slicer as cam_slicer } from '../mode/cam/slicer.js'; import { Slicer as cam_slicer } from '../mode/cam/slicer.js';
import { Slicer as topo_slicer } from '../mode/cam/slicer_topo.js'; import { Slicer as topo_slicer } from '../mode/cam/slicer_topo.js';
import { Probe, Trace, raster_slice } from '../mode/cam/topo3.js'; import { Probe, Trace, raster_slice } from '../mode/cam/topo3.js';
import { Topo as Topo4, rotatePoints } from '../mode/cam/topo4.js';
import { wasm_ctrl } from '../../geo/wasm.js'; import { wasm_ctrl } from '../../geo/wasm.js';
const clib = self.ClipperLib; const clib = self.ClipperLib;
@ -53,9 +54,13 @@ function debug() {
// console.log(`[${name}]`, ...arguments); // console.log(`[${name}]`, ...arguments);
} }
let invalid = 0;
const funcs = self.minion = { const funcs = self.minion = {
invalid(data, seq, cmd) { invalid(data, seq, cmd) {
if (invalid++ < 20) {
// prevent flooding console and killing session
console.error({ invalid_minion_command: cmd, data }); console.error({ invalid_minion_command: cmd, data });
}
reply({ seq, error: `invalid command (${cmd})` }); reply({ seq, error: `invalid command (${cmd})` });
}, },
@ -271,7 +276,7 @@ const funcs = self.minion = {
topo4_slice(data, seq) { topo4_slice(data, seq) {
const { slice, resolution } = data; const { slice, resolution } = data;
const vertices = cache.vertices; const vertices = cache.vertices;
const recs = new kiri.topo_slicer(slice.index) const recs = new topo_slicer(slice.index)
.setFromArray(vertices, slice) .setFromArray(vertices, slice)
.slice(resolution) .slice(resolution)
.map(rec => { .map(rec => {

View file

@ -3,7 +3,6 @@
import '../../add/array.js'; import '../../add/array.js';
import '../../add/class.js'; import '../../add/class.js';
import '../../add/three.js'; import '../../add/three.js';
import '../../ext/jszip.js';
import { base } from '../../geo/base.js'; import { base } from '../../geo/base.js';
import { codec } from '../core/codec.js'; import { codec } from '../core/codec.js';
@ -17,6 +16,7 @@ import { wasm_ctrl } from '../../geo/wasm.js';
import { version } from '../../moto/license.js'; import { version } from '../../moto/license.js';
import { Widget, newWidget } from '../core/widget.js'; import { Widget, newWidget } from '../core/widget.js';
import { load } from "../../load/png.js"; import { load } from "../../load/png.js";
import { JSZip } from '../../ext/jszip-esm.js';
import { CAM } from '../mode/cam/driver-be.js'; import { CAM } from '../mode/cam/driver-be.js';
import { DRAG } from '../mode/drag/driver.js'; import { DRAG } from '../mode/drag/driver.js';

View file

@ -3,7 +3,7 @@
'use strict'; 'use strict';
import { THREE } from '../ext/three.js'; import { THREE } from '../ext/three.js';
import JSZip from '../ext/jszip.esm.js'; import { JSZip } from '../ext/jszip-esm.js';
let { BufferAttribute, Matrix4 } = THREE; let { BufferAttribute, Matrix4 } = THREE;
@ -218,7 +218,7 @@ function extractItems(records) {
*/ */
export function parseAsync(data) { export function parseAsync(data) {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
let zip = await JSZip.loadAsync(data); let zip = await new JSZip().loadAsync(data);
let models = {}; let models = {};
for (let [key, value] of Object.entries(zip.files)) { for (let [key, value] of Object.entries(zip.files)) {
if (key.endsWith(".model")) { if (key.endsWith(".model")) {

View file

@ -82,7 +82,7 @@ function init() {
motoClient.on('ready', restore_space); motoClient.on('ready', restore_space);
// start worker // start worker
motoClient.start('/lib/mesh/work.js?' + version); motoClient.start('../lib/mesh/work.js?' + version);
// trigger space event binding // trigger space event binding
call.space_init({ space: space, platform }); call.space_init({ space: space, platform });
@ -91,7 +91,11 @@ function init() {
call.ui_build(); call.ui_build();
// hide url params // hide url params
history.replaceState({}, '', '/mesh/'); let wlp = window.location.pathname;
let mio = wlp.indexOf('/mesh/');
if (mio >= 0) {
history.replaceState({}, '', wlp.substring(0,mio + 6));
}
// for electron // for electron
self.mesh = { api }; self.mesh = { api };

View file

@ -88,8 +88,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"></script>
<!-- <script src="/lib/kiri-run/engine.js" type="module"></script> --> <!-- <script src="/lib/kiri-run/engine.js" type="module"></script> -->
<script type="module"> <script type="module">
import { Engine } from '/lib/kiri-run/engine.js'; import { Engine } from '/lib/kiri/run/engine.js';
const engine = new Engine();
let edit, saveit = ""; let edit, saveit = "";
function $(id) { return document.getElementById(id) } function $(id) { return document.getElementById(id) }
function onload() { function onload() {
@ -127,7 +126,7 @@
eval(`(function(){${code}})()`); eval(`(function(){${code}})()`);
} }
function demoFDM() { function demoFDM() {
engine new Engine()
.setListener(display_message) .setListener(display_message)
.load("/obj/cube.stl") .load("/obj/cube.stl")
.then(eng => eng.setProcess({ .then(eng => eng.setProcess({
@ -144,7 +143,7 @@
} }
function demoCAM(){ function demoCAM(){
engine new Engine()
.setListener(display_message) .setListener(display_message)
.load("/obj/cube.stl") .load("/obj/cube.stl")
// should to call widget.setTopZ here ideally // should to call widget.setTopZ here ideally
@ -244,7 +243,7 @@
} }
function demoLASER(){ function demoLASER(){
engine new Engine()
.setListener(display_message) .setListener(display_message)
.load("/obj/cube.stl") .load("/obj/cube.stl")
// should to call widget.setTopZ here ideally // should to call widget.setTopZ here ideally

View file

@ -82,7 +82,7 @@
} }
</style> </style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"></script>
<script src="/lib/kiri-run/frame.js"></script> <script src="/lib/kiri/run/frame.js"></script>
<script> <script>
let defc = [ let defc = [
"let api = kiri.frame;", "let api = kiri.frame;",

View file

@ -34,7 +34,7 @@
/* surface size configuration and font import */ /* surface size configuration and font import */
@font-face { @font-face {
font-family: 'Russo One'; font-family: 'Russo One';
src: url('/moto/russo-one.ttf'); src: url('../moto/russo-one.ttf');
} }
@media only screen and (max-width : 1000px) { @media only screen and (max-width : 1000px) {
.top-menu > span > label { .top-menu > span > label {

View file

@ -14,7 +14,7 @@
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default"> <meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta http-equiv="origin-trial" content="Aixo1di7jfA+ystPFy70vGYyODYVAju1y8EQWwdkdtl5sB7i990oDCnqLQntpfl7NsW74wz99Og7xgQqeLKiBQsAAAB0eyJvcmlnaW4iOiJodHRwczovL2dyaWQuc3BhY2U6NDQzIiwiZmVhdHVyZSI6IlVucmVzdHJpY3RlZFNoYXJlZEFycmF5QnVmZmVyIiwiZXhwaXJ5IjoxNzUzMTQyNDAwLCJpc1N1YmRvbWFpbiI6dHJ1ZX0="> <meta http-equiv="origin-trial" content="AobHyD9RXtmfFOTcY506ANNJjG8lYuuvQ3/C7YJvprSP6Kk2pjKIBSKAUbUh64Nx6aCZaqlZ+7sSSswiIFHflgYAAAB0eyJvcmlnaW4iOiJodHRwczovL2dyaWQuc3BhY2U6NDQzIiwiZmVhdHVyZSI6IlVucmVzdHJpY3RlZFNoYXJlZEFycmF5QnVmZmVyIiwiZXhwaXJ5IjoxNzY5NDcyMDAwLCJpc1N1YmRvbWFpbiI6dHJ1ZX0=">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Kiri:Moto</title> <title>Kiri:Moto</title>
<link rel="icon" href="favicon.ico"> <link rel="icon" href="favicon.ico">

View file

@ -875,6 +875,8 @@ self.lang['en-us'] = {
cx_fast_l: ["disable overhang detection","can be faster and use less","memory with complex models","but fails with overhangs","try enabling if slicing","hangs during shadowing"], cx_fast_l: ["disable overhang detection","can be faster and use less","memory with complex models","but fails with overhangs","try enabling if slicing","hangs during shadowing"],
cx_true_s: "true shadow", cx_true_s: "true shadow",
cx_true_l: ["computationally correct shadow","will be slower but produce","better cuts for complex parts"], cx_true_l: ["computationally correct shadow","will be slower but produce","better cuts for complex parts"],
cx_arce_s: "arc output",
cx_arce_l: ["EXPERIMENTAL: enable path to arc conversion"],
cx_arct_s: "arc tolerance", cx_arct_s: "arc tolerance",
cx_arct_l: ["convert cicrular paths to arcs;","center point drift tolerance","when matching arc points","consider values around 0.15","in mm, or 0.006 inches;","High values may cause","unexpected behavior","0 to disable"], cx_arct_l: ["convert cicrular paths to arcs;","center point drift tolerance","when matching arc points","consider values around 0.15","in mm, or 0.006 inches;","High values may cause","unexpected behavior","0 to disable"],
cx_arcr_s: "arc resolution", cx_arcr_s: "arc resolution",

View file

@ -1,5 +1,5 @@
{ {
"name": "Kiri:Moto 4.2.0", "name": "Kiri:Moto 4.3.0",
"short_name": "Kiri:Moto", "short_name": "Kiri:Moto",
"description": "Slicer for 3D printers, CNC mills, laser cutters and more", "description": "Slicer for 3D printers, CNC mills, laser cutters and more",
"start_url": "/kiri/", "start_url": "/kiri/",
@ -9,7 +9,7 @@
"background_color": "#ffffff", "background_color": "#ffffff",
"orientation": "landscape-primary", "orientation": "landscape-primary",
"icons": [ "icons": [
{ "src": "/kiri/logo-cube-512.png", "type": "image/png", "sizes": "512x512" }, { "src": "./logo-cube-512.png", "type": "image/png", "sizes": "512x512" },
{ "src": "/kiri/logo-cube-144.png", "type": "image/png", "sizes": "144x144" } { "src": "./logo-cube-144.png", "type": "image/png", "sizes": "144x144" }
] ]
} }

View file

@ -10,7 +10,7 @@
@font-face { @font-face {
font-family: 'Russo One'; font-family: 'Russo One';
src: url('/moto/russo-one.ttf'); src: url('../moto/russo-one.ttf');
} }
/** defaults */ /** defaults */