diff --git a/.gitignore b/.gitignore index 868a1da7..c66e12fd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,11 @@ app.json build data dist +jszip-esm.js logs mod node_modules package-lock.json three.js tmp -pack \ No newline at end of file +pack diff --git a/app.js b/app.js index aa58c139..4c27cc73 100644 --- a/app.js +++ b/app.js @@ -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', '')); 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 }); diff --git a/bin/webpack-jszip-bundle.js b/bin/webpack-jszip-bundle.js new file mode 100644 index 00000000..9552f32f --- /dev/null +++ b/bin/webpack-jszip-bundle.js @@ -0,0 +1,7 @@ +import * as JSZip from "../node_modules/jszip/dist/jszip.js"; + +export default JSZip; + +export { + JSZip +}; diff --git a/bin/webpack-jszip-esm.js b/bin/webpack-jszip-esm.js new file mode 100644 index 00000000..eb3c3616 --- /dev/null +++ b/bin/webpack-jszip-esm.js @@ -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, + }, +}; diff --git a/notes.md b/notes.md index acd628ab..a7036e77 100644 --- a/notes.md +++ b/notes.md @@ -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 diff --git a/package.json b/package.json index 2d3d5567..112e16e8 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/src/ext/jszip.esm.js b/src/ext/jszip.esm.js deleted file mode 100644 index 58a5a1de..00000000 --- a/src/ext/jszip.esm.js +++ /dev/null @@ -1,7 +0,0 @@ -/** Copyright Stewart Allen -- All Rights Reserved */ - -//import original library -import * as JSZip from "./jszip.js"; - -//export object attached to the window object -export default self.JSZip; diff --git a/src/geo/csg.js b/src/geo/csg.js index 0aec7c09..f49138d1 100644 --- a/src/geo/csg.js +++ b/src/geo/csg.js @@ -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; diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 6e665522..8cc0f14d 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -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() { diff --git a/src/geo/polygons.js b/src/geo/polygons.js index 07cea123..6d4d8128 100644 --- a/src/geo/polygons.js +++ b/src/geo/polygons.js @@ -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); } diff --git a/src/geo/wasm.js b/src/geo/wasm.js index 87ec5cdd..72c7578f 100644 --- a/src/geo/wasm.js +++ b/src/geo/wasm.js @@ -1,9 +1,9 @@ /** Copyright Stewart Allen -- 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); } diff --git a/src/kiri/core/api.js b/src/kiri/core/api.js index c81a6954..c66292a3 100644 --- a/src/kiri/core/api.js +++ b/src/kiri/core/api.js @@ -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'; diff --git a/src/kiri/core/boxes.js b/src/kiri/core/boxes.js index ac18430e..7e6cbaed 100644 --- a/src/kiri/core/boxes.js +++ b/src/kiri/core/boxes.js @@ -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; diff --git a/src/kiri/core/client.js b/src/kiri/core/client.js index 363161fb..a6a5a116 100644 --- a/src/kiri/core/client.js +++ b/src/kiri/core/client.js @@ -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); } }); }, diff --git a/src/kiri/core/conf.js b/src/kiri/core/conf.js index e69c247b..9096bd1c 100644 --- a/src/kiri/core/conf.js +++ b/src/kiri/core/conf.js @@ -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, diff --git a/src/kiri/core/consts.js b/src/kiri/core/consts.js index edaf0568..a332391e 100644 --- a/src/kiri/core/consts.js +++ b/src/kiri/core/consts.js @@ -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'; diff --git a/src/kiri/core/function.js b/src/kiri/core/function.js index a8462a66..b17eb4dd 100644 --- a/src/kiri/core/function.js +++ b/src/kiri/core/function.js @@ -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}); diff --git a/src/kiri/core/init.js b/src/kiri/core/init.js index c7829d66..6757f3d5 100644 --- a/src/kiri/core/init.js +++ b/src/kiri/core/init.js @@ -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:'',title:"restart"}), - anim.play = newButton(null,"anim.play",{icon:'',title:"play"}), - anim.pause = newButton(null,"anim.pause",{icon:'',title:"pause"}), - anim.step = newButton(null,"anim.step",{icon:'',title:"single step"}), - anim.speed = newButton(null,"anim.fast",{icon:'',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:'',title:"transparency",class:"padleft"}), - anim.model = newButton(null,"anim.model",{icon:'',title:"show model"}), - anim.shade = newButton(null,"anim.stock",{icon:'',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:''})), - (ui.ssmDun = newButton(undefined, onButtonClick, {icon:''})), - (ui.ssmClr = newButton(undefined, onButtonClick, {icon:''})) - ], {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:''})), - (ui.tabDun = newButton(undefined, onButtonClick, {icon:''})), - (ui.tabClr = newButton(undefined, onButtonClick, {icon:''})) - ], {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:''})), - (ui.faceDun = newButton(undefined, onButtonClick, {icon:''})), - (ui.faceClr = newButton(undefined, onButtonClick, {icon:''})) - ], {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; } diff --git a/src/kiri/core/print.js b/src/kiri/core/print.js index 3fc4b559..6be1665d 100644 --- a/src/kiri/core/print.js +++ b/src/kiri/core/print.js @@ -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 }); } } diff --git a/src/kiri/core/settings.js b/src/kiri/core/settings.js index db9d40ae..1831126e 100644 --- a/src/kiri/core/settings.js +++ b/src/kiri/core/settings.js @@ -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 => { diff --git a/src/kiri/core/ui.js b/src/kiri/core/ui.js index ce25cd9d..b0fcee75 100644 --- a/src/kiri/core/ui.js +++ b/src/kiri/core/ui.js @@ -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; diff --git a/src/kiri/dev/fdm/ULIO.3D.BRIX.json b/src/kiri/dev/fdm/ULIO.3D.BRIX.json index 113f20a7..0a1aadf9 100644 --- a/src/kiri/dev/fdm/ULIO.3D.BRIX.json +++ b/src/kiri/dev/fdm/ULIO.3D.BRIX.json @@ -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, diff --git a/src/kiri/dev/fdm/ULIO.3D.STEAM.json b/src/kiri/dev/fdm/ULIO.3D.STEAM.json index b6239564..5270560e 100644 --- a/src/kiri/dev/fdm/ULIO.3D.STEAM.json +++ b/src/kiri/dev/fdm/ULIO.3D.STEAM.json @@ -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, diff --git a/src/kiri/mode/cam/anim-3d-fe.js b/src/kiri/mode/cam/anim-3d-fe.js index 295e043e..3f31d97f 100644 --- a/src/kiri/mode/cam/anim-3d-fe.js +++ b/src/kiri/mode/cam/anim-3d-fe.js @@ -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 }); diff --git a/src/kiri/mode/cam/cl-origin.js b/src/kiri/mode/cam/cl-origin.js new file mode 100644 index 00000000..5b63f8ac --- /dev/null +++ b/src/kiri/mode/cam/cl-origin.js @@ -0,0 +1,140 @@ +/** Copyright Stewart Allen -- 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
[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(); +} diff --git a/src/kiri/mode/cam/cl-stock.js b/src/kiri/mode/cam/cl-stock.js index 8d2e291e..dbb1777d 100644 --- a/src/kiri/mode/cam/cl-stock.js +++ b/src/kiri/mode/cam/cl-stock.js @@ -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, diff --git a/src/kiri/mode/cam/cl-tab.js b/src/kiri/mode/cam/cl-tab.js index b65df0d5..8237065e 100644 --- a/src/kiri/mode/cam/cl-tab.js +++ b/src/kiri/mode/cam/cl-tab.js @@ -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) { diff --git a/src/kiri/mode/cam/cl-trace.js b/src/kiri/mode/cam/cl-trace.js index aef93434..25755670 100644 --- a/src/kiri/mode/cam/cl-trace.js +++ b/src/kiri/mode/cam/cl-trace.js @@ -1,7 +1,7 @@ /** Copyright Stewart Allen -- 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; diff --git a/src/kiri/mode/cam/client.js b/src/kiri/mode/cam/client.js index d085e882..08d1d656 100644 --- a/src/kiri/mode/cam/client.js +++ b/src/kiri/mode/cam/client.js @@ -1,6 +1,6 @@ /** Copyright Stewart Allen -- 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(); diff --git a/src/kiri/mode/cam/init-menu.js b/src/kiri/mode/cam/init-menu.js new file mode 100644 index 00000000..69becf60 --- /dev/null +++ b/src/kiri/mode/cam/init-menu.js @@ -0,0 +1,128 @@ +/** Copyright Stewart Allen -- 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:'',title:"restart"}), + anim.play = newButton(null,"anim.play",{icon:'',title:"play"}), + anim.pause = newButton(null,"anim.pause",{icon:'',title:"pause"}), + anim.step = newButton(null,"anim.step",{icon:'',title:"single step"}), + anim.speed = newButton(null,"anim.fast",{icon:'',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:'',title:"transparency",class:"padleft"}), + anim.model = newButton(null,null,{icon:'',title:"show model"}), + anim.shade = newButton(null,null,{icon:'',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:''})), + (ui.tabDun = newButton(undefined, onButtonClick, {icon:''})), + (ui.tabClr = newButton(undefined, onButtonClick, {icon:''})) + ], {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}), + + }; +}; diff --git a/src/kiri/mode/cam/op-contour.js b/src/kiri/mode/cam/op-contour.js index c4860bb6..ae7e089a 100644 --- a/src/kiri/mode/cam/op-contour.js +++ b/src/kiri/mode/cam/op-contour.js @@ -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(); diff --git a/src/kiri/mode/cam/op-drill.js b/src/kiri/mode/cam/op-drill.js index 80ff6521..eefde0cf 100644 --- a/src/kiri/mode/cam/op-drill.js +++ b/src/kiri/mode/cam/op-drill.js @@ -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()); } diff --git a/src/kiri/mode/cam/op-index.js b/src/kiri/mode/cam/op-index.js index becd0441..d226aac0 100644 --- a/src/kiri/mode/cam/op-index.js +++ b/src/kiri/mode/cam/op-index.js @@ -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) { diff --git a/src/kiri/mode/cam/op-lathe.js b/src/kiri/mode/cam/op-lathe.js index 8bdf0361..915ae7e1 100644 --- a/src/kiri/mode/cam/op-lathe.js +++ b/src/kiri/mode/cam/op-lathe.js @@ -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); } diff --git a/src/kiri/mode/cam/op-outline.js b/src/kiri/mode/cam/op-outline.js index 9a9a90fb..9daa9519 100644 --- a/src/kiri/mode/cam/op-outline.js +++ b/src/kiri/mode/cam/op-outline.js @@ -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); diff --git a/src/kiri/mode/cam/op-register.js b/src/kiri/mode/cam/op-register.js index 54a359e9..a4533868 100644 --- a/src/kiri/mode/cam/op-register.js +++ b/src/kiri/mode/cam/op-register.js @@ -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); diff --git a/src/kiri/mode/cam/op-rough.js b/src/kiri/mode/cam/op-rough.js index bbfdaa29..06e2630e 100644 --- a/src/kiri/mode/cam/op-rough.js +++ b/src/kiri/mode/cam/op-rough.js @@ -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; diff --git a/src/kiri/mode/cam/op-shadow.js b/src/kiri/mode/cam/op-shadow.js index 91c6f91a..5c782636 100644 --- a/src/kiri/mode/cam/op-shadow.js +++ b/src/kiri/mode/cam/op-shadow.js @@ -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; diff --git a/src/kiri/mode/cam/op-trace.js b/src/kiri/mode/cam/op-trace.js index ab83409e..6ec46dba 100644 --- a/src/kiri/mode/cam/op-trace.js +++ b/src/kiri/mode/cam/op-trace.js @@ -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) { diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index 3124cd40..2d8bcd8e 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -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); diff --git a/src/kiri/mode/cam/slice.js b/src/kiri/mode/cam/slice.js index d6ac64d1..3afce49a 100644 --- a/src/kiri/mode/cam/slice.js +++ b/src/kiri/mode/cam/slice.js @@ -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; -} +} \ No newline at end of file diff --git a/src/kiri/mode/cam/slicer_topo.js b/src/kiri/mode/cam/slicer_topo.js index 27c19b57..3fe74ca6 100644 --- a/src/kiri/mode/cam/slicer_topo.js +++ b/src/kiri/mode/cam/slicer_topo.js @@ -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 -- 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) { diff --git a/src/kiri/mode/cam/topo3.js b/src/kiri/mode/cam/topo3.js index 4116ac21..84a54fce 100644 --- a/src/kiri/mode/cam/topo3.js +++ b/src/kiri/mode/cam/topo3.js @@ -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 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 p2.y) { const tp = p1; p1 = p2; p2 = tp }; // eliminate if points "under" other lines - for (let j=0; 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 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= flatness) { nup.push(p1, p2); } diff --git a/src/kiri/mode/cam/topo4.js b/src/kiri/mode/cam/topo4.js index 4c5e5388..5bd4f528 100644 --- a/src/kiri/mode/cam/topo4.js +++ b/src/kiri/mode/cam/topo4.js @@ -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 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); }; diff --git a/src/kiri/mode/fdm/init-menu.js b/src/kiri/mode/fdm/init-menu.js new file mode 100644 index 00000000..bb34b97f --- /dev/null +++ b/src/kiri/mode/fdm/init-menu.js @@ -0,0 +1,188 @@ +/** Copyright Stewart Allen -- 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:''})), + (ui.ssmDun = newButton(undefined, onButtonClick, {icon:''})), + (ui.ssmClr = newButton(undefined, onButtonClick, {icon:''})) + ], {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'), + + }; +} diff --git a/src/kiri/mode/laser/init-menu.js b/src/kiri/mode/laser/init-menu.js new file mode 100644 index 00000000..14d40eee --- /dev/null +++ b/src/kiri/mode/laser/init-menu.js @@ -0,0 +1,74 @@ +/** Copyright Stewart Allen -- 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:''})), + (ui.faceDun = newButton(undefined, onButtonClick, {icon:''})), + (ui.faceClr = newButton(undefined, onButtonClick, {icon:''})) + ], {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 }), + + }; + +} diff --git a/src/kiri/mode/sla/init-menu.js b/src/kiri/mode/sla/init-menu.js new file mode 100644 index 00000000..fa45d671 --- /dev/null +++ b/src/kiri/mode/sla/init-menu.js @@ -0,0 +1,58 @@ +/** Copyright Stewart Allen -- 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"), + + }; + +} diff --git a/src/kiri/run/engine.js b/src/kiri/run/engine.js index 2b14bbe0..abd69b2a 100644 --- a/src/kiri/run/engine.js +++ b/src/kiri/run/engine.js @@ -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 }; diff --git a/src/kiri/run/minion.js b/src/kiri/run/minion.js index 58c25f9d..ae1c2920 100644 --- a/src/kiri/run/minion.js +++ b/src/kiri/run/minion.js @@ -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 => { diff --git a/src/kiri/run/worker.js b/src/kiri/run/worker.js index aa7a14e0..afccb19e 100644 --- a/src/kiri/run/worker.js +++ b/src/kiri/run/worker.js @@ -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'; diff --git a/src/load/3mf.js b/src/load/3mf.js index 49d676d0..bf392988 100644 --- a/src/load/3mf.js +++ b/src/load/3mf.js @@ -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")) { diff --git a/src/main/mesh.js b/src/main/mesh.js index b1271a04..aaae2bee 100644 --- a/src/main/mesh.js +++ b/src/main/mesh.js @@ -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 }; diff --git a/web/kiri/engine.html b/web/kiri/engine.html index 4f298f3d..da417539 100644 --- a/web/kiri/engine.html +++ b/web/kiri/engine.html @@ -88,8 +88,7 @@ - +