Merge branch 'rel-4.3'
This commit is contained in:
commit
5d3e32e576
30 changed files with 644 additions and 206 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -9,7 +9,8 @@ jszip-esm.js
|
||||||
logs
|
logs
|
||||||
mod
|
mod
|
||||||
node_modules
|
node_modules
|
||||||
|
pack
|
||||||
package-lock.json
|
package-lock.json
|
||||||
three.js
|
three.js
|
||||||
tmp
|
tmp
|
||||||
pack
|
web/boot/bundle.bin
|
||||||
|
|
|
||||||
31
app.js
31
app.js
|
|
@ -37,6 +37,7 @@ let util;
|
||||||
let dir;
|
let dir;
|
||||||
let log;
|
let log;
|
||||||
let pre;
|
let pre;
|
||||||
|
let alt;
|
||||||
|
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
class AppEmitter extends EventEmitter {}
|
class AppEmitter extends EventEmitter {}
|
||||||
|
|
@ -64,6 +65,7 @@ function init(mod) {
|
||||||
startTime = time();
|
startTime = time();
|
||||||
lastmod = mod.util.lastmod;
|
lastmod = mod.util.lastmod;
|
||||||
logger = mod.log;
|
logger = mod.log;
|
||||||
|
alt = ENV.alt;
|
||||||
pre = ENV.pre || mod.meta.pre;
|
pre = ENV.pre || mod.meta.pre;
|
||||||
debug = ENV.debug || mod.meta.debug;
|
debug = ENV.debug || mod.meta.debug;
|
||||||
oversion = ENV.over || mod.meta.over;
|
oversion = ENV.over || mod.meta.over;
|
||||||
|
|
@ -191,6 +193,19 @@ function init(mod) {
|
||||||
append[key] = minify(append[key]);
|
append[key] = minify(append[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (alt) {
|
||||||
|
for (let [ key, val ] of Object.entries(append)) {
|
||||||
|
let src = `src/main/${key}.js`;
|
||||||
|
if (!fs.existsSync(src)) {
|
||||||
|
logger.log('missing:', src);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fs.mkdirSync(`alt/main`, { recursive: true });
|
||||||
|
let body = fs.readFileSync(src);
|
||||||
|
fs.writeFileSync(`alt/main/${key}.js`, body + val);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// either add module assets to path or require(init.js)
|
// either add module assets to path or require(init.js)
|
||||||
|
|
@ -288,14 +303,14 @@ function initModule(mod, file, dir) {
|
||||||
const path = mod.dir + '/' + dir + '/' + file;
|
const path = mod.dir + '/' + dir + '/' + file;
|
||||||
try {
|
try {
|
||||||
const body = fs.readFileSync(path);
|
const body = fs.readFileSync(path);
|
||||||
// console.log({ inject: code, file, opt });
|
logger.log({ inject: code, file, opt });
|
||||||
if (opt.first) {
|
if (opt.first) {
|
||||||
append[code] = body.toString() + '\n' + append[code];
|
append[code] = body.toString() + '\n' + append[code];
|
||||||
} else {
|
} else {
|
||||||
append[code] += body.toString() + '\n';
|
append[code] += body.toString() + '\n';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log({ missing_file: path, dir, mod });
|
logger.log({ missing_file: path, dir, mod });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onload(fn) {
|
onload(fn) {
|
||||||
|
|
@ -430,6 +445,10 @@ function generateDevices() {
|
||||||
let dstr = JSON.stringify(devs);
|
let dstr = JSON.stringify(devs);
|
||||||
util.mkdir(PATH.join(dir,"src","pack"));
|
util.mkdir(PATH.join(dir,"src","pack"));
|
||||||
fs.writeFileSync(PATH.join(dir,"src","pack","kiri-devs.js"), `export const devices = ${dstr};`);
|
fs.writeFileSync(PATH.join(dir,"src","pack","kiri-devs.js"), `export const devices = ${dstr};`);
|
||||||
|
if (alt) {
|
||||||
|
fs.mkdirSync('alt/pack', { recursive: true });
|
||||||
|
fs.writeFileSync(PATH.join(dir,"alt","pack","kiri-devs.js"), `export const devices = ${dstr};`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function minify(code) {
|
function minify(code) {
|
||||||
|
|
@ -526,7 +545,13 @@ function fixedmap(prefix, map) {
|
||||||
// HTTP 307 redirect
|
// HTTP 307 redirect
|
||||||
function redir(path, type) {
|
function redir(path, type) {
|
||||||
return (req, res, next) => {
|
return (req, res, next) => {
|
||||||
http.redirect(res, path, type);
|
let query = req.url.split('?')[1];
|
||||||
|
let rpath = path;
|
||||||
|
if (query && path.indexOf('?') < 0) {
|
||||||
|
rpath += `?${query}`;
|
||||||
|
}
|
||||||
|
// console.log({ redir: req.url, to: path, query, new_path: rpath });
|
||||||
|
http.redirect(res, rpath, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
{
|
{
|
||||||
|
"compress": false,
|
||||||
"outputs": {
|
"outputs": {
|
||||||
"bundle": "./web/kiri/bundle.bin"
|
"bundle": "./web/boot/bundle.bin"
|
||||||
},
|
},
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{ "root": "./web", "prefix": "" },
|
{ "root": "web", "prefix": "" },
|
||||||
{ "root": "./src", "prefix": "lib" },
|
{ "root": "alt", "prefix": "lib"},
|
||||||
{ "root": "./src/wasm", "prefix": "wasm" },
|
{ "root": "src", "prefix": "lib"},
|
||||||
{ "root": "./src/wasm", "prefix": "lib/kiri/wasm" }
|
{ "root": "src/wasm", "prefix": "wasm" },
|
||||||
|
{ "root": "src/wasm", "prefix": "lib/kiri/wasm" }
|
||||||
|
],
|
||||||
|
"excludes": [
|
||||||
|
"src/pack",
|
||||||
|
"web/boot/bundle.bin"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,21 @@ import path from 'path';
|
||||||
import uglify from 'uglify-js';
|
import uglify from 'uglify-js';
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
|
|
||||||
const cfg = JSON.parse(fs.readFileSync(process.argv[2] || './bundle.config.json', 'utf8'));
|
const cfg = JSON.parse(fs.readFileSync(process.argv[2] || './bin/bundle.config.json', 'utf8'));
|
||||||
const compress = process.env.UGLIFY ? true : false
|
const { inputs, outputs, excludes, compress } = cfg;
|
||||||
|
const envcomp = process.env.COMPRESS ? JSON.parse(process.env.COMPRESS) : undefined;
|
||||||
|
const debug = process.env.DEBUG;
|
||||||
|
|
||||||
// --- recursive directory walker (follows symlinks safely) ---
|
// --- recursive directory walker (follows symlinks safely) ---
|
||||||
function* walk(dir, seen = new Set()) {
|
function* walk(dir, seen = new Set()) {
|
||||||
|
dir = path.normalize(dir);
|
||||||
|
if (excludes.indexOf(dir) >= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const real = fs.realpathSync(dir);
|
const real = fs.realpathSync(dir);
|
||||||
if (seen.has(real)) {
|
if (seen.has(real)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -25,13 +35,19 @@ function* walk(dir, seen = new Set()) {
|
||||||
|
|
||||||
// --- collect all files according to config ---
|
// --- collect all files according to config ---
|
||||||
let entries = [];
|
let entries = [];
|
||||||
for (const input of cfg.inputs) {
|
let virtMap = new Set();
|
||||||
|
for (const input of inputs) {
|
||||||
const root = input.root;
|
const root = input.root;
|
||||||
const prefix = input.prefix || '';
|
const prefix = input.prefix || '';
|
||||||
for (const file of walk(root)) {
|
for (const file of walk(root)) {
|
||||||
const rel = path.relative(root, file).replace(/\\/g, '/');
|
const rel = path.relative(root, file).replace(/\\/g, '/');
|
||||||
const virt = prefix ? `${prefix}/${rel}` : rel;
|
const virt = prefix ? `${prefix}/${rel}` : rel;
|
||||||
entries.push({ file, virt });
|
if (virtMap.has(virt)) {
|
||||||
|
console.log('pre-existing', virt, rel, root);
|
||||||
|
} else {
|
||||||
|
entries.push({ file, virt });
|
||||||
|
virtMap.add(virt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,28 +56,54 @@ const dataParts = [];
|
||||||
const entryMeta = [];
|
const entryMeta = [];
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let cache = new Map();
|
let cache = new Map();
|
||||||
|
let compEnable = (envcomp ?? compress);
|
||||||
|
let cacheDir = cfg.cacheDir ?? ".bcache";
|
||||||
|
|
||||||
|
if (compEnable) {
|
||||||
|
fs.mkdirSync(cacheDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileTime(path) {
|
||||||
|
try {
|
||||||
|
return (fs.statSync(path).ctimeMs/1000) | 0;
|
||||||
|
} catch (e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const { file, virt } of entries) {
|
for (const { file, virt } of entries) {
|
||||||
|
if (excludes.indexOf(file) >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let record = cache.get(file);
|
let record = cache.get(file);
|
||||||
if (record) {
|
if (record) {
|
||||||
// console.log({ dup: file });
|
if (debug) console.log({ alias: virt, from: file });
|
||||||
entryMeta.push({ ...record, virt });
|
entryMeta.push({ ...record, virt });
|
||||||
} else {
|
} else {
|
||||||
|
if (debug) console.log({ write: virt, from: file });
|
||||||
let data = fs.readFileSync(file);
|
let data = fs.readFileSync(file);
|
||||||
if (compress && file.endsWith("js")) {
|
if (compEnable && file.endsWith("js")) {
|
||||||
console.log('compress', file);
|
let cpath = path.resolve(cacheDir, file);
|
||||||
data = uglify.minify(data.toString(), {
|
if (fileTime(cpath) > fileTime(file)) {
|
||||||
compress: {
|
data = fs.readFileSync(cpath);
|
||||||
merge_vars: false,
|
console.log('cached', file);
|
||||||
unused: false
|
} else {
|
||||||
|
console.log('compress', file);
|
||||||
|
data = uglify.minify(data.toString(), {
|
||||||
|
compress: {
|
||||||
|
merge_vars: false,
|
||||||
|
unused: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// console.log({ to: typeof(data), data });
|
||||||
|
if (!data.code) {
|
||||||
|
if (debug) console.log({ skip: file });
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
});
|
data = Buffer.from(data.code);
|
||||||
// console.log({ to: typeof(data), data });
|
fs.mkdirSync(path.dirname(cpath), { recursive: true });
|
||||||
if (!data.code) {
|
fs.writeFileSync(cpath, data);
|
||||||
console.log({ skip: file });
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
data = Buffer.from(data.code);
|
|
||||||
}
|
}
|
||||||
record = { virt, offset, length: data.length };
|
record = { virt, offset, length: data.length };
|
||||||
cache.set(file, record);
|
cache.set(file, record);
|
||||||
|
|
@ -99,7 +141,7 @@ const header = Buffer.concat([count, ...headerParts]);
|
||||||
const full = Buffer.concat([header, ...dataParts]);
|
const full = Buffer.concat([header, ...dataParts]);
|
||||||
|
|
||||||
// --- write final bundle ---
|
// --- write final bundle ---
|
||||||
const outPath = cfg.outputs.bundle || './bundle.bin';
|
const outPath = outputs.bundle || './bundle.bin';
|
||||||
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
||||||
fs.writeFileSync(outPath, full); // ⚡ write raw binary
|
fs.writeFileSync(outPath, full); // ⚡ write raw binary
|
||||||
|
|
||||||
|
|
|
||||||
1
notes.md
1
notes.md
|
|
@ -32,7 +32,6 @@
|
||||||
* `B` multi-extruder rendering of raft fails to offset the rest of the print
|
* `B` multi-extruder rendering of raft fails to offset the rest of the print
|
||||||
* `B` multi-extruder purge blocks fail to generate properly for rafts
|
* `B` multi-extruder purge blocks fail to generate properly for rafts
|
||||||
|
|
||||||
* `F` new parameter to cap output flow rate (on printer device)
|
|
||||||
* `F` new parameters for bridging speed / bridge fan control
|
* `F` new parameters for bridging speed / bridge fan control
|
||||||
* `F` convert ranges to z offsets while continuing to show layer #
|
* `F` convert ranges to z offsets while continuing to show layer #
|
||||||
* `F` support pillar top/bottom should conform to part
|
* `F` support pillar top/bottom should conform to part
|
||||||
|
|
|
||||||
10
package.json
10
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "grid-apps",
|
"name": "grid-apps",
|
||||||
"version": "4.3.3",
|
"version": "4.3.4",
|
||||||
"description": "grid.space 3d slicing & modeling tools",
|
"description": "grid.space 3d slicing & modeling tools",
|
||||||
"author": "Stewart Allen <sa@grid.space>",
|
"author": "Stewart Allen <sa@grid.space>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
"three": "^0.178.0",
|
"three": "^0.178.0",
|
||||||
"three-mesh-bvh": "^0.7.6",
|
"three-mesh-bvh": "^0.7.6",
|
||||||
"uglify-js": "3.14.5",
|
"uglify-js": "3.14.5",
|
||||||
"validator": ">=13.7.0",
|
"validator": ">=13.15.15",
|
||||||
"ws": "^8.18.2"
|
"ws": "^8.18.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -82,7 +82,7 @@
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"setup": "npm run setup-mods && npm i && npm run clean && npm run dev -- --dryrun && npm run webpack-ext",
|
"setup": "npm run setup-mods && npm i && npm run clean && npm run dev -- --alt --dryrun && npm run webpack-ext",
|
||||||
"setup-mods": "cd mods && npm i",
|
"setup-mods": "cd mods && npm i",
|
||||||
"build": "npm run prebuild && electron-builder",
|
"build": "npm run prebuild && electron-builder",
|
||||||
"build-nopublish": "npm run prebuild && electron-builder --publish never",
|
"build-nopublish": "npm run prebuild && electron-builder --publish never",
|
||||||
|
|
@ -93,13 +93,13 @@
|
||||||
"build-win-arm": "npm run build -- --win --arm64",
|
"build-win-arm": "npm run build -- --win --arm64",
|
||||||
"build-mac": "npm run build -- --mac --arm64",
|
"build-mac": "npm run build -- --mac --arm64",
|
||||||
"build-mac-intel": "npm run build -- --mac --x64",
|
"build-mac-intel": "npm run build -- --mac --x64",
|
||||||
"clean": "rm -rf build data dist src/pack tmp src.old web.old",
|
"clean": "rm -rf build data dist src/pack tmp src.old web.old .bcache",
|
||||||
"docs-dev": "docusaurus start --config conf/docusaurus.config.js",
|
"docs-dev": "docusaurus start --config conf/docusaurus.config.js",
|
||||||
"docs-build": "docusaurus build --config conf/docusaurus.config.js",
|
"docs-build": "docusaurus build --config conf/docusaurus.config.js",
|
||||||
"docs-serve": "docusaurus serve --config conf/docusaurus.config.js",
|
"docs-serve": "docusaurus serve --config conf/docusaurus.config.js",
|
||||||
"docs-check": "prettier --config ./conf/prettier.config.js ./docs --check",
|
"docs-check": "prettier --config ./conf/prettier.config.js ./docs --check",
|
||||||
"dev": "npm run webpack-ext && gs-app-server --debug --single",
|
"dev": "npm run webpack-ext && gs-app-server --debug --single",
|
||||||
"prod": "npm run webpack-ext && npm run webpack-src prod && gs-app-server",
|
"prod": "npm run webpack-ext && npm run webpack-src prod && COMPRESS=1 node bin/bundler.mjs && gs-app-server",
|
||||||
"prod-pack": "npm run webpack-ext && npm run webpack-src prod",
|
"prod-pack": "npm run webpack-ext && npm run webpack-src prod",
|
||||||
"prod-dryrun": "gs-app-server --dryrun",
|
"prod-dryrun": "gs-app-server --dryrun",
|
||||||
"start": "npm run prebuild prod && electron .",
|
"start": "npm run prebuild prod && electron .",
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,9 @@ Widget.prototype.encode = function(state) {
|
||||||
json: json, // safe for JSON (float32array mess)
|
json: json, // safe for JSON (float32array mess)
|
||||||
group: this.group.id,
|
group: this.group.id,
|
||||||
track: this.track,
|
track: this.track,
|
||||||
geo: json ? Array.from(geo) : geo
|
geo: json ? Array.from(geo) : geo,
|
||||||
|
meta: this.meta,
|
||||||
|
anno: this.anno,
|
||||||
};
|
};
|
||||||
return coded;
|
return coded;
|
||||||
};
|
};
|
||||||
|
|
@ -234,8 +236,12 @@ registerDecoder(TYPE.WIDGET, function(v, state) {
|
||||||
const id = v.id,
|
const id = v.id,
|
||||||
group = v.group || id,
|
group = v.group || id,
|
||||||
track = v.track || undefined,
|
track = v.track || undefined,
|
||||||
widget = newWidget(id, Widget.Groups.forid(group));
|
widget = newWidget(id, Widget.Groups.forid(group)),
|
||||||
|
meta= v.meta || widget.meta,
|
||||||
|
anno = v.anno || widget.anno;
|
||||||
|
|
||||||
|
widget.meta = meta;
|
||||||
|
widget.anno = anno;
|
||||||
widget.loadVertices(v.json ? v.geo.toFloat32() : v.geo);
|
widget.loadVertices(v.json ? v.geo.toFloat32() : v.geo);
|
||||||
widget.saved = Date.now();
|
widget.saved = Date.now();
|
||||||
if (track && track.pos) {
|
if (track && track.pos) {
|
||||||
|
|
|
||||||
|
|
@ -270,9 +270,7 @@ export const conf = {
|
||||||
firstLayerFanSpeed: 0,
|
firstLayerFanSpeed: 0,
|
||||||
firstLayerFillRate: 35,
|
firstLayerFillRate: 35,
|
||||||
firstLayerFlatten: 0,
|
firstLayerFlatten: 0,
|
||||||
firstLayerLineMult: 1.0,
|
|
||||||
firstLayerNozzleTemp: 0,
|
firstLayerNozzleTemp: 0,
|
||||||
firstLayerPrintMult: 1.0,
|
|
||||||
firstLayerRate: 30,
|
firstLayerRate: 30,
|
||||||
firstLayerYOffset: 0,
|
firstLayerYOffset: 0,
|
||||||
firstSliceHeight: 0.25,
|
firstSliceHeight: 0.25,
|
||||||
|
|
@ -293,6 +291,7 @@ export const conf = {
|
||||||
outputInvertY: false,
|
outputInvertY: false,
|
||||||
outputLayerRetract: false,
|
outputLayerRetract: false,
|
||||||
outputLoops: 0,
|
outputLoops: 0,
|
||||||
|
outputMaxFlowrate: 15,
|
||||||
outputMinLayerTime: 10,
|
outputMinLayerTime: 10,
|
||||||
outputMinSpeed: 5.0,
|
outputMinSpeed: 5.0,
|
||||||
outputPurgeTower: 0,
|
outputPurgeTower: 0,
|
||||||
|
|
@ -302,7 +301,7 @@ export const conf = {
|
||||||
outputRetractDwell: 20,
|
outputRetractDwell: 20,
|
||||||
outputRetractSpeed: 40,
|
outputRetractSpeed: 40,
|
||||||
outputRetractWipe: 0,
|
outputRetractWipe: 0,
|
||||||
outputScarfLength: 5,
|
outputScarfLength: 0,
|
||||||
outputSeekrate: 80,
|
outputSeekrate: 80,
|
||||||
outputShellMult: 1.25,
|
outputShellMult: 1.25,
|
||||||
outputShortPoly: 0.0,
|
outputShortPoly: 0.0,
|
||||||
|
|
@ -319,7 +318,6 @@ export const conf = {
|
||||||
sliceFillAngle: 45,
|
sliceFillAngle: 45,
|
||||||
sliceFillGrow: 0,
|
sliceFillGrow: 0,
|
||||||
sliceFillOverlap: 0.35,
|
sliceFillOverlap: 0.35,
|
||||||
sliceFillRate: 0,
|
|
||||||
sliceFillRepeat: 1,
|
sliceFillRepeat: 1,
|
||||||
sliceFillSparse: 0.25,
|
sliceFillSparse: 0.25,
|
||||||
sliceFillType: "hex",
|
sliceFillType: "hex",
|
||||||
|
|
@ -333,7 +331,6 @@ export const conf = {
|
||||||
sliceShellOrder: "in-out",
|
sliceShellOrder: "in-out",
|
||||||
sliceShells: 3,
|
sliceShells: 3,
|
||||||
sliceSolidMinArea: 1,
|
sliceSolidMinArea: 1,
|
||||||
sliceSolidRate: 0,
|
|
||||||
sliceSupportAngle: 50,
|
sliceSupportAngle: 50,
|
||||||
sliceSupportArea: 0.1,
|
sliceSupportArea: 0.1,
|
||||||
sliceSupportDensity: 0.1,
|
sliceSupportDensity: 0.1,
|
||||||
|
|
@ -500,6 +497,8 @@ export const conf = {
|
||||||
camLatheSpeed: 500,
|
camLatheSpeed: 500,
|
||||||
camLatheSpindle: 1000,
|
camLatheSpindle: 1000,
|
||||||
camLatheTool: 1000,
|
camLatheTool: 1000,
|
||||||
|
camLatheOffStart: 0,
|
||||||
|
camLatheOffEnd: 0,
|
||||||
camLevelDown: 0,
|
camLevelDown: 0,
|
||||||
camLevelInset: 0.5,
|
camLevelInset: 0.5,
|
||||||
camLevelOver: 0.75,
|
camLevelOver: 0.75,
|
||||||
|
|
|
||||||
|
|
@ -5,31 +5,31 @@
|
||||||
import { CSG } from '../../../geo/csg.js';
|
import { CSG } from '../../../geo/csg.js';
|
||||||
import { Tool } from './tool.js';
|
import { Tool } from './tool.js';
|
||||||
|
|
||||||
let nextMeshID = 1;
|
let nextMeshID = 1,
|
||||||
let stock, center, rez;
|
stock,
|
||||||
let path, pathIndex, tool, tools, last;
|
path,
|
||||||
|
pathIndex,
|
||||||
let stockZ;
|
tool,
|
||||||
let stockIndexMsg = false;
|
tools,
|
||||||
let stockSlices;
|
last,
|
||||||
let stockIndex;
|
stockZ,
|
||||||
let startTime;
|
stockIndexMsg = false,
|
||||||
|
stockSlices,
|
||||||
let toolID = -1;
|
stockIndex,
|
||||||
let toolMesh;
|
startTime,
|
||||||
let toolRadius;
|
toolID = -1,
|
||||||
let toolUpdateMsg;
|
toolMesh,
|
||||||
|
toolRadius,
|
||||||
let animateClear = false;
|
toolUpdateMsg,
|
||||||
let animating = false;
|
showStock = false,
|
||||||
let moveDist = 0;
|
animateClear = false,
|
||||||
let renderDist = 0;
|
animating = false,
|
||||||
let renderDone = false;
|
renderDone = false,
|
||||||
let renderPause = 10;
|
renderSteps = 0,
|
||||||
let renderSteps = 0;
|
renderSpeed = 0,
|
||||||
let renderSpeed = 0;
|
indexCount = 0,
|
||||||
let indexCount = 0;
|
updates = 0,
|
||||||
let updates = 0;
|
maxR;
|
||||||
|
|
||||||
export function init(worker) {
|
export function init(worker) {
|
||||||
const { dispatch } = worker;
|
const { dispatch } = worker;
|
||||||
|
|
@ -45,8 +45,6 @@ export function init(worker) {
|
||||||
path = print.output.flat();
|
path = print.output.flat();
|
||||||
tools = settings.tools;
|
tools = settings.tools;
|
||||||
stock = settings.stock;
|
stock = settings.stock;
|
||||||
rez = 1 / Math.sqrt(density / (stock.x * stock.y));
|
|
||||||
|
|
||||||
tool = null;
|
tool = null;
|
||||||
last = null;
|
last = null;
|
||||||
animating = false;
|
animating = false;
|
||||||
|
|
@ -56,11 +54,14 @@ export function init(worker) {
|
||||||
startTime = 0;
|
startTime = 0;
|
||||||
updates = 0;
|
updates = 0;
|
||||||
stockZ = isIndexed ? 0 : stock.z;
|
stockZ = isIndexed ? 0 : stock.z;
|
||||||
|
maxR = Math.hypot(stock.y, stock.z);
|
||||||
|
|
||||||
stockSlices = [];
|
stockSlices = [];
|
||||||
const { x, y, z } = stock;
|
const { x, y, z } = stock;
|
||||||
const sliceCount = parseInt(settings.controller.animesh || 2000) / 100;
|
const sliceCount = parseInt(settings.controller.animesh || 2000) / 100;
|
||||||
const sliceWidth = stock.x / sliceCount;
|
const sliceWidth = stock.x / sliceCount;
|
||||||
|
|
||||||
|
if (showStock)
|
||||||
for (let i = 0; i < sliceCount; i++) {
|
for (let i = 0; i < sliceCount; i++) {
|
||||||
let xmin = -(x / 2) + (i * sliceWidth) + sliceWidth / 2;
|
let xmin = -(x / 2) + (i * sliceWidth) + sliceWidth / 2;
|
||||||
let slice = new Stock(sliceWidth, y, z).translate(xmin, 0, 0);
|
let slice = new Stock(sliceWidth, y, z).translate(xmin, 0, 0);
|
||||||
|
|
@ -74,7 +75,6 @@ export function init(worker) {
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch.animate2 = function (data, send) {
|
dispatch.animate2 = function (data, send) {
|
||||||
renderPause = data.pause || renderPause;
|
|
||||||
renderSpeed = data.speed || 0;
|
renderSpeed = data.speed || 0;
|
||||||
if (animating) {
|
if (animating) {
|
||||||
return send.done();
|
return send.done();
|
||||||
|
|
@ -126,7 +126,6 @@ function renderPath(send) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pathIndex++;
|
pathIndex++;
|
||||||
// console.log(next.point.z);
|
|
||||||
|
|
||||||
if (next.tool && (!tool || tool.getID() !== next.tool.getID())) {
|
if (next.tool && (!tool || tool.getID() !== next.tool.getID())) {
|
||||||
// on real tool change, go to safe Z first
|
// on real tool change, go to safe Z first
|
||||||
|
|
@ -143,44 +142,50 @@ function renderPath(send) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = toolID;
|
const id = toolID;
|
||||||
const rezstep = rez * 2;
|
const rezstep = Math.min(tool.maxDiameter(), 1) / 4;
|
||||||
|
// console.log({ rezstep });
|
||||||
|
|
||||||
if (last) {
|
if (last) {
|
||||||
|
// console.log(last.point, next.point);
|
||||||
const lp = last.point, np = next.point;
|
const lp = last.point, np = next.point;
|
||||||
last = next;
|
last = next;
|
||||||
|
|
||||||
|
// render move line on client
|
||||||
|
send.data({ line: [ lp, np ]});
|
||||||
|
|
||||||
// dwell ops have no point
|
// dwell ops have no point
|
||||||
if (!np || !lp) {
|
if (!np || !lp) {
|
||||||
return renderPath(send);
|
return renderPath(send);
|
||||||
}
|
}
|
||||||
|
|
||||||
let dx = np.x - lp.x,
|
let dx = np.x - lp.x,
|
||||||
dy = np.y - lp.y,
|
dy = np.y - lp.y,
|
||||||
dz = np.z - lp.z,
|
dz = np.z - lp.z,
|
||||||
da = Math.abs((np.a || 0) - (lp.a || 0)),
|
da = (np.a ?? 0) - (lp.a ?? 0), // delta rotation degrees
|
||||||
dr = (da / 360) * (2 * Math.PI * Math.max(np.z, lp.z)),
|
AD = maxR * 2, // arc diameter @ max z
|
||||||
dist = Math.sqrt(dx * dx + dy * dy + dz * dz + dr * dr);
|
dr = (Math.abs(da) / 360) * AD, // delta rotation arc
|
||||||
|
dist = Math.max(Math.hypot(dx, dy, dz), dr); // motion distance
|
||||||
moveDist += dist;
|
|
||||||
|
|
||||||
// skip moves that are less than grid resolution
|
|
||||||
if (moveDist < rezstep) {
|
|
||||||
// console.log('skip', moveDist, rezstep, next);
|
|
||||||
renderPath(send);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// console.log({ dz, da, AD, dr, dist });
|
||||||
|
// sub-divide long moves for animation and better boolean ops
|
||||||
const md = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz), dr);
|
const md = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz), dr);
|
||||||
const st = Math.ceil(md / rezstep);
|
const st = Math.ceil(md / rezstep); // steps
|
||||||
const mx = dx / st, my = dy / st, mz = dz / st;
|
const mx = dx / st, my = dy / st, mz = dz / st, ma = da / st;
|
||||||
// const sd = Math.sqrt(mx*mx + my*my + mz*mz + dr*dr);
|
const sd = Math.sqrt(mx * mx + my * my + mz * mz + ma * ma);
|
||||||
const sd = Math.sqrt(mx * mx + my * my + Math.min(1, mz * mz) + dr * dr);
|
const sp = next.speed;
|
||||||
const moves = [];
|
const moves = [];
|
||||||
for (let i = 0, x = lp.x, y = lp.y, z = lp.z; i < st; i++) {
|
for (let i = 0, x = lp.x, y = lp.y, z = lp.z, a = lp.a; i < st; i++) {
|
||||||
moves.push({ x, y, z, a: lp.a, md: sd, dx, dy, dz });
|
moves.push({ x, y, z, a, md: sd, dx, dy, dz, sp });
|
||||||
x += mx;
|
x += mx;
|
||||||
y += my;
|
y += my;
|
||||||
z += mz;
|
z += mz;
|
||||||
|
a += ma;
|
||||||
}
|
}
|
||||||
moveDist = 0;
|
// console.log({ dist: md, steps: st, speed: next.speed, moves });
|
||||||
moves.push({ ...next.point, md: sd });
|
|
||||||
|
// push last point
|
||||||
|
moves.push({ ...next.point, md: sd, sp });
|
||||||
|
// console.log(...moves);
|
||||||
renderMoves(id, moves, send);
|
renderMoves(id, moves, send);
|
||||||
} else {
|
} else {
|
||||||
last = next;
|
last = next;
|
||||||
|
|
@ -197,7 +202,7 @@ function renderMoves(id, moves, send, seed = 0) {
|
||||||
if (!pos) {
|
if (!pos) {
|
||||||
throw `no pos @ ${index} of ${moves.length}`;
|
throw `no pos @ ${index} of ${moves.length}`;
|
||||||
}
|
}
|
||||||
const { dx, dy, dz } = pos;
|
const { dx, dy, dz, sp, md } = pos;
|
||||||
toolMove(pos);
|
toolMove(pos);
|
||||||
// console.log('renderMoves', {id, moves, seed});
|
// console.log('renderMoves', {id, moves, seed});
|
||||||
let subs = 0;
|
let subs = 0;
|
||||||
|
|
@ -208,15 +213,14 @@ function renderMoves(id, moves, send, seed = 0) {
|
||||||
subs++;
|
subs++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// console.log({ index, subs });
|
|
||||||
renderDist += pos.md;
|
|
||||||
// pause renderer at specified offsets
|
// pause renderer at specified offsets
|
||||||
if (renderSpeed && renderDist >= renderSpeed) {
|
if (renderSpeed) {
|
||||||
renderDist = 0;
|
let time = sp ? (md / sp) * 60000 : 10;
|
||||||
|
// console.log({ time, ...pos });
|
||||||
renderUpdate(send);
|
renderUpdate(send);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
renderMoves(id, moves, send, index + 1);
|
renderMoves(id, moves, send, index + 1);
|
||||||
}, renderPause);
|
}, time / renderSpeed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,24 +10,26 @@ const { client } = api;
|
||||||
let meshes = {},
|
let meshes = {},
|
||||||
button = {},
|
button = {},
|
||||||
label = {},
|
label = {},
|
||||||
|
lineTracker,
|
||||||
material,
|
material,
|
||||||
speedPauses = [0, 0, 0, 0, 0],
|
|
||||||
speedValues = [1, 2, 4, 8, 32],
|
speedValues = [1, 2, 4, 8, 32],
|
||||||
speedNames = ["1x", "2x", "4x", "8x", "!!"],
|
speedNames = ["1x", "2x", "4x", "8x", "!!"],
|
||||||
speedMax = speedValues.length - 1,
|
speedMax = speedValues.length - 1,
|
||||||
speedIndex = 0,
|
speedIndex = 0,
|
||||||
speed,
|
speed,
|
||||||
origin,
|
origin,
|
||||||
color = 0;
|
color = 0,
|
||||||
|
A2R = Math.PI / 180;
|
||||||
|
|
||||||
export function animate_clear2(api) {
|
export function animate_clear2(api) {
|
||||||
let { anim } = api.ui;
|
let { anim } = api.ui;
|
||||||
client.animate_cleanup2();
|
lineTracker?.clear();
|
||||||
Object.keys(meshes).forEach(id => deleteMesh(id));
|
Object.keys(meshes).forEach(id => deleteMesh(id));
|
||||||
api.widgets.setAxisIndex(0);
|
api.widgets.setAxisIndex(0);
|
||||||
api.uc.setVisible(anim.laba, true);
|
api.uc.setVisible(anim.laba, true);
|
||||||
api.uc.setVisible(anim.vala, true);
|
api.uc.setVisible(anim.vala, true);
|
||||||
anim.vala.value = "0.0";
|
anim.vala.value = "0.0";
|
||||||
|
client.animate_cleanup2();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function animate2(api, delay) {
|
export function animate2(api, delay) {
|
||||||
|
|
@ -86,8 +88,9 @@ Object.assign(client, {
|
||||||
},
|
},
|
||||||
|
|
||||||
animate_setup2(settings, ondone) {
|
animate_setup2(settings, ondone) {
|
||||||
|
initPathMesh();
|
||||||
color = settings.controller.dark ? 0x888888 : 0;
|
color = settings.controller.dark ? 0x888888 : 0;
|
||||||
material = new THREE.MeshPhongMaterial({
|
material = material ?? new THREE.MeshPhongMaterial({
|
||||||
flatShading: true,
|
flatShading: true,
|
||||||
transparent: false,
|
transparent: false,
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
|
|
@ -102,6 +105,66 @@ Object.assign(client, {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function initPathMesh() {
|
||||||
|
if (lineTracker) {
|
||||||
|
return lineTracker.clear();
|
||||||
|
}
|
||||||
|
let count = 0,
|
||||||
|
max = 10000,
|
||||||
|
rot = new THREE.Euler(0,0,0),
|
||||||
|
geo = new THREE.BufferGeometry(),
|
||||||
|
mat = new THREE.LineBasicMaterial({ color: 0xffff00 }),
|
||||||
|
pos = new Float32Array(max * 6),
|
||||||
|
lines = new THREE.LineSegments(geo, mat),
|
||||||
|
vec = new THREE.Vector3(0,0,0),
|
||||||
|
ang = new THREE.Vector3(1,0,0);
|
||||||
|
geo.setAttribute('position', new THREE.BufferAttribute(pos, 3));
|
||||||
|
let track = lineTracker = {
|
||||||
|
xro(p) {
|
||||||
|
vec.set(p.x, p.y, p.z);
|
||||||
|
vec.applyAxisAngle(ang, p.a * A2R);
|
||||||
|
},
|
||||||
|
add(p1, p2) {
|
||||||
|
let ind = count * 6;
|
||||||
|
track.xro(p1);
|
||||||
|
pos[ind+0] = vec.x;
|
||||||
|
pos[ind+1] = vec.y;
|
||||||
|
pos[ind+2] = vec.z;
|
||||||
|
track.xro(p2);
|
||||||
|
pos[ind+3] = vec.x;
|
||||||
|
pos[ind+4] = vec.y;
|
||||||
|
pos[ind+5] = vec.z;
|
||||||
|
count++;
|
||||||
|
if (count >= max) {
|
||||||
|
let opos = pos;
|
||||||
|
max += 10000;
|
||||||
|
pos = new Float32Array(max * 6),
|
||||||
|
pos.set(opos);
|
||||||
|
geo.setAttribute('position', new THREE.BufferAttribute(pos, 3));
|
||||||
|
}
|
||||||
|
geo.setDrawRange(0, count * 2);
|
||||||
|
geo.attributes.position.needsUpdate = true;
|
||||||
|
track.visible(true);
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
count = 0;
|
||||||
|
geo.setDrawRange(0,0);
|
||||||
|
track.rotate(0);
|
||||||
|
track.visible(false);
|
||||||
|
},
|
||||||
|
rotate(angle) {
|
||||||
|
rot.x = angle * A2R;
|
||||||
|
lines.setRotationFromEuler(rot);
|
||||||
|
},
|
||||||
|
visible(bool) {
|
||||||
|
if (bool !== lines.visible) {
|
||||||
|
lines.visible = bool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
space.world.add(lines);
|
||||||
|
}
|
||||||
|
|
||||||
function meshAdd(id, ind, pos, ilen, plen) {
|
function meshAdd(id, ind, pos, ilen, plen) {
|
||||||
const geo = new THREE.BufferGeometry();
|
const geo = new THREE.BufferGeometry();
|
||||||
const pa = plen ? pos.subarray(0, plen * 3) : pos;
|
const pa = plen ? pos.subarray(0, plen * 3) : pos;
|
||||||
|
|
@ -132,6 +195,7 @@ function meshUpdate(id, ind, pos, ilen, plen) {
|
||||||
|
|
||||||
function deleteMesh(id) {
|
function deleteMesh(id) {
|
||||||
space.world.remove(meshes[id]);
|
space.world.remove(meshes[id]);
|
||||||
|
meshes[id].geometry.dispose();
|
||||||
delete meshes[id];
|
delete meshes[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,8 +229,7 @@ function play(opts) {
|
||||||
}
|
}
|
||||||
client.animate2({
|
client.animate2({
|
||||||
speed,
|
speed,
|
||||||
steps: steps || Infinity,
|
steps: steps || Infinity
|
||||||
pause: speedPauses[speedIndex]
|
|
||||||
}, handleUpdate);
|
}, handleUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,8 +240,7 @@ function fast(opts) {
|
||||||
button.pause.style.display = '';
|
button.pause.style.display = '';
|
||||||
client.animate2({
|
client.animate2({
|
||||||
speed,
|
speed,
|
||||||
steps: steps || Infinity,
|
steps: steps || Infinity
|
||||||
pause: speedPauses[speedIndex]
|
|
||||||
}, handleUpdate);
|
}, handleUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,6 +278,7 @@ function handleUpdate(data) {
|
||||||
if (data.stock_index !== undefined) {
|
if (data.stock_index !== undefined) {
|
||||||
api.widgets.setAxisIndex(data.stock_index);
|
api.widgets.setAxisIndex(data.stock_index);
|
||||||
label.a.value = -data.stock_index.toFixed(1);
|
label.a.value = -data.stock_index.toFixed(1);
|
||||||
|
lineTracker.rotate(-data.stock_index);
|
||||||
}
|
}
|
||||||
if (data.mesh_index) {
|
if (data.mesh_index) {
|
||||||
const { id, index } = data.mesh_index;
|
const { id, index } = data.mesh_index;
|
||||||
|
|
@ -229,9 +292,12 @@ function handleUpdate(data) {
|
||||||
const { id, ind, pos, ilen, plen } = data.mesh_update;
|
const { id, ind, pos, ilen, plen } = data.mesh_update;
|
||||||
meshUpdate(id, ind, pos, ilen, plen);
|
meshUpdate(id, ind, pos, ilen, plen);
|
||||||
}
|
}
|
||||||
if (data && data.progress) {
|
if (data.progress) {
|
||||||
label.progress.value = (data.progress * 100).toFixed(1);
|
label.progress.value = (data.progress * 100).toFixed(1);
|
||||||
}
|
}
|
||||||
|
if (data.line) {
|
||||||
|
lineTracker.add(...data.line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSpeed(inc = 0) {
|
function updateSpeed(inc = 0) {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { selectHoles } from './cl-hole.js';
|
||||||
import { selectHelical } from './cl-helical.js';
|
import { selectHelical } from './cl-helical.js';
|
||||||
import { surfaceAdd } from './cl-surface.js';
|
import { surfaceAdd } from './cl-surface.js';
|
||||||
import { traceAdd } from './cl-trace.js';
|
import { traceAdd } from './cl-trace.js';
|
||||||
|
import { opRender } from './client.js';
|
||||||
|
|
||||||
const { MODES } = api.const;
|
const { MODES } = api.const;
|
||||||
const { uc: UC } = api;
|
const { uc: UC } = api;
|
||||||
|
|
@ -349,7 +350,9 @@ export function createPopOps() {
|
||||||
tolerance: 'camTolerance',
|
tolerance: 'camTolerance',
|
||||||
filter: 'camContourFilter',
|
filter: 'camContourFilter',
|
||||||
leave: 'camContourLeave',
|
leave: 'camContourLeave',
|
||||||
linear: 'camLatheLinear'
|
linear: 'camLatheLinear',
|
||||||
|
offStart: 'camLatheOffStart',
|
||||||
|
offEnd: 'camLatheOffEnd'
|
||||||
}).inputs = {
|
}).inputs = {
|
||||||
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
|
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
|
||||||
// axis: UC.newSelect(LANG.cd_axis, {}, "xyaxis"),
|
// axis: UC.newSelect(LANG.cd_axis, {}, "xyaxis"),
|
||||||
|
|
@ -360,6 +363,9 @@ export function createPopOps() {
|
||||||
step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 100.0) }),
|
step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 100.0) }),
|
||||||
angle: UC.newInput(LANG.cc_sang_s, { title: LANG.cc_sang_l, convert: UC.toFloat, bound: UC.bound(0.01, 180.0) }),
|
angle: UC.newInput(LANG.cc_sang_s, { title: LANG.cc_sang_l, convert: UC.toFloat, bound: UC.bound(0.01, 180.0) }),
|
||||||
sep: UC.newBlank({ class: "pop-sep" }),
|
sep: UC.newBlank({ class: "pop-sep" }),
|
||||||
|
offStart: UC.newInput(LANG.ci_laso_s, { title: LANG.ci_laso_l, convert: UC.toFloat}),
|
||||||
|
offEnd: UC.newInput(LANG.ci_laeo_s, { title: LANG.ci_laeo_l, convert: UC.toFloat}),
|
||||||
|
sep: UC.newBlank({ class: "pop-sep" }),
|
||||||
tolerance: UC.newInput(LANG.ou_toll_s, { title: LANG.ou_toll_l, convert: UC.toFloat, bound: UC.bound(0, 10.0), units: true, round: 4 }),
|
tolerance: UC.newInput(LANG.ou_toll_s, { title: LANG.ou_toll_l, convert: UC.toFloat, bound: UC.bound(0, 10.0), units: true, round: 4 }),
|
||||||
leave: UC.newInput(LANG.cf_leav_s, { title: LANG.cf_leav_l, convert: UC.toFloat, bound: UC.bound(0, 100) }),
|
leave: UC.newInput(LANG.cf_leav_s, { title: LANG.cf_leav_l, convert: UC.toFloat, bound: UC.bound(0, 100) }),
|
||||||
sep: UC.newBlank({ class: "pop-sep" }),
|
sep: UC.newBlank({ class: "pop-sep" }),
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ const { widgets: WIDGETS } = api;
|
||||||
class Client {
|
class Client {
|
||||||
animVer = 0;
|
animVer = 0;
|
||||||
camStock;
|
camStock;
|
||||||
|
showStock = true;
|
||||||
current;
|
current;
|
||||||
currentIndex;
|
currentIndex;
|
||||||
flipping;
|
flipping;
|
||||||
|
|
@ -640,7 +641,8 @@ export function init() {
|
||||||
});
|
});
|
||||||
|
|
||||||
api.event.on("cam.stock.toggle", (bool) => {
|
api.event.on("cam.stock.toggle", (bool) => {
|
||||||
env.camStock && (env.camStock.visible = bool ?? !env.camStock.visible);
|
env.showStock = bool ?? !env.showStock;
|
||||||
|
updateStock();
|
||||||
});
|
});
|
||||||
|
|
||||||
api.event.on("boolean.click", api.platform.update_bounds);
|
api.event.on("boolean.click", api.platform.update_bounds);
|
||||||
|
|
|
||||||
|
|
@ -257,17 +257,18 @@ export function cam_export(print, online) {
|
||||||
points--;
|
points--;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let gn;
|
|
||||||
if (out.emit >= 0 && out.emit <= 3) gn = `G${out.emit}`;
|
let dx = opt.dx || newpos.x - pos.x,
|
||||||
let speed = out.speed,
|
|
||||||
arc = out.emit == 2 || out.emit == 3,
|
|
||||||
center = out.center,
|
|
||||||
nl = (compact_output && lastGn === gn) ? [] : [gn],
|
|
||||||
dx = opt.dx || newpos.x - pos.x,
|
|
||||||
dy = opt.dy || newpos.y - pos.y,
|
dy = opt.dy || newpos.y - pos.y,
|
||||||
dz = opt.dz || newpos.z - pos.z,
|
dz = opt.dz || newpos.z - pos.z,
|
||||||
da = newpos.a != pos.a,
|
da = newpos.a != pos.a,
|
||||||
maxf = dz ? maxZd : maxXYd,
|
gv = (dz >= 0 && out.emit >= 0 && out.emit <= 3) ? out.emit : 1,
|
||||||
|
gn = `G${gv}`,
|
||||||
|
speed = out.speed,
|
||||||
|
arc = out.emit == 2 || out.emit == 3,
|
||||||
|
center = out.center,
|
||||||
|
nl = (compact_output && lastGn === gn) ? [] : [gn],
|
||||||
|
maxf = (dz < 0 ? maxZd : maxXYd) || speed,
|
||||||
feed = Math.min(speed || maxf, maxf),
|
feed = Math.min(speed || maxf, maxf),
|
||||||
dist = Math.sqrt(dx * dx + dy * dy + dz * dz),
|
dist = Math.sqrt(dx * dx + dy * dy + dz * dz),
|
||||||
newFeed = feed && feed !== pos.f;
|
newFeed = feed && feed !== pos.f;
|
||||||
|
|
|
||||||
|
|
@ -412,7 +412,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert short planar moves to cuts in some cases
|
// convert short planar moves to cuts in some cases
|
||||||
if (hasDelta && !isRough && !isArc && isMove && deltaXY <= moveLen && deltaZ <= 0 && !lasering) {
|
if (hasDelta && !isArc && isMove && deltaXY <= moveLen && deltaZ <= 0 && !lasering) {
|
||||||
let iscontour = tolerance > 0;
|
let iscontour = tolerance > 0;
|
||||||
let isflat = absDeltaZ < 0.001;
|
let isflat = absDeltaZ < 0.001;
|
||||||
// restrict this to contouring
|
// restrict this to contouring
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,8 @@ export class Topo {
|
||||||
x: axis === "x",
|
x: axis === "x",
|
||||||
y: axis === "y"
|
y: axis === "y"
|
||||||
},
|
},
|
||||||
zMin = min.z + 0.0001,
|
|
||||||
tolerance = op.tolerance,
|
tolerance = op.tolerance,
|
||||||
resolution = tolerance ? tolerance : 1 / Math.sqrt(density / (span.x * span.y)),
|
resolution = (tolerance ? tolerance : 1 / Math.sqrt(density / (span.x * span.y))).round(5),
|
||||||
step = this.step = (tool.traceOffset() * 2) * op.step,
|
step = this.step = (tool.traceOffset() * 2) * op.step,
|
||||||
angle = this.angle = op.angle || 1;
|
angle = this.angle = op.angle || 1;
|
||||||
|
|
||||||
|
|
@ -52,17 +51,20 @@ export class Topo {
|
||||||
console.log(widget.id, 'topo4 auto tolerance', resolution.round(4));
|
console.log(widget.id, 'topo4 auto tolerance', resolution.round(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.zBottom = state.zBottom;
|
this.zBottom = state.zBottom ?? 0;
|
||||||
this.resolution = resolution;
|
this.resolution = resolution;
|
||||||
this.vertices = widget.getGeoVertices({ unroll: true, translate: true });
|
this.vertices = widget.getGeoVertices({ unroll: true, translate: true });
|
||||||
this.tabverts = widget.getTabVertices();
|
this.tabverts = widget.getTabVertices();
|
||||||
this.tool = tool.generateProfile(resolution).profile;
|
this.tool = tool.generateProfile(resolution).profile;
|
||||||
this.maxo = tool.profileDim.maxo * resolution;
|
this.maxo = tool.profileDim.maxo * resolution;
|
||||||
this.diam = tool.fluteDiameter();
|
this.diam = tool.fluteDiameter();
|
||||||
|
this.unit = tool.unitScale();
|
||||||
this.zoff = widget.track.top || 0;
|
this.zoff = widget.track.top || 0;
|
||||||
this.leave = op.leave || 0;
|
this.leave = op.leave || 0;
|
||||||
this.linear = op.linear || false;
|
this.linear = op.linear || false;
|
||||||
this.lineColor = state.settings.controller.dark ? 0xffff00 : 0x555500;
|
this.lineColor = state.settings.controller.dark ? 0xffff00 : 0x555500;
|
||||||
|
this.offStart = op.offStart ?? 0;
|
||||||
|
this.offEnd = op.offEnd ?? 0;
|
||||||
|
|
||||||
onupdate(0, "lathe");
|
onupdate(0, "lathe");
|
||||||
|
|
||||||
|
|
@ -86,8 +88,8 @@ export class Topo {
|
||||||
}
|
}
|
||||||
|
|
||||||
async slice(onupdate) {
|
async slice(onupdate) {
|
||||||
const { vertices, resolution, tabverts, zoff } = this;
|
const { vertices, resolution, tabverts, zoff, offStart, offEnd, tool, unit } = this;
|
||||||
|
const { minions } = self.kiri_worker;
|
||||||
const range = this.range = { min: Infinity, max: -Infinity };
|
const range = this.range = { min: Infinity, max: -Infinity };
|
||||||
const box = this.box = new THREE.Box2();
|
const box = this.box = new THREE.Box2();
|
||||||
|
|
||||||
|
|
@ -101,9 +103,10 @@ export class Topo {
|
||||||
range.max = Math.max(range.max, x);
|
range.max = Math.max(range.max, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add tool radius to slice min/max range ot fully carve part
|
// add tool diameter to slice min/max range to fully carve part
|
||||||
range.min -= this.diam / 2;
|
range.min += offStart * unit;
|
||||||
range.max += this.diam / 2;
|
range.max -= offEnd * unit;
|
||||||
|
range.max += resolution * 2;
|
||||||
|
|
||||||
// merge in tab vertices here so they don't affect slice range / dimensions
|
// merge in tab vertices here so they don't affect slice range / dimensions
|
||||||
for (let i = 0, l = tabverts.length; i < l; i += 3) {
|
for (let i = 0, l = tabverts.length; i < l; i += 3) {
|
||||||
|
|
@ -116,24 +119,31 @@ export class Topo {
|
||||||
// re-create shared vertex array for workers
|
// re-create shared vertex array for workers
|
||||||
this.vertices = [].appendAll(vertices).appendAll(tabverts).toFloat32().toShared();
|
this.vertices = [].appendAll(vertices).appendAll(tabverts).toFloat32().toShared();
|
||||||
|
|
||||||
|
const zSpan = range.max - range.min;
|
||||||
const shards = Math.ceil(Math.min(25, vertices.length / 27000));
|
const shards = Math.ceil(Math.min(25, vertices.length / 27000));
|
||||||
const step = (range.max - range.min) / shards;
|
const totalSteps = Math.ceil(zSpan / resolution);
|
||||||
|
const stardSteps = Math.ceil(totalSteps / shards);
|
||||||
|
const stepWidth = stardSteps * resolution;
|
||||||
|
|
||||||
let index = 0;
|
|
||||||
let slices = this.slices = [];
|
let slices = this.slices = [];
|
||||||
let slice = { min: range.min, max: range.min + step, index };
|
for (let i=0; i<shards; i++) {
|
||||||
|
let s;
|
||||||
for (let z = range.min; z < range.max; z += resolution) {
|
slices.push(s = {
|
||||||
if (z > slice.max) {
|
min: range.min + i * stepWidth,
|
||||||
slices.push(slice);
|
max: Math.min(range.max, range.min + (i+1) * stepWidth),
|
||||||
slice = { min: z, max: z + step, index };
|
index: i * stardSteps
|
||||||
}
|
});
|
||||||
index++;
|
|
||||||
}
|
}
|
||||||
slices.push(slice);
|
|
||||||
// console.log({ shards, range, step, slices });
|
|
||||||
|
|
||||||
const { minions } = self.kiri_worker;
|
// console.log({
|
||||||
|
// shards,
|
||||||
|
// step: stepWidth,
|
||||||
|
// range,
|
||||||
|
// resolution,
|
||||||
|
// slices: slices.slice(),
|
||||||
|
// minions
|
||||||
|
// });
|
||||||
|
|
||||||
if (minions?.running > 1) {
|
if (minions?.running > 1) {
|
||||||
return await this.sliceMinions(onupdate);
|
return await this.sliceMinions(onupdate);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -217,10 +227,10 @@ export class Topo {
|
||||||
|
|
||||||
const tlen = tool.length;
|
const tlen = tool.length;
|
||||||
const slen = slices.length;
|
const slen = slices.length;
|
||||||
const sinc = Math.max(1, Math.ceil(step / resolution));
|
|
||||||
const heights = [];
|
const heights = [];
|
||||||
|
|
||||||
// cull slice lines to only the ones in range (5x faster)
|
// console.log({ tlen, slen, sinc, slices: slices.map(s => s.z) });
|
||||||
|
// cull slice lines to only the ones in range (~5x faster)
|
||||||
const oslices = [];
|
const oslices = [];
|
||||||
for (let slice of slices) {
|
for (let slice of slices) {
|
||||||
const lines = slice.lines;
|
const lines = slice.lines;
|
||||||
|
|
@ -244,7 +254,9 @@ export class Topo {
|
||||||
// iterate over all slices (real x = z)
|
// iterate over all slices (real x = z)
|
||||||
// find max real z using z ray intersect from tool point to slice lines + offset
|
// find max real z using z ray intersect from tool point to slice lines + offset
|
||||||
let lz = 0;
|
let lz = 0;
|
||||||
for (let si = 0; si < slen; si += sinc) {
|
for (let sz = 0; ; sz += step) {
|
||||||
|
let si = Math.ceil(sz / resolution);
|
||||||
|
if (si >= oslices.length) break;
|
||||||
const rx = oslices[si].z;
|
const rx = oslices[si].z;
|
||||||
let mz = -Infinity;
|
let mz = -Infinity;
|
||||||
// iterate over tool offsets
|
// iterate over tool offsets
|
||||||
|
|
@ -281,12 +293,12 @@ export class Topo {
|
||||||
}
|
}
|
||||||
if (mz === -Infinity && xo === 0 && yo === 0) {
|
if (mz === -Infinity && xo === 0 && yo === 0) {
|
||||||
// tool tip is off the model
|
// tool tip is off the model
|
||||||
continue;
|
// continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mz === -Infinity) {
|
if (mz === -Infinity) {
|
||||||
mz = zBottom; // Math.min(0, lz)
|
mz = zBottom;
|
||||||
} else if (zBottom && mz < zBottom) {
|
} else if (mz < zBottom) {
|
||||||
mz = zBottom;
|
mz = zBottom;
|
||||||
}
|
}
|
||||||
heights.push(rx, 0, lz = mz);
|
heights.push(rx, 0, lz = mz);
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,11 @@ export function init() {
|
||||||
"sliceFillType": LANG.fi_type,
|
"sliceFillType": LANG.fi_type,
|
||||||
"sliceFillWidth": LANG.fi_wdth_s,
|
"sliceFillWidth": LANG.fi_wdth_s,
|
||||||
"sliceFillSparse": LANG.fi_pcnt_s,
|
"sliceFillSparse": LANG.fi_pcnt_s,
|
||||||
"sliceFillRate": LANG.ou_feed_s,
|
|
||||||
"sliceFillGrow": LANG.fi_grow_s,
|
"sliceFillGrow": LANG.fi_grow_s,
|
||||||
"sliceSolidRate": LANG.ou_fini_s,
|
|
||||||
"sliceSolidMinArea": LANG.ad_msol_s,
|
"sliceSolidMinArea": LANG.ad_msol_s,
|
||||||
"sliceTopLayers": LANG.sl_ltop_s,
|
"sliceTopLayers": LANG.sl_ltop_s,
|
||||||
"sliceBottomLayers": LANG.sl_lbot_s,
|
"sliceBottomLayers": LANG.sl_lbot_s,
|
||||||
"firstLayerRate": LANG.fl_rate_s,
|
"firstLayerRate": LANG.fl_rate_s,
|
||||||
"firstLayerPrintMult": LANG.fl_mult_s,
|
|
||||||
"firstLayerFanSpeed": LANG.ou_fans_s,
|
"firstLayerFanSpeed": LANG.ou_fans_s,
|
||||||
// prepare
|
// prepare
|
||||||
"sliceShellOrder": LANG.sl_ordr_s,
|
"sliceShellOrder": LANG.sl_ordr_s,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { fdm_export } from './export.js';
|
||||||
// fil = filament diameter
|
// fil = filament diameter
|
||||||
// slice = slice height
|
// slice = slice height
|
||||||
function extrudePerMM(noz, fil, slice) {
|
function extrudePerMM(noz, fil, slice) {
|
||||||
|
// (ratio of nozzle to filament) * (ratio of slice to nozzle)
|
||||||
return ((Math.PI * util.sqr(noz / 2)) / (Math.PI * util.sqr(fil / 2))) * (slice / noz);
|
return ((Math.PI * util.sqr(noz / 2)) / (Math.PI * util.sqr(fil / 2))) * (slice / noz);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
{ extrudeAbs } = device,
|
{ extrudeAbs } = device,
|
||||||
{ exportThumb } = controller,
|
{ exportThumb } = controller,
|
||||||
{ extrudeMM, extrudePerMM } = FDM,
|
{ extrudeMM, extrudePerMM } = FDM,
|
||||||
extused = Object.keys(print.extruders).map(v => parseInt(v)),
|
|
||||||
timeFactor = (device.gcodeTime || 1) * 1.5,
|
timeFactor = (device.gcodeTime || 1) * 1.5,
|
||||||
decimals = config.gcode_decimals || 4,
|
decimals = config.gcode_decimals || 4,
|
||||||
zMoveMax = device.deviceZMax || 0,
|
zMoveMax = device.deviceZMax || 0,
|
||||||
|
|
@ -49,7 +48,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
last = null,
|
last = null,
|
||||||
zpos = 0,
|
zpos = 0,
|
||||||
bmax = 0,
|
bmax = 0,
|
||||||
blastz = 0,
|
|
||||||
belt_add_y = (process.firstLayerYOffset || 0) - (belty || 0),
|
belt_add_y = (process.firstLayerYOffset || 0) - (belty || 0),
|
||||||
oloops = process.outputLoops || 0,
|
oloops = process.outputLoops || 0,
|
||||||
zhop = process.zHopDistance || 0, // range
|
zhop = process.zHopDistance || 0, // range
|
||||||
|
|
@ -58,10 +56,8 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
retDist = process.outputRetractDist || 0, // range
|
retDist = process.outputRetractDist || 0, // range
|
||||||
retSpeed = process.outputRetractSpeed * 60 || 1, // range
|
retSpeed = process.outputRetractSpeed * 60 || 1, // range
|
||||||
retDwell = process.outputRetractDwell || 0, // range
|
retDwell = process.outputRetractDwell || 0, // range
|
||||||
timeDwell = retDwell / 1000,
|
|
||||||
scarfZ = process.outputScarfLength ? true : false,
|
scarfZ = process.outputScarfLength ? true : false,
|
||||||
arcDist = isBelt || scarfZ ? 0 : (process.arcTolerance || 0),
|
arcDist = isBelt || scarfZ ? 0 : (process.arcTolerance || 0),
|
||||||
arcMin = 1,
|
|
||||||
arcRes = 20,
|
arcRes = 20,
|
||||||
arcDev = 0.5,
|
arcDev = 0.5,
|
||||||
arcMax = 40,
|
arcMax = 40,
|
||||||
|
|
@ -85,7 +81,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
fanSpeedSave = undefined,
|
fanSpeedSave = undefined,
|
||||||
fanSpeedBase = 0,
|
fanSpeedBase = 0,
|
||||||
fanSpeed = 0,
|
fanSpeed = 0,
|
||||||
lastType = undefined,
|
|
||||||
lastNozzleTemp = nozzleTemp,
|
lastNozzleTemp = nozzleTemp,
|
||||||
lastBedTemp = bedTemp,
|
lastBedTemp = bedTemp,
|
||||||
lastFanSpeed = fanSpeed,
|
lastFanSpeed = fanSpeed,
|
||||||
|
|
@ -169,7 +164,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
retDist = params.outputRetractDist || 0; // range
|
retDist = params.outputRetractDist || 0; // range
|
||||||
retSpeed = params.outputRetractSpeed * 60 || 1; // range
|
retSpeed = params.outputRetractSpeed * 60 || 1; // range
|
||||||
retDwell = params.outputRetractDwell || 0; // range
|
retDwell = params.outputRetractDwell || 0; // range
|
||||||
timeDwell = retDwell / 1000;
|
|
||||||
nozzleTemp = layer === 0 ?
|
nozzleTemp = layer === 0 ?
|
||||||
params.firstLayerNozzleTemp || params.outputTemp :
|
params.firstLayerNozzleTemp || params.outputTemp :
|
||||||
params.outputTemp || params.firstLayerNozzleTemp;
|
params.outputTemp || params.firstLayerNozzleTemp;
|
||||||
|
|
@ -470,7 +464,7 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
if (isBelt) {
|
if (isBelt) {
|
||||||
let zheight = path ? path.height || 0 : 0;
|
let zheight = path ? path.height || 0 : 0;
|
||||||
epos.x = originCenter ? -pos.x : device.bedWidth - pos.x;
|
epos.x = originCenter ? -pos.x : device.bedWidth - pos.x;
|
||||||
epos.z = blastz = pos.z * icos;
|
epos.z = pos.z * icos;
|
||||||
epos.y = -pos.y + epos.z * bcos + belt_add_y;
|
epos.y = -pos.y + epos.z * bcos + belt_add_y;
|
||||||
lout = epos;
|
lout = epos;
|
||||||
}
|
}
|
||||||
|
|
@ -635,7 +629,7 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
|
|
||||||
// emit comment on output type chage
|
// emit comment on output type chage
|
||||||
if (last && out.type !== last.type) {
|
if (last && out.type !== last.type) {
|
||||||
lastType = subst.feature = out.type;
|
subst.feature = out.type;
|
||||||
if (gcodeFeature && gcodeFeature.length) {
|
if (gcodeFeature && gcodeFeature.length) {
|
||||||
appendAllSub(gcodeFeature);
|
appendAllSub(gcodeFeature);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -752,7 +746,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if new point is off the arc
|
// if new point is off the arc
|
||||||
// if (deem || depm || desp || dc > arcDist || cc.r < arcMin || cc.r > arcMax || dist > cc.r) {
|
|
||||||
if (deem || depm || desp || dc * arcQ.center.length / arcQ.rSum > arcDist || dist > cc.r || cc.r > arcMax || radFault || !arcValid()) {
|
if (deem || depm || desp || dc * arcQ.center.length / arcQ.rSum > arcDist || dist > cc.r || cc.r > arcMax || radFault || !arcValid()) {
|
||||||
// let debug = [deem, depm, desp, dc * arcQ.center.length / arcQ.rSum > arcDist, dist > cc.r, cc.r > arcMax, radFault];
|
// let debug = [deem, depm, desp, dc * arcQ.center.length / arcQ.rSum > arcDist, dist > cc.r, cc.r > arcMax, radFault];
|
||||||
if (arcQ.length === 4) {
|
if (arcQ.length === 4) {
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ export function menu() {
|
||||||
_____: newGroup(LANG.sw_menu, $('fdm-walls'), { modes:FDM, driven, hideable, separator, group:"fdm-walls" }),
|
_____: newGroup(LANG.sw_menu, $('fdm-walls'), { modes:FDM, driven, hideable, separator, group:"fdm-walls" }),
|
||||||
sliceShells: newInput(LANG.sl_shel_s, { title:LANG.sl_shel_l, convert:toFloat }),
|
sliceShells: newInput(LANG.sl_shel_s, { title:LANG.sl_shel_l, convert:toFloat }),
|
||||||
sliceLineWidth: newInput(LANG.sl_line_s, { title:LANG.sl_line_l, convert:toFloat, bound:bound(0,5) }),
|
sliceLineWidth: newInput(LANG.sl_line_s, { title:LANG.sl_line_l, convert:toFloat, bound:bound(0,5) }),
|
||||||
|
sliceFillOverlap: newInput(LANG.fi_over_s, {title:LANG.fi_over_l, convert:toFloat, bound:bound(0.0,1.0)}),
|
||||||
separator: newBlank({ class:"set-sep", driven }),
|
separator: newBlank({ class:"set-sep", driven }),
|
||||||
sliceShellOrder: newSelect(LANG.sl_ordr_s, { title:LANG.sl_ordr_l}, "shell"),
|
sliceShellOrder: newSelect(LANG.sl_ordr_s, { title:LANG.sl_ordr_l}, "shell"),
|
||||||
sliceDetectThin: newSelect(LANG.ad_thin_s, { title: LANG.ad_thin_l, action: thinWallSave }, "thin"),
|
sliceDetectThin: newSelect(LANG.ad_thin_s, { title: LANG.ad_thin_l, action: thinWallSave }, "thin"),
|
||||||
|
|
@ -86,19 +87,17 @@ export function menu() {
|
||||||
sliceZInterleave: newBoolean(LANG.ad_zint_s, onBooleanClick, {title:LANG.ad_zint_l, show:zIntShow}),
|
sliceZInterleave: newBoolean(LANG.ad_zint_s, onBooleanClick, {title:LANG.ad_zint_l, show:zIntShow}),
|
||||||
_____: newGroup(LANG.fs_menu, $('fdm-solid'), { modes:FDM, driven, hideable, separator, group:"fdm-solid" }),
|
_____: newGroup(LANG.fs_menu, $('fdm-solid'), { modes:FDM, driven, hideable, separator, group:"fdm-solid" }),
|
||||||
sliceFillAngle: newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:toFloat}),
|
sliceFillAngle: newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:toFloat}),
|
||||||
sliceFillOverlap: newInput(LANG.fi_over_s, {title:LANG.fi_over_l, convert:toFloat, bound:bound(0.0,2.0)}),
|
|
||||||
sliceFillGrow: newInput(LANG.fi_grow_s, {title:LANG.fi_grow_l, convert:toFloat}),
|
sliceFillGrow: newInput(LANG.fi_grow_s, {title:LANG.fi_grow_l, convert:toFloat}),
|
||||||
sliceSolidMinArea: newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:toFloat}),
|
sliceSolidMinArea: newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:toFloat}),
|
||||||
_____: newGroup(LANG.fi_menu, $('fdm-fill'), { modes:FDM, driven, hideable, separator, group:"fdm-fill" }),
|
_____: newGroup(LANG.fi_menu, $('fdm-fill'), { modes:FDM, driven, hideable, separator, group:"fdm-fill" }),
|
||||||
sliceFillType: newSelect(LANG.fi_type, {trigger}, "infill"),
|
sliceFillType: newSelect(LANG.fi_type, {trigger}, "infill"),
|
||||||
sliceFillSparse: newInput(LANG.fi_pcnt_s, {title:LANG.fi_pcnt_l, convert:toFloat, bound:bound(0.0,1.0), show:hasInfill}),
|
sliceFillSparse: newInput(LANG.fi_pcnt_s, {title:LANG.fi_pcnt_l, convert:toFloat, bound:bound(0.0,1.0), show:hasInfill}),
|
||||||
sliceFillRepeat: newInput(LANG.fi_rept_s, {title:LANG.fi_rept_l, convert:toInt, bound:bound(1,10), show:fillIsLinear}),
|
sliceFillRepeat: newInput(LANG.fi_rept_s, {title:LANG.fi_rept_l, convert:toInt, bound:bound(1,10), show:fillIsLinear}),
|
||||||
separator: newBlank({ class:"set-sep", driven }),
|
|
||||||
sliceFillRate: newInput(LANG.ou_feed_s, {title:LANG.ou_feed_l, convert:toInt, bound:bound(0,500)}),
|
|
||||||
sliceSolidRate: newInput(LANG.ou_fini_s, {title:LANG.ou_fini_l, convert:toInt, bound:bound(0,500)}),
|
|
||||||
_____: newGroup(LANG.fh_menu, $('fdm-heat'), { modes:FDM, driven, hideable, separator, group:"fdm-heat" }),
|
_____: newGroup(LANG.fh_menu, $('fdm-heat'), { modes:FDM, driven, hideable, separator, group:"fdm-heat" }),
|
||||||
outputTemp: newInput(LANG.ou_nozl_s, {title:LANG.ou_nozl_l, convert:toInt}),
|
outputTemp: newInput(LANG.ou_nozl_s, {title:LANG.ou_nozl_l, convert:toInt}),
|
||||||
outputBedTemp: newInput(LANG.ou_bedd_s, {title:LANG.ou_bedd_l, convert:toInt}),
|
outputBedTemp: newInput(LANG.ou_bedd_s, {title:LANG.ou_bedd_l, convert:toInt}),
|
||||||
|
separator: newBlank({ class:"set-sep", driven }),
|
||||||
|
outputDraftShield: newBoolean(LANG.fr_draf_s, onBooleanClick, {title:LANG.fr_draf_l, trigger, show:() => isNotBelt()}),
|
||||||
_____: newGroup(LANG.fc_menu, $('fdm-cool'), { modes:FDM, driven, hideable, separator, group:"fdm-cool" }),
|
_____: newGroup(LANG.fc_menu, $('fdm-cool'), { modes:FDM, driven, hideable, separator, group:"fdm-cool" }),
|
||||||
outputFanLayer: newInput(LANG.ou_fanl_s, { title:LANG.ou_fanl_l, convert:toInt, bound:bound(0,255) }),
|
outputFanLayer: newInput(LANG.ou_fanl_s, { title:LANG.ou_fanl_l, convert:toInt, bound:bound(0,255) }),
|
||||||
outputFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255)}),
|
outputFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255)}),
|
||||||
|
|
@ -137,9 +136,6 @@ export function menu() {
|
||||||
firstLayerFlatten: newInput(LANG.fl_flat_s, {title:LANG.fl_flat_l, convert:toFloat, show:isBelt}),
|
firstLayerFlatten: newInput(LANG.fl_flat_s, {title:LANG.fl_flat_l, convert:toFloat, show:isBelt}),
|
||||||
firstLayerRate: newInput(LANG.fl_rate_s, {title:LANG.fl_rate_l, convert:toFloat}),
|
firstLayerRate: newInput(LANG.fl_rate_s, {title:LANG.fl_rate_l, convert:toFloat}),
|
||||||
firstLayerFillRate: newInput(LANG.fl_frat_s, {title:LANG.fl_frat_l, convert:toFloat, show:isNotBelt}),
|
firstLayerFillRate: newInput(LANG.fl_frat_s, {title:LANG.fl_frat_l, convert:toFloat, show:isNotBelt}),
|
||||||
separator: newBlank({ class:"set-sep", driven, show:isNotBelt }),
|
|
||||||
firstLayerLineMult: newInput(LANG.fl_sfac_s, {title:LANG.fl_sfac_l, convert:toFloat, bound:bound(0.5,2), show:isNotBelt}),
|
|
||||||
firstLayerPrintMult: newInput(LANG.fl_mult_s, {title:LANG.fl_mult_l, convert:toFloat}),
|
|
||||||
separator: newBlank({ class:"set-sep", driven, show:isBelt }),
|
separator: newBlank({ class:"set-sep", driven, show:isBelt }),
|
||||||
firstLayerBrim: newInput(LANG.fl_brim_s, {title:LANG.fl_brim_l, convert:toInt, show:isBelt}),
|
firstLayerBrim: newInput(LANG.fl_brim_s, {title:LANG.fl_brim_l, convert:toInt, show:isBelt}),
|
||||||
firstLayerBrimIn: newInput(LANG.fl_brin_s, {title:LANG.fl_brin_l, convert:toInt, show:isBelt}),
|
firstLayerBrimIn: newInput(LANG.fl_brin_s, {title:LANG.fl_brin_l, convert:toInt, show:isBelt}),
|
||||||
|
|
@ -154,7 +150,6 @@ export function menu() {
|
||||||
outputRaftSpacing: newInput(LANG.fr_spac_s, {title:LANG.fr_spac_l, convert:toFloat, bound:bound(0.0,3.0), show: () => ui.outputRaft.checked && isNotBelt() }),
|
outputRaftSpacing: newInput(LANG.fr_spac_s, {title:LANG.fr_spac_l, convert:toFloat, bound:bound(0.0,3.0), show: () => ui.outputRaft.checked && isNotBelt() }),
|
||||||
separator: newBlank({ class:"set-sep", driven, show:isNotBelt }),
|
separator: newBlank({ class:"set-sep", driven, show:isNotBelt }),
|
||||||
outputRaft: newBoolean(LANG.fr_nabl_s, onBooleanClick, {title:LANG.fr_nabl_l, trigger, show:() => isNotBelt()}),
|
outputRaft: newBoolean(LANG.fr_nabl_s, onBooleanClick, {title:LANG.fr_nabl_l, trigger, show:() => isNotBelt()}),
|
||||||
outputDraftShield: newBoolean(LANG.fr_draf_s, onBooleanClick, {title:LANG.fr_draf_l, trigger, show:() => isNotBelt()}),
|
|
||||||
_____: newGroup(LANG.ou_menu, $('fdm-output'), { modes:FDM, driven, hideable, separator, group:"fdm-out" }),
|
_____: newGroup(LANG.ou_menu, $('fdm-output'), { modes:FDM, driven, hideable, separator, group:"fdm-out" }),
|
||||||
outputFeedrate: newInput(LANG.ou_feed_s, {title:LANG.ou_feed_l, convert:toInt}),
|
outputFeedrate: newInput(LANG.ou_feed_s, {title:LANG.ou_feed_l, convert:toInt}),
|
||||||
outputFinishrate: newInput(LANG.ou_fini_s, {title:LANG.ou_fini_l, convert:toInt}),
|
outputFinishrate: newInput(LANG.ou_fini_s, {title:LANG.ou_fini_l, convert:toInt}),
|
||||||
|
|
@ -177,6 +172,7 @@ export function menu() {
|
||||||
arcTolerance: newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, convert:toFloat, bound:bound(0,1.0), show:() => { return isNotBelt() }}),
|
arcTolerance: newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, convert:toFloat, bound:bound(0,1.0), show:() => { return isNotBelt() }}),
|
||||||
outputRetractDwell: newInput(LANG.ad_rdwl_s, {title:LANG.ad_rdwl_l, convert:toInt}),
|
outputRetractDwell: newInput(LANG.ad_rdwl_s, {title:LANG.ad_rdwl_l, convert:toInt}),
|
||||||
outputMinSpeed: newInput(LANG.ad_mins_s, {title:LANG.ad_mins_l, convert:toFloat, bound:bound(1,200)}),
|
outputMinSpeed: newInput(LANG.ad_mins_s, {title:LANG.ad_mins_l, convert:toFloat, bound:bound(1,200)}),
|
||||||
|
outputMaxFlowrate: newInput(LANG.ad_maxf_s, {title:LANG.ad_maxf_l, convert:toFloat, bound:bound(1,200)}),
|
||||||
outputPurgeTower: newInput(LANG.ad_purg_s, {title:LANG.ad_purg_l, convert:toInt, bound:bound(0,1000)}),
|
outputPurgeTower: newInput(LANG.ad_purg_s, {title:LANG.ad_purg_l, convert:toInt, bound:bound(0,1000)}),
|
||||||
outputScarfLength: newInput(LANG.ad_scar_s, {title:LANG.ad_scar_l, convert:toFloat, bound:bound(0,1000)}),
|
outputScarfLength: newInput(LANG.ad_scar_s, {title:LANG.ad_scar_l, convert:toFloat, bound:bound(0,1000)}),
|
||||||
outputShortPoly: newInput(LANG.ad_spol_s, {title:LANG.ad_spol_l, convert:toFloat, bound:bound(0,10000)}),
|
outputShortPoly: newInput(LANG.ad_spol_s, {title:LANG.ad_spol_l, convert:toFloat, bound:bound(0,10000)}),
|
||||||
|
|
|
||||||
|
|
@ -82,12 +82,9 @@ export function slicePost(data, options) {
|
||||||
let trg = shellFrac > 0.5 ? 1 : parts - 1;
|
let trg = shellFrac > 0.5 ? 1 : parts - 1;
|
||||||
sliceShells += rem >= trg ? 1 : 0;
|
sliceShells += rem >= trg ? 1 : 0;
|
||||||
}
|
}
|
||||||
const isFirst = index === 0;
|
|
||||||
const height = process.sliceHeight;
|
|
||||||
const spaceMult = isFirst ? process.firstLayerLineMult || 1 : 1;
|
|
||||||
const count = isSynth ? 1 : sliceShells;
|
const count = isSynth ? 1 : sliceShells;
|
||||||
const offset = shellOffset * spaceMult;
|
const offset = shellOffset;
|
||||||
const fillOff = fillOffset * spaceMult;
|
const fillOff = fillOffset;
|
||||||
const thinType = isSynth ? undefined : process.sliceDetectThin;
|
const thinType = isSynth ? undefined : process.sliceDetectThin;
|
||||||
const nutops = [];
|
const nutops = [];
|
||||||
// co-locate shell processing with top generation in slicer
|
// co-locate shell processing with top generation in slicer
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
import { FDM } from './driver-be.js';
|
||||||
import { base, util } from '../../../geo/base.js';
|
import { base, util } from '../../../geo/base.js';
|
||||||
import { poly2polyEmit, tip2tipEmit } from '../../../geo/paths.js';
|
import { poly2polyEmit, tip2tipEmit } from '../../../geo/paths.js';
|
||||||
import { newBounds } from '../../../geo/bounds.js';
|
import { newBounds } from '../../../geo/bounds.js';
|
||||||
|
|
@ -682,7 +683,6 @@ export async function fdm_prepare(widgets, settings, update) {
|
||||||
for (let rec of layer) {
|
for (let rec of layer) {
|
||||||
overate = rec.overate >= 0 ? rec.overate : overate;
|
overate = rec.overate >= 0 ? rec.overate : overate;
|
||||||
let brate = params.firstLayerRate || firstLayerRate;
|
let brate = params.firstLayerRate || firstLayerRate;
|
||||||
let bmult = params.firstLayerPrintMult || params.firstLayerPrintMult;
|
|
||||||
let point = rec.point;
|
let point = rec.point;
|
||||||
let belty = rec.belty = -point.y + (point.z * belt.slope);
|
let belty = rec.belty = -point.y + (point.z * belt.slope);
|
||||||
let lowrate = belty <= thresh ? brate : overate || brate;
|
let lowrate = belty <= thresh ? brate : overate || brate;
|
||||||
|
|
@ -694,7 +694,6 @@ export async function fdm_prepare(widgets, settings, update) {
|
||||||
if (rec.emit && belty <= thresh && lastout && Math.abs(lastout.belty - belty) < 0.01) {
|
if (rec.emit && belty <= thresh && lastout && Math.abs(lastout.belty - belty) < 0.01) {
|
||||||
// apply base speed to segments touching belt
|
// apply base speed to segments touching belt
|
||||||
rec.speed = params.firstLayerRate || Math.min(rec.speed, lowrate);
|
rec.speed = params.firstLayerRate || Math.min(rec.speed, lowrate);
|
||||||
rec.emit *= bmult;
|
|
||||||
rec.fan = params.firstLayerFanSpeed;
|
rec.fan = params.firstLayerFanSpeed;
|
||||||
minx = Math.min(minx, point.x, lastout.point.x);
|
minx = Math.min(minx, point.x, lastout.point.x);
|
||||||
maxx = Math.max(maxx, point.x, lastout.point.x);
|
maxx = Math.max(maxx, point.x, lastout.point.x);
|
||||||
|
|
@ -794,10 +793,22 @@ export async function fdm_prepare(widgets, settings, update) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output and route Slice (shell,fill) paths for a single Slice
|
||||||
|
*
|
||||||
|
* @param {*} print Print output Object
|
||||||
|
* @param {*} slice current slice being Output
|
||||||
|
* @param {*} startPoint starting point for this layer
|
||||||
|
* @param {*} offset start point offset
|
||||||
|
* @param {*} output output prebuffer
|
||||||
|
* @param {*} opt options
|
||||||
|
*/
|
||||||
function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
const { settings } = print;
|
const { settings } = print;
|
||||||
const { device } = settings;
|
const { device } = settings;
|
||||||
const { bedWidth, bedDepth, bedRound } = device;
|
const { bedWidth, bedDepth, bedRound } = device;
|
||||||
|
const { extrudePerMM } = FDM;
|
||||||
|
|
||||||
let preout = [],
|
let preout = [],
|
||||||
process = opt.params || settings.process,
|
process = opt.params || settings.process,
|
||||||
|
|
@ -806,6 +817,7 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
scarfLength = process.outputScarfLength || 0,
|
scarfLength = process.outputScarfLength || 0,
|
||||||
minLayerTime = process.outputMinLayerTime || 0,
|
minLayerTime = process.outputMinLayerTime || 0,
|
||||||
nozzleSize = process.sliceLineWidth || device.extruders[extruder].extNozzle,
|
nozzleSize = process.sliceLineWidth || device.extruders[extruder].extNozzle,
|
||||||
|
nozzles = device.extruders.map(e => e.extNozzle),
|
||||||
firstLayer = (opt.first || false) && !opt.support,
|
firstLayer = (opt.first || false) && !opt.support,
|
||||||
thinWall = nozzleSize * (opt.thinWall || 1.75),
|
thinWall = nozzleSize * (opt.thinWall || 1.75),
|
||||||
retractDist = opt.retractOver || (nozzleSize * 5),
|
retractDist = opt.retractOver || (nozzleSize * 5),
|
||||||
|
|
@ -817,17 +829,17 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
fanSpeed = process.outputFanSpeed,
|
fanSpeed = process.outputFanSpeed,
|
||||||
firstShellSpeed = process.firstLayerRate,
|
firstShellSpeed = process.firstLayerRate,
|
||||||
firstFillSpeed = process.firstLayerFillRate,
|
firstFillSpeed = process.firstLayerFillRate,
|
||||||
firstPrintMult = process.firstLayerPrintMult,
|
|
||||||
finishSpeed = firstLayer ? firstShellSpeed : (opt.speed || process.outputFinishrate),
|
finishSpeed = firstLayer ? firstShellSpeed : (opt.speed || process.outputFinishrate),
|
||||||
printSpeed = opt.speed || (firstLayer ? firstShellSpeed : process.outputFeedrate),
|
printSpeed = opt.speed || (firstLayer ? firstShellSpeed : process.outputFeedrate),
|
||||||
fillSpeed = opt.speed || opt.fillSpeed || (firstLayer ? firstFillSpeed || firstShellSpeed : process.outputFeedrate),
|
fillSpeed = opt.speed || opt.fillSpeed || (firstLayer ? firstFillSpeed || firstShellSpeed : process.outputFeedrate),
|
||||||
infillSpeed = process.sliceFillRate || opt.infillSpeed || fillSpeed || printSpeed,
|
infillSpeed = opt.infillSpeed || fillSpeed || printSpeed,
|
||||||
moveSpeed = process.outputSeekrate,
|
moveSpeed = process.outputSeekrate,
|
||||||
minSpeed = process.outputMinSpeed,
|
minSpeed = process.outputMinSpeed,
|
||||||
origin = startPoint.add(offset),
|
origin = startPoint.add(offset),
|
||||||
zhop = process.zHopDistance || 0,
|
zhop = process.zHopDistance || 0,
|
||||||
zmax = opt.zmax,
|
zmax = opt.zmax,
|
||||||
antiBacklash = process.antiBacklash,
|
antiBacklash = process.antiBacklash,
|
||||||
|
maxFlowRate = process.outputMaxFlowrate,
|
||||||
wipeDist = process.outputRetractWipe || 0,
|
wipeDist = process.outputRetractWipe || 0,
|
||||||
isBelt = device.bedBelt,
|
isBelt = device.bedBelt,
|
||||||
bedOffset = originCenter ? {
|
bedOffset = originCenter ? {
|
||||||
|
|
@ -851,11 +863,6 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
shellOrder = -shellOrder;
|
shellOrder = -shellOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update fill speed when solids detected
|
|
||||||
if (slice.finishSolids) {
|
|
||||||
fillSpeed = process.sliceSolidRate || finishSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// on flats layers also slow down shell print speed
|
// on flats layers also slow down shell print speed
|
||||||
if (slice.isFlatsLayer) {
|
if (slice.isFlatsLayer) {
|
||||||
printSpeed = finishSpeed || printSpeed;
|
printSpeed = finishSpeed || printSpeed;
|
||||||
|
|
@ -867,13 +874,6 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
printSpeed /= 2;
|
printSpeed /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply first layer extrusion multipliers
|
|
||||||
if (firstLayer) {
|
|
||||||
fillMult *= firstPrintMult;
|
|
||||||
shellMult *= firstPrintMult;
|
|
||||||
sparseMult *= firstPrintMult;
|
|
||||||
}
|
|
||||||
|
|
||||||
function retract() {
|
function retract() {
|
||||||
let array = preout.length ? preout : output;
|
let array = preout.length ? preout : output;
|
||||||
if (array.length) {
|
if (array.length) {
|
||||||
|
|
@ -1595,16 +1595,34 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
if (!p.emit) continue;
|
if (!p.emit) continue;
|
||||||
let mms = p.speed ?? 1;
|
let mms = p.speed ?? 1;
|
||||||
let dst = lp?.distTo2D(p.point) ?? 0;
|
let dst = lp?.distTo2D(p.point) ?? 0;
|
||||||
|
lp = p.point;
|
||||||
if (dst) {
|
if (dst) {
|
||||||
tt += (dst/mms);
|
tt += (dst/mms);
|
||||||
td += dst;
|
td += dst;
|
||||||
|
p.dst = dst;
|
||||||
}
|
}
|
||||||
lp = p.point;
|
|
||||||
}
|
}
|
||||||
if (minLayerTime && tt < minLayerTime) {
|
if (minLayerTime && tt < minLayerTime) {
|
||||||
let fact = tt / minLayerTime;
|
let fact = tt / minLayerTime;
|
||||||
for (let p of preout) {
|
for (let p of preout) {
|
||||||
if (p.emit) p.speed = Math.max(p.speed * fact, minSpeed);
|
if (p.emit) {
|
||||||
|
p.speed = Math.max(p.speed * fact, minSpeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply speed damping when output exceeds volumetric flowrate cap
|
||||||
|
if (maxFlowRate) {
|
||||||
|
for (let p of preout) {
|
||||||
|
if (p.emit && p.dst) {
|
||||||
|
let vfr = nozzles[p.tool] * slice.height * p.speed;
|
||||||
|
if (vfr > maxFlowRate) {
|
||||||
|
let old = p.speed;
|
||||||
|
let damp = maxFlowRate / vfr;
|
||||||
|
p.speed = p.speed * damp;
|
||||||
|
// console.log({ vfr, damp, old, new: p.speed });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1617,7 +1635,7 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
print.addPrintPoints(preout, output);
|
print.addPrintPoints(preout, output);
|
||||||
|
|
||||||
// bridge layer fan change
|
// bridge layer fan change
|
||||||
if (slice.isBridgeLayer) {
|
if (preout.length && slice.isBridgeLayer) {
|
||||||
preout[0].fan = Math.round(fanSpeed / 3);
|
preout[0].fan = Math.round(fanSpeed / 3);
|
||||||
preout.peek().fan = fanSpeed;
|
preout.peek().fan = fanSpeed;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -498,7 +498,6 @@ export function fdm_slice(settings, widget, onupdate, ondone) {
|
||||||
// just the top/bottom special solid layers or range defined solid layers
|
// just the top/bottom special solid layers or range defined solid layers
|
||||||
forSlices(0.15, 0.2, slice => {
|
forSlices(0.15, 0.2, slice => {
|
||||||
let range = slice.params;
|
let range = slice.params;
|
||||||
let spaceMult = slice.index === 0 ? process.firstLayerLineMult || 1 : 1;
|
|
||||||
let isBottom = slice.index < bottomLayers;
|
let isBottom = slice.index < bottomLayers;
|
||||||
let isTop = topLayers && slice.index > slices.length - topLayers - 1;
|
let isTop = topLayers && slice.index > slices.length - topLayers - 1;
|
||||||
let isTopBase = isTop && slice.index === slices.length - topLayers;
|
let isTopBase = isTop && slice.index === slices.length - topLayers;
|
||||||
|
|
@ -506,7 +505,7 @@ export function fdm_slice(settings, widget, onupdate, ondone) {
|
||||||
let isSolid = (isBottom || ((isTop || isDense) && !vaseMode)) && !isSynth;
|
let isSolid = (isBottom || ((isTop || isDense) && !vaseMode)) && !isSynth;
|
||||||
let solidWidth = isSolid ? range.sliceFillWidth || 1 : 0;
|
let solidWidth = isSolid ? range.sliceFillWidth || 1 : 0;
|
||||||
if (solidWidth) {
|
if (solidWidth) {
|
||||||
let fillSpace = fillSpacing * spaceMult * solidWidth;
|
let fillSpace = fillSpacing * solidWidth;
|
||||||
doSolidLayerFill(slice, fillSpace, sliceFillAngle);
|
doSolidLayerFill(slice, fillSpace, sliceFillAngle);
|
||||||
}
|
}
|
||||||
if (slice.index === slices.length - 1) {
|
if (slice.index === slices.length - 1) {
|
||||||
|
|
@ -755,8 +754,7 @@ export function fdm_slice(settings, widget, onupdate, ondone) {
|
||||||
let params = slice.params || process;
|
let params = slice.params || process;
|
||||||
let first = slice.index === 0;
|
let first = slice.index === 0;
|
||||||
let solidWidth = params.sliceFillWidth || 1;
|
let solidWidth = params.sliceFillWidth || 1;
|
||||||
let spaceMult = first ? params.firstLayerLineMult || 1 : 1;
|
let fillSpace = fillSpacing * solidWidth;
|
||||||
let fillSpace = fillSpacing * spaceMult * solidWidth;
|
|
||||||
let solidMinArea = params.sliceSolidMinArea;
|
let solidMinArea = params.sliceSolidMinArea;
|
||||||
doSolidsFill(slice, fillSpace, sliceFillAngle, solidMinArea, promises);
|
doSolidsFill(slice, fillSpace, sliceFillAngle, solidMinArea, promises);
|
||||||
sliceFillAngle = (sliceFillAngle + 90.0) % 360;
|
sliceFillAngle = (sliceFillAngle + 90.0) % 360;
|
||||||
|
|
|
||||||
|
|
@ -315,7 +315,6 @@ const funcs = self.minion = {
|
||||||
|
|
||||||
const topo4 = Object.assign(new Topo4(), cache.lathe);
|
const topo4 = Object.assign(new Topo4(), cache.lathe);
|
||||||
const heights = topo4.lathePath(stmp, tool);
|
const heights = topo4.lathePath(stmp, tool);
|
||||||
|
|
||||||
reply({ seq, heights });
|
reply({ seq, heights });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
const terms = {
|
const terms = {
|
||||||
COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved",
|
COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved",
|
||||||
LICENSE: "See the license.md file included with the source distribution",
|
LICENSE: "See the license.md file included with the source distribution",
|
||||||
VERSION: "4.3.3"
|
VERSION: "4.3.4"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const beta = 0;
|
export const beta = 0;
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,6 @@ let WIN = self.window || {},
|
||||||
fontColor = '#333333',
|
fontColor = '#333333',
|
||||||
fontScale = 1.4, // computed relative to grid size
|
fontScale = 1.4, // computed relative to grid size
|
||||||
rulerColor,
|
rulerColor,
|
||||||
axisColor,
|
|
||||||
axesOn = true,
|
axesOn = true,
|
||||||
volumeOn = true,
|
volumeOn = true,
|
||||||
viewControl,
|
viewControl,
|
||||||
|
|
@ -388,7 +387,6 @@ function setPlatformColor(color) {
|
||||||
function setFont(options) {
|
function setFont(options) {
|
||||||
if (options.color) fontColor = options.color;
|
if (options.color) fontColor = options.color;
|
||||||
if (options.scale) fontScale = options.scale;
|
if (options.scale) fontScale = options.scale;
|
||||||
if (options.axisColor) axisColor = options.axisColor;
|
|
||||||
if (options.rulerColor) rulerColor = options.rulerColor;
|
if (options.rulerColor) rulerColor = options.rulerColor;
|
||||||
updateRulers();
|
updateRulers();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
67
web/boot/index.html
Normal file
67
web/boot/index.html
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Kiri:Moto (loading)</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
position: fixed;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="module">
|
||||||
|
import { version } from '/lib/moto/license.js';
|
||||||
|
(async () => {
|
||||||
|
const reg = await navigator.serviceWorker.register('/boot/service.js', { scope: '/' });
|
||||||
|
await reg.update();
|
||||||
|
await navigator.serviceWorker.ready;
|
||||||
|
|
||||||
|
if (!navigator.serviceWorker.controller) {
|
||||||
|
console.log('No controller → reloading under SW control');
|
||||||
|
location.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn url params into key/value map
|
||||||
|
const map = Object.fromEntries(
|
||||||
|
(location.search || location.hash).slice(1).split('&')
|
||||||
|
.map(v => v.split('='))
|
||||||
|
.map(([k, v = true]) => [k, v])
|
||||||
|
);
|
||||||
|
if (map.debug) {
|
||||||
|
console.log({ map });
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.serviceWorker.controller.postMessage({
|
||||||
|
clear: map.clear,
|
||||||
|
disable: map.disable,
|
||||||
|
version: map.version || version
|
||||||
|
});
|
||||||
|
|
||||||
|
navigator.serviceWorker.addEventListener('message', e => {
|
||||||
|
console.log('[SM]', e.data);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (map.mesh) {
|
||||||
|
location.replace('/mesh/index.html');
|
||||||
|
} else {
|
||||||
|
location.replace('/kiri/index.html');
|
||||||
|
}
|
||||||
|
}, map.delay || 10);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>Booting Grid.Space</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
203
web/boot/service.js
Normal file
203
web/boot/service.js
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
// --- configurable ---
|
||||||
|
const CACHE_VERSION = 'boot-001';
|
||||||
|
const BUNDLE_URL = '/boot/bundle.bin';
|
||||||
|
const VERSION_KEY = '/__version__';
|
||||||
|
|
||||||
|
let loaded = 0;
|
||||||
|
let mode = 'cached';
|
||||||
|
let currentVersion = null;
|
||||||
|
|
||||||
|
// --- logging ---
|
||||||
|
const log = (...a) => console.log('[SW]', ...a);
|
||||||
|
self.addEventListener('install', e => log('install'));
|
||||||
|
self.addEventListener('activate', e => log('activate'));
|
||||||
|
// self.addEventListener('fetch', e => log('fetch', e.request.url));
|
||||||
|
|
||||||
|
// --- lifecycle ---
|
||||||
|
self.skipWaiting();
|
||||||
|
self.addEventListener('activate', e => e.waitUntil(clients.claim()));
|
||||||
|
|
||||||
|
// --- mode control ---
|
||||||
|
self.addEventListener('message', e => {
|
||||||
|
const data = e.data || {};
|
||||||
|
const { clear, disable, version } = data;
|
||||||
|
if (clear) {
|
||||||
|
clearCache();
|
||||||
|
}
|
||||||
|
if (disable) {
|
||||||
|
mode = 'transparent';
|
||||||
|
broadcast('transparent mode');
|
||||||
|
unregister();
|
||||||
|
} else if (version !== undefined) {
|
||||||
|
mode = 'cached';
|
||||||
|
log('version', version);
|
||||||
|
ensureVersion(version);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- unregister current service worker ---
|
||||||
|
async function unregister() {
|
||||||
|
await self.registration.unregister();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- erase existing cache ---
|
||||||
|
async function clearCache() {
|
||||||
|
log('clearing cache');
|
||||||
|
await caches.delete(CACHE_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- version check and preload ---
|
||||||
|
async function ensureVersion(incomingVersion) {
|
||||||
|
const cache = await caches.open(CACHE_VERSION);
|
||||||
|
let needPreload = false;
|
||||||
|
|
||||||
|
const existing = await cache.match(VERSION_KEY);
|
||||||
|
if (!existing) {
|
||||||
|
log('no version stored → preload');
|
||||||
|
needPreload = true;
|
||||||
|
} else {
|
||||||
|
const stored = await existing.text();
|
||||||
|
if (stored !== incomingVersion) {
|
||||||
|
log(`mismatch ${stored} ≠ ${incomingVersion} → preload`);
|
||||||
|
needPreload = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needPreload) {
|
||||||
|
await preloadBundle();
|
||||||
|
await cache.put(
|
||||||
|
VERSION_KEY,
|
||||||
|
new Response(String(incomingVersion), { headers: { 'Content-Type': 'text/plain' } })
|
||||||
|
);
|
||||||
|
broadcast(`preload added ${loaded} files`);
|
||||||
|
} else {
|
||||||
|
broadcast('preload cached');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- fetch with redirect handler ---
|
||||||
|
async function fetch_safe(req) {
|
||||||
|
const res = await fetch(req);
|
||||||
|
if (res.redirected) {
|
||||||
|
log({ was_redirected_cloning: res.url });
|
||||||
|
// clone into a fresh non-redirect response
|
||||||
|
const clone = res.clone();
|
||||||
|
return new Response(await clone.blob(), {
|
||||||
|
headers: clone.headers,
|
||||||
|
status: 200,
|
||||||
|
statusText: 'OK'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- fetch handler ---
|
||||||
|
self.addEventListener('fetch', e => {
|
||||||
|
const { request } = e;
|
||||||
|
const url = new URL(request.url);
|
||||||
|
|
||||||
|
if (request.method !== 'GET') return;
|
||||||
|
|
||||||
|
if (mode == 'transparent') {
|
||||||
|
e.respondWith(fetch_safe(e.request));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.pathname.endsWith("/")) {
|
||||||
|
return e.respondWith(redirectOr404(appendURL(url, 'index.html').pathname));
|
||||||
|
} else if (url.pathname.indexOf(".") < 0) {
|
||||||
|
return e.respondWith(redirectOr404(appendURL(url, '/index.html').pathname));
|
||||||
|
} else {
|
||||||
|
e.respondWith(fromCacheOrNetwork(request));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- helpers ---
|
||||||
|
function appendURL(url, append) {
|
||||||
|
return new URL(url.origin + url.pathname + append + url.search);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function redirectOr404(path) {
|
||||||
|
const cache = await caches.open(CACHE_VERSION);
|
||||||
|
const hit = await cache.match(path, { ignoreSearch: true });
|
||||||
|
if (hit) {
|
||||||
|
return Response.redirect(path, 302);
|
||||||
|
} else {
|
||||||
|
return new Response('Not Found', { status: 404, statusText: 'Not Found' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fromCacheOrNetwork(req) {
|
||||||
|
const cache = await caches.open(CACHE_VERSION);
|
||||||
|
const hit = await cache.match(req, { ignoreSearch: true });
|
||||||
|
if (hit) {
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
const net = await fetch_safe(req);
|
||||||
|
cache.put(req, net.clone());
|
||||||
|
log(`put: ${net.url}`);
|
||||||
|
return net;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function broadcast(message) {
|
||||||
|
const clientsList = await clients.matchAll({ type: 'window' });
|
||||||
|
for (const client of clientsList) {
|
||||||
|
client.postMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sec_headers = {
|
||||||
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||||
|
'Cross-Origin-Embedder-Policy': 'require-corp'
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- preload & unpack bundle ---
|
||||||
|
async function preloadBundle() {
|
||||||
|
loaded = 0;
|
||||||
|
const cache = await caches.open(CACHE_VERSION);
|
||||||
|
const res = await fetch(BUNDLE_URL, { cache: 'no-store' });
|
||||||
|
const buf = await res.arrayBuffer();
|
||||||
|
const files = await unpackBundle(buf);
|
||||||
|
await Promise.all(
|
||||||
|
Object.entries(files).map(([path, blob]) => {
|
||||||
|
const ext = path.split('.').pop();
|
||||||
|
const type =
|
||||||
|
ext === 'html' ? 'text/html' :
|
||||||
|
ext === 'js' ? 'application/javascript' :
|
||||||
|
ext === 'css' ? 'text/css' :
|
||||||
|
ext === 'json' ? 'application/json' :
|
||||||
|
ext === 'wasm' ? 'application/wasm' :
|
||||||
|
ext === 'svg' ? 'image/svg+xml' :
|
||||||
|
'application/octet-stream';
|
||||||
|
const headers = { 'Content-Type': type, ...sec_headers };
|
||||||
|
const resp = new Response(blob, { headers });
|
||||||
|
loaded++;
|
||||||
|
return cache.put('/' + path, resp);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- simple bundle format ---
|
||||||
|
// [count:uint32][entries...][file data...]
|
||||||
|
// entry: [nlen:uint16][name][offset:uint32][length:uint32]
|
||||||
|
async function unpackBundle(buf) {
|
||||||
|
const view = new DataView(buf);
|
||||||
|
let pos = 0;
|
||||||
|
const count = view.getUint32(pos, true); pos += 4;
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const table = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const nlen = view.getUint16(pos, true); pos += 2;
|
||||||
|
const name = decoder.decode(new Uint8Array(buf, pos, nlen)); pos += nlen;
|
||||||
|
const offset = view.getUint32(pos, true); pos += 4;
|
||||||
|
const len = view.getUint32(pos, true); pos += 4;
|
||||||
|
table.push({ name, offset, len });
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = {};
|
||||||
|
for (const { name, offset, len } of table)
|
||||||
|
files[name] = buf.slice(offset, offset + len);
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
@ -1378,7 +1378,7 @@ details[open] summary::after {
|
||||||
padding: 0 0 0 0;
|
padding: 0 0 0 0;
|
||||||
}
|
}
|
||||||
#oplist {
|
#oplist {
|
||||||
max-height: 70vh;
|
max-height: calc(70vh - 200px);
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding-top: 1px;
|
padding-top: 1px;
|
||||||
|
|
|
||||||
|
|
@ -356,7 +356,7 @@ self.lang['en-us'] = {
|
||||||
ad_thin_l: ["detect and fill gaps","between shell walls"],
|
ad_thin_l: ["detect and fill gaps","between shell walls"],
|
||||||
|
|
||||||
// FDM SHELLS
|
// FDM SHELLS
|
||||||
sl_line_s: "shell width",
|
sl_line_s: "line width",
|
||||||
sl_line_l: ["extrusion width in millimeters","0 = nozzle diameter"],
|
sl_line_l: ["extrusion width in millimeters","0 = nozzle diameter"],
|
||||||
sl_ordr_s: "shell order",
|
sl_ordr_s: "shell order",
|
||||||
sl_ordr_l: ["output shell order","inside to outside","or outside to inside","affects surface quality"],
|
sl_ordr_l: ["output shell order","inside to outside","or outside to inside","affects surface quality"],
|
||||||
|
|
@ -369,8 +369,8 @@ self.lang['en-us'] = {
|
||||||
|
|
||||||
// FDM SOLID
|
// FDM SOLID
|
||||||
fs_menu: "solid fill",
|
fs_menu: "solid fill",
|
||||||
fi_over_s: "shell overlap",
|
fi_over_s: "fill overlap",
|
||||||
fi_over_l: ["overlap with shell and other fill","for improved extrusion bonding","as fraction of nozzle diameter","0.0 - 2.0"],
|
fi_over_l: ["overlap between shells and solid or sparse infill. can improve bonding to shells","as fraction of line width","0.0 - 1.0"],
|
||||||
fi_angl_s: "start angle",
|
fi_angl_s: "start angle",
|
||||||
fi_angl_l: ["starting angle in degrees","90 degrees added to ","each following layer","applies only to solid layers"],
|
fi_angl_l: ["starting angle in degrees","90 degrees added to ","each following layer","applies only to solid layers"],
|
||||||
fi_grow_s: "area expand",
|
fi_grow_s: "area expand",
|
||||||
|
|
@ -654,6 +654,10 @@ self.lang['en-us'] = {
|
||||||
ci_face_l: "select face to rotate facing up",
|
ci_face_l: "select face to rotate facing up",
|
||||||
ci_line_s: "linear",
|
ci_line_s: "linear",
|
||||||
ci_line_l: "make linear passes along X then rotate Y",
|
ci_line_l: "make linear passes along X then rotate Y",
|
||||||
|
ci_laso_s: "offset start",
|
||||||
|
ci_laso_l: ["x offet from left of part. negative values are outside of the part. in workspace units"],
|
||||||
|
ci_laeo_s: "offset end",
|
||||||
|
ci_laeo_l: ["x offet from right of part. negative values are outside of the part. in workspace units"],
|
||||||
|
|
||||||
// CNC LASER On/Off Operations (Carvera)
|
// CNC LASER On/Off Operations (Carvera)
|
||||||
cl_powr_s: "power",
|
cl_powr_s: "power",
|
||||||
|
|
@ -675,10 +679,6 @@ self.lang['en-us'] = {
|
||||||
cl_maxz_s: "max z height",
|
cl_maxz_s: "max z height",
|
||||||
cl_maxz_l: ["highest Z band position. when set to 0, uses the highest Z point of the workspace"],
|
cl_maxz_l: ["highest Z band position. when set to 0, uses the highest Z point of the workspace"],
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// CNC CUTOUT TABS
|
// CNC CUTOUT TABS
|
||||||
ct_menu: "tabs",
|
ct_menu: "tabs",
|
||||||
ct_angl_s: "angle",
|
ct_angl_s: "angle",
|
||||||
|
|
@ -864,10 +864,12 @@ self.lang['en-us'] = {
|
||||||
ad_rdwl_l: ["time between re-engaging","filament and movement","in milliseconds"],
|
ad_rdwl_l: ["time between re-engaging","filament and movement","in milliseconds"],
|
||||||
ad_scst_s: "shell coast",
|
ad_scst_s: "shell coast",
|
||||||
ad_scst_l: ["non-printing end","of perimeter shells","in millimeters"],
|
ad_scst_l: ["non-printing end","of perimeter shells","in millimeters"],
|
||||||
ad_msol_s: "min area",
|
ad_msol_s: "area filter",
|
||||||
ad_msol_l: ["minimum area (mm^2)","required to keep solid","must be > 0.1"],
|
ad_msol_l: ["minimum area (mm^2)","bridges and flats less than this are culled"],
|
||||||
ad_mins_s: "min speed",
|
ad_mins_s: "min speed",
|
||||||
ad_mins_l: ["minimum speed","for short segments"],
|
ad_mins_l: ["minimum speed","for short segments"],
|
||||||
|
ad_maxf_s: "max flowrate",
|
||||||
|
ad_maxf_l: ["maximum volumetric flowrate supported by this print head and filament","in millimeters^3 / second"],
|
||||||
ad_scar_s: "scarf length",
|
ad_scar_s: "scarf length",
|
||||||
ad_scar_l: ["ease up length for scarf joint. disables arc detection","0 = disabled"],
|
ad_scar_l: ["ease up length for scarf joint. disables arc detection","0 = disabled"],
|
||||||
ad_spol_s: "short path",
|
ad_spol_s: "short path",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "Kiri:Moto 4.3.3",
|
"name": "Kiri:Moto 4.3.4",
|
||||||
"short_name": "Kiri:Moto",
|
"short_name": "Kiri:Moto",
|
||||||
"description": "Slicer for 3D printers, CNC mills, laser cutters and more",
|
"description": "Slicer for 3D printers, CNC mills, laser cutters and more",
|
||||||
"start_url": "/kiri/",
|
"start_url": "/kiri/",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue