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

3
.gitignore vendored
View file

@ -5,10 +5,11 @@ app.json
build
data
dist
jszip-esm.js
logs
mod
node_modules
package-lock.json
three.js
tmp
pack
pack

153
app.js
View file

@ -36,6 +36,7 @@ let http;
let util;
let dir;
let log;
let pre;
const EventEmitter = require('events');
class AppEmitter extends EventEmitter {}
@ -46,9 +47,13 @@ netdb.create = async function(map = {}) {
Object.assign(map, JSON.parse(fs.readFileSync(map.conf)));
}
const client = new netdb();
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.base) await client.use(map.base);
try {
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.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 });
return client;
};
@ -59,6 +64,7 @@ function init(mod) {
startTime = time();
lastmod = mod.util.lastmod;
logger = mod.log;
pre = ENV.pre || mod.meta.pre;
debug = ENV.debug || mod.meta.debug;
oversion = ENV.over || mod.meta.over;
crossOrigin = ENV.xorigin || mod.meta.xorigin || false;
@ -72,17 +78,24 @@ function init(mod) {
dversion = debug ? `_${version}` : version;
forceUseCache = ENV.cache ? true : false;
const approot = PATH.join("main","gapp");
const refcache = {};
const callstack = [];
let xxxx = false;
if (!ENV.electron) {
generateDevices();
}
if (pre) {
for (let [k,v] of Object.entries(productionMap)) {
productionMap[`${pre}${k}`] = `${pre}${v}`;
}
}
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 || "*";
if (!Array.isArray(vmatch)) {
vmatch = [ vmatch ];
@ -93,17 +106,28 @@ function init(mod) {
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(handleOptions);
mod.add(serveWasm);
mod.add(serveCode);
mod.add(fullpath({
"/kiri" : redir("/kiri/", 301),
"/mesh" : redir("/mesh/", 301),
"/meta" : redir("/meta/", 301),
"/kiri/index.html" : redir("/kiri/", 301),
"/mesh/index.html" : redir("/mesh/", 301),
"/meta/index.html" : redir("/meta/", 301)
"/kiri" : redir((pre??"") + "/kiri/", 301),
"/mesh" : redir((pre??"") + "/mesh/", 301),
"/meta" : redir((pre??"") + "/meta/", 301),
"/kiri/index.html" : redir((pre??"") + "/kiri/", 301),
"/mesh/index.html" : redir((pre??"") + "/mesh/", 301),
"/meta/index.html" : redir((pre??"") + "/meta/", 301)
}));
mod.add(handleVersion);
mod.add(fixedmap("/api/", api));
@ -116,8 +140,6 @@ function init(mod) {
});
}
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("/obj/", "web/obj");
mod.static("/font/", "web/font");
@ -126,8 +148,8 @@ function init(mod) {
mod.static("/moto/", "web/moto");
mod.static("/kiri/", "web/kiri");
// module loader
function load_modules(root, force) {
// load modules
lastmod(`${dir}/${root}`) && fs.readdirSync(`${dir}/${root}`).forEach(mdir => {
const modpath = `${root}/${mdir}`;
if (dir.charAt(0) === '.' && !ENV.single) return;
@ -152,7 +174,7 @@ function init(mod) {
// load optional local modules
load_modules('mods');
// run loads injected by modules
// run load functions injected by modules
while (load.length) {
try {
load.shift()();
@ -161,7 +183,7 @@ function init(mod) {
}
}
// minify appends
// minify appends in production mode
if (!debug) {
for (let key of Object.keys(append)) {
append[key] = minify(append[key]);
@ -177,6 +199,9 @@ function loadModule(mod, dir) {
if (lastmod(`${mod.dir}/${dir}/.ignore`)) {
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`) ?
initModule(mod, `./${dir}/init.js`, 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
api: api,
adm: {
setver: (ver) => { oversion = ver },
crossOrigin: (bool) => { crossOrigin = bool }
setver(ver) { oversion = ver },
crossOrigin(bool) { crossOrigin = bool }
},
events,
const: {
args: {},
meta: mod.meta,
debug: debug,
debug,
moddir: dir,
rootdir: mod.dir,
version: oversion || version
@ -209,35 +234,21 @@ function initModule(mod, file, dir) {
},
mod: mods,
util: {
guid,
time,
log: logger.log,
time: time,
guid: guid,
logger: log.new,
mkdirs: util.mkdir,
isfile: util.isfile,
confdir: util.confdir,
datadir: util.datadir,
lastmod: lastmod,
obj2string: obj2string,
string2obj: string2obj,
isfile: util.isfile,
getCookieValue: cookieValue,
logger: log.new
},
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 });
}
obj2string(o) { return JSON.stringify(o) },
string2obj(s) { return JSON.parse(s) },
},
path: {
any: arg => { mod.add(arg) },
any(arg) { mod.add(arg) },
code() {
const [ path, file ] = [ ...arguments ];
if (lastmod(file)) {
@ -251,13 +262,13 @@ function initModule(mod, file, dir) {
console.log({ MISSING_CODE: path, file });
}
},
full: arg => { mod.add(fullpath(arg)) },
map: arg => { mod.add(fixedmap(arg)) },
pre: arg => { mod.add(prepath(arg)) },
full(arg) { mod.add(fullpath(arg)) },
map(arg) { mod.add(fixedmap(arg)) },
pre(arg) { mod.add(prepath(arg)) },
redir: redir,
remap: remap,
setup: fn => { setupFn = fn },
static: (root, pre) => {
setup(fn) { setupFn = fn },
static(root, pre) {
mod.static(pre || "/", root);
},
},
@ -271,22 +282,29 @@ function initModule(mod, file, dir) {
ws: {
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);
},
onexit: (fn) => {
onexit(fn) {
mod.on.exit(fn);
}
});
}
// prevent caching of specified modules
const cachever = {};
function promise(resolve, reject) {
return new Promise(resolve, reject);
}
function rval() {
return (Math.round(Math.random()*0xffffffff)).toString(36);
}
@ -299,14 +317,6 @@ function time() {
return Date.now();
}
function obj2string(o) {
return JSON.stringify(o);
}
function string2obj(s) {
return JSON.parse(s);
}
function handleSetup(req, res, next) {
if (setupFn) {
setupFn(req, res, next);
@ -322,12 +332,16 @@ const productionMap = {
'/lib/kiri/run/engine.js' : '/lib/pack/kiri-eng.js',
'/lib/kiri/run/minion.js' : '/lib/pack/kiri-pool.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) {
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) {
return http.redirect(res, `${req.url},ver:${vstr}`);
} else {
@ -550,7 +564,8 @@ function rewriteHtmlVersion(req, res, next) {
"/lib/main/mesh.js"
].indexOf(req.app.path) >= 0) {
addCorsHeaders(req, res);
const data = append[req.app.path.split('/')[3].split('.')[0]];
// const data = append[req.app.path.split('/')[3].split('.')[0]];
const data = append[req.app.path.split('.')[0].split('/').pop()];
if (!data) return next();
if (debug) logger.log({ append: req.app.path, data: data.length });

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
* `F` experiment with structuredClone(), SharedArrayBuffer, createImageBitmap()
* `F` test #private fields, array.at() for negative indices
* `F` option to hide platform when viewing from below
# Kiri:Moto
@ -14,7 +12,6 @@
* `P` refactor vertex replacement and widget update matrix tracking
* `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` faster ray intersect https://github.com/gkjohnson/three-mesh-bvh/
* `P` try material clipping planes for slice range selection
@ -26,7 +23,7 @@
* `F` complete and expose grouping feature
* `F` add svgnest-like arrange algorithm
* `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
@ -34,6 +31,7 @@
* `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
* `F` better thin-wall (like arachne)
* `F` add scarf seams
* `F` add 'random' and 'seam' start point options
* `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` 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
* `B` auto bed resizing leaves the origin in the wrong place at first
@ -69,7 +61,6 @@
# 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` 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
@ -84,18 +75,14 @@
* `F` add `match faces` option in `outline` operation
* `F` add {progress} substitution and maybe {time-remaining} if can be calc'd
* `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 lead-in milling (requires adding clamp / no go areas)
* `F` add linear clearing strategy
* `F` add adaptive clearing strategy
* `F` add ease-down support to trace op
* `F` add support for tapered ball mills
* `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` 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` ease-in and ease-out especially on tab cut-out start/stop
* `F` maintain several part orientations + op chains in a single profile
@ -104,14 +91,8 @@
* `P` outer corner arc moves
* `P` log Z interpolation for contour XYZ moves
* `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
# SLA
* `P` blit layers w/ 3JS ortho camera to canvas
# Laser
* `F` add PLT / HP-GL output format (https://en.wikipedia.org/wiki/HP-GL)
@ -124,8 +105,9 @@
# Mesh:Tool
* font to path with https://github.com/paulzi/svg-text-to-path
* 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
* send to Kiri:Moto workspace (or update model vertices in place)
* better z snap using just vertexes from face intersected
@ -134,6 +116,5 @@
* add flatten/crush op: for z bottoms (or surfaces?)
* allow setting model/group origin for scale/rotate
* fix mirror to work with groups (just models currently)
* bounding box toggle should be global, not selection
* add analyze results dialog
* remove/hide auto-repair function

View file

@ -36,7 +36,7 @@
},
"dependencies": {
"@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/net-level-client": "^0.2.3",
"@tracespace/parser": "^5.0.0-next.0",
@ -49,9 +49,11 @@
"earcut": "^3.0.1",
"express-useragent": "^1.0.13",
"fast-glob": "^3.3.3",
"i": "^0.3.7",
"jszip": "^3.7.1",
"manifold-3d": "^3.1.1",
"moment": "^2.29.4",
"npm": "^11.4.2",
"prettier": "^3.5.3",
"react-responsive-carousel": "^3.2.23",
"serve-static": "^1.14.1",
@ -79,7 +81,7 @@
"webpack-cli": "^5.1.4"
},
"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-nopublish": "npm run prebuild && electron-builder --publish never",
"build-debug": "npm run prebuild && DEBUG=electron-builder electron-builder",
@ -94,8 +96,9 @@
"docs-build": "docusaurus build --config conf/docusaurus.config.js",
"docs-serve": "docusaurus serve --config conf/docusaurus.config.js",
"docs-check": "prettier --config ./conf/prettier.config.js ./docs --check",
"dev": "npm run webpack3 && gs-app-server --debug --single",
"prod": "npm run webpack prod && gs-app-server",
"dev": "npm run webpack-ext && gs-app-server --debug --single",
"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",
"start": "npm run prebuild prod && electron .",
"start-dev": "npm run prebuild prod && electron . --devel",
@ -103,11 +106,13 @@
"start-ddb": "npm run prebuild && electron . --devel --debugg",
"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",
"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",
"preinstall": "node bin/install-pre.js",
"webpack": "npm run webpack3 && node bin/esbuild.config.mjs",
"webpack3": "npx webpack --config bin/webpack-three-esm.js"
"webpack-ext": "npm run webpack-three && npm run webpack-zip",
"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",
"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;
manifold({
locateFile() { return "/wasm/manifold.wasm" }
locateFile() { return "../wasm/manifold.wasm" }
}).then(inst => {
inst.setup();
Instance = inst;

View file

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

View file

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

View file

@ -1,9 +1,9 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { base } from '../geo/base.js';
import { newPolygon } from './polygon.js';
const { config } = base;
const factor = config.clipper;
export const wasm_ctrl = {
@ -54,7 +54,7 @@ function writePoly(view, poly, inner) {
function readPoly(view, z) {
let points = view.readU16(true);
if (points === 0) return;
let poly = base.newPolygon();
let poly = newPolygon();
while (points-- > 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 { 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 { catalog, dialog, event, group, help, hide, image } 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;
export function clearboxes() {
boxes.length = 0;
}
export function getlastbox() {
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 = {}) {
delbox(name);
const box = boxes[name] = new Mesh(
const box = new Mesh(
new BoxGeometry(dim.x, dim.y, dim.z),
new MeshPhongMaterial({
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.y = point.y;
box.position.z = point.z;

View file

@ -2,6 +2,9 @@
import { api } from './api.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
let debug = self.debug === true,
@ -199,7 +202,7 @@ export const client = {
send("rotate", { settings }, reply => {
if (reply.group) {
// 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;
}
} else if (callback) {
@ -280,10 +283,10 @@ export const client = {
y: -origin.y,
z: origin.z
};
const output = kiri.newPrint().parseSVG(code, offset);
const output = newPrint().parseSVG(code, offset);
send("parse_svg", output, reply => {
if (reply.parsed) {
done(kiri.codec.decode(reply.parsed));
done(codec.decode(reply.parsed));
}
});
return;
@ -293,7 +296,7 @@ export const client = {
progress(reply.progress);
}
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,
camExpertFast: false,
camTrueShadow: false,
camArcTolerance: 0.006,
camArcEnabled: false,
camArcTolerance: 0.15,
camArcResolution: 5,
camForceZMax: false,
camFirstZMax: false,

View file

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

View file

@ -133,7 +133,7 @@ function prepareSlices(callback, scale = 1, offset = 0) {
if (msg && msg !== lastMsg) {
let mark = Date.now();
if (lastMsg) {
let key = widgets.length > 1 ?
let key = slicing.length > 1 ?
`${widget.id}_${segNumber++}_${lastMsg}` :
`${segNumber++}_${lastMsg}`
segtimes[key] = mark - startTime;
@ -158,7 +158,10 @@ function prepareSlices(callback, scale = 1, offset = 0) {
if (sliced) {
// update segment time
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);
}
@ -237,7 +240,10 @@ function prepareSlices(callback, scale = 1, offset = 0) {
};
for (let widget of slicing) {
// 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
if (widget.belt) {
widget.stack.obj.rotate(widget.belt);
@ -452,8 +458,7 @@ function cancelWorker() {
}
function parseCode(code, type) {
const { conf, event, show, widgets, view } = api;
const { stacks } = kiri;
const { conf, event, show, stacks, widgets, view } = api;
const settings = conf.get();
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 initWEDM } from '../mode/wedm/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,
{ 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() {
let level = ui.detail.options[ui.detail.selectedIndex];
if (level) {
@ -123,11 +118,6 @@ function speedSave() {
api.view.update_speeds();
}
function zAnchorSave() {
api.conf.update();
api.platform.update_top_z();
}
function setThreaded(bool) {
if (bool) {
client.pool.start();
@ -234,6 +224,8 @@ function onBooleanClick(el) {
api.devices.update_laser_state();
}
api.event.on('click.boolean', onBooleanClick);
function onButtonClick(ev) {
let target = ev.target;
while (target && target.tagName !== 'BUTTON') {
@ -242,6 +234,8 @@ function onButtonClick(ev) {
api.event.emit("button.click", target);
}
api.event.on('click.button', onButtonClick);
function inputHasFocus() {
let active = DOC.activeElement;
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() {
return api.device.isBelt();
}
@ -845,7 +834,6 @@ function init_one() {
// api augmentation with local functions
api.device.export = deviceExport;
let anim = ui.anim = {};
Object.assign(ui, {
tracker: tracker,
@ -982,33 +970,6 @@ function init_one() {
stockDepth: $('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 */
_____: 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')),
/** FDM Settings */
_____: 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'),
...menuFDM(),
/** CAM Settings */
_____: 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}),
...menuCAM(),
/** LASER/DRAG/WJET/WEDM cut tool Settings */
_____: 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 }),
...menuLaser(),
/** SLA SETTINGS */
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"),
...menuSLA(),
layers: uc.setGroup($("layers")),
@ -1367,6 +1089,8 @@ function init_one() {
});
// 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();
initDRAG();
initFDM();
@ -1380,27 +1104,6 @@ function init_one() {
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
const mobile = space.info.mob;
const slbar = mobile ? 80 : 30;
@ -2099,8 +1802,12 @@ function init_two() {
sdb.kiri_beta = beta;
}
// hide url params
history.replaceState({}, '', '/kiri/');
// hide url params but preserve version root (when present)
let wlp = WIN.location.pathname;
let kio = wlp.indexOf('/kiri/');
if (kio >= 0) {
history.replaceState({}, '', wlp.substring(0,kio + 6));
}
// lift curtain
$('curtain').style.display = 'none';
@ -2165,7 +1872,7 @@ function init_lang() {
}
export function run() {
init_lang();
client.start();
init_lang();
return api;
}

View file

@ -613,10 +613,15 @@ class Print {
scope.belt = belt;
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 });
}
}

View file

@ -9,6 +9,7 @@ import { space } from '../../moto/space.js';
import { local } from '../../data/local.js';
import { utils } from './utils.js';
import { version } from '../../moto/license.js';
import { JSZip } from '../../ext/jszip-esm.js';
const { areEqual, ls2o, js2o } = utils;
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
function updateFieldsFromSettings(setrec, uirec = api.ui, trace) {
function updateFieldsFromSettings(setrec, uirec = api.ui, opt = {}) {
if (!setrec) {
updateFieldsFromSettings(settings.device);
updateFieldsFromSettings(settings.process);
@ -229,6 +230,9 @@ function updateFieldsFromSettings(setrec, uirec = api.ui, trace) {
updateExtruderFields(settings.device);
return;
}
if (opt.trace) {
console.log({ setrec, uirec });
}
for (let key in setrec) {
if (!setrec.hasOwnProperty(key)) {
continue;
@ -238,6 +242,9 @@ function updateFieldsFromSettings(setrec, uirec = api.ui, trace) {
continue;
}
let uie = uirec[key], typ = uie ? uie.type : null;
if (opt.trace) {
console.log({ key, uie, typ, val });
}
if (typ === 'text') {
if (uie.setv) {
uie.setv(val);
@ -271,6 +278,8 @@ function updateFieldsFromSettings(setrec, uirec = api.ui, trace) {
} else {
uie.value = '';
}
} else if (opt.trace) {
console.log('skipped', { key, val, typ });
}
}
}
@ -651,7 +660,7 @@ function settingsImportUrl(url, ask) {
function settingsImportZip(data, ask) {
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)) {
if (key === "workspace.json") {
value.async("string").then(json => {

View file

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

View file

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

View file

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

View file

@ -87,10 +87,10 @@ Object.assign(client, {
animate_setup2(settings, ondone) {
color = settings.controller.dark ? 0x888888 : 0;
material = new THREE.MeshMatcapMaterial({
material = new THREE.MeshPhongMaterial({
flatShading: true,
transparent: false,
opacity: 0.9,
opacity: 0.5,
color: 0x888888,
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.renderOrder = 2;
let lo = 0.5;
let lidat = [
lo, lo, lo, lo, lo, -lo,

View file

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

View file

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

View file

@ -1,6 +1,6 @@
/** 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 { load } from '../../../load/file.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 { updateStock } from './cl-stock.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 { holeSelOn, selectHolesDone, clearHolesRec } from './cl-hole.js';
import { surfaceOn, surfaceDone } from './cl-surface.js';
import { helicalOn, helicalDone } from './cl-helical.js';
import { originSelectDone } from './cl-origin.js';
const DEG2RAD = Math.PI / 180;
const RAD2DEG = 180 / Math.PI;
const hasSharedArrays = self.SharedArrayBuffer ? true : false;
const { VIEWS, STACKS } = api.const;
const { noop, space } = api;
const { noop } = api;
const { ui: UI, uc: UC } = api;
const { widgets: WIDGETS } = api;
@ -55,6 +56,9 @@ export function isDark() {
export function clearPops() {
if (env.func.unpop) env.func.unpop();
originSelectDone();
selectHolesDone();
surfaceDone();
tabDone();
traceDone();
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;
if (obj && obj.slices) {
const nadd = [];
filter = function(slices) {
filter = function (slices) {
for (let slice of slices) {
if (obj.slices(slice, idx++)) {
nadd.push(slice);
@ -38,8 +38,10 @@ class OpContour extends CamOp {
async slice(progress) {
let { op, state } = this;
let { addSlices } = state;
let { addSlices, settings, updateToolDiams } = state;
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
// not generated when drilling-only. then all z moves use bounds max.
// also generates x and y contouring when selected
@ -69,7 +71,7 @@ class OpContour extends CamOp {
let { camOut, polyEmit, newLayer, printPoint, lastPoint } = 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 depthFirst = process.camDepthFirst;
let depthData = [];
@ -78,7 +80,7 @@ class OpContour extends CamOp {
setSpindle(op.spindle);
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) {
// ignore debug slices
@ -88,17 +90,17 @@ class OpContour extends CamOp {
let polys = [], poly, emit;
slice.camLines.forEach(function (poly) {
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) {
depthData.appendAll(polys);
} else {
printPoint = tip2tipEmit(polys, printPoint, function(el, point, count) {
printPoint = tip2tipEmit(polys, printPoint, function (el, point, count) {
poly = el.poly;
if (poly.last() === point) {
poly.reverse();
}
poly.forEachPoint(function(point, pidx) {
poly.forEachPoint(function (point, pidx) {
camOut(point.clone(), pidx > 0, stepover);
}, false);
});
@ -107,12 +109,12 @@ class OpContour extends CamOp {
}
if (depthFirst) {
printPoint = tip2tipEmit(depthData, printPoint, function(el, point, count) {
printPoint = tip2tipEmit(depthData, printPoint, function (el, point, count) {
let poly = el.poly;
if (poly.last() === point) {
poly.reverse();
}
poly.forEachPoint(function(point, pidx) {
poly.forEachPoint(function (point, pidx) {
camOut(point.clone(), pidx > 0, stepover);
}, false);
newLayer();

View file

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

View file

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

View file

@ -2,6 +2,7 @@
import { CamOp } from './op.js';
import { Tool } from './tool.js';
import { generate as topo4_generate } from './topo4.js';
import { newPoint } from '../../../geo/point.js';
function createFilter(op) {
@ -12,7 +13,7 @@ function createFilter(op) {
let idx = 0;
if (obj && obj.slices) {
const nadd = [];
filter = function(slices) {
filter = function (slices) {
for (let slice of slices) {
if (obj.slices(slice, idx++)) {
nadd.push(slice);
@ -38,7 +39,7 @@ class OpLathe extends CamOp {
let { addSlices } = state;
let filter = createFilter(op);
this.topo = await Topo4({
this.topo = await topo4_generate({
op,
state,
onupdate: (pct, msg) => {
@ -109,7 +110,7 @@ class OpLathe extends CamOp {
// camOut(last = last.clone().setZ(zmax), 0);
// camOut(last = last.clone().setA(amax), 0);
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);
}

View file

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

View file

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

View file

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

View file

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

View file

@ -107,15 +107,28 @@ class OpTrace extends CamOp {
for (let z of zs) {
let clip = [], shadow;
shadow = shadowAt(z);
POLY.subtract([ poly ], shadow, clip, undefined, undefined, 0);
// 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);
}
if (op.outline) {
POLY.clearInner(clip);
}
if (clip.length === 0) {
continue;
}
let slice = newSliceOut(z);
let count = 999;
let slice = newSliceOut(z);
slice.camTrace = { tool, rate, plunge };
if (toolDiam) {
const offs = [ -toolDiam / 2, -toolOver ];
@ -132,12 +145,15 @@ class OpTrace extends CamOp {
slice.camLines = POLY.flatten(slice.camLines, null, true);
}
POLY.setWinding(slice.camLines, cutdir, false);
slice.output()
.setLayer("trace", {line: color}, false)
.addPolys(slice.camLines)
if (debug && shadow) slice.output()
.setLayer("trace shadow", {line: 0xff8811}, false)
.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");
zpro += zinc;
addSlices(slice);
@ -254,6 +270,7 @@ class OpTrace extends CamOp {
case "clear":
const zbo = widget.track.top - widget.track.box.d;
let zmap = {};
polys = POLY.nest(polys);
for (let poly of polys) {
let z = minZ(poly.minZ());
if (offover) {

View file

@ -88,6 +88,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
engageFactor = process.camFullEngage,
arcTolerance = process.camArcTolerance,
arcRes = toRadians(process.camArcResolution),
arcEnabled = process.camArcEnabled && arcTolerance > 0 && arcRes > 0,
tolerance = 0,
drillDown = 0,
drillLift = 0,
@ -177,6 +178,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
}
feedRate = feed || feedRate || plunge;
plungeRate = Math.min(feedRate || plunge, plunge || plungeRate || feedRate);
// console.log({ setTool: toolID, feed, plunge, plungeRate });
}
function setLasering(bool, power = 0) {
@ -241,7 +243,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
break;
}
}
camOut(point.clone().setZ(zmax));
camOut(point.clone().setZ(zmax_outer));
points.forEach(function (point, index) {
camOut(point, 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);
}
})
camOut(point.clone().setZ(zmax), 0);
camOut(point.clone().setZ(zmax_outer), 0);
newLayer();
}
@ -317,7 +319,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
p.x + wmx,
p.y + wmy,
p.z + zadd
)
).setA(p.a ?? lastPoint?.a);
}
/**
@ -342,7 +344,8 @@ export function prepEach(widget, settings, print, firstPoint, update) {
// console.log({radius})
const isArc = emit == 2 || emit == 3;
//apply widget movement pos
// apply widget movement pos
const pointA = point.a;
point = applyWidgetMovement(point);
// console.log(point.z);
@ -354,15 +357,13 @@ export function prepEach(widget, settings, print, firstPoint, update) {
let rate = feedRate * factor;
// carry rotation forward when not overridden
if (point.a === undefined && lastPoint) {
point.a = lastPoint.a;
} else if (lastPoint && point.a !== undefined && lastPoint.a !== undefined) {
let DA = lastPoint.a - point.a;
if (lastPoint && pointA !== undefined && lastPoint.a !== undefined) {
let DA = lastPoint.a - pointA;
let MZ = Math.max(lastPoint.z, point.z)
// find arc length
let AL = (Math.abs(DA) / 360) * (2 * Math.PI * MZ);
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
// console.log({ DA, MZ, AL }, lerp.length);
for (let a of lerp) {
@ -461,16 +462,27 @@ export function prepEach(widget, settings, print, firstPoint, update) {
}
// set new plunge rate
if (!lasering && deltaZ < -tolerance && !isLathe) {
if (!lasering && !isLathe && deltaZ < -tolerance) {
let threshold = Math.min(deltaXY / 2, absDeltaZ),
modifier = threshold / absDeltaZ;
if (synthPlunge && threshold && modifier && deltaXY > tolerance) {
// use modifier to speed up long XY move plunge rates
// console.log('modifier', modifier);
rate = Math.round(plungeRate + ((feedRate - plungeRate) * modifier));
cut = 1;
rate = Math.max(
plungeRate,
Math.round(plungeRate + ((feedRate - plungeRate) * modifier))
);
} 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) {
camOut(pointA.clone(), 0, { factor: engageFactor });
// if first point, move to and call export function
arcQ.push(pointA);
if (arcEnabled) arcQ.push(pointA);
}
lastPoint = arcExport(pointB, pointA);
}, !poly.isClosed(), startIndex);
@ -782,7 +794,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
function arcExport(point, lastp) {
let dist = lastp ? point.distTo2D(lastp) : 0;
if (lastp) {
if (dist > lineTolerance && lastp) {
if (arcEnabled && dist > lineTolerance && lastp) {
let rec = Object.assign(point, { dist });
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,
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
const var_compute = () => {
let { camStockX, camStockY, camStockZ, camStockOffset } = proc;
@ -46,9 +55,9 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
x: camStockX,
y: camStockY,
z: camStockZ
}
track = widget.track;
};
({ camZTop, camZBottom, camZThru } = proc);
track = widget.track;
wztop = track.top;
ztOff = isIndexed ? (stock.z - bounds.dim.z) / 2 : (stock.z - wztop);
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;
zThru = camZThru;
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;
bottom_gap = zbOff;
bottom_part = 0;
@ -223,7 +224,7 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
}
function setAxisIndex(degrees = 0, absolute = true) {
axisIndex = absolute ? degrees : axisIndex + degrees;
axisIndex = absolute ? degrees : (axisIndex || 0) + degrees;
axisRotation = (Math.PI / 180) * axisIndex;
widget.setAxisIndex(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
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 }));
opTot += opList.peek().weight();
}
} else {
opList.push(new OPS.shadow(state, { type: "shadow", silent: true }));
opTot += opList.peek().weight();
}
// 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) {
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
zBottom = isIndexed ? camZBottom : camZBottom - zbOff;
let slicerOpts = { flatoff: 0.001 }
let slicer = new cam_slicer(widget, slicerOpts);
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);
});
polys = POLY.union(polys, 0.001, true);
polys = POLY.union(polys, 0, true);
return polys;
}
}

View file

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

View file

@ -1,7 +1,7 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
const HPI = Math.PI/2;
const RAD2DEG = 180/Math.PI;
const HPI = Math.PI / 2;
const RAD2DEG = 180 / Math.PI;
class Tool {
constructor(settings, id, number) {
@ -102,7 +102,7 @@ class Tool {
return this.tool.type === "tapermill";
}
isDrill(){
isDrill() {
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;
} 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;
}else if(drill){
z_offset = -dist_from_center /45;
} else if (drill) {
z_offset = -dist_from_center / 45;
}
toolOffset.push(dx, dy, z_offset);
} 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 DEG2RAD = Math.PI / 180;
class Topo {
export class Topo {
constructor() { }
async generate(opt = {}) {
@ -37,7 +37,7 @@ class Topo {
boundsY = maxY - minY,
inside = contour.inside,
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),
toolOffset = tool.generateProfile(resolution).profile,
toolDiameter = tool.fluteDiameter(),
@ -68,11 +68,11 @@ class Topo {
},
data = topo.data,
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,
tabHeight = Math.max(process.camTabsHeight + zBottom, tabsMax),
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,
gridDelta = Math.floor(partOff / resolution),
debug = false,
@ -92,8 +92,8 @@ class Topo {
if (tabs) {
clipTab.appendAll(tabs.map(tab => {
let ctab = POLY.expand([tab.poly], toolDiameter/2);
ctab.forEach(ct => ct.z = tab.dim.z/2 + tab.pos.z);
let ctab = POLY.expand([tab.poly], toolDiameter / 2);
ctab.forEach(ct => ct.z = tab.dim.z / 2 + tab.pos.z);
return ctab;
}).flat());
}
@ -197,7 +197,7 @@ class Topo {
const range = { min: Infinity, max: -Infinity };
// 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 z = vertices[i + 2];
vertices[i] = z;
@ -226,9 +226,11 @@ class Topo {
// define sharded ranges
if (minions.running > 1) {
dispatch.putCache({ key: widget.id, data: vertices }, { done: data => {
// console.log({ put_cache_done: data });
}});
dispatch.putCache({ key: widget.id, data: vertices }, {
done: data => {
// console.log({ put_cache_done: data });
}
});
let promises = slices.map(slice => {
return new Promise(resolve => {
@ -256,9 +258,11 @@ class Topo {
return box2;
});
dispatch.clearCache({}, { done: data => {
// console.log({ clear_cache_done: data });
}});
dispatch.clearCache({}, {
done: data => {
// console.log({ clear_cache_done: data });
}
});
} else {
@ -340,8 +344,8 @@ class Topo {
let promise = new Promise(resolve => {
resolver = () => {
// sort output slices (required for async)
slicesY.sort((a,b) => a.z - b.z);
slicesX.sort((a,b) => a.z - b.z);
slicesY.sort((a, b) => a.z - b.z);
slicesX.sort((a, b) => a.z - b.z);
newslices.appendAll(slicesY);
newslices.appendAll(slicesX);
resolve();
@ -369,7 +373,7 @@ class Topo {
let slice = newSlice(gridx);
slice.camLines = segments;
slice.output()
.setLayer("contour y", {face: color, line: color})
.setLayer("contour y", { face: color, line: color })
.addPolys(segments);
slicesY.push(slice);
}
@ -398,7 +402,7 @@ class Topo {
let slice = newSlice(gridy);
slice.camLines = segments;
slice.output()
.setLayer("contour x", {face: color, line: color})
.setLayer("contour x", { face: color, line: color })
.addPolys(segments);
slicesX.push(slice);
}
@ -431,7 +435,7 @@ export class Probe {
this.params = params;
// 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,
sy = stepsY,
xl = sx - 1,
@ -461,13 +465,13 @@ export class Probe {
// export z probe function
const rx = 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));
py = Math.round(ry * (py - minY));
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 iy = Math.round(ry * (py - minY));
return data[ix * stepsY + iy] || zMin;
@ -496,11 +500,11 @@ export class Trace {
this.slice = slice = [];
}
const newtrace = this.newtrace = function() {
const newtrace = this.newtrace = function () {
trace = newPolygon().setOpen();
}
const end_poly = this.end_poly = function(point) {
const end_poly = this.end_poly = function (point) {
if (latent) {
trace.push(latent);
}
@ -520,7 +524,7 @@ export class Trace {
}
}
const log = function(map) {
const log = function (map) {
for (let key in map) {
const val = map[key];
if (typeof val === 'number') {
@ -530,7 +534,7 @@ export class Trace {
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 lastP = lastPP;//trace.last();
@ -550,7 +554,7 @@ export class Trace {
if (curvesOnly) {
const dv = contourX ? Math.abs(lastP.x - x) : Math.abs(lastP.y - y);
// const dz = lastPP.z - z;
const angle = Math.atan2( Math.abs(dz), dv) * RAD2DEG;
const angle = Math.atan2(Math.abs(dz), dv) * RAD2DEG;
if (angle > maxangle) {
end_poly();
}
@ -568,7 +572,7 @@ export class Trace {
const object = this.object = this;
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 zok = checkZ ? checkZ <= poly.z : true;
object.tabZ = poly.z;
@ -590,9 +594,9 @@ export class Trace {
// in this case, z is appended to clip tabs in topo constructor
// we pass it as a side-channel and re-consitute here
if (clipTab)
for (let i=0, l=clipTab.length; i<l; i++) {
clipTab[i].z = clipTabZ[i];
}
for (let i = 0, l = clipTab.length; i < l; i++) {
clipTab[i].z = clipTabZ[i];
}
if (minions && this.cross.concurrent) {
minions.broadcast("trace_init", codec.encode({
@ -649,7 +653,7 @@ export class Trace {
let { from, to, x, gridx, gridy } = params;
const step = resolution * density;
const checkr = newPoint(0,0);
const checkr = newPoint(0, 0);
newslice();
newtrace();
for (let y = from; y < to; y += step) {
@ -687,7 +691,7 @@ export class Trace {
let { from, to, y, gridx, gridy } = params;
const step = resolution * density;
const checkr = newPoint(0,0);
const checkr = newPoint(0, 0);
newslice();
newtrace();
for (let x = from; x < to; x += step) {
@ -734,7 +738,7 @@ export function raster_slice(inputs) {
// emit an array of valid line-pairs
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;
// eliminate vertical
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
if (p1.y > p2.y) { const tp = p1; p1 = p2; p2 = tp };
// eliminate if points "under" other lines
for (let j=0; j<len; j++) {
for (let j = 0; j < len; j++) {
// 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;
// sort p3,p4 by y for comparison
if (p3.y > p4.y) { const tp = p3; p3 = p4; p4 = tp };
// 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
if (p1.y >= p3.y && p2.y <= p4.y) continue outer;
}
@ -766,8 +770,8 @@ export function raster_slice(inputs) {
gridi = gridx * stepsY + gridy;
gridv = data[gridi] || zMin;
// strategy using raw lines (faster slice, but more lines)
for (i=0, il=points.length; i<il; i += 2) {
const p1 = points[i], p2 = points[i+1];
for (i = 0, il = points.length; i < il; i += 2) {
const p1 = points[i], p2 = points[i + 1];
// one endpoint above grid
const crossz = (p1.z > gridv || p2.z > gridv);
// segment crosses grid y
@ -783,7 +787,7 @@ export function raster_slice(inputs) {
if (nz > gridv) {
gridv = data[gridi] = Math.max(nz, zMin);
if (slice) slice.output()
.setLayer("heights", {face: 0, line: 0})
.setLayer("heights", { face: 0, line: 0 })
.addLine(
newPoint(p1.x, y, 0),
newPoint(p1.x, y, gridv)
@ -797,9 +801,9 @@ export function raster_slice(inputs) {
// remove flat lines when curvesOnly
if (curvesOnly) {
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 p2 = p[i+1];
const p2 = p[i + 1];
if (Math.abs(p1.z - p2.z) >= flatness) {
nup.push(p1, p2);
}

View file

@ -4,6 +4,7 @@ import { codec } from '../../core/codec.js';
import { newPoint } from '../../../geo/point.js';
import { newPolygon } from '../../../geo/polygon.js';
import { sliceConnect } from '../../../geo/slicer.js';
import { newSlice } from '../../core/slice.js';
import { Tool } from './tool.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() { }
async generate(opt = {}) {
@ -91,7 +92,7 @@ class Topo4 {
const box = this.box = new THREE.Box2();
// 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 z = vertices[i + 2] + zoff;
vertices[i] = z;
@ -105,7 +106,7 @@ class Topo4 {
range.max += this.diam / 2;
// 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 z = tabverts[i + 2] + zoff;
tabverts[i] = z;
@ -132,7 +133,8 @@ class Topo4 {
slices.push(slice);
// 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);
} else {
return await this.sliceWorker(onupdate);
@ -158,7 +160,7 @@ class Topo4 {
slice.index = rec.index;
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));
shared.set(points);
slice.shared = shared;
@ -166,7 +168,7 @@ class Topo4 {
return slice;
});
output.appendAll(recs);
onupdate(++complete /slices.length);
onupdate(++complete / slices.length);
}
return output;
@ -195,14 +197,15 @@ class Topo4 {
.map(rec => newSlice(rec.z)
.addTops(rec.polys)
.setFields({ shared: rec.shared }))
.sort((a,b) => a.z - b.z);
.sort((a, b) => a.z - b.z);
clearCache();
return output;
}
async lathe(onupdate) {
if (kiri.minions.running > 1) {
const { minions } = self.kiri_worker;
if (minions?.running > 1) {
return await this.latheMinions(onupdate);
} else {
return await this.latheWorker(onupdate);
@ -223,7 +226,7 @@ class Topo4 {
const lines = slice.lines;
const plen = lines.length;
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
let py0 = lines[i++];
const pz0 = lines[i++];
@ -245,7 +248,7 @@ class Topo4 {
const rx = oslices[si].z;
let mz = -Infinity;
// 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)
const xo = tool[ti++]; // x grid offset (slice)
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 lines = slice.lines;
const plen = lines.length;
for (let i = 0; i < plen; ) {
for (let i = 0; i < plen;) {
++i; // skip x which should match slice.z
let py0 = lines[i++];
const pz0 = lines[i++];
@ -293,7 +296,7 @@ class Topo4 {
}
async latheWorker(onupdate) {
const { sliced, tool, zoff, leave } = this;
const { sliced, tool, zoff, leave, linear } = this;
const rota = this.angle * DEG2RAD;
const steps = (Math.PI * 2) / rota;
@ -316,19 +319,32 @@ class Topo4 {
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) {
let slice = newSlice(count);
slice.camLines = [ newPolygon().setOpen() ];
slice.camLines = [newPolygon().setOpen()];
paths.push(slice);
}
for (let rec of recs) {
const { degrees, heights } = rec;
[...heights].group(3).forEach((a,i) => {
// 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) );
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) {
const { degrees, heights } = rec;
[...heights].group(3).forEach((a, i) => {
// 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));
});
}
}
for (let slice of paths) {
@ -342,7 +358,7 @@ class Topo4 {
slice.camLines[0].push(repeat.clone().setA(repeat.a - 360));
slice.output()
.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 });
@ -392,20 +408,18 @@ class Topo4 {
await Promise.all(promises);
recs.sort((a, b) => { return b.angle - a.angle });
// let linear = true;
count = linear ? recs.length : recs[0].heights.length / 3;
while (count-- > 0) {
let slice = newSlice(count);
slice.camLines = [ newPolygon().setOpen() ];
slice.camLines = [newPolygon().setOpen()];
paths.push(slice);
}
if (linear) {
recs.forEach((rec,i) => {
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) );
paths[i].camLines[0].push(newPoint(a[0], a[1], a[2] + leave).setA(degrees));
});
if (i % 2 === 1) {
paths[i].camLines[0].reverse();
@ -414,9 +428,9 @@ class Topo4 {
} else {
for (let rec of recs) {
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
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()
.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 });
@ -444,24 +458,27 @@ class Topo4 {
}
putCache(key, data) {
kiri.worker.putCache({ key, data }, { done: data => { } });
const { dispatch } = self.kiri_worker;
dispatch.putCache({ key, data }, { done: data => { } });
}
clearCache() {
kiri.worker.clearCache({}, { done: data => { } });
const { dispatch } = self.kiri_worker;
dispatch.clearCache({}, { done: data => { } });
}
queue(cmd, params) {
const { minions } = self.kiri_worker;
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);
}
export function Topo4(opt) {
return new Topo4().generate(opt);
export async function 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';
class Engine {
constructor() {
constructor({ workURL, poolURL } = {}) {
this.widget = newWidget();
this.settings = {
mode: "FDM",
@ -22,7 +22,13 @@ class Engine {
widget: { [this.widget.id]: {} }
};
this.listener = () => { };
client.start();
try {
client.setWorkPath(workURL);
client.setPoolPath(poolURL);
client.restart();
} catch (error) {
console.log({ error });
}
}
load(url) {
@ -190,7 +196,7 @@ class Engine {
}
export function newEngine() {
return new Engine();
return new Engine(...arguments);
}
export { Engine };

View file

@ -11,10 +11,11 @@ import { codec, encode, encodePointArray } from '../core/codec.js';
import { doTopShells } from '../mode/fdm/post.js';
import { newPoint } from '../../geo/point.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 topo_slicer } from '../mode/cam/slicer_topo.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';
const clib = self.ClipperLib;
@ -53,9 +54,13 @@ function debug() {
// console.log(`[${name}]`, ...arguments);
}
let invalid = 0;
const funcs = self.minion = {
invalid(data, seq, cmd) {
console.error({ invalid_minion_command: cmd, data });
if (invalid++ < 20) {
// prevent flooding console and killing session
console.error({ invalid_minion_command: cmd, data });
}
reply({ seq, error: `invalid command (${cmd})` });
},
@ -271,7 +276,7 @@ const funcs = self.minion = {
topo4_slice(data, seq) {
const { slice, resolution } = data;
const vertices = cache.vertices;
const recs = new kiri.topo_slicer(slice.index)
const recs = new topo_slicer(slice.index)
.setFromArray(vertices, slice)
.slice(resolution)
.map(rec => {

View file

@ -3,7 +3,6 @@
import '../../add/array.js';
import '../../add/class.js';
import '../../add/three.js';
import '../../ext/jszip.js';
import { base } from '../../geo/base.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 { Widget, newWidget } from '../core/widget.js';
import { load } from "../../load/png.js";
import { JSZip } from '../../ext/jszip-esm.js';
import { CAM } from '../mode/cam/driver-be.js';
import { DRAG } from '../mode/drag/driver.js';

View file

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

View file

@ -82,7 +82,7 @@ function init() {
motoClient.on('ready', restore_space);
// start worker
motoClient.start('/lib/mesh/work.js?' + version);
motoClient.start('../lib/mesh/work.js?' + version);
// trigger space event binding
call.space_init({ space: space, platform });
@ -91,7 +91,11 @@ function init() {
call.ui_build();
// 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
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="/lib/kiri-run/engine.js" type="module"></script> -->
<script type="module">
import { Engine } from '/lib/kiri-run/engine.js';
const engine = new Engine();
import { Engine } from '/lib/kiri/run/engine.js';
let edit, saveit = "";
function $(id) { return document.getElementById(id) }
function onload() {
@ -127,7 +126,7 @@
eval(`(function(){${code}})()`);
}
function demoFDM() {
engine
new Engine()
.setListener(display_message)
.load("/obj/cube.stl")
.then(eng => eng.setProcess({
@ -144,7 +143,7 @@
}
function demoCAM(){
engine
new Engine()
.setListener(display_message)
.load("/obj/cube.stl")
// should to call widget.setTopZ here ideally
@ -244,7 +243,7 @@
}
function demoLASER(){
engine
new Engine()
.setListener(display_message)
.load("/obj/cube.stl")
// should to call widget.setTopZ here ideally

View file

@ -82,7 +82,7 @@
}
</style>
<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>
let defc = [
"let api = kiri.frame;",

View file

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

View file

@ -14,7 +14,7 @@
<meta name="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 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">
<title>Kiri:Moto</title>
<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_true_s: "true shadow",
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_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",

View file

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

View file

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