Merge branch 'rel-4.5'

This commit is contained in:
Stewart Allen 2025-11-24 12:58:38 -05:00
commit afbbb13e9c
31 changed files with 268 additions and 207 deletions

View file

@ -661,7 +661,7 @@ self.kiri.load(api => {
} }
}); });
api.event.on("init-done", function() { api.event.on("load-done", function() {
if (init) { if (init) {
return; return;
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "grid-apps", "name": "grid-apps",
"version": "4.4.0", "version": "4.5.0",
"description": "grid.space 3d slicing & modeling tools", "description": "grid.space 3d slicing & modeling tools",
"author": "Stewart Allen <sa@grid.space>", "author": "Stewart Allen <sa@grid.space>",
"license": "MIT", "license": "MIT",
@ -40,7 +40,7 @@
"@gridspace/app-server": "^0.0.20", "@gridspace/app-server": "^0.0.20",
"@gridspace/basic-ftp": "github:gridspace/basic-ftp#v5.0.5-gridspace", "@gridspace/basic-ftp": "github:gridspace/basic-ftp#v5.0.5-gridspace",
"@gridspace/net-level-client": "^0.2.3", "@gridspace/net-level-client": "^0.2.3",
"@gridspace/raster-path": "^1.0.6", "@gridspace/raster-path": "^1.0.7",
"@tracespace/parser": "^5.0.0-next.0", "@tracespace/parser": "^5.0.0-next.0",
"@tweenjs/tween.js": "^16.6.0", "@tweenjs/tween.js": "^16.6.0",
"aedes": "^0.51.3", "aedes": "^0.51.3",
@ -73,6 +73,7 @@
"@docusaurus/preset-classic": "^3.9.0", "@docusaurus/preset-classic": "^3.9.0",
"@electron/notarize": "^3.0.0", "@electron/notarize": "^3.0.0",
"copy-webpack-plugin": "^12.0.2", "copy-webpack-plugin": "^12.0.2",
"cross-env": "^10.1.0",
"docusaurus-lunr-search": "^3.6.0", "docusaurus-lunr-search": "^3.6.0",
"dotenv": "latest", "dotenv": "latest",
"electron": "^35.0.1", "electron": "^35.0.1",
@ -86,7 +87,7 @@
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"
}, },
"scripts": { "scripts": {
"build-debug": "npm run prebuild && DEBUG=electron-builder electron-builder", "build-debug": "npm run prebuild && cross-env DEBUG=electron-builder electron-builder",
"build-linux-arm": "npm run build -- --linux --arm64", "build-linux-arm": "npm run build -- --linux --arm64",
"build-linux": "npm run build -- --linux --x64", "build-linux": "npm run build -- --linux --x64",
"build-mac-intel": "npm run build -- --mac --x64", "build-mac-intel": "npm run build -- --mac --x64",
@ -97,7 +98,7 @@
"build": "npm run prebuild && electron-builder", "build": "npm run prebuild && electron-builder",
"bundle:dev": "node bin/bundler.mjs bin/bundle.config.json", "bundle:dev": "node bin/bundler.mjs bin/bundle.config.json",
"bundle:prod": "node bin/bundler.mjs bin/bundle-prod.config.json", "bundle:prod": "node bin/bundler.mjs bin/bundle-prod.config.json",
"bundle": "COMPRESS=1 npm run bundle:prod", "bundle": "cross-env COMPRESS=1 npm run bundle:prod",
"clean": "rm -rf alt build data dist src/pack src.old tmp web.old .bcache", "clean": "rm -rf alt build data dist src/pack src.old tmp web.old .bcache",
"dev": "npm run pack-dev && gs-app-server --debug --single", "dev": "npm run pack-dev && gs-app-server --debug --single",
"docs-build": "docusaurus build --config conf/docusaurus.config.js", "docs-build": "docusaurus build --config conf/docusaurus.config.js",
@ -111,11 +112,10 @@
"pack-dev": "npm run webpack-src && npm run dryrun && npm run bundle:dev", "pack-dev": "npm run webpack-src && npm run dryrun && npm run bundle:dev",
"pack-prod": "npm run webpack-src prod && npm run dryrun && npm run bundle", "pack-prod": "npm run webpack-src prod && npm run dryrun && npm run bundle",
"postbuild": "node bin/electron-post.js", "postbuild": "node bin/electron-post.js",
"prebuild": "export ELECTRON_BUILD=1 && npm run setup && npm run pack-dev && node bin/electron-pre.js", "prebuild": "npm run setup && cross-env ELECTRON_BUILD=1 npm run pack-dev && node bin/electron-pre.js",
"preinstall": "node bin/install-pre.js", "preinstall": "node bin/install-pre.js",
"prod": "npm run pack-prod && gs-app-server", "prod": "npm run pack-prod && gs-app-server",
"setup-mods": "cd mods && npm i", "setup": "npm run clean && npm i && npm run webpack-ext",
"setup": "npm run clean && npm run setup-mods && npm i && npm run webpack-ext",
"start-dbg": "npm run prebuild && electron . --debugg", "start-dbg": "npm run prebuild && electron . --debugg",
"start-ddb": "npm run prebuild && electron . --devel --debugg", "start-ddb": "npm run prebuild && electron . --devel --debugg",
"start-dev": "npm run prebuild prod && electron . --devel", "start-dev": "npm run prebuild prod && electron . --devel",

View file

@ -537,7 +537,7 @@ export const conf = {
camPocketOver: 0.25, camPocketOver: 0.25,
camPocketPlunge: 200, camPocketPlunge: 200,
camPocketRefine: 20, camPocketRefine: 20,
camPocketSmooth: 0, camPocketSmooth: 1,
camPocketSpeed: 250, camPocketSpeed: 250,
camPocketSpindle: 1000, camPocketSpindle: 1000,
camPocketTool: 1000, camPocketTool: 1000,
@ -932,6 +932,7 @@ export const conf = {
freeLayout: true, freeLayout: true,
healMesh: false, healMesh: false,
lineType: "path", lineType: "path",
manifold: false,
ortho: false, ortho: false,
reverseZoom: true, reverseZoom: true,
scrolls: true, scrolls: true,

View file

@ -155,6 +155,7 @@ function booleanSave() {
control.freeLayout = ui.freeLayout.checked; control.freeLayout = ui.freeLayout.checked;
control.healMesh = ui.healMesh.checked; control.healMesh = ui.healMesh.checked;
control.ortho = ui.ortho.checked; control.ortho = ui.ortho.checked;
control.manifold = ui.manifold.checked;
control.reverseZoom = ui.reverseZoom.checked; control.reverseZoom = ui.reverseZoom.checked;
control.scrolls = ui.scrolls.checked; control.scrolls = ui.scrolls.checked;
control.shiny = ui.shiny.checked; control.shiny = ui.shiny.checked;
@ -1046,6 +1047,7 @@ function init_one() {
showSpeeds: newBoolean(LANG.op_sped_s, speedSave, {title:LANG.op_sped_l}), showSpeeds: newBoolean(LANG.op_sped_s, speedSave, {title:LANG.op_sped_l}),
shiny: newBoolean(LANG.op_shny_s, booleanSave, {title:LANG.op_shny_l, modes:FDM}), shiny: newBoolean(LANG.op_shny_s, booleanSave, {title:LANG.op_shny_l, modes:FDM}),
lineType: newSelect(LANG.op_line_s, {title: LANG.op_line_l, action: lineTypeSave, modes:FDM}, "linetype"), lineType: newSelect(LANG.op_line_s, {title: LANG.op_line_l, action: lineTypeSave, modes:FDM}, "linetype"),
manifold: newBoolean(LANG.op_mani_s, booleanSave, {title: LANG.op_mani_l, modes:CAM}, "manifold"),
animesh: newSelect(LANG.op_anim_s, {title: LANG.op_anim_l, action: aniMeshSave, modes:CAM}, "animesh"), animesh: newSelect(LANG.op_anim_s, {title: LANG.op_anim_l, action: aniMeshSave, modes:CAM}, "animesh"),
units: newSelect(LANG.op_unit_s, {title: LANG.op_unit_l, action: unitsSave, modes:CAM}, "units"), units: newSelect(LANG.op_unit_s, {title: LANG.op_unit_l, action: unitsSave, modes:CAM}, "units"),
edgeangle: newInput(LANG.op_spoa_s, {title:LANG.op_spoa_l, convert:toFloat, size:3}), edgeangle: newInput(LANG.op_spoa_s, {title:LANG.op_spoa_l, convert:toFloat, size:3}),
@ -1639,6 +1641,7 @@ function init_two() {
ui.devel.checked = control.devel; ui.devel.checked = control.devel;
ui.freeLayout.checked = control.freeLayout; ui.freeLayout.checked = control.freeLayout;
ui.healMesh.checked = control.healMesh; ui.healMesh.checked = control.healMesh;
ui.manifold.checked = control.manifold;
ui.ortho.checked = control.ortho; ui.ortho.checked = control.ortho;
ui.reverseZoom.checked = control.reverseZoom; ui.reverseZoom.checked = control.reverseZoom;
ui.showOrigin.checked = control.showOrigin; ui.showOrigin.checked = control.showOrigin;

View file

@ -163,19 +163,19 @@ function platformUpdateTopZ() {
const wzmax = bounds.max.z; const wzmax = bounds.max.z;
const zdelta = process.camZOffset || 0; const zdelta = process.camZOffset || 0;
switch (process.camZAnchor) { switch (process.camZAnchor) {
case 'top': case 'top':
widget.setTopZ(stock.z - zdelta); widget.setTopZ(stock.z - zdelta, true);
break; break;
case 'middle': case 'middle':
if (widget.isIndexed) { if (widget.isIndexed) {
widget.setTopZ(zdelta); widget.setTopZ(zdelta, true);
} else { } else {
widget.setTopZ(stock.z - (stock.z - wzmax) / 2 + zdelta); widget.setTopZ(stock.z - (stock.z - wzmax) / 2 + zdelta, true);
} }
break; break;
case 'bottom': case 'bottom':
widget.setTopZ(wzmax + zdelta); widget.setTopZ(wzmax + zdelta, true);
break; break;
} }
} else { } else {
widget.setTopZ(0); widget.setTopZ(0);

View file

@ -473,10 +473,12 @@ class Widget {
/** /**
* moves top of widget to given Z * moves top of widget to given Z
* only non-zero in CAM mode
* *
* @param {number} z position * @param {number} z position
* @param {boolean} cam mode
*/ */
setTopZ(z) { setTopZ(z, cam) {
let mesh = this.mesh, let mesh = this.mesh,
track = this.track, track = this.track,
mbb = mesh.getBoundingBox(), mbb = mesh.getBoundingBox(),
@ -488,7 +490,7 @@ class Widget {
track.top = mbz; track.top = mbz;
} }
// difference between top of stock/bounds and top of widget (cam mode) // difference between top of stock/bounds and top of widget (cam mode)
track.tzoff = mbz - z; track.tzoff = cam ? mbz - z : 0;
this._updateMeshPosition(); this._updateMeshPosition();
} }
@ -503,8 +505,7 @@ class Widget {
} }
_move(x, y, z, abs) { _move(x, y, z, abs) {
let mesh = this.mesh, let mat = this.getMaterial(),
mat = this.getMaterial(),
pos = this.track.pos; pos = this.track.pos;
// do not allow moves in pure slice view // do not allow moves in pure slice view
if (!mat.visible) return; if (!mat.visible) return;
@ -528,13 +529,10 @@ class Widget {
track = this.track, track = this.track,
tzoff = track.tzoff, tzoff = track.tzoff,
top = track.top, top = track.top,
tz = tzoff !== top ? -tzoff : 0, tz = -tzoff,
{ x, y, z } = track.pos; { x, y, z } = track.pos;
if (track.indexed) { if (track.indexed) {
let rad = track.indexRad; let rad = track.indexRad;
let ln = track.box.d / 2;
let dx = Math.sin(rad) * ln;
let dy = ln - Math.cos(rad) * ln;
this.mesh.rotation.x = -rad; this.mesh.rotation.x = -rad;
z = top; z = top;
} else { } else {

View file

@ -21,7 +21,6 @@ let nextMeshID = 1,
toolMesh, toolMesh,
toolRadius, toolRadius,
toolUpdateMsg, toolUpdateMsg,
showStock = false,
animateClear = false, animateClear = false,
animating = false, animating = false,
renderDone = false, renderDone = false,
@ -36,7 +35,7 @@ export function init(worker) {
dispatch.animate_setup2 = function (data, send) { dispatch.animate_setup2 = function (data, send) {
const { settings } = data; const { settings } = data;
const { process } = settings; const { controller, process } = settings;
const print = worker.current.print; const print = worker.current.print;
const density = parseInt(settings.controller.animesh) * 1000; const density = parseInt(settings.controller.animesh) * 1000;
const isIndexed = process.camStockIndexed; const isIndexed = process.camStockIndexed;
@ -61,14 +60,14 @@ export function init(worker) {
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) if (controller.manifold)
for (let i = 0; i < sliceCount; i++) { for (let i = 0; i < sliceCount; i++) {
let xmin = -(x / 2) + (i * sliceWidth) + sliceWidth / 2; let xmin = -(x / 2) + (i * sliceWidth) + sliceWidth / 2;
let slice = new Stock(sliceWidth, y, z).translate(xmin, 0, 0); let slice = new Stock(sliceWidth, y, z).translate(xmin, 0, 0);
stockSlices.push(slice); stockSlices.push(slice);
slice.updateMesh([]); slice.updateMesh([]);
slice.send(send); slice.send(send);
// send({ mesh_move: { id: slice.id, pos: { x:0, y:0, z: stock.z/2} } }); // send.data({ mesh_move: { id: slice.id, pos: { x:0, y:0, z: stock.z/2} } });
} }
send.done(); send.done();
@ -175,7 +174,7 @@ function renderPath(send) {
const sp = next.speed; const sp = next.speed;
const moves = []; const moves = [];
for (let i = 0, x = lp.x, y = lp.y, z = lp.z, a = lp.a; 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, md: sd, dx, dy, dz, sp }); moves.push({ x, y, z, a, md: sd, dx, dy, dz, da, sp });
x += mx; x += mx;
y += my; y += my;
z += mz; z += mz;
@ -202,11 +201,11 @@ 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, sp, md } = pos; const { dx, dy, dz, da, sp, md } = pos;
toolMove(pos); toolMove(pos);
// console.log('renderMoves', {id, moves, seed}); // console.log('renderMoves', {id, moves, seed});
let subs = 0; let subs = 0;
if (dx || dy || dz < 0) if (dx || dy || dz < 0 || da)
for (let slice of stockSlices) { for (let slice of stockSlices) {
if (slice.bounds.intersectsBox(toolMesh.bounds)) { if (slice.bounds.intersectsBox(toolMesh.bounds)) {
slice.subtractTool(toolMesh); slice.subtractTool(toolMesh);

View file

@ -19,6 +19,8 @@ let meshes = {},
speed, speed,
origin, origin,
color = 0, color = 0,
dark = false,
manifold = false,
A2R = Math.PI / 180; A2R = Math.PI / 180;
export function animate_clear2(api) { export function animate_clear2(api) {
@ -35,6 +37,8 @@ export function animate_clear2(api) {
export function animate2(api, delay) { export function animate2(api, delay) {
let alert = api.alerts.show("building animation"); let alert = api.alerts.show("building animation");
let settings = api.conf.get(); let settings = api.conf.get();
dark = settings.controller.dark;
manifold = settings.controller.manifold;
client.animate_setup2(settings, data => { client.animate_setup2(settings, data => {
handleUpdate(data); handleUpdate(data);
if (data) { if (data) {
@ -50,7 +54,8 @@ export function animate2(api, delay) {
speed: anim.speed, speed: anim.speed,
trans: anim.trans, trans: anim.trans,
model: anim.model, model: anim.model,
shade: anim.shade shade: anim.shade,
path: anim.path
}); });
Object.assign(label, { Object.assign(label, {
progress: anim.progress, progress: anim.progress,
@ -61,6 +66,14 @@ export function animate2(api, delay) {
a: anim.vala a: anim.vala
}); });
if (manifold) {
anim.trans.classList.remove('hide');
// anim.shade.classList.remove('hide');
} else {
anim.trans.classList.add('hide');
// anim.shade.classList.add('hide');
}
updateSpeed(0); updateSpeed(0);
setTimeout(step, delay || 0); setTimeout(step, delay || 0);
toggleTrans(undefined, false); toggleTrans(undefined, false);
@ -74,6 +87,7 @@ export function animate2(api, delay) {
button.trans.onclick = toggleTrans; button.trans.onclick = toggleTrans;
button.model.onclick = toggleModel; button.model.onclick = toggleModel;
button.shade.onclick = toggleStock; button.shade.onclick = toggleStock;
button.path.onclick = togglePath;
button.play.style.display = ''; button.play.style.display = '';
button.pause.style.display = 'none'; button.pause.style.display = 'none';
@ -89,7 +103,7 @@ Object.assign(client, {
animate_setup2(settings, ondone) { animate_setup2(settings, ondone) {
initPathMesh(); initPathMesh();
color = settings.controller.dark ? 0x888888 : 0; color = dark ? 0x888888 : 0;
material = material ?? new THREE.MeshPhongMaterial({ material = material ?? new THREE.MeshPhongMaterial({
flatShading: true, flatShading: true,
transparent: false, transparent: false,
@ -113,11 +127,12 @@ function initPathMesh() {
max = 10000, max = 10000,
rot = new THREE.Euler(0,0,0), rot = new THREE.Euler(0,0,0),
geo = new THREE.BufferGeometry(), geo = new THREE.BufferGeometry(),
mat = new THREE.LineBasicMaterial({ color: 0xffff00 }), mat = new THREE.LineBasicMaterial({ color: dark ? 0xffff00 : 0x771100 }),
pos = new Float32Array(max * 6), pos = new Float32Array(max * 6),
lines = new THREE.LineSegments(geo, mat), lines = new THREE.LineSegments(geo, mat),
vec = new THREE.Vector3(0,0,0), vec = new THREE.Vector3(0,0,0),
ang = new THREE.Vector3(1,0,0); ang = new THREE.Vector3(1,0,0),
show = api.local.get('cam.anim.path') ?? true;
geo.setAttribute('position', new THREE.BufferAttribute(pos, 3)); geo.setAttribute('position', new THREE.BufferAttribute(pos, 3));
let track = lineTracker = { let track = lineTracker = {
xro(p) { xro(p) {
@ -157,9 +172,15 @@ function initPathMesh() {
lines.setRotationFromEuler(rot); lines.setRotationFromEuler(rot);
}, },
visible(bool) { visible(bool) {
if (bool !== lines.visible) { if (show && bool !== lines.visible) {
lines.visible = bool; lines.visible = bool;
} else if (!show) {
lines.visible = false;
} }
},
show(bool) {
show = bool;
lines.visible = bool;
} }
} }
space.world.add(lines); space.world.add(lines);
@ -205,8 +226,15 @@ function toggleModel(ev, bool) {
} }
function toggleStock(ev, bool, set) { function toggleStock(ev, bool, set) {
set !== false && api.local.toggle('cam.anim.stock', bool); bool = api.local.toggle('cam.anim.stock', bool);
return api.event.emit('cam.stock.toggle', bool ?? undefined); for (let [ id, mesh ] of Object.entries(meshes)) {
if (id >= 0) mesh.visible = bool;
}
}
function togglePath(ev, bool, set) {
bool = api.local.toggle('cam.anim.path', bool);
lineTracker?.show(bool);
} }
function toggleTrans(ev, bool) { function toggleTrans(ev, bool) {

View file

@ -283,7 +283,7 @@ export function createPopOps() {
plunge: UC.newInput(LANG.cc_plng_s, { title: LANG.cc_plng_l, convert: UC.toInt, units: true }), plunge: UC.newInput(LANG.cc_plng_s, { title: LANG.cc_plng_l, convert: UC.toInt, units: true }),
down: UC.newInput(LANG.cc_sdwn_s, { title: LANG.cc_sdwn_l, convert: UC.toFloat, units: true }), down: UC.newInput(LANG.cc_sdwn_s, { title: LANG.cc_sdwn_l, convert: UC.toFloat, units: true }),
step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 1.0), show: () => env.popOp.outline.rec.wide }), step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 1.0), show: () => env.popOp.outline.rec.wide }),
steps: UC.newInput(LANG.cc_sovc_s, { title: LANG.cc_sovc_l, convert: UC.toInt, bound: UC.bound(1, 5), show: () => env.popOp.outline.rec.wide }), steps: UC.newInput(LANG.cc_sovc_s, { title: LANG.cc_sovc_l, convert: UC.toInt, bound: UC.bound(1, 500), show: () => env.popOp.outline.rec.wide }),
sep: UC.newBlank({ class: "pop-sep" }), sep: UC.newBlank({ class: "pop-sep" }),
top: UC.newBoolean(LANG.co_clrt_s, undefined, { title: LANG.co_clrt_l }), top: UC.newBoolean(LANG.co_clrt_s, undefined, { title: LANG.co_clrt_l }),
inside: UC.newBoolean(LANG.co_olin_s, undefined, { title: LANG.co_olin_l, show: (op) => { return !op.inputs.outside.checked } }), inside: UC.newBoolean(LANG.co_olin_s, undefined, { title: LANG.co_olin_l, show: (op) => { return !op.inputs.outside.checked } }),

View file

@ -140,8 +140,8 @@ function pointHoverUp(int, ev) {
x += stock.x / 2; x += stock.x / 2;
y += stock.y / 2; y += stock.y / 2;
} }
process.camOriginOffX = x; process.camOriginOffX = x - stock.center.x;
process.camOriginOffY = y; process.camOriginOffY = y - stock.center.y;
process.camOriginOffZ = z; process.camOriginOffZ = z;
api.settings.update_fields(process); api.settings.update_fields(process);
api.platform.update_origin(); api.platform.update_origin();

View file

@ -144,7 +144,7 @@ export function traceHoverUp(int, ev) {
export function traceToggle(obj, skip) { export function traceToggle(obj, skip) {
let material = obj.material[0] || obj.material; let material = obj.material[0] || obj.material;
if (!material) return; if (!(material && obj.trace)) return;
let { color, colorSave } = material; let { color, colorSave } = material;
let { widget, poly } = obj.trace; let { widget, poly } = obj.trace;
let areas = env.poppedRec.areas; let areas = env.poppedRec.areas;

View file

@ -58,6 +58,7 @@ export function menu() {
anim.trans = newButton(null,null,{icon:'<i class="fa-solid fa-border-none"></i>',title:"transparency",class:"padleft"}), anim.trans = newButton(null,null,{icon:'<i class="fa-solid fa-border-none"></i>',title:"transparency",class:"padleft"}),
anim.model = newButton(null,null,{icon:'<i class="fa-solid fa-eye"></i>',title:"show model"}), anim.model = newButton(null,null,{icon:'<i class="fa-solid fa-eye"></i>',title:"show model"}),
anim.shade = newButton(null,null,{icon:'<i class="fa-solid fa-cube"></i>',title:"stock box"}), anim.shade = newButton(null,null,{icon:'<i class="fa-solid fa-cube"></i>',title:"stock box"}),
anim.path = newButton(null,null,{icon:'<i class="fa-solid fa-draw-polygon"></i>',title:"path"}),
]) ])
}, },

View file

@ -17,9 +17,11 @@ class OpOutline extends CamOp {
async slice(progress) { async slice(progress) {
let { op, state } = this; let { op, state } = this;
let { settings, widget, slicer, addSlices, tshadow, thruHoles, unsafe, color } = state; let { settings, slicer, addSlices, tshadow, thruHoles, unsafe, color, widget } = state;
let { updateToolDiams, tabs, cutTabs, cutPolys, workarea, zMax, shadowAt } = state; let { updateToolDiams, tabs, cutTabs, cutPolys, workarea, zMax, shadowAt } = state;
let { process, stock } = settings; let { process, stock } = settings;
let { controller } = settings;
let center_off = widget.track.pos ?? { x: 0, y: 0, z: 0};
if (op.down <= 0) { if (op.down <= 0) {
throw `invalid step down "${op.down}"`; throw `invalid step down "${op.down}"`;
@ -39,6 +41,8 @@ class OpOutline extends CamOp {
let indices = slicer.interval(op.down, intopt); let indices = slicer.interval(op.down, intopt);
let trueShadow = process.camTrueShadow === true; let trueShadow = process.camTrueShadow === true;
let lastShadowZ; let lastShadowZ;
let stockClip;
// shift out first (top-most) slice // shift out first (top-most) slice
indices.shift(); indices.shift();
// add flats to shadow // add flats to shadow
@ -168,16 +172,12 @@ class OpOutline extends CamOp {
let stepover = toolDiam * op.step; let stepover = toolDiam * op.step;
let wideCuts = [] //accumulator for wide cuts let wideCuts = [] //accumulator for wide cuts
for (let c = (op.steps || 1); c > 0; c--){ for (let c = (op.steps || 1); c > 0; c--){
offset.slice().forEach(op => { let wideCut = POLY.expand(offset.clone(true), stepover * c, slice.z, [], 1);
// clone removes inners but the real solution is wideCut.forEach(cut =>{ //set order of cuts when wide
// to limit expanded shells to through holes cut.order = c
let wideCut = POLY.expand([op.clone(true)], stepover*c, slice.z, [], 1); if(cut.inner) cut.inner.forEach(inn =>{ inn.order = c })
wideCut.forEach(cut =>{ //set order of cuts when wide
cut.order = c
if(cut.inner) cut.inner.forEach(inn =>{ inn.order = c })
});
wideCuts.push(...wideCut)
}); });
wideCuts.push(...wideCut)
} }
offset.appendAll(wideCuts); offset.appendAll(wideCuts);
} }
@ -187,16 +187,19 @@ class OpOutline extends CamOp {
addDogbones(offset, toolDiam / 5); addDogbones(offset, toolDiam / 5);
} }
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
let { center } = stock;
let x = center.x - center_off.x;
let y = center.y - center_off.y;
stockClip = newPolygon().centerRectangle({ x, y }, stock.x + 0.001, stock.y + 0.001);
offset = cutPolys([stockClip], offset, slice.z, true);
}
if (tabs) { if (tabs) {
tabs.forEach(tab => { tabs.forEach(tab => {
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat(); tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
}); });
offset = cutTabs(tabs, offset, slice.z); offset = cutTabs(tabs, offset);
}
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
let rect = newPolygon().centerRectangle({x:0,y:0}, stock.x, stock.y);
offset = cutPolys([rect], offset, slice.z, true);
} }
// offset.xout(`slice ${slice.z}`); // offset.xout(`slice ${slice.z}`);
@ -208,9 +211,15 @@ class OpOutline extends CamOp {
// project empty up and render // project empty up and render
for (let slice of slices) { for (let slice of slices) {
if (false) slice.output() if (controller.devel) slice.output()
.setLayer("slice", {line: 0xaaaa00}, false) .setLayer("slice", {line: 0xaaaa00}, false)
.addPolys(slice.topPolys()) .addPolys(slice.topPolys())
if (controller.devel) slice.output()
.setLayer("shadow", {line: 0x557799}, false)
.addPolys(slice.shadow)
if (controller.devel && stockClip) slice.output()
.setLayer("stock clip", {line: 0x557799}, false)
.addPolys([ stockClip ])
slice.output() slice.output()
.setLayer(state.layername, {face: color, line: color}) .setLayer(state.layername, {face: color, line: color})
.addPolys(slice.camLines); .addPolys(slice.camLines);
@ -224,9 +233,9 @@ class OpOutline extends CamOp {
let { op, state, sliceOut } = this; let { op, state, sliceOut } = this;
let { setTool, setSpindle, setPrintPoint } = ops; let { setTool, setSpindle, setPrintPoint } = ops;
let { polyEmit, depthOutlinePath } = ops; let { polyEmit, depthOutlinePath } = ops;
let { camOut, newLayer, printPoint } = ops; let { newLayer, printPoint } = ops;
let { settings, widget } = state; let { settings } = state;
let { process, controller } = settings; let { process } = settings;
let easeDown = process.camEaseDown; let easeDown = process.camEaseDown;
let toolDiam = this.toolDiam; let toolDiam = this.toolDiam;

View file

@ -19,13 +19,11 @@ class OpPocket extends CamOp {
async slice(progress) { async slice(progress) {
const pocket = this; const pocket = this;
const debug = false;
let { op, state } = this; let { op, state } = this;
let { tool, rate, down, plunge, expand, contour, smooth, tolerance } = op; let { tool, rate, down, plunge, expand, contour, smooth, tolerance } = op;
let { ov_botz, ov_conv } = op; let { ov_botz, ov_conv } = op;
let { settings, widget, addSlices, zBottom, zThru, tabs, color } = state; let { settings, widget, addSlices, zBottom, tabs, color } = state;
let { updateToolDiams, cutTabs, healPolys, shadowAt, workarea } = state; let { updateToolDiams, cutTabs, healPolys, shadowAt, workarea } = state;
let { process } = settings;
zBottom = ov_botz ? workarea.bottom_stock + ov_botz : zBottom; zBottom = ov_botz ? workarea.bottom_stock + ov_botz : zBottom;
// generate tracing offsets from chosen features // generate tracing offsets from chosen features
let sliceOut; let sliceOut;
@ -36,24 +34,18 @@ class OpPocket extends CamOp {
let cutdir = ov_conv; let cutdir = ov_conv;
let engrave = contour && op.engrave; let engrave = contour && op.engrave;
let zTop = workarea.top_z; let zTop = workarea.top_z;
let devel = settings.controller.devel;
let smoothVal = (smooth ?? 0) / 10;
if (contour) { if (contour) {
down = 0; down = 0;
this.topo = await Topo({ this.contour = {
// onupdate: (update, msg) => { axis: "-",
onupdate: (index, total, msg) => { inside: true,
progress((index / total) * 0.9, msg); nogpu: true,
}, step: toolOver,
ondone: (slices) => { tolerance,
// console.log({ contour: slices }); tool,
}, };
contour: {
tool,
tolerance,
inside: true,
axis: "-"
},
state: state
});
} }
updateToolDiams(toolDiam); updateToolDiams(toolDiam);
if (tabs) { if (tabs) {
@ -69,7 +61,7 @@ class OpPocket extends CamOp {
sliceOut.push(slice); sliceOut.push(slice);
return slice; return slice;
} }
function clearZ(polys, z, down) { async function clearZ(polys, z, down) {
if (down) { if (down) {
// adjust step down to a value <= down that // adjust step down to a value <= down that
// ends on the lowest z specified // ends on the lowest z specified
@ -92,14 +84,14 @@ class OpPocket extends CamOp {
let clip = [], shadow; let clip = [], shadow;
if (contour) { if (contour) {
if (smooth) { if (smooth) {
clip = POLY.offset(POLY.offset([ poly ], smooth), -smooth); clip = POLY.offset(POLY.offset([ poly ], smoothVal), -smoothVal);
} else { } else {
clip = [ poly ]; clip = [ poly ];
} }
} else { } else {
shadow = shadowAt(z); shadow = shadowAt(z);
if (smooth) { if (smooth) {
shadow = POLY.setZ(POLY.offset(POLY.offset(shadow, smooth), -smooth), z); shadow = POLY.setZ(POLY.offset(POLY.offset(shadow, smoothVal), -smoothVal), z);
} }
POLY.subtract([ poly ], shadow, clip, undefined, undefined, 0); POLY.subtract([ poly ], shadow, clip, undefined, undefined, 0);
if (op.outline) { if (op.outline) {
@ -130,16 +122,16 @@ class OpPocket extends CamOp {
} }
POLY.setWinding(slice.camLines, cutdir, false); POLY.setWinding(slice.camLines, cutdir, false);
if (contour) { if (contour) {
slice.camLines = pocket.conform(slice.camLines, op.refine, engrave, pct => { slice.camLines = await pocket.conform(slice.camLines, op.refine, engrave, pct => {
progress(0.9 + (zpro + zinc * pct) * 0.1, "conform"); progress(0.9 + (zpro + zinc * pct) * 0.1, "conform");
}); });
} }
slice.output() slice.output()
.setLayer(state.layername, {line: color}, false) .setLayer(state.layername, {line: color}, false)
.addPolys(slice.camLines) .addPolys(slice.camLines)
if (debug && shadow) slice.output() if (devel && shadow) slice.output()
.setLayer("pocket shadow", {line: 0xff8811}, false) .setLayer("pocket shadow", {line: 0xff8811}, false)
.addPolys(shadow) .addPolys(shadow);
if (!contour) { if (!contour) {
progress(zpro, "pocket"); progress(zpro, "pocket");
} }
@ -168,23 +160,37 @@ class OpPocket extends CamOp {
outline = POLY.setWinding(outline, cutdir, false); outline = POLY.setWinding(outline, cutdir, false);
outline = healPolys(outline); outline = healPolys(outline);
if (smooth) { if (smooth) {
outline = POLY.offset(POLY.offset(outline, smooth), -smooth); outline = POLY.offset(POLY.offset(outline, smoothVal), -smoothVal);
} }
if (outline.length) { if (outline.length) {
// option to skip interior features (holes, pillars) // option to skip interior features (holes, pillars)
if (op.outline) { if (op.outline) {
POLY.clearInner(outline); POLY.clearInner(outline);
} }
if (debug) newSliceOut(zmin).output() await clearZ(outline, zmin + 0.0001, down);
if (devel && sliceOut?.length) sliceOut[0].output()
.setLayer("pocket area", {line: 0x1188ff}, false) .setLayer("pocket area", {line: 0x1188ff}, false)
.addPolys(outline) .addPolys(outline)
clearZ(outline, zmin + 0.0001, down);
progress(1, "pocket"); progress(1, "pocket");
} }
} }
// mold cam output lines to the surface of the topo offset by tool geometry // mold cam output lines to the surface of the topo offset by tool geometry
conform(camLines, refine, engrave, progress) { async conform(camLines, refine, engrave, progress) {
if (!this.topo) {
console.log('deferred topo');
this.topo = await Topo({
// onupdate: (update, msg) => {
onupdate: (index, total, msg) => {
progress((index / total) * 0.9, msg);
},
ondone: (slices) => {
// console.log({ contour: slices });
},
contour: this.contour,
state: this.state
});
}
const topo = this.topo; const topo = this.topo;
// re-segment polygon to a higher resolution // re-segment polygon to a higher resolution
const hirez = camLines.map(p => p.segment(topo.tolerance * 2)); const hirez = camLines.map(p => p.segment(topo.tolerance * 2));
@ -236,7 +242,7 @@ class OpPocket extends CamOp {
prepare(ops, progress) { prepare(ops, progress) {
let { op, state, pockets } = this; let { op, state, pockets } = this;
let { setTool, setSpindle, setTolerance, sliceOutput, getPrintPoint } = ops; let { getPrintPoint , pocket, setTool, setSpindle, setTolerance } = ops;
let { process } = state.settings; let { process } = state.settings;
setTool(op.tool, op.rate); setTool(op.tool, op.rate);
@ -269,11 +275,12 @@ class OpPocket extends CamOp {
} }
if (min.pocket) { if (min.pocket) {
min.pocket.used = true; min.pocket.used = true;
sliceOutput(min.pocket, { pocket({
cutdir: op.ov_conv, cutdir: op.ov_conv,
depthFirst: process.camDepthFirst && !state.isIndexed, depthFirst: process.camDepthFirst && !state.isIndexed,
easeDown: op.down && process.easeDown ? op.down : 0, easeDown: op.down && process.easeDown ? op.down : 0,
progress: (n,m) => progress(n/m, "pocket") progress: (n,m) => progress(n/m, "pocket"),
slices: min.pocket
}); });
} else { } else {
break; break;

View file

@ -15,11 +15,12 @@ class OpRough extends CamOp {
async slice(progress) { async slice(progress) {
let { op, state } = this; let { op, state } = this;
let { settings, slicer, addSlices, unsafe, color } = state; let { settings, slicer, addSlices, unsafe, color, widget } = state;
let { updateToolDiams, thruHoles, tabs, cutTabs, cutPolys } = state; let { updateToolDiams, thruHoles, tabs, cutTabs, cutPolys } = state;
let { ztOff, zMax, shadowAt, isIndexed} = state; let { ztOff, zMax, shadowAt, isIndexed} = state;
let { workarea } = state; let { workarea } = state;
let { process, stock } = settings; let { controller, process, stock } = settings;
let center_off = widget.track.pos ?? { x: 0, y: 0, z: 0};
if (op.down <= 0) { if (op.down <= 0) {
throw `invalid step down "${op.down}"`; throw `invalid step down "${op.down}"`;
@ -36,6 +37,12 @@ class OpRough extends CamOp {
updateToolDiams(toolDiam); updateToolDiams(toolDiam);
if (tabs) {
tabs.forEach(tab => {
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
});
}
// clear the stock above the area to be roughed out // clear the stock above the area to be roughed out
if (workarea.top_z > workarea.top_part && !cutThruBypass) { if (workarea.top_z > workarea.top_part && !cutThruBypass) {
let shadow = state.shadow.base.clone(); let shadow = state.shadow.base.clone();
@ -58,9 +65,6 @@ class OpRough extends CamOp {
slice.z = z; slice.z = z;
let polys = POLY.setZ(facing.clone(true), slice.z + roughLeaveZ); let polys = POLY.setZ(facing.clone(true), slice.z + roughLeaveZ);
if (tabs) { if (tabs) {
tabs.forEach(tab => {
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
});
polys = cutTabs(tabs, polys, slice.z); polys = cutTabs(tabs, polys, slice.z);
} }
slice.camLines = polys; slice.camLines = polys;
@ -206,20 +210,6 @@ class OpRough extends CamOp {
} }
} }
if (tabs) {
tabs.forEach(tab => {
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
});
offset = cutTabs(tabs, offset, slice.z);
}
if (!offset) return;
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
let rect = newPolygon().centerRectangle(stock.center, stock.x, stock.y);
offset = cutPolys([rect], offset, slice.z, true);
}
// elimate double inset on inners // elimate double inset on inners
offset.forEach(op => { offset.forEach(op => {
if (op.inner) { if (op.inner) {
@ -236,24 +226,38 @@ class OpRough extends CamOp {
} }
}); });
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
let { center } = stock;
let x = center.x - center_off.x;
let y = center.y - center_off.y;
let rect = newPolygon().centerRectangle({ x, y }, stock.x + 0.001, stock.y + 0.001);
offset = cutPolys([rect], offset, slice.z, true);
}
if (!offset) return;
slice.camLines = offset; slice.camLines = offset;
if (roughLeaveZ) { if (roughLeaveZ) {
// offset roughing in Z as well to minimize // offset roughing in Z as well to minimize tool marks on curved surfaces
// tool marks on curved surfaces
// const roughLeaveZ = 1 * Math.min(roughDown, roughLeave / 2); // const roughLeaveZ = 1 * Math.min(roughDown, roughLeave / 2);
slice.camLines.forEach(p => { for (let poly of slice.camLines) {
p.setZ(p.getZ() + roughLeaveZ); for (let point of poly.points) point.z += roughLeaveZ;
}); }
}
if (controller.devel) {
if (tabs) slice.output()
.setLayer("tabs clip", {line: 0xaa00aa}, true)
.addPolys(tabs.map(tab => tab.off).flat());
slice.output()
.setLayer("slice", {line: 0xaaaa00}, true)
.addPolys(slice.topPolys())
// .setLayer("top shadow", {line: 0x0000aa})
// .addPolys(tshadow)
// .setLayer("rough shadow", {line: 0x00aa00})
// .addPolys(shadow)
.setLayer("shadow clip", {line: 0xaa0000})
.addPolys(shell);
} }
if (false) slice.output()
.setLayer("slice", {line: 0xaaaa00}, true)
.addPolys(slice.topPolys())
// .setLayer("top shadow", {line: 0x0000aa})
// .addPolys(tshadow)
// .setLayer("rough shadow", {line: 0x00aa00})
// .addPolys(shadow)
.setLayer("rough shell", {line: 0xaa0000})
.addPolys(shell);
progress(0.5 + 0.5 * (index / slices.length)); progress(0.5 + 0.5 * (index / slices.length));
}); });
@ -278,6 +282,9 @@ class OpRough extends CamOp {
} }
slices.forEach(slice => { slices.forEach(slice => {
if (slice.camLines && tabs) {
slice.camLines = cutTabs(tabs, slice.camLines);
}
slice.output() slice.output()
.setLayer(state.layername, {face: color, line: color}) .setLayer(state.layername, {face: color, line: color})
.addPolys(slice.camLines); .addPolys(slice.camLines);
@ -289,15 +296,13 @@ class OpRough extends CamOp {
prepare(ops, progress) { prepare(ops, progress) {
let { op, state, sliceOut, camFaces } = this; let { op, state, sliceOut, camFaces } = this;
let { setTool, setSpindle, setPrintPoint, sliceOutput, polyEmit } = ops; let { setTool, setSpindle, setPrintPoint, pocket, polyEmit, newLayer, printPoint } = ops;
let { camOut, newLayer, printPoint } = ops;
let { settings } = state; let { settings } = state;
let { process } = settings; let { process } = settings;
let easeDown = process.camEaseDown; let easeDown = process.camEaseDown;
let cutdir = op.ov_conv; let cutdir = op.ov_conv;
let depthFirst = process.camDepthFirst && !state.isIndexed; let depthFirst = process.camDepthFirst && !state.isIndexed;
let depthData = [];
setTool(op.tool, op.rate, op.plunge); setTool(op.tool, op.rate, op.plunge);
setSpindle(op.spindle); setSpindle(op.spindle);
@ -325,11 +330,12 @@ class OpRough extends CamOp {
// output the roughing passes // output the roughing passes
setPrintPoint(printPoint); setPrintPoint(printPoint);
sliceOutput(sliceOut, { pocket({
cutdir, cutdir,
depthFirst, depthFirst,
easeDown: op.down && easeDown ? 0.001 : 0, easeDown: op.down && easeDown ? 0.001 : 0,
progress: (n,m) => progress(n/m, "routing") progress: (n,m) => progress(n/m, "routing"),
slices: sliceOut
}); });
} }
} }

View file

@ -295,7 +295,6 @@ class OpTrace extends CamOp {
prepare(ops, progress) { prepare(ops, progress) {
let { op, state } = this; let { op, state } = this;
let { settings } = state;
let { setTool, setSpindle } = ops; let { setTool, setSpindle } = ops;
setTool(op.tool, op.rate); setTool(op.tool, op.rate);

View file

@ -337,8 +337,10 @@ export function prepEach(widget, settings, print, firstPoint, update) {
// 1. move to safe z of current point preserving angle // 1. move to safe z of current point preserving angle
// 2. move to safe z of new point preserving old angle // 2. move to safe z of new point preserving old angle
// 3. move to safe z of new point with new angle // 3. move to safe z of new point with new angle
if (lop !== currentOp && lastPoint) { if (lop && lop !== currentOp && lastPoint) {
camOut(lastPoint.clone().setZ(zsafe).setA(lastPoint.a), 0); // compensate for applyWidgetMovement() applied to lastPoint
let lpo = lastPoint.clone().move({ x: -wmx, y: -wmy, z: -zadd });
camOut(lpo.clone().setZ(zsafe).setA(lastPoint.a), 0);
camOut(point.clone().setZ(zsafe).setA(lastPoint.a), 0); camOut(point.clone().setZ(zsafe).setA(lastPoint.a), 0);
camOut(point.clone().setZ(zsafe), 0); camOut(point.clone().setZ(zsafe), 0);
} }
@ -552,16 +554,15 @@ export function prepEach(widget, settings, print, firstPoint, update) {
* output an array of slices that form a pocket * output an array of slices that form a pocket
* used by rough and pocket ops, does not support arcs * used by rough and pocket ops, does not support arcs
* *
* @param {Slice[]} top-down Z stack of slices * @param {Slice[]} slices top-down Z stack of slices
* @param {*} opts * @param {boolean} cutdir true=CW false=CCW
* @param {boolean} depthFirst prioritize cut depth in pockets by nesting
*/ */
function sliceOutput(sliceOut, opts = {}) { function pocket({ slices, cutdir, depthFirst, easeDown, progress }) {
const { cutdir, depthFirst, easeDown, progress } = opts;
let total = 0; let total = 0;
let depthData = []; let depthData = [];
for (let slice of sliceOut) { for (let slice of slices) {
let polys = [], t = [], c = []; let polys = [], t = [], c = [];
POLY.flatten(slice.camLines).forEach(function (poly) { POLY.flatten(slice.camLines).forEach(function (poly) {
let child = poly.parent; let child = poly.parent;
@ -588,7 +589,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
}, { swapdir: false }); }, { swapdir: false });
newLayer(); newLayer();
} }
progress(++total, sliceOut.length); progress(++total, slices.length);
} }
// crucially returns true for -0 as well as other negative #s // crucially returns true for -0 as well as other negative #s
@ -682,45 +683,32 @@ export function prepEach(widget, settings, print, firstPoint, update) {
// make top start offset configurable // make top start offset configurable
printPoint = firstPoint || origin; printPoint = firstPoint || origin;
// accumulated data for depth-first optimizations
// let depthData = {
// rough: [],
// outline: [],
// roughDiam: 0,
// outlineDiam: 0,
// contourx: [],
// contoury: [],
// trace: [],
// drill: [],
// layer: 0,
// };
let ops = { let ops = {
stock,
setTool,
setDrill,
setSpindle,
setTolerance,
setPrintPoint,
setLasering,
getPrintPoint() { return printPoint },
printPoint,
newLayer,
addGCode, addGCode,
bounds,
camOut, camOut,
polyEmit,
poly2polyEmit,
tip2tipEmit,
depthRoughPath,
depthOutlinePath, depthOutlinePath,
sliceOutput, depthRoughPath,
emitDrills, emitDrills,
emitTrace, emitTrace,
bounds, getPrintPoint() { return printPoint },
lastPoint: () => { return lastPoint },
newLayer,
pocket,
poly2polyEmit,
polyEmit,
printPoint,
setDrill,
setLasering,
setPrintPoint,
setSpindle,
setTolerance,
setTool,
stock,
tip2tipEmit,
zclear, zclear,
zmax, zmax,
zsafe, zsafe,
lastPoint: () => { return lastPoint }
}; };
let opSum = 0; let opSum = 0;

View file

@ -119,6 +119,10 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
ondone(msg); ondone(msg);
} }
function alert(msg) {
onupdate(null,null,msg);
}
if (unsafe) { if (unsafe) {
console.log("disabling overhang safeties"); console.log("disabling overhang safeties");
} }
@ -135,20 +139,20 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
let maxDelta = 1e-3; let maxDelta = 1e-3;
if (stock.x + maxDelta < (bounds.max.x - bounds.min.x)) { if (stock.x + maxDelta < (bounds.max.x - bounds.min.x)) {
return error('stock X too small for part. resize stock or use offset stock'); alert('stock X too small for part');
} }
if (stock.y + maxDelta < (bounds.max.y - bounds.min.y)) { if (stock.y + maxDelta < (bounds.max.y - bounds.min.y)) {
return error('stock Y too small for part. resize stock or use offset stock'); alert('stock Y too small for part');
} }
if (stock.z + maxDelta < (bounds.max.z - bounds.min.z)) { if (stock.z + maxDelta < (bounds.max.z - bounds.min.z)) {
return error('stock Z too small for part. resize stock or use offset stock'); alert('stock Z too small for part');
} }
} }
if (zMin >= bounds.max.z) { if (zMin >= bounds.max.z) {
return error(`invalid z bottom ${(zMin / units).round(3)} >= bounds z max ${(zMax / units).round(3)}`); alert(`invalid z bottom ${(zMin / units).round(3)} >= bounds z max ${(zMax / units).round(3)}`);
} }
let mark = Date.now(); let mark = Date.now();
@ -158,31 +162,31 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
let shadows = {}; let shadows = {};
let slicer; let slicer;
let state = { let state = {
settings, addSlices,
widget,
bounds, bounds,
tabs, color,
cutTabs, computeShadows,
cutPolys,
contourPolys, contourPolys,
cutPolys,
cutTabs,
dark,
healPolys, healPolys,
isIndexed,
ops: opList,
setAxisIndex,
settings,
shadowAt, shadowAt,
slicer, slicer,
addSlices, tabs,
isIndexed, unsafe,
setAxisIndex,
updateToolDiams,
updateSlicer, updateSlicer,
computeShadows, updateToolDiams,
widget,
zBottom, zBottom,
zMax,
zThru, zThru,
ztOff, ztOff,
zMax, zTop
zTop,
unsafe,
color,
dark,
ops: opList
}; };
let tracker = setSliceTracker({ rotation: 0 }); let tracker = setSliceTracker({ rotation: 0 });
@ -801,7 +805,7 @@ function cutTabs(tabs, offset) {
function cutPolys(polys, offset) { function cutPolys(polys, offset) {
let noff = []; let noff = [];
offset.forEach(op => noff.appendAll(op.cut(POLY.union(polys, 0, true)))); offset.forEach(op => noff.appendAll( op.cut(POLY.union(polys, 0, true), true) ));
return healPolys(noff); return healPolys(noff);
} }

View file

@ -102,7 +102,7 @@ export class Topo {
newslices.push(debug); newslices.push(debug);
} }
if (webGPU) { if (webGPU && !contour.nogpu) {
// invert tool Z offset for gpu code // invert tool Z offset for gpu code
let toolBounds = new THREE.Box3() let toolBounds = new THREE.Box3()
.expandByPoint({ x: -toolDiameter/2, y: -toolDiameter/2, z: 0 }) .expandByPoint({ x: -toolDiameter/2, y: -toolDiameter/2, z: 0 })
@ -137,8 +137,9 @@ export class Topo {
// console.timeEnd('swap XY vertices'); // console.timeEnd('swap XY vertices');
} }
let trace = contour.trace;
let gpu = await self.get_raster_gpu({ let gpu = await self.get_raster_gpu({
mode: "planar", mode: trace ? "tracing" : "planar",
resolution resolution
}); });
let xStep = density; let xStep = density;

View file

@ -291,8 +291,9 @@ export function fdm_export(print, online, ondone, ondebug) {
append(`; Bed type: ${bedType}`); append(`; Bed type: ${bedType}`);
append(`; Target: ${filter[mode]}`); append(`; Target: ${filter[mode]}`);
// inject thumbnail preview // inject thumbnail preview
if (exportThumb && worker.snap) { let { current } = self.kiri_worker ?? {};
let { width, height, url } = worker.snap; if (exportThumb && current?.snap) {
let { width, height, url } = current.snap;
let data = url.substring(url.indexOf(',') + 1); let data = url.substring(url.indexOf(',') + 1);
append(`; thumbnail begin ${width} ${height} ${data.length}`); append(`; thumbnail begin ${width} ${height} ${data.length}`);
for (let i=0; i<data.length; i += 78) { for (let i=0; i<data.length; i += 78) {

View file

@ -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.4.0" VERSION: "4.5.0"
}; };
export const beta = 0; export const beta = 0;

View file

@ -143,7 +143,7 @@ async function _fetch(e) {
if (url.pathname.endsWith("/")) { if (url.pathname.endsWith("/")) {
return e.respondWith(redirectOr404(appendURL(url, 'index.html').pathname)); return e.respondWith(redirectOr404(appendURL(url, 'index.html').pathname));
} else if (url.pathname.indexOf(".") < 0) { } else if (url.pathname.indexOf(".") < 0 || url.pathname.endsWith("/boot")) {
return e.respondWith(redirectOr404(appendURL(url, '/index.html').pathname)); return e.respondWith(redirectOr404(appendURL(url, '/index.html').pathname));
} else { } else {
e.respondWith(fromCacheOrNetwork(request)); e.respondWith(fromCacheOrNetwork(request));

View file

@ -318,6 +318,8 @@ self.lang['da-dk'] = {
op_unit_l: ["arbejdsområde enheder påvirker","hastigheder og distancer"], op_unit_l: ["arbejdsområde enheder påvirker","hastigheder og distancer"],
op_anim_s: "animer", op_anim_s: "animer",
op_anim_l: ["animation mesh tæthed","højere er mere tæt","tager mere hukommelse","og er langsommere"], op_anim_l: ["animation mesh tæthed","højere er mere tæt","tager mere hukommelse","og er langsommere"],
op_mani_s: "mangfoldig",
op_mani_l: ["i indekseret materiale tilstand","brug boolesk visualisering","dette er meget dyrt","og sandsynligvis meget langsom"],
op_exgl_l: ["vis Grid:Local enheder i eksport dialog","inkluderer Grid:Bots og OctoPrint plugins"], op_exgl_l: ["vis Grid:Local enheder i eksport dialog","inkluderer Grid:Bots og OctoPrint plugins"],
op_exgh_l: ["legacy Grid:Host lokalt netværk support"], op_exgh_l: ["legacy Grid:Host lokalt netværk support"],
op_exop_l: ["legacy OctoPrint lokalt netværk support","Grid:Space OctoPrint plugin anbefalet"], op_exop_l: ["legacy OctoPrint lokalt netværk support","Grid:Space OctoPrint plugin anbefalet"],

View file

@ -319,6 +319,8 @@ self.lang['de-de'] = {
op_unit_l: ["Arbeitsbereich-Einheiten beeinflussen","Geschwindigkeiten und Distanzen"], op_unit_l: ["Arbeitsbereich-Einheiten beeinflussen","Geschwindigkeiten und Distanzen"],
op_anim_s: "Animieren", op_anim_s: "Animieren",
op_anim_l: ["Animations-Mesh-Dichte","höher ist dichter","benötigt mehr Speicher","und ist langsamer"], op_anim_l: ["Animations-Mesh-Dichte","höher ist dichter","benötigt mehr Speicher","und ist langsamer"],
op_mani_s: "mannigfaltig",
op_mani_l: ["im indizierten Materialvorrat-Modus","boolesche Visualisierung verwenden","dies ist sehr rechenintensiv","und wahrscheinlich sehr langsam"],
op_exgl_l: ["Grid:Local Geräte im Export-Dialog anzeigen","inkludiert Grid:Bots und OctoPrint Plugins"], op_exgl_l: ["Grid:Local Geräte im Export-Dialog anzeigen","inkludiert Grid:Bots und OctoPrint Plugins"],
op_exgh_l: ["Legacy Grid:Host lokale Netzwerkunterstützung"], op_exgh_l: ["Legacy Grid:Host lokale Netzwerkunterstützung"],
op_exop_l: ["Legacy OctoPrint lokale Netzwerkunterstützung","Grid:Space OctoPrint Plugin empfohlen"], op_exop_l: ["Legacy OctoPrint lokale Netzwerkunterstützung","Grid:Space OctoPrint Plugin empfohlen"],

View file

@ -319,6 +319,8 @@ self.lang['en-us'] = {
op_unit_l: ["workspace units affects","speeds and distances"], op_unit_l: ["workspace units affects","speeds and distances"],
op_anim_s: "animate", op_anim_s: "animate",
op_anim_l: ["animation mesh density","higher is more dense","takes more memory","and is slower"], op_anim_l: ["animation mesh density","higher is more dense","takes more memory","and is slower"],
op_mani_s: "manifold",
op_mani_l: ["in stock indexed mode","use boolean visualiation","this is very expensive","and likely very slow"],
op_exgl_l: ["show Grid:Local devices in export dialog","includes Grid:Bots and OctoPrint plugins"], op_exgl_l: ["show Grid:Local devices in export dialog","includes Grid:Bots and OctoPrint plugins"],
op_exgh_l: ["legacy Grid:Host local network support"], op_exgh_l: ["legacy Grid:Host local network support"],
op_exop_l: ["legacy OctoPrint local network support","Grid:Space OctoPrint plugin recommended"], op_exop_l: ["legacy OctoPrint local network support","Grid:Space OctoPrint plugin recommended"],

View file

@ -319,6 +319,8 @@ self.lang['es-es'] = {
op_unit_l: ["las unidades del espacio de trabajo afectan","velocidades y distancias"], op_unit_l: ["las unidades del espacio de trabajo afectan","velocidades y distancias"],
op_anim_s: "animar", op_anim_s: "animar",
op_anim_l: ["densidad de malla de animación","mayor es más denso","requiere más memoria","y es más lento"], op_anim_l: ["densidad de malla de animación","mayor es más denso","requiere más memoria","y es más lento"],
op_mani_s: "variedad",
op_mani_l: ["en modo de material indexado","usar visualización booleana","esto es muy costoso","y probablemente muy lento"],
op_exgl_l: ["mostrar dispositivos Grid:Local en diálogo de exportación","incluye Grid:Bots y plugins de OctoPrint"], op_exgl_l: ["mostrar dispositivos Grid:Local en diálogo de exportación","incluye Grid:Bots y plugins de OctoPrint"],
op_exgh_l: ["soporte heredado de red local Grid:Host"], op_exgh_l: ["soporte heredado de red local Grid:Host"],
op_exop_l: ["soporte heredado de red local OctoPrint","se recomienda el plugin Grid:Space OctoPrint"], op_exop_l: ["soporte heredado de red local OctoPrint","se recomienda el plugin Grid:Space OctoPrint"],

View file

@ -319,6 +319,8 @@ self.lang['fr-fr'] = {
op_unit_l: ["les unités de l'espace de travail affectent","les vitesses et distances"], op_unit_l: ["les unités de l'espace de travail affectent","les vitesses et distances"],
op_anim_s: "animer", op_anim_s: "animer",
op_anim_l: ["densité du maillage d'animation","plus élevé est plus dense","nécessite plus de mémoire","et est plus lent"], op_anim_l: ["densité du maillage d'animation","plus élevé est plus dense","nécessite plus de mémoire","et est plus lent"],
op_mani_s: "variété",
op_mani_l: ["en mode matériau indexé","utiliser la visualisation booléenne","ceci est très coûteux","et probablement très lent"],
op_exgl_l: ["afficher les appareils Grid:Local dans le dialogue d'export","inclut les plugins Grid:Bots et OctoPrint"], op_exgl_l: ["afficher les appareils Grid:Local dans le dialogue d'export","inclut les plugins Grid:Bots et OctoPrint"],
op_exgh_l: ["support réseau local Grid:Host legacy"], op_exgh_l: ["support réseau local Grid:Host legacy"],
op_exop_l: ["support réseau local OctoPrint legacy","plugin Grid:Space OctoPrint recommandé"], op_exop_l: ["support réseau local OctoPrint legacy","plugin Grid:Space OctoPrint recommandé"],

View file

@ -318,6 +318,8 @@ self.lang['pl-pl'] = {
op_unit_l: ["jednostki przestrzeni roboczej wpływają","na prędkości i odległości"], op_unit_l: ["jednostki przestrzeni roboczej wpływają","na prędkości i odległości"],
op_anim_s: "animuj", op_anim_s: "animuj",
op_anim_l: ["gęstość siatki animacji","wyższa jest bardziej gęsta","zajmuje więcej pamięci","i jest wolniejsza"], op_anim_l: ["gęstość siatki animacji","wyższa jest bardziej gęsta","zajmuje więcej pamięci","i jest wolniejsza"],
op_mani_s: "rozmaitość",
op_mani_l: ["w trybie indeksowanego materiału","użyj wizualizacji boolowskiej","to jest bardzo kosztowne","i prawdopodobnie bardzo wolne"],
op_exgl_l: ["pokaż urządzenia Grid:Local w oknie dialogowym eksportu","zawiera Grid:Bots i wtyczki OctoPrint"], op_exgl_l: ["pokaż urządzenia Grid:Local w oknie dialogowym eksportu","zawiera Grid:Bots i wtyczki OctoPrint"],
op_exgh_l: ["starsze wsparcie sieci lokalnej Grid:Host"], op_exgh_l: ["starsze wsparcie sieci lokalnej Grid:Host"],
op_exop_l: ["starsze wsparcie sieci lokalnej OctoPrint","zalecana wtyczka Grid:Space OctoPrint"], op_exop_l: ["starsze wsparcie sieci lokalnej OctoPrint","zalecana wtyczka Grid:Space OctoPrint"],

View file

@ -319,6 +319,8 @@ self.lang['pt-pt'] = {
op_unit_l: ["unidades de espaço de trabalho afecta","velocidades e distâncias"], op_unit_l: ["unidades de espaço de trabalho afecta","velocidades e distâncias"],
op_anim_s: "animar", op_anim_s: "animar",
op_anim_l: ["densidade da malha de animação","mais alto é mais denso","usa mais memória","e é mais lento"], op_anim_l: ["densidade da malha de animação","mais alto é mais denso","usa mais memória","e é mais lento"],
op_mani_s: "variedade",
op_mani_l: ["no modo de material indexado","usar visualização booleana","isso é muito caro","e provavelmente muito lento"],
op_exgl_l: ["mostrar dispositivos Grid:Local no diálogo de exportação","inclui Grid:Bots e plugins OctoPrint"], op_exgl_l: ["mostrar dispositivos Grid:Local no diálogo de exportação","inclui Grid:Bots e plugins OctoPrint"],
op_exgh_l: ["suporte de rede local Grid:Host legado"], op_exgh_l: ["suporte de rede local Grid:Host legado"],
op_exop_l: ["suporte de rede local OctoPrint legado","plugin Grid:Space OctoPrint recomendado"], op_exop_l: ["suporte de rede local OctoPrint legado","plugin Grid:Space OctoPrint recomendado"],

View file

@ -318,6 +318,8 @@ self.lang['zh'] = {
op_unit_l: ["工作区单位影响","速度和距离"], op_unit_l: ["工作区单位影响","速度和距离"],
op_anim_s: "动画", op_anim_s: "动画",
op_anim_l: ["动画网格密度","更高则更密集","占用更多内存","速度更慢"], op_anim_l: ["动画网格密度","更高则更密集","占用更多内存","速度更慢"],
op_mani_s: "流形",
op_mani_l: ["在索引材料模式下","使用布尔可视化","这非常昂贵","而且可能非常慢"],
op_exgl_l: ["在导出对话框中显示Grid:Local设备","包括Grid:Bots和OctoPrint插件"], op_exgl_l: ["在导出对话框中显示Grid:Local设备","包括Grid:Bots和OctoPrint插件"],
op_exgh_l: ["传统Grid:Host本地网络支持"], op_exgh_l: ["传统Grid:Host本地网络支持"],
op_exop_l: ["传统OctoPrint本地网络支持","推荐使用Grid:Space OctoPrint插件"], op_exop_l: ["传统OctoPrint本地网络支持","推荐使用Grid:Space OctoPrint插件"],

View file

@ -1,8 +1,8 @@
{ {
"name": "Kiri:Moto 4.4.0", "name": "Kiri:Moto 4.5.0",
"short_name": "Kiri:Moto", "short_name": "Kiri:Moto",
"description": "Slicer for 3D printers, CNC mills, laser cutters and more", "description": "Slicer for 3D printers, CNC mills, laser cutters and more",
"start_url": "/boot/", "start_url": "/boot/index.html",
"id": "/kiri/", "id": "/kiri/",
"display": "standalone", "display": "standalone",
"theme_color": "#7e99b7", "theme_color": "#7e99b7",