Merge branch 'master' of https://github.com/GridSpace/grid-apps into drill-optimization
This commit is contained in:
commit
efb41e14fa
128 changed files with 8990 additions and 4345 deletions
|
|
@ -5,38 +5,37 @@
|
|||
import { CSG } from '../../../geo/csg.js';
|
||||
import { Tool } from './tool.js';
|
||||
|
||||
let nextMeshID = 1;
|
||||
let stock, center, rez;
|
||||
let path, pathIndex, tool, tools, last;
|
||||
|
||||
let stockZ;
|
||||
let stockIndexMsg = false;
|
||||
let stockSlices;
|
||||
let stockIndex;
|
||||
let startTime;
|
||||
|
||||
let toolID = -1;
|
||||
let toolMesh;
|
||||
let toolRadius;
|
||||
let toolUpdateMsg;
|
||||
|
||||
let animateClear = false;
|
||||
let animating = false;
|
||||
let moveDist = 0;
|
||||
let renderDist = 0;
|
||||
let renderDone = false;
|
||||
let renderPause = 10;
|
||||
let renderSteps = 0;
|
||||
let renderSpeed = 0;
|
||||
let indexCount = 0;
|
||||
let updates = 0;
|
||||
let nextMeshID = 1,
|
||||
stock,
|
||||
path,
|
||||
pathIndex,
|
||||
tool,
|
||||
tools,
|
||||
last,
|
||||
stockZ,
|
||||
stockIndexMsg = false,
|
||||
stockSlices,
|
||||
stockIndex,
|
||||
startTime,
|
||||
toolID = -1,
|
||||
toolMesh,
|
||||
toolRadius,
|
||||
toolUpdateMsg,
|
||||
animateClear = false,
|
||||
animating = false,
|
||||
renderDone = false,
|
||||
renderSteps = 0,
|
||||
renderSpeed = 0,
|
||||
indexCount = 0,
|
||||
updates = 0,
|
||||
maxR;
|
||||
|
||||
export function init(worker) {
|
||||
const { dispatch } = worker;
|
||||
|
||||
dispatch.animate_setup2 = function (data, send) {
|
||||
const { settings } = data;
|
||||
const { process } = settings;
|
||||
const { controller, process } = settings;
|
||||
const print = worker.current.print;
|
||||
const density = parseInt(settings.controller.animesh) * 1000;
|
||||
const isIndexed = process.camStockIndexed;
|
||||
|
|
@ -45,8 +44,6 @@ export function init(worker) {
|
|||
path = print.output.flat();
|
||||
tools = settings.tools;
|
||||
stock = settings.stock;
|
||||
rez = 1 / Math.sqrt(density / (stock.x * stock.y));
|
||||
|
||||
tool = null;
|
||||
last = null;
|
||||
animating = false;
|
||||
|
|
@ -56,25 +53,27 @@ export function init(worker) {
|
|||
startTime = 0;
|
||||
updates = 0;
|
||||
stockZ = isIndexed ? 0 : stock.z;
|
||||
maxR = Math.hypot(stock.y, stock.z);
|
||||
|
||||
stockSlices = [];
|
||||
const { x, y, z } = stock;
|
||||
const sliceCount = parseInt(settings.controller.animesh || 2000) / 100;
|
||||
const sliceWidth = stock.x / sliceCount;
|
||||
|
||||
if (controller.manifold)
|
||||
for (let i = 0; i < sliceCount; i++) {
|
||||
let xmin = -(x / 2) + (i * sliceWidth) + sliceWidth / 2;
|
||||
let slice = new Stock(sliceWidth, y, z).translate(xmin, 0, 0);
|
||||
stockSlices.push(slice);
|
||||
slice.updateMesh([]);
|
||||
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();
|
||||
};
|
||||
|
||||
dispatch.animate2 = function (data, send) {
|
||||
renderPause = data.pause || renderPause;
|
||||
renderSpeed = data.speed || 0;
|
||||
if (animating) {
|
||||
return send.done();
|
||||
|
|
@ -126,7 +125,6 @@ function renderPath(send) {
|
|||
return;
|
||||
}
|
||||
pathIndex++;
|
||||
// console.log(next.point.z);
|
||||
|
||||
if (next.tool && (!tool || tool.getID() !== next.tool.getID())) {
|
||||
// on real tool change, go to safe Z first
|
||||
|
|
@ -143,44 +141,50 @@ function renderPath(send) {
|
|||
}
|
||||
|
||||
const id = toolID;
|
||||
const rezstep = rez * 2;
|
||||
const rezstep = Math.min(tool.maxDiameter(), 1) / 4;
|
||||
// console.log({ rezstep });
|
||||
|
||||
if (last) {
|
||||
// console.log(last.point, next.point);
|
||||
const lp = last.point, np = next.point;
|
||||
last = next;
|
||||
|
||||
// render move line on client
|
||||
send.data({ line: [ lp, np ]});
|
||||
|
||||
// dwell ops have no point
|
||||
if (!np || !lp) {
|
||||
return renderPath(send);
|
||||
}
|
||||
|
||||
let dx = np.x - lp.x,
|
||||
dy = np.y - lp.y,
|
||||
dz = np.z - lp.z,
|
||||
da = Math.abs((np.a || 0) - (lp.a || 0)),
|
||||
dr = (da / 360) * (2 * Math.PI * Math.max(np.z, lp.z)),
|
||||
dist = Math.sqrt(dx * dx + dy * dy + dz * dz + dr * dr);
|
||||
|
||||
moveDist += dist;
|
||||
|
||||
// skip moves that are less than grid resolution
|
||||
if (moveDist < rezstep) {
|
||||
// console.log('skip', moveDist, rezstep, next);
|
||||
renderPath(send);
|
||||
return;
|
||||
}
|
||||
da = (np.a ?? 0) - (lp.a ?? 0), // delta rotation degrees
|
||||
AD = maxR * 2, // arc diameter @ max z
|
||||
dr = (Math.abs(da) / 360) * AD, // delta rotation arc
|
||||
dist = Math.max(Math.hypot(dx, dy, dz), dr); // motion distance
|
||||
|
||||
// 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 st = Math.ceil(md / rezstep);
|
||||
const mx = dx / st, my = dy / st, mz = dz / st;
|
||||
// const sd = Math.sqrt(mx*mx + my*my + mz*mz + dr*dr);
|
||||
const sd = Math.sqrt(mx * mx + my * my + Math.min(1, mz * mz) + dr * dr);
|
||||
const st = Math.ceil(md / rezstep); // steps
|
||||
const mx = dx / st, my = dy / st, mz = dz / st, ma = da / st;
|
||||
const sd = Math.sqrt(mx * mx + my * my + mz * mz + ma * ma);
|
||||
const sp = next.speed;
|
||||
const moves = [];
|
||||
for (let i = 0, x = lp.x, y = lp.y, z = lp.z; i < st; i++) {
|
||||
moves.push({ x, y, z, a: lp.a, md: sd, dx, dy, dz });
|
||||
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, da, sp });
|
||||
x += mx;
|
||||
y += my;
|
||||
z += mz;
|
||||
a += ma;
|
||||
}
|
||||
moveDist = 0;
|
||||
moves.push({ ...next.point, md: sd });
|
||||
// console.log({ dist: md, steps: st, speed: next.speed, moves });
|
||||
|
||||
// push last point
|
||||
moves.push({ ...next.point, md: sd, sp });
|
||||
// console.log(...moves);
|
||||
renderMoves(id, moves, send);
|
||||
} else {
|
||||
last = next;
|
||||
|
|
@ -197,26 +201,25 @@ function renderMoves(id, moves, send, seed = 0) {
|
|||
if (!pos) {
|
||||
throw `no pos @ ${index} of ${moves.length}`;
|
||||
}
|
||||
const { dx, dy, dz } = pos;
|
||||
const { dx, dy, dz, da, sp, md } = pos;
|
||||
toolMove(pos);
|
||||
// console.log('renderMoves', {id, moves, seed});
|
||||
let subs = 0;
|
||||
if (dx || dy || dz < 0)
|
||||
if (dx || dy || dz < 0 || da)
|
||||
for (let slice of stockSlices) {
|
||||
if (slice.bounds.intersectsBox(toolMesh.bounds)) {
|
||||
slice.subtractTool(toolMesh);
|
||||
subs++;
|
||||
}
|
||||
}
|
||||
// console.log({ index, subs });
|
||||
renderDist += pos.md;
|
||||
// pause renderer at specified offsets
|
||||
if (renderSpeed && renderDist >= renderSpeed) {
|
||||
renderDist = 0;
|
||||
if (renderSpeed) {
|
||||
let time = sp ? (md / sp) * 60000 : 10;
|
||||
// console.log({ time, ...pos });
|
||||
renderUpdate(send);
|
||||
setTimeout(() => {
|
||||
renderMoves(id, moves, send, index + 1);
|
||||
}, renderPause);
|
||||
}, time / renderSpeed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,29 +10,35 @@ const { client } = api;
|
|||
let meshes = {},
|
||||
button = {},
|
||||
label = {},
|
||||
lineTracker,
|
||||
material,
|
||||
speedPauses = [0, 0, 0, 0, 0],
|
||||
speedValues = [1, 2, 4, 8, 32],
|
||||
speedNames = ["1x", "2x", "4x", "8x", "!!"],
|
||||
speedMax = speedValues.length - 1,
|
||||
speedIndex = 0,
|
||||
speed,
|
||||
origin,
|
||||
color = 0;
|
||||
color = 0,
|
||||
dark = false,
|
||||
manifold = false,
|
||||
A2R = Math.PI / 180;
|
||||
|
||||
export function animate_clear2(api) {
|
||||
let { anim } = api.ui;
|
||||
client.animate_cleanup2();
|
||||
lineTracker?.clear();
|
||||
Object.keys(meshes).forEach(id => deleteMesh(id));
|
||||
api.widgets.setAxisIndex(0);
|
||||
api.uc.setVisible(anim.laba, true);
|
||||
api.uc.setVisible(anim.vala, true);
|
||||
anim.vala.value = "0.0";
|
||||
client.animate_cleanup2();
|
||||
}
|
||||
|
||||
export function animate2(api, delay) {
|
||||
let alert = api.alerts.show("building animation");
|
||||
let settings = api.conf.get();
|
||||
dark = settings.controller.dark;
|
||||
manifold = settings.controller.manifold;
|
||||
client.animate_setup2(settings, data => {
|
||||
handleUpdate(data);
|
||||
if (data) {
|
||||
|
|
@ -48,7 +54,8 @@ export function animate2(api, delay) {
|
|||
speed: anim.speed,
|
||||
trans: anim.trans,
|
||||
model: anim.model,
|
||||
shade: anim.shade
|
||||
shade: anim.shade,
|
||||
path: anim.path
|
||||
});
|
||||
Object.assign(label, {
|
||||
progress: anim.progress,
|
||||
|
|
@ -59,6 +66,14 @@ export function animate2(api, delay) {
|
|||
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);
|
||||
setTimeout(step, delay || 0);
|
||||
toggleTrans(undefined, false);
|
||||
|
|
@ -72,6 +87,7 @@ export function animate2(api, delay) {
|
|||
button.trans.onclick = toggleTrans;
|
||||
button.model.onclick = toggleModel;
|
||||
button.shade.onclick = toggleStock;
|
||||
button.path.onclick = togglePath;
|
||||
button.play.style.display = '';
|
||||
button.pause.style.display = 'none';
|
||||
|
||||
|
|
@ -86,8 +102,9 @@ Object.assign(client, {
|
|||
},
|
||||
|
||||
animate_setup2(settings, ondone) {
|
||||
color = settings.controller.dark ? 0x888888 : 0;
|
||||
material = new THREE.MeshPhongMaterial({
|
||||
initPathMesh();
|
||||
color = dark ? 0x888888 : 0;
|
||||
material = material ?? new THREE.MeshPhongMaterial({
|
||||
flatShading: true,
|
||||
transparent: false,
|
||||
opacity: 0.5,
|
||||
|
|
@ -102,6 +119,73 @@ 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: dark ? 0xffff00 : 0x771100 }),
|
||||
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),
|
||||
show = api.local.get('cam.anim.path') ?? true;
|
||||
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 (show && bool !== lines.visible) {
|
||||
lines.visible = bool;
|
||||
} else if (!show) {
|
||||
lines.visible = false;
|
||||
}
|
||||
},
|
||||
show(bool) {
|
||||
show = bool;
|
||||
lines.visible = bool;
|
||||
}
|
||||
}
|
||||
space.world.add(lines);
|
||||
}
|
||||
|
||||
function meshAdd(id, ind, pos, ilen, plen) {
|
||||
const geo = new THREE.BufferGeometry();
|
||||
const pa = plen ? pos.subarray(0, plen * 3) : pos;
|
||||
|
|
@ -132,6 +216,7 @@ function meshUpdate(id, ind, pos, ilen, plen) {
|
|||
|
||||
function deleteMesh(id) {
|
||||
space.world.remove(meshes[id]);
|
||||
meshes[id].geometry.dispose();
|
||||
delete meshes[id];
|
||||
}
|
||||
|
||||
|
|
@ -141,8 +226,15 @@ function toggleModel(ev, bool) {
|
|||
}
|
||||
|
||||
function toggleStock(ev, bool, set) {
|
||||
set !== false && api.local.toggle('cam.anim.stock', bool);
|
||||
return api.event.emit('cam.stock.toggle', bool ?? undefined);
|
||||
bool = api.local.toggle('cam.anim.stock', bool);
|
||||
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) {
|
||||
|
|
@ -165,8 +257,7 @@ function play(opts) {
|
|||
}
|
||||
client.animate2({
|
||||
speed,
|
||||
steps: steps || Infinity,
|
||||
pause: speedPauses[speedIndex]
|
||||
steps: steps || Infinity
|
||||
}, handleUpdate);
|
||||
}
|
||||
|
||||
|
|
@ -177,8 +268,7 @@ function fast(opts) {
|
|||
button.pause.style.display = '';
|
||||
client.animate2({
|
||||
speed,
|
||||
steps: steps || Infinity,
|
||||
pause: speedPauses[speedIndex]
|
||||
steps: steps || Infinity
|
||||
}, handleUpdate);
|
||||
}
|
||||
|
||||
|
|
@ -216,6 +306,7 @@ function handleUpdate(data) {
|
|||
if (data.stock_index !== undefined) {
|
||||
api.widgets.setAxisIndex(data.stock_index);
|
||||
label.a.value = -data.stock_index.toFixed(1);
|
||||
lineTracker.rotate(-data.stock_index);
|
||||
}
|
||||
if (data.mesh_index) {
|
||||
const { id, index } = data.mesh_index;
|
||||
|
|
@ -229,9 +320,12 @@ function handleUpdate(data) {
|
|||
const { id, ind, pos, ilen, plen } = data.mesh_update;
|
||||
meshUpdate(id, ind, pos, ilen, plen);
|
||||
}
|
||||
if (data && data.progress) {
|
||||
if (data.progress) {
|
||||
label.progress.value = (data.progress * 100).toFixed(1);
|
||||
}
|
||||
if (data.line) {
|
||||
lineTracker.add(...data.line);
|
||||
}
|
||||
}
|
||||
|
||||
function updateSpeed(inc = 0) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { selectHoles } from './cl-hole.js';
|
|||
import { selectHelical } from './cl-helical.js';
|
||||
import { surfaceAdd } from './cl-surface.js';
|
||||
import { traceAdd } from './cl-trace.js';
|
||||
import { opRender } from './client.js';
|
||||
|
||||
const { MODES } = api.const;
|
||||
const { uc: UC } = api;
|
||||
|
|
@ -206,7 +207,7 @@ export function createPopOps() {
|
|||
sep: UC.newBlank({ class: "pop-sep" }),
|
||||
spindle: UC.newInput(LANG.cc_spnd_s, { title: LANG.cc_spnd_l, convert: UC.toInt, show: hasSpindle }),
|
||||
step: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: UC.toFloat, bound: UC.bound(0.01, 1.0) }),
|
||||
stepz: UC.newInput(LANG.cc_sdwn_s, { title: LANG.cc_sdwn_l, convert: UC.toFloat, bound: UC.bound(0, 100.0) }),
|
||||
stepz: UC.newInput(LANG.cc_sdwn_s, { title: LANG.cc_sdwn_l, convert: UC.toFloat, units: true, bound: UC.bound(0, 100.0) }),
|
||||
rate: UC.newInput(LANG.cc_feed_s, { title: LANG.cc_feed_l, convert: UC.toInt, units: true }),
|
||||
down: UC.newInput(LANG.cc_loff_s, { title: LANG.cc_loff_l, convert: UC.toFloat, units: true }),
|
||||
inset: UC.newInput(LANG.cc_lxyo_s, { title: LANG.cc_lxyo_l, convert: UC.toFloat, units: true, show: () => !env.popOp.level.rec.stock }),
|
||||
|
|
@ -282,7 +283,7 @@ export function createPopOps() {
|
|||
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 }),
|
||||
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" }),
|
||||
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 } }),
|
||||
|
|
@ -349,7 +350,9 @@ export function createPopOps() {
|
|||
tolerance: 'camTolerance',
|
||||
filter: 'camContourFilter',
|
||||
leave: 'camContourLeave',
|
||||
linear: 'camLatheLinear'
|
||||
linear: 'camLatheLinear',
|
||||
offStart: 'camLatheOffStart',
|
||||
offEnd: 'camLatheOffEnd'
|
||||
}).inputs = {
|
||||
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
|
||||
// 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) }),
|
||||
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" }),
|
||||
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 }),
|
||||
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" }),
|
||||
|
|
|
|||
|
|
@ -140,8 +140,8 @@ function pointHoverUp(int, ev) {
|
|||
x += stock.x / 2;
|
||||
y += stock.y / 2;
|
||||
}
|
||||
process.camOriginOffX = x;
|
||||
process.camOriginOffY = y;
|
||||
process.camOriginOffX = x - stock.center.x;
|
||||
process.camOriginOffY = y - stock.center.y;
|
||||
process.camOriginOffZ = z;
|
||||
api.settings.update_fields(process);
|
||||
api.platform.update_origin();
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ export function traceHoverUp(int, ev) {
|
|||
|
||||
export function traceToggle(obj, skip) {
|
||||
let material = obj.material[0] || obj.material;
|
||||
if (!material) return;
|
||||
if (!(material && obj.trace)) return;
|
||||
let { color, colorSave } = material;
|
||||
let { widget, poly } = obj.trace;
|
||||
let areas = env.poppedRec.areas;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { Layers } from '../../core/layers.js';
|
|||
import { Stack } from '../../core/stack.js';
|
||||
import { updateStock } from './cl-stock.js';
|
||||
import { createPopOps } from './cl-ops.js';
|
||||
import { tabAdd, tabDone, tabClear, restoreTabs, rotateTabs, updateTabs, clearTabs } from './cl-tab.js';
|
||||
import { tabAdd, tabDone, tabClear, restoreTabs, rotateTabs, updateTabs, clearTabs, mirrorTabs } from './cl-tab.js';
|
||||
import { traceOn, traceDone, unselectTraces } from './cl-trace.js';
|
||||
import { holeSelOn, selectHolesDone, clearHolesRec } from './cl-hole.js';
|
||||
import { surfaceOn, surfaceDone } from './cl-surface.js';
|
||||
|
|
@ -29,6 +29,7 @@ const { widgets: WIDGETS } = api;
|
|||
class Client {
|
||||
animVer = 0;
|
||||
camStock;
|
||||
showStock = true;
|
||||
current;
|
||||
currentIndex;
|
||||
flipping;
|
||||
|
|
@ -320,6 +321,7 @@ export function opRender() {
|
|||
el.classList.add("disabled");
|
||||
}
|
||||
bounds.push(el);
|
||||
let parent = $('camops');
|
||||
let timer = null;
|
||||
let inside = true;
|
||||
let popped = false;
|
||||
|
|
@ -329,14 +331,14 @@ export function opRender() {
|
|||
}
|
||||
el.rec = rec;
|
||||
el.unpop = () => {
|
||||
let pos = [...el.childNodes].indexOf(poprec.div);
|
||||
let pos = [...parent.childNodes].indexOf(poprec.div);
|
||||
if (pos >= 0) {
|
||||
el.removeChild(poprec.div);
|
||||
parent.removeChild(poprec.div);
|
||||
}
|
||||
popped = false;
|
||||
};
|
||||
function onEnter(ev) {
|
||||
if ((surfaceOn || traceOn) && env.poppedRec != rec) {
|
||||
if ((clock || surfaceOn || traceOn) && env.poppedRec != rec) {
|
||||
return;
|
||||
}
|
||||
if (popped && env.poppedRec != rec) {
|
||||
|
|
@ -351,22 +353,24 @@ export function opRender() {
|
|||
popped = true;
|
||||
poprec.use(rec);
|
||||
env.hoveredOp = el;
|
||||
if (!clock) {
|
||||
// offset Y position of pop div by % of Y screen location of button
|
||||
el.appendChild(poprec.div);
|
||||
poprec.addNote();
|
||||
const { innerHeight } = window;
|
||||
const brect = ev.target.getBoundingClientRect();
|
||||
const prect = poprec.div.getBoundingClientRect();
|
||||
const pcty = (brect.top / innerHeight) * 0.9;
|
||||
const offpx = -pcty * prect.height;
|
||||
poprec.div.style.transform = `translateY(${offpx}px)`;
|
||||
}
|
||||
// offset Y position of pop div by % of Y screen location of button
|
||||
parent.appendChild(poprec.div);
|
||||
poprec.addNote();
|
||||
const { innerHeight } = window;
|
||||
const brect = ev.target.getBoundingClientRect();
|
||||
const prect = parent.getBoundingClientRect();
|
||||
const Prect = poprec.div.getBoundingClientRect();
|
||||
const tdiff = prect.top - brect.top;
|
||||
const botoff = innerHeight - (brect.top + Prect.height);
|
||||
const offpx = -tdiff + (botoff < 0 ? botoff : -Prect.height/3);
|
||||
poprec.div.style.transform = `translateY(${offpx}px)`;
|
||||
poprec.div.onmouseenter = () => { inside = true };
|
||||
poprec.div.onmouseleave = onLeave;
|
||||
// option click event appears latent
|
||||
// and overides the sticky settings
|
||||
setTimeout(() => {
|
||||
UC.setSticky(false);
|
||||
}, 0);
|
||||
}, 10);
|
||||
}
|
||||
function onLeave(ev) {
|
||||
inside = false;
|
||||
|
|
@ -637,7 +641,8 @@ export function init() {
|
|||
});
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -12,10 +12,8 @@ export function cam_export(print, online) {
|
|||
const widget = print.widgets[0];
|
||||
if (!widget) return;
|
||||
|
||||
console.log({ cam_export: print });
|
||||
|
||||
const { settings } = print;
|
||||
const { device, tools } = settings;
|
||||
const { device } = settings;
|
||||
|
||||
let i,
|
||||
time = 0,
|
||||
|
|
@ -257,17 +255,18 @@ export function cam_export(print, online) {
|
|||
points--;
|
||||
return;
|
||||
}
|
||||
let gn;
|
||||
if (out.emit >= 0 && out.emit <= 3) gn = `G${out.emit}`;
|
||||
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,
|
||||
|
||||
let dx = opt.dx || newpos.x - pos.x,
|
||||
dy = opt.dy || newpos.y - pos.y,
|
||||
dz = opt.dz || newpos.z - pos.z,
|
||||
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),
|
||||
dist = Math.sqrt(dx * dx + dy * dy + dz * dz),
|
||||
newFeed = feed && feed !== pos.f;
|
||||
|
|
@ -424,7 +423,9 @@ export function cam_export(print, online) {
|
|||
toolz[out.tool] = ctool;
|
||||
}
|
||||
point = out.point;
|
||||
if (!point || point.mod) return;
|
||||
if (!point || point.mod) {
|
||||
return;
|
||||
}
|
||||
// ensure not point is modified twice
|
||||
point.mod = 1;
|
||||
if (isOffset) {
|
||||
|
|
|
|||
|
|
@ -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.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.path = newButton(null,null,{icon:'<i class="fa-solid fa-draw-polygon"></i>',title:"path"}),
|
||||
])
|
||||
},
|
||||
|
||||
|
|
@ -105,7 +106,7 @@ export function menu() {
|
|||
separator: newBlank({ class:"set-sep", driven }),
|
||||
camEaseAngle: newInput(LANG.ou_eang_s, {title:LANG.ou_eang_l, convert:toFloat, bound:bound(0.1,85), show:() => ui.camEaseDown.checked}),
|
||||
camFullEngage: newInput(LANG.ou_feng_s, {title:LANG.ou_feng_l, convert:toFloat, bound:bound(0.1,1.0)}),
|
||||
_____: newGroup(LANG.co_menu, $('cam-origin'), { modes:CAM, driven, separator }),
|
||||
_____: newGroup(LANG.or_menu, $('cam-origin'), { modes:CAM, driven, separator }),
|
||||
camOriginTop: newBoolean(LANG.or_topp_s, onBooleanClick, {title:LANG.or_topp_l}),
|
||||
camOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, {title:LANG.or_cntr_l}),
|
||||
separator: newBlank({ class:"set-sep", driven }),
|
||||
|
|
|
|||
|
|
@ -4,26 +4,70 @@ import { CamOp } from './op.js';
|
|||
import { Tool } from './tool.js';
|
||||
import { generate as Topo } from './topo3.js';
|
||||
import { newPoint } from '../../../geo/point.js';
|
||||
import { newPolygon } from '../../../geo/polygon.js';
|
||||
import { tip2tipEmit } from '../../../geo/paths.js';
|
||||
|
||||
function createFilter(op) {
|
||||
function createFilter(op, origin, axis) {
|
||||
// console.log({ origin, axis });
|
||||
let ok = () => true;
|
||||
let filter = slices => slices;
|
||||
let filterString = op.filter?.map(l => l.trim()).join('\n');
|
||||
if (filterString) {
|
||||
try {
|
||||
const obj = eval(`( ${filterString} )`);
|
||||
let idx = 0;
|
||||
if (obj && obj.slices) {
|
||||
const nadd = [];
|
||||
filter = function (slices) {
|
||||
for (let slice of slices) {
|
||||
if (obj.slices(slice, idx++)) {
|
||||
nadd.push(slice);
|
||||
let box = obj?.box;
|
||||
let slice_fn = obj?.slices;
|
||||
let index = 0;
|
||||
const accept = [];
|
||||
filter = function (slices) {
|
||||
for (let slice of slices) {
|
||||
if (slice_fn && slice_fn(slice, index++)) {
|
||||
accept.push(slice);
|
||||
} else if (box) {
|
||||
// slice.z = x when axis = y
|
||||
// slice.z = y when axis = x
|
||||
let { x, y, z } = box;
|
||||
x = x ?? [ -Infinity, Infinity ];
|
||||
y = y ?? [ -Infinity, Infinity ];
|
||||
z = z ?? [ -Infinity, Infinity ];
|
||||
let ok = false;
|
||||
if (axis === 'x') {
|
||||
let sy = slice.z + origin.y;
|
||||
if (sy >= y[0] && sy <= y[1]) {
|
||||
ok = true;
|
||||
for (let p of slice.camLines) {
|
||||
p.points = p.points.filter(p =>
|
||||
p.x - origin.x >= x[0] && p.x - origin.x <= x[1] &&
|
||||
p.z - origin.z >= z[0] && p.z - origin.z <= z[1]
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let sx = slice.z - origin.x;
|
||||
if (sx >= x[0] && sx <= x[1]) {
|
||||
ok = true;
|
||||
for (let p of slice.camLines) {
|
||||
p.points = p.points.filter(p =>
|
||||
p.y + origin.y >= y[0] && p.y + origin.y <= y[1] &&
|
||||
p.z - origin.z >= z[0] && p.z - origin.z <= z[1]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
slice.camLines = slice.camLines.map(p => {
|
||||
if (p.points.length > 1) {
|
||||
return newPolygon(p.points).setOpen(true);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}).filter(p => p);
|
||||
accept.push(slice);
|
||||
}
|
||||
}
|
||||
return nadd;
|
||||
};
|
||||
}
|
||||
}
|
||||
return accept;
|
||||
};
|
||||
} catch (e) {
|
||||
console.log('filter parse error', e, op.filter);
|
||||
}
|
||||
|
|
@ -38,8 +82,8 @@ class OpContour extends CamOp {
|
|||
|
||||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { addSlices, settings, updateToolDiams } = state;
|
||||
let filter = createFilter(op);
|
||||
let { color, addSlices, settings, updateToolDiams } = state;
|
||||
let filter = createFilter(op, settings.origin, op.axis.toLowerCase());
|
||||
let toolDiam = this.toolDiam = new Tool(settings, op.tool).fluteDiameter();
|
||||
updateToolDiams(toolDiam);
|
||||
// we need topo for safe travel moves when roughing and outlining
|
||||
|
|
@ -54,6 +98,11 @@ class OpContour extends CamOp {
|
|||
slices = filter(slices);
|
||||
this.sliceOut = slices;
|
||||
addSlices(slices);
|
||||
for (let slice of slices) {
|
||||
slice.output()
|
||||
.setLayer(`contour ${op.axis}`, { face: color, line: color })
|
||||
.addPolys(slice.camLines);
|
||||
}
|
||||
},
|
||||
contour: op,
|
||||
state: state
|
||||
|
|
@ -64,11 +113,11 @@ class OpContour extends CamOp {
|
|||
|
||||
prepare(ops, progress) {
|
||||
let { op, state, sliceOut } = this;
|
||||
let { settings, widget } = state;
|
||||
let { settings } = state;
|
||||
let { process } = settings;
|
||||
|
||||
let { setTolerance, setTool, setSpindle, setPrintPoint } = ops;
|
||||
let { camOut, polyEmit, newLayer, printPoint, lastPoint } = ops;
|
||||
let { camOut, newLayer, printPoint, lastPoint } = ops;
|
||||
let { bounds, zmax } = ops;
|
||||
|
||||
let toolDiam = this.toolDiam;
|
||||
|
|
@ -87,9 +136,9 @@ class OpContour extends CamOp {
|
|||
if (!slice.camLines) {
|
||||
continue;
|
||||
}
|
||||
let polys = [], poly, emit;
|
||||
let polys = [], poly;
|
||||
slice.camLines.forEach(function (poly) {
|
||||
if (depthFirst) poly = poly.clone(true);
|
||||
if (depthFirst) poly = poly.clone(true).annotate({ slice: slice.index + 1 });
|
||||
polys.push({ first: poly.first(), last: poly.last(), poly: poly });
|
||||
});
|
||||
if (depthFirst) {
|
||||
|
|
@ -101,7 +150,7 @@ class OpContour extends CamOp {
|
|||
poly.reverse();
|
||||
}
|
||||
poly.forEachPoint(function (point, pidx) {
|
||||
camOut(point.clone(), pidx > 0, stepover);
|
||||
camOut(point.clone(), pidx > 0, { moveLen: stepover });
|
||||
}, false);
|
||||
});
|
||||
newLayer();
|
||||
|
|
@ -115,7 +164,7 @@ class OpContour extends CamOp {
|
|||
poly.reverse();
|
||||
}
|
||||
poly.forEachPoint(function (point, pidx) {
|
||||
camOut(point.clone(), pidx > 0, stepover);
|
||||
camOut(point.clone().annotate({ slice: poly.slice }), pidx > 0, { moveLen: stepover });
|
||||
}, false);
|
||||
newLayer();
|
||||
return lastPoint();
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ class OpDrill extends CamOp {
|
|||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { settings, addSlices, widget, updateToolDiams } = state;
|
||||
let { zBottom, zThru, thruHoles, color } = state;
|
||||
let { drills, drillThrough } = op
|
||||
let { color } = state;
|
||||
let { drills } = op
|
||||
|
||||
let drillTool = new Tool(settings, op.tool),
|
||||
drillToolDiam = drillTool.fluteDiameter(),
|
||||
|
|
@ -47,7 +47,7 @@ class OpDrill extends CamOp {
|
|||
slice.camLines = [poly];
|
||||
|
||||
slice.output()
|
||||
.setLayer("drill", { face: color, line: color })
|
||||
.setLayer(state.layername, { face: color, line: color })
|
||||
.addPolys(slice.camLines);
|
||||
addSlices(slice);
|
||||
sliceOut.push(slice);
|
||||
|
|
@ -55,11 +55,10 @@ class OpDrill extends CamOp {
|
|||
}
|
||||
|
||||
prepare(ops, progress) {
|
||||
let { op, state } = this;
|
||||
let { settings, widget, addSlices, updateToolDiams } = state;
|
||||
let { op } = this;
|
||||
let { setTool, setSpindle, setDrill, emitDrills } = ops;
|
||||
setTool(op.tool, undefined, op.rate);
|
||||
setDrill(op.down, op.lift, op.dwell, op.thru);
|
||||
setDrill(op.down, op.lift, op.dwell);
|
||||
setSpindle(op.spindle);
|
||||
emitDrills(this.sliceOut.map(slice => slice.camLines).flat());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export class OpIndex extends CamOp {
|
|||
}
|
||||
|
||||
prepare(ops, progress) {
|
||||
const { lastPoint, zmax, zclear, camOut, stock } = ops;
|
||||
const { lastPoint, zmax, zclear, camOut, stock, newLayer } = ops;
|
||||
let last = lastPoint();
|
||||
if (last) {
|
||||
// max point of stock corner radius when rotating (safe z when indexing)
|
||||
|
|
@ -34,7 +34,7 @@ export class OpIndex extends CamOp {
|
|||
// move above rotating stock
|
||||
camOut(last = last.clone().setZ(zmove), 0);
|
||||
// issue rotation command
|
||||
camOut(last = last.clone().setY(0).setA(this.degrees), 0);
|
||||
camOut(last = last.clone().setZ(zmove).setA(this.degrees), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,22 +6,23 @@ import { generate as topo4_generate } from './topo4.js';
|
|||
import { newPoint } from '../../../geo/point.js';
|
||||
|
||||
function createFilter(op) {
|
||||
let ok = () => true;
|
||||
let filter = slices => slices;
|
||||
if (op.filter) {
|
||||
let filterString = op.filter?.map(l => l.trim()).join('\n');
|
||||
if (filterString) {
|
||||
try {
|
||||
const obj = eval(`( ${op.filter.join('\n')} )`);
|
||||
let idx = 0;
|
||||
if (obj && obj.slices) {
|
||||
const nadd = [];
|
||||
filter = function (slices) {
|
||||
for (let slice of slices) {
|
||||
if (obj.slices(slice, idx++)) {
|
||||
nadd.push(slice);
|
||||
}
|
||||
const obj = eval(`( ${filterString} )`);
|
||||
const accept = [];
|
||||
let index = 0;
|
||||
let sl_fn = obj?.slices ?? ok;
|
||||
filter = function (slices) {
|
||||
for (let slice of slices) {
|
||||
if (sl_fn(slice, index++)) {
|
||||
accept.push(slice);
|
||||
}
|
||||
return nadd;
|
||||
};
|
||||
}
|
||||
}
|
||||
return accept;
|
||||
};
|
||||
} catch (e) {
|
||||
console.log('filter parse error', e, op.filter);
|
||||
}
|
||||
|
|
@ -36,9 +37,8 @@ class OpLathe extends CamOp {
|
|||
|
||||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { addSlices } = state;
|
||||
let { addSlices, color } = state;
|
||||
let filter = createFilter(op);
|
||||
|
||||
this.topo = await topo4_generate({
|
||||
op,
|
||||
state,
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ class OpLevel extends CamOp {
|
|||
|
||||
let points = [];
|
||||
let clear = op.stock ?
|
||||
[ newPolygon().centerRectangle({x:0,y:0,z:0}, stock.x, stock.y) ] :
|
||||
[ newPolygon().centerRectangle({x:0,y:0,z:0}, stock.x + toolDiam/2, stock.y) ] :
|
||||
POLY.outer(POLY.offset(tshadow, toolDiam * (op.inset || 0)));
|
||||
|
||||
POLY.fillArea(clear, 90, stepOver, points);
|
||||
POLY.fillArea(clear, 1090, stepOver, points);
|
||||
|
||||
let layers = this.layers = [];
|
||||
for (let z of zList) {
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@ class OpOutline extends CamOp {
|
|||
|
||||
async slice(progress) {
|
||||
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 { process, stock } = settings;
|
||||
let { controller } = settings;
|
||||
let center_off = widget.track.pos ?? { x: 0, y: 0, z: 0};
|
||||
|
||||
if (op.down <= 0) {
|
||||
throw `invalid step down "${op.down}"`;
|
||||
|
|
@ -39,6 +41,8 @@ class OpOutline extends CamOp {
|
|||
let indices = slicer.interval(op.down, intopt);
|
||||
let trueShadow = process.camTrueShadow === true;
|
||||
let lastShadowZ;
|
||||
let stockClip;
|
||||
|
||||
// shift out first (top-most) slice
|
||||
indices.shift();
|
||||
// add flats to shadow
|
||||
|
|
@ -168,16 +172,12 @@ class OpOutline extends CamOp {
|
|||
let stepover = toolDiam * op.step;
|
||||
let wideCuts = [] //accumulator for wide cuts
|
||||
for (let c = (op.steps || 1); c > 0; c--){
|
||||
offset.slice().forEach(op => {
|
||||
// clone removes inners but the real solution is
|
||||
// to limit expanded shells to through holes
|
||||
let wideCut = POLY.expand([op.clone(true)], stepover*c, slice.z, [], 1);
|
||||
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)
|
||||
let wideCut = POLY.expand(offset.clone(true), stepover * c, slice.z, [], 1);
|
||||
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)
|
||||
}
|
||||
offset.appendAll(wideCuts);
|
||||
}
|
||||
|
|
@ -187,16 +187,19 @@ class OpOutline extends CamOp {
|
|||
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) {
|
||||
tabs.forEach(tab => {
|
||||
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
|
||||
});
|
||||
offset = cutTabs(tabs, offset, slice.z);
|
||||
}
|
||||
|
||||
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 = cutTabs(tabs, offset);
|
||||
}
|
||||
|
||||
// offset.xout(`slice ${slice.z}`);
|
||||
|
|
@ -208,11 +211,17 @@ class OpOutline extends CamOp {
|
|||
|
||||
// project empty up and render
|
||||
for (let slice of slices) {
|
||||
if (false) slice.output()
|
||||
if (controller.devel) slice.output()
|
||||
.setLayer("slice", {line: 0xaaaa00}, false)
|
||||
.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()
|
||||
.setLayer("outline", {face: color, line: color})
|
||||
.setLayer(state.layername, {face: color, line: color})
|
||||
.addPolys(slice.camLines);
|
||||
}
|
||||
|
||||
|
|
@ -224,9 +233,9 @@ class OpOutline extends CamOp {
|
|||
let { op, state, sliceOut } = this;
|
||||
let { setTool, setSpindle, setPrintPoint } = ops;
|
||||
let { polyEmit, depthOutlinePath } = ops;
|
||||
let { camOut, newLayer, printPoint } = ops;
|
||||
let { settings, widget } = state;
|
||||
let { process, controller } = settings;
|
||||
let { newLayer, printPoint } = ops;
|
||||
let { settings } = state;
|
||||
let { process } = settings;
|
||||
|
||||
let easeDown = process.camEaseDown;
|
||||
let toolDiam = this.toolDiam;
|
||||
|
|
|
|||
|
|
@ -19,13 +19,11 @@ class OpPocket extends CamOp {
|
|||
|
||||
async slice(progress) {
|
||||
const pocket = this;
|
||||
const debug = false;
|
||||
let { op, state } = this;
|
||||
let { tool, rate, down, plunge, expand, contour, smooth, tolerance } = 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 { process } = settings;
|
||||
zBottom = ov_botz ? workarea.bottom_stock + ov_botz : zBottom;
|
||||
// generate tracing offsets from chosen features
|
||||
let sliceOut;
|
||||
|
|
@ -36,24 +34,18 @@ class OpPocket extends CamOp {
|
|||
let cutdir = ov_conv;
|
||||
let engrave = contour && op.engrave;
|
||||
let zTop = workarea.top_z;
|
||||
let devel = settings.controller.devel;
|
||||
let smoothVal = (smooth ?? 0) / 10;
|
||||
if (contour) {
|
||||
down = 0;
|
||||
this.topo = await Topo({
|
||||
// onupdate: (update, msg) => {
|
||||
onupdate: (index, total, msg) => {
|
||||
progress((index / total) * 0.9, msg);
|
||||
},
|
||||
ondone: (slices) => {
|
||||
// console.log({ contour: slices });
|
||||
},
|
||||
contour: {
|
||||
tool,
|
||||
tolerance,
|
||||
inside: true,
|
||||
axis: "-"
|
||||
},
|
||||
state: state
|
||||
});
|
||||
this.contour = {
|
||||
axis: "-",
|
||||
inside: true,
|
||||
nogpu: true,
|
||||
step: toolOver,
|
||||
tolerance,
|
||||
tool,
|
||||
};
|
||||
}
|
||||
updateToolDiams(toolDiam);
|
||||
if (tabs) {
|
||||
|
|
@ -69,7 +61,7 @@ class OpPocket extends CamOp {
|
|||
sliceOut.push(slice);
|
||||
return slice;
|
||||
}
|
||||
function clearZ(polys, z, down) {
|
||||
async function clearZ(polys, z, down) {
|
||||
if (down) {
|
||||
// adjust step down to a value <= down that
|
||||
// ends on the lowest z specified
|
||||
|
|
@ -92,14 +84,14 @@ class OpPocket extends CamOp {
|
|||
let clip = [], shadow;
|
||||
if (contour) {
|
||||
if (smooth) {
|
||||
clip = POLY.offset(POLY.offset([ poly ], smooth), -smooth);
|
||||
clip = POLY.offset(POLY.offset([ poly ], smoothVal), -smoothVal);
|
||||
} else {
|
||||
clip = [ poly ];
|
||||
}
|
||||
} else {
|
||||
shadow = shadowAt(z);
|
||||
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);
|
||||
if (op.outline) {
|
||||
|
|
@ -130,16 +122,16 @@ class OpPocket extends CamOp {
|
|||
}
|
||||
POLY.setWinding(slice.camLines, cutdir, false);
|
||||
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");
|
||||
});
|
||||
}
|
||||
slice.output()
|
||||
.setLayer("pocket", {line: color}, false)
|
||||
.setLayer(state.layername, {line: color}, false)
|
||||
.addPolys(slice.camLines)
|
||||
if (debug && shadow) slice.output()
|
||||
if (devel && shadow) slice.output()
|
||||
.setLayer("pocket shadow", {line: 0xff8811}, false)
|
||||
.addPolys(shadow)
|
||||
.addPolys(shadow);
|
||||
if (!contour) {
|
||||
progress(zpro, "pocket");
|
||||
}
|
||||
|
|
@ -168,23 +160,37 @@ class OpPocket extends CamOp {
|
|||
outline = POLY.setWinding(outline, cutdir, false);
|
||||
outline = healPolys(outline);
|
||||
if (smooth) {
|
||||
outline = POLY.offset(POLY.offset(outline, smooth), -smooth);
|
||||
outline = POLY.offset(POLY.offset(outline, smoothVal), -smoothVal);
|
||||
}
|
||||
if (outline.length) {
|
||||
// option to skip interior features (holes, pillars)
|
||||
if (op.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)
|
||||
.addPolys(outline)
|
||||
clearZ(outline, zmin + 0.0001, down);
|
||||
progress(1, "pocket");
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
// re-segment polygon to a higher resolution
|
||||
const hirez = camLines.map(p => p.segment(topo.tolerance * 2));
|
||||
|
|
@ -236,7 +242,7 @@ class OpPocket extends CamOp {
|
|||
|
||||
prepare(ops, progress) {
|
||||
let { op, state, pockets } = this;
|
||||
let { setTool, setSpindle, setTolerance, sliceOutput, getPrintPoint } = ops;
|
||||
let { getPrintPoint , pocket, setTool, setSpindle, setTolerance } = ops;
|
||||
let { process } = state.settings;
|
||||
|
||||
setTool(op.tool, op.rate);
|
||||
|
|
@ -269,11 +275,12 @@ class OpPocket extends CamOp {
|
|||
}
|
||||
if (min.pocket) {
|
||||
min.pocket.used = true;
|
||||
sliceOutput(min.pocket, {
|
||||
pocket({
|
||||
cutdir: op.ov_conv,
|
||||
depthFirst: process.camDepthFirst && !state.isIndexed,
|
||||
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 {
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class OpRegister extends CamOp {
|
|||
|
||||
let tool = new Tool(settings, op.tool);
|
||||
let sliceOut = this.sliceOut = [];
|
||||
let axis = op.axis.toLowerCase();
|
||||
|
||||
updateToolDiams(tool.fluteDiameter());
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ class OpRegister extends CamOp {
|
|||
return;
|
||||
}
|
||||
|
||||
switch (op.axis.toLowerCase()) {
|
||||
switch (axis) {
|
||||
case "x":
|
||||
if (op.points == 3) {
|
||||
pathPoints.push(newPoint(boundMinX - cutOffset, centerY, 0));
|
||||
|
|
@ -64,6 +65,7 @@ class OpRegister extends CamOp {
|
|||
}
|
||||
break;
|
||||
case "-":
|
||||
case "=":
|
||||
let halfOffset = toolOffset / 2,
|
||||
loopMinX = boundMinX - cutOffset,
|
||||
loopMaxX = boundMaxX + cutOffset,
|
||||
|
|
@ -84,7 +86,7 @@ class OpRegister extends CamOp {
|
|||
}
|
||||
function rept(count, step, fn) {
|
||||
while (count-- > 0) {
|
||||
fn(step, count === 0);
|
||||
fn(step, count);
|
||||
step = -step;
|
||||
}
|
||||
}
|
||||
|
|
@ -93,9 +95,16 @@ class OpRegister extends CamOp {
|
|||
addSlices(slice);
|
||||
sliceOut.push(slice);
|
||||
start(z);
|
||||
rept(4, halfOffset, oy => {
|
||||
rept(4, halfOffset, (oy, count) => {
|
||||
move(0, -oy);
|
||||
move(deltaX, 0);
|
||||
if (axis === '=' && count % 2 === 1) {
|
||||
move(deltaX/2, 0);
|
||||
move(0, oy/2);
|
||||
move(0, -oy/2);
|
||||
move(deltaX/2, 0);
|
||||
} else {
|
||||
move(deltaX, 0);
|
||||
}
|
||||
});
|
||||
rept(4, halfOffset, ox => {
|
||||
move(ox, 0);
|
||||
|
|
@ -136,11 +145,10 @@ class OpRegister extends CamOp {
|
|||
}
|
||||
|
||||
prepare(ops, progress) {
|
||||
let { op, state } = this;
|
||||
let { settings, widget, addSlices, updateToolDiams } = state;
|
||||
let { op } = this;
|
||||
let { setTool, setSpindle, setDrill, emitDrills } = ops;
|
||||
|
||||
if (op.axis === '-') {
|
||||
if (op.axis === '-' || op.axis === '=') {
|
||||
setTool(op.tool, op.feed, op.rate);
|
||||
setSpindle(op.spindle);
|
||||
for (let slice of this.sliceOut) {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,12 @@ class OpRough extends CamOp {
|
|||
|
||||
async slice(progress) {
|
||||
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 { ztOff, zMax, shadowAt, isIndexed} = 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) {
|
||||
throw `invalid step down "${op.down}"`;
|
||||
|
|
@ -36,6 +37,12 @@ class OpRough extends CamOp {
|
|||
|
||||
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
|
||||
if (workarea.top_z > workarea.top_part && !cutThruBypass) {
|
||||
let shadow = state.shadow.base.clone();
|
||||
|
|
@ -58,14 +65,11 @@ class OpRough extends CamOp {
|
|||
slice.z = z;
|
||||
let polys = POLY.setZ(facing.clone(true), slice.z + roughLeaveZ);
|
||||
if (tabs) {
|
||||
tabs.forEach(tab => {
|
||||
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
|
||||
});
|
||||
polys = cutTabs(tabs, polys, slice.z);
|
||||
}
|
||||
slice.camLines = polys;
|
||||
slice.output()
|
||||
.setLayer("roughing", {face: color, line: color})
|
||||
.setLayer(state.layername, {face: color, line: color})
|
||||
.addPolys(slice.camLines);
|
||||
addSlices(slice);
|
||||
camFaces.push(slice);
|
||||
|
|
@ -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
|
||||
offset.forEach(op => {
|
||||
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;
|
||||
if (roughLeaveZ) {
|
||||
// offset roughing in Z as well to minimize
|
||||
// tool marks on curved surfaces
|
||||
// offset roughing in Z as well to minimize tool marks on curved surfaces
|
||||
// const roughLeaveZ = 1 * Math.min(roughDown, roughLeave / 2);
|
||||
slice.camLines.forEach(p => {
|
||||
p.setZ(p.getZ() + roughLeaveZ);
|
||||
});
|
||||
for (let poly of slice.camLines) {
|
||||
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));
|
||||
});
|
||||
|
||||
|
|
@ -278,8 +282,11 @@ class OpRough extends CamOp {
|
|||
}
|
||||
|
||||
slices.forEach(slice => {
|
||||
if (slice.camLines && tabs) {
|
||||
slice.camLines = cutTabs(tabs, slice.camLines);
|
||||
}
|
||||
slice.output()
|
||||
.setLayer("roughing", {face: color, line: color})
|
||||
.setLayer(state.layername, {face: color, line: color})
|
||||
.addPolys(slice.camLines);
|
||||
});
|
||||
this.sliceOut = slices.filter(slice => slice.camLines);
|
||||
|
|
@ -289,15 +296,13 @@ class OpRough extends CamOp {
|
|||
|
||||
prepare(ops, progress) {
|
||||
let { op, state, sliceOut, camFaces } = this;
|
||||
let { setTool, setSpindle, setPrintPoint, sliceOutput, polyEmit } = ops;
|
||||
let { camOut, newLayer, printPoint } = ops;
|
||||
let { setTool, setSpindle, setPrintPoint, pocket, polyEmit, newLayer, printPoint } = ops;
|
||||
let { settings } = state;
|
||||
let { process } = settings;
|
||||
|
||||
let easeDown = process.camEaseDown;
|
||||
let cutdir = op.ov_conv;
|
||||
let depthFirst = process.camDepthFirst && !state.isIndexed;
|
||||
let depthData = [];
|
||||
|
||||
setTool(op.tool, op.rate, op.plunge);
|
||||
setSpindle(op.spindle);
|
||||
|
|
@ -325,11 +330,12 @@ class OpRough extends CamOp {
|
|||
|
||||
// output the roughing passes
|
||||
setPrintPoint(printPoint);
|
||||
sliceOutput(sliceOut, {
|
||||
pocket({
|
||||
cutdir,
|
||||
depthFirst,
|
||||
easeDown: op.down && easeDown ? 0.001 : 0,
|
||||
progress: (n,m) => progress(n/m, "routing")
|
||||
progress: (n,m) => progress(n/m, "routing"),
|
||||
slices: sliceOut
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ class OpTrace extends CamOp {
|
|||
}
|
||||
POLY.setWinding(slice.camLines, offset === "outside" ? !cutdir : cutdir, false);
|
||||
slice.output()
|
||||
.setLayer("trace follow", {line: color}, false)
|
||||
.setLayer(state.layername, {line: color}, false)
|
||||
.addPolys(slice.camLines)
|
||||
}
|
||||
function clearZnew(polys, z, down) {
|
||||
|
|
@ -154,7 +154,7 @@ class OpTrace extends CamOp {
|
|||
.setLayer("trace poly", {line: 0x1188ff}, false)
|
||||
.addPolys([ poly ])
|
||||
slice.output()
|
||||
.setLayer("trace", {line: color}, false)
|
||||
.setLayer(state.layername, {line: color}, false)
|
||||
.addPolys(slice.camLines)
|
||||
progress(zpro, "trace");
|
||||
zpro += zinc;
|
||||
|
|
@ -295,7 +295,6 @@ class OpTrace extends CamOp {
|
|||
|
||||
prepare(ops, progress) {
|
||||
let { op, state } = this;
|
||||
let { settings } = state;
|
||||
let { setTool, setSpindle } = ops;
|
||||
|
||||
setTool(op.tool, op.rate);
|
||||
|
|
|
|||
|
|
@ -56,8 +56,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
|
||||
if (widget.camops.length === 0 || widget.meta.disabled) return;
|
||||
|
||||
let device = settings.device,
|
||||
process = settings.process,
|
||||
let { device, process } = settings,
|
||||
isIndexed = process.camStockIndexed,
|
||||
startCenter = process.camOriginCenter,
|
||||
alignTop = settings.controller.alignTop,
|
||||
|
|
@ -65,7 +64,6 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
stockz = stock.z * (isIndexed ? 0.5 : 1),
|
||||
outer = settings.bounds || widget.getPositionBox(),
|
||||
outerz = outer.max.z,
|
||||
slices = widget.slices,
|
||||
zclear = (process.camZClearance || 1),
|
||||
zmax_force = process.camForceZMax || false,
|
||||
zmax_outer = stockz + zclear,
|
||||
|
|
@ -75,13 +73,13 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
boundsz = isIndexed ? stock.z / 2 : bounds.max.z + ztOff,
|
||||
zadd = !isIndexed ? stock.z - boundsz : alignTop ? outerz - boundsz : 0,
|
||||
zmax = outerz + zclear + (process.camOriginOffZ || 0),
|
||||
zsafe = isIndexed ? Math.hypot(bounds.dim.y, bounds.dim.z) / 2 + zclear : zmax,
|
||||
wmpos = widget.track.pos,
|
||||
wmx = wmpos.x,
|
||||
wmy = wmpos.y,
|
||||
originx = (startCenter ? 0 : -stock.x / 2) + (process.camOriginOffX || 0),
|
||||
originy = (startCenter ? 0 : -stock.y / 2) + (process.camOriginOffY || 0),
|
||||
origin = newPoint(originx + wmx, originy + wmy, zmax),
|
||||
output = print.output,
|
||||
easeDown = process.camEaseDown,
|
||||
easeAngle = process.camEaseAngle,
|
||||
depthFirst = process.camDepthFirst,
|
||||
|
|
@ -94,13 +92,11 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
drillDown = 0,
|
||||
drillLift = 0,
|
||||
drillDwell = 0,
|
||||
drillThru = 0,
|
||||
lasering = false,
|
||||
laserPower = 0,
|
||||
newOutput = print.output || [],
|
||||
layerOut = [],
|
||||
printPoint,
|
||||
isNewMode,
|
||||
isPocket,
|
||||
isContour,
|
||||
isRough,
|
||||
|
|
@ -112,6 +108,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
toolDiamMove,
|
||||
plungeRate = process.camFastFeedZ,
|
||||
feedRate,
|
||||
lastOp,
|
||||
lastTool,
|
||||
lastPush,
|
||||
lastPoint,
|
||||
|
|
@ -187,11 +184,10 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
laserPower = power;
|
||||
}
|
||||
|
||||
function setDrill(down, lift, dwell, thru) {
|
||||
function setDrill(down, lift, dwell) {
|
||||
drillDown = down;
|
||||
drillLift = lift;
|
||||
drillDwell = dwell;
|
||||
drillThru = thru;
|
||||
}
|
||||
|
||||
function emitDrills(polys) {
|
||||
|
|
@ -264,10 +260,11 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
*/
|
||||
function layerPush(point, emit, speed, tool, options) {
|
||||
const { type, center, arcPoints } = options ?? {};
|
||||
const dz = (point && lastPush && lastPush.point) ? point.z - lastPush.point.z : 0;
|
||||
if (dz < 0 && speed > plungeRate) {
|
||||
const dz = (point && lastPush?.point) ? point.z - lastPush.point.z : 0;
|
||||
if (dz < -0.05 && speed > plungeRate) {
|
||||
speed = plungeRate;
|
||||
}
|
||||
// if (options?.type !== 'lerp') console.log( point, currentOp.type );
|
||||
layerOut.mode = currentOp;
|
||||
if (lasering) {
|
||||
let power = emit ? laserPower : 0;
|
||||
|
|
@ -320,7 +317,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
p.x + wmx,
|
||||
p.y + wmy,
|
||||
p.z + zadd
|
||||
).setA(p.a ?? lastPoint?.a);
|
||||
).setA(p.a ?? lastPoint?.a).annotate({ slice: p.slice });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -333,7 +330,26 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
* @param {number} opts.factor speed scale factor
|
||||
*/
|
||||
function camOut(point, emit = 1, opts) {
|
||||
// console.trace({point, emit, opts})
|
||||
let lop = lastOp;
|
||||
lastOp = currentOp;
|
||||
|
||||
// on operation changes:
|
||||
// 1. move to safe z of current point preserving angle
|
||||
// 2. move to safe z of new point preserving old angle
|
||||
// 3. move to safe z of new point with new angle
|
||||
if (lop && lop !== currentOp && lastPoint) {
|
||||
// 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), 0);
|
||||
}
|
||||
|
||||
if (lop?.type === 'index' && lop !== currentOp ) {
|
||||
// console.log('post index first point', point);
|
||||
camOut(point.clone().setZ(lastPoint.z).setA(lastPoint.a), 1);
|
||||
}
|
||||
|
||||
let {
|
||||
center = {},
|
||||
clockwise = true,
|
||||
|
|
@ -342,14 +358,12 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
factor = 1,
|
||||
} = opts ?? {}
|
||||
|
||||
// console.log({radius})
|
||||
const isArc = emit == 2 || emit == 3;
|
||||
const pointA = point.a;
|
||||
|
||||
// apply widget movement pos
|
||||
const pointA = point.a;
|
||||
point = applyWidgetMovement(point);
|
||||
|
||||
// console.log(point.z);
|
||||
if (nextIsMove) {
|
||||
emit = 0;
|
||||
nextIsMove = false;
|
||||
|
|
@ -370,7 +384,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
for (let a of lerp) {
|
||||
let lp = point.clone().setA(a);
|
||||
// console.log(lp.a, lp.x, lp.y, lp.z);
|
||||
lastPoint = layerPush(
|
||||
layerPush(
|
||||
lp,
|
||||
emit,
|
||||
rate,
|
||||
|
|
@ -403,16 +417,20 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
layerPush(point.clone().setZ(stockz + 1), 0, 0, tool);
|
||||
// change to cutting move for remainder of plunge
|
||||
emit = 1;
|
||||
isMove = false;
|
||||
}
|
||||
|
||||
// drop points too close together
|
||||
if (!isLathe && !isArc && deltaXY < 0.001 && point.z === lastPoint.z) {
|
||||
if (!isLathe && !isArc && deltaXY < 0.001 && point.z === lastPoint.z && point.a === lastPoint.a) {
|
||||
// console.trace(["drop dup",lastPoint,point]);
|
||||
return;
|
||||
}
|
||||
|
||||
// no jump moves in contour mode to adjacent slice points
|
||||
let steady = lastPoint && currentOp.type === 'contour' && Math.abs(point.slice - lastPoint.slice) < 4;
|
||||
|
||||
// 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 isflat = absDeltaZ < 0.001;
|
||||
// restrict this to contouring
|
||||
|
|
@ -431,7 +449,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
} else if (point.z < lastPoint.z) {
|
||||
layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool);
|
||||
}
|
||||
} else if (isMove) {
|
||||
} else if (isMove && !steady) {
|
||||
// for longer moves, check the terrain to see if we need to go up and over
|
||||
const bigXY = (deltaXY > moveLen && !lasering);
|
||||
const bigZ = (deltaZ > toolDiam / 2 && deltaXY > tolerance);
|
||||
|
|
@ -478,13 +496,15 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
}
|
||||
|
||||
// set new plunge rate
|
||||
let tmprate;
|
||||
if (false)
|
||||
if (!lasering && !isLathe && deltaZ < -tolerance) {
|
||||
let threshold = Math.min(deltaXY / 2, absDeltaZ),
|
||||
modifier = threshold / absDeltaZ;
|
||||
if (synthPlunge && threshold && modifier && deltaXY > tolerance) {
|
||||
// use modifier to speed up long XY move plunge rates
|
||||
// console.log('modifier', modifier);
|
||||
rate = Math.max(
|
||||
tmprate = Math.max(
|
||||
plungeRate,
|
||||
Math.round(plungeRate + ((feedRate - plungeRate) * modifier))
|
||||
);
|
||||
|
|
@ -492,7 +512,8 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
let L = Math.hypot(deltaXY, absDeltaZ);
|
||||
let limXY = feedRate * L / deltaXY;
|
||||
let limZ = plungeRate * L / Math.abs(deltaZ);
|
||||
rate = Math.min(feedRate, limXY, limZ);
|
||||
tmprate = Math.min(feedRate, limXY, limZ);
|
||||
console.log({ rate_override: rate, was: rate });
|
||||
// let zps = len / absDeltaZ;
|
||||
// rate = Math.max(
|
||||
// plungeRate,
|
||||
|
|
@ -508,7 +529,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
lastPoint = layerPush(
|
||||
point,
|
||||
clockwise ? 2 : 3,
|
||||
rate,
|
||||
tmprate ?? rate,
|
||||
tool,
|
||||
{
|
||||
center,
|
||||
|
|
@ -523,7 +544,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
lastPoint = layerPush(
|
||||
point,
|
||||
emit,
|
||||
rate,
|
||||
tmprate ?? rate,
|
||||
tool
|
||||
);
|
||||
}
|
||||
|
|
@ -533,16 +554,15 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
* output an array of slices that form a pocket
|
||||
* used by rough and pocket ops, does not support arcs
|
||||
*
|
||||
* @param {Slice[]} top-down Z stack of slices
|
||||
* @param {*} opts
|
||||
* @param {Slice[]} slices top-down Z stack of slices
|
||||
* @param {boolean} cutdir true=CW false=CCW
|
||||
* @param {boolean} depthFirst prioritize cut depth in pockets by nesting
|
||||
*/
|
||||
function sliceOutput(sliceOut, opts = {}) {
|
||||
const { cutdir, depthFirst, easeDown, progress } = opts;
|
||||
|
||||
function pocket({ slices, cutdir, depthFirst, easeDown, progress }) {
|
||||
let total = 0;
|
||||
let depthData = [];
|
||||
|
||||
for (let slice of sliceOut) {
|
||||
for (let slice of slices) {
|
||||
let polys = [], t = [], c = [];
|
||||
POLY.flatten(slice.camLines).forEach(function (poly) {
|
||||
let child = poly.parent;
|
||||
|
|
@ -569,7 +589,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
}, { swapdir: false });
|
||||
newLayer();
|
||||
}
|
||||
progress(++total, sliceOut.length);
|
||||
progress(++total, slices.length);
|
||||
}
|
||||
|
||||
// crucially returns true for -0 as well as other negative #s
|
||||
|
|
@ -663,44 +683,32 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
// make top start offset configurable
|
||||
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 = {
|
||||
stock,
|
||||
setTool,
|
||||
setDrill,
|
||||
setSpindle,
|
||||
setTolerance,
|
||||
setPrintPoint,
|
||||
setLasering,
|
||||
getPrintPoint() { return printPoint },
|
||||
printPoint,
|
||||
newLayer,
|
||||
addGCode,
|
||||
bounds,
|
||||
camOut,
|
||||
polyEmit,
|
||||
poly2polyEmit,
|
||||
tip2tipEmit,
|
||||
depthRoughPath,
|
||||
depthOutlinePath,
|
||||
sliceOutput,
|
||||
depthRoughPath,
|
||||
emitDrills,
|
||||
emitTrace,
|
||||
bounds,
|
||||
getPrintPoint() { return printPoint },
|
||||
lastPoint: () => { return lastPoint },
|
||||
newLayer,
|
||||
pocket,
|
||||
poly2polyEmit,
|
||||
polyEmit,
|
||||
printPoint,
|
||||
setDrill,
|
||||
setLasering,
|
||||
setPrintPoint,
|
||||
setSpindle,
|
||||
setTolerance,
|
||||
setTool,
|
||||
stock,
|
||||
tip2tipEmit,
|
||||
zclear,
|
||||
zmax,
|
||||
lastPoint: () => { return lastPoint }
|
||||
zsafe,
|
||||
};
|
||||
|
||||
let opSum = 0;
|
||||
|
|
@ -973,8 +981,8 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
|
||||
|
||||
let arcPoints = arcToPath(from, to, arcPreviewRes, { clockwise, center })
|
||||
.map(applyWidgetMovement);
|
||||
|
||||
.map(applyWidgetMovement);
|
||||
|
||||
// console.log({arcPoints})
|
||||
|
||||
camOut(from, 1);
|
||||
|
|
@ -1096,7 +1104,10 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
|||
if (lastPoint && newOutput.length) {
|
||||
let lastLayer = newOutput.filter(layer => Array.isArray(layer)).peek();
|
||||
if (Array.isArray(lastLayer)) {
|
||||
print.addOutput(lastLayer, printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool);
|
||||
printPoint = lastPoint.clone();
|
||||
if (printPoint.z < zmax_outer) printPoint.setZ(zmax_outer);
|
||||
print.addOutput(lastLayer, printPoint, 0, 0, tool);
|
||||
// print.addOutput(lastLayer, printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool);
|
||||
}
|
||||
}
|
||||
// console.log("prepare output", newOutput);
|
||||
|
|
|
|||
|
|
@ -119,6 +119,10 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
|||
ondone(msg);
|
||||
}
|
||||
|
||||
function alert(msg) {
|
||||
onupdate(null,null,msg);
|
||||
}
|
||||
|
||||
if (unsafe) {
|
||||
console.log("disabling overhang safeties");
|
||||
}
|
||||
|
|
@ -135,20 +139,20 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
|||
let maxDelta = 1e-3;
|
||||
|
||||
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)) {
|
||||
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)) {
|
||||
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) {
|
||||
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();
|
||||
|
|
@ -158,31 +162,31 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
|||
let shadows = {};
|
||||
let slicer;
|
||||
let state = {
|
||||
settings,
|
||||
widget,
|
||||
addSlices,
|
||||
bounds,
|
||||
tabs,
|
||||
cutTabs,
|
||||
cutPolys,
|
||||
color,
|
||||
computeShadows,
|
||||
contourPolys,
|
||||
cutPolys,
|
||||
cutTabs,
|
||||
dark,
|
||||
healPolys,
|
||||
isIndexed,
|
||||
ops: opList,
|
||||
setAxisIndex,
|
||||
settings,
|
||||
shadowAt,
|
||||
slicer,
|
||||
addSlices,
|
||||
isIndexed,
|
||||
setAxisIndex,
|
||||
updateToolDiams,
|
||||
tabs,
|
||||
unsafe,
|
||||
updateSlicer,
|
||||
computeShadows,
|
||||
updateToolDiams,
|
||||
widget,
|
||||
zBottom,
|
||||
zMax,
|
||||
zThru,
|
||||
ztOff,
|
||||
zMax,
|
||||
zTop,
|
||||
unsafe,
|
||||
color,
|
||||
dark,
|
||||
ops: opList
|
||||
zTop
|
||||
};
|
||||
let tracker = setSliceTracker({ rotation: 0 });
|
||||
|
||||
|
|
@ -310,8 +314,11 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
|||
workover.bottom_z = isIndexed ? valz.ov_botz : bottom_stock + valz.ov_botz;
|
||||
workover.bottom_cut = Math.max(workover.bottom_z, -zThru);
|
||||
}
|
||||
// state.workarea = workover;
|
||||
let { note, type } = op.op;
|
||||
let named = note ? note.split(' ').filter(v => v.charAt(0) === '#') : [];
|
||||
let layername = named.length ? named : (note ? `${type} (${note})` : type);
|
||||
Object.assign(state, {
|
||||
layername,
|
||||
zBottom,
|
||||
zThru,
|
||||
ztOff,
|
||||
|
|
@ -688,7 +695,7 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
|||
let opts = { each: onEach, progress: (num, total) => onProgress(num / total * 0.5, "slice") };
|
||||
await slicer.slice(indices, opts);
|
||||
let shadowedDrills = false;
|
||||
// console.log("slices",slices)
|
||||
|
||||
for (let [i, slice] of slices.entries()) {
|
||||
for (let top of slice.tops) {
|
||||
// console.log("slicing",slice.z,top)
|
||||
|
|
@ -698,13 +705,13 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
|||
continue;
|
||||
}
|
||||
for (let poly of inner) {
|
||||
if (poly.points.length < 7) continue;
|
||||
|
||||
if (poly.points.length < 7) {
|
||||
continue;
|
||||
}
|
||||
let center = poly.calcCircleCenter();
|
||||
center.area = poly.area();
|
||||
center.overlapping = [center];
|
||||
center.depth = 0;
|
||||
// console.log("center",center)
|
||||
if (poly.circularity() < 0.98) {
|
||||
// if not circular, don't add to holes
|
||||
continue;
|
||||
|
|
@ -717,8 +724,7 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
|||
let overlap = false;
|
||||
for (let [i, circle] of circles.entries()) {
|
||||
let dist = circle.distTo2D(center);
|
||||
|
||||
// //if on the same xy point,
|
||||
// if on the same xy point,
|
||||
if (dist <= centerDiff) {
|
||||
// console.log("overlap",center,circle);
|
||||
circle.overlapping.push(center);
|
||||
|
|
@ -732,12 +738,10 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
|||
}
|
||||
onProgress(0.5 + (i / slices.length * 0.25), "recognize circles");
|
||||
}
|
||||
|
||||
let drills = [];
|
||||
|
||||
for (let [i, c] of circles.entries()) {
|
||||
let overlapping = c.overlapping;
|
||||
|
||||
let last = overlapping.shift();
|
||||
while (overlapping.length) {
|
||||
let circ = overlapping.shift();
|
||||
|
|
@ -775,14 +779,33 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
|||
return drills;
|
||||
}
|
||||
|
||||
function cutTabs(tabs, offset, z, inter) {
|
||||
tabs = tabs.filter(tab => z < tab.pos.z + tab.dim.z / 2).map(tab => tab.off).flat();
|
||||
return cutPolys(tabs, offset, z, false);
|
||||
function cutTabs(tabs, offset) {
|
||||
let out = [];
|
||||
for (let tab of tabs) {
|
||||
tab.top = tab.pos.z + tab.dim.z / 2;
|
||||
}
|
||||
for (let poly of offset) {
|
||||
let polyZ = poly.getZ();
|
||||
let cut = tabs.filter(tab => tab.top >= polyZ);
|
||||
let lo = poly.cut(cut.map(tab => tab.off).flat(), false);
|
||||
let hi = [];
|
||||
for (let tab of cut) {
|
||||
hi.appendAll(POLY.setZ(poly.cut(tab.off, true), tab.top));
|
||||
}
|
||||
if (hi.length) {
|
||||
let heal = healPolys([ ...lo, ...hi ], false);
|
||||
POLY.setWinding(heal, poly.isClockwise());
|
||||
out.push(...heal);
|
||||
} else {
|
||||
out.push(poly);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function cutPolys(polys, offset, z, inter) {
|
||||
function cutPolys(polys, offset) {
|
||||
let noff = [];
|
||||
offset.forEach(op => noff.appendAll(op.cut(POLY.union(polys, 0, true), inter)));
|
||||
offset.forEach(op => noff.appendAll( op.cut(POLY.union(polys, 0, true), true) ));
|
||||
return healPolys(noff);
|
||||
}
|
||||
|
||||
|
|
@ -799,7 +822,17 @@ function contourPolys(widget, polys) {
|
|||
}
|
||||
}
|
||||
|
||||
function healPolys(noff) {
|
||||
function dedup(arr, same) {
|
||||
const out = [];
|
||||
let prev;
|
||||
for (const x of arr) {
|
||||
if (!prev || !same(prev, x)) out.push(x);
|
||||
prev = x;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function healPolys(noff, sameZ = true) {
|
||||
for (let p of noff) {
|
||||
if (p.appearsClosed()) {
|
||||
p.points.pop();
|
||||
|
|
@ -818,29 +851,29 @@ function healPolys(noff) {
|
|||
let s2 = ntmp[j];
|
||||
if (!s2) continue;
|
||||
// require polys at same Z to heal
|
||||
if (Math.abs(s1.getZ() - s2.getZ()) > 0.01) {
|
||||
if (sameZ && Math.abs(s1.getZ() - s2.getZ()) > 0.01) {
|
||||
continue;
|
||||
}
|
||||
if (!(s1.open && s2.open)) continue;
|
||||
if (s1.last().isMergable2D(s2.first())) {
|
||||
s1.addPoints(s2.points.slice(1));
|
||||
s1.addPoints(s2.points);
|
||||
ntmp[j] = null;
|
||||
continue outer;
|
||||
}
|
||||
if (s2.last().isMergable2D(s1.first())) {
|
||||
s2.addPoints(s1.points.slice(1));
|
||||
s2.addPoints(s1.points);
|
||||
ntmp[i] = null;
|
||||
continue outer;
|
||||
}
|
||||
if (s1.first().isMergable2D(s2.first())) {
|
||||
s1.reverse();
|
||||
s1.addPoints(s2.points.slice(1));
|
||||
s1.addPoints(s2.points);
|
||||
ntmp[j] = null;
|
||||
continue outer;
|
||||
}
|
||||
if (s1.last().isMergable2D(s2.last())) {
|
||||
s2.reverse();
|
||||
s1.addPoints(s2.points.slice(1));
|
||||
s1.addPoints(s2.points);
|
||||
ntmp[j] = null;
|
||||
continue outer;
|
||||
}
|
||||
|
|
@ -854,9 +887,14 @@ function healPolys(noff) {
|
|||
}
|
||||
// close poly if head meets tail
|
||||
for (let poly of noff) {
|
||||
if (poly.open && poly.first().isMergable2D(poly.last())) {
|
||||
poly.points.pop();
|
||||
poly.open = false;
|
||||
poly.points = dedup(poly.points, (a,b) => a.isMergable3D(b));
|
||||
if (poly.open) {
|
||||
if (poly.first().isMergable3D(poly.last())) {
|
||||
poly.points.pop();
|
||||
poly.open = false;
|
||||
} else if (poly.first().isMergable2D(poly.last())) {
|
||||
poly.open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const { sliceConnect, sliceDedup } = slicer;
|
|||
const { config } = base;
|
||||
const timing = false;
|
||||
const zDecimal = 3;
|
||||
const epsilon = 10e-5;
|
||||
|
||||
const begin = function () {
|
||||
if (timing) console.time(...arguments);
|
||||
|
|
@ -94,7 +95,7 @@ export class Slicer {
|
|||
countZ(p3.z);
|
||||
}
|
||||
// use co-flat and co-line detection to adjust slice Z
|
||||
if (p1.z === p2.z && p2.z === p3.z) {
|
||||
if (Math.abs(p1.z - p2.z) < epsilon && Math.abs(p2.z - p3.z) < epsilon) {
|
||||
// detect zFlat faces to avoid slicing directly on them
|
||||
let zkey = p1.z.toFixed(zDecimal),
|
||||
area = Math.abs(util.area2(p1, p2, p3)) / 2;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@ export class Topo {
|
|||
|
||||
async generate(opt = {}) {
|
||||
let { state, contour, onupdate, ondone } = opt;
|
||||
let { widget, settings, tshadow, center, tabs, color } = opt.state;
|
||||
let { widget, settings, tshadow, tabs } = opt.state;
|
||||
|
||||
let { controller, process } = settings,
|
||||
{ webGPU } = controller,
|
||||
animesh = parseInt(controller.animesh || 100) * 2500,
|
||||
axis = contour.axis.toLowerCase(),
|
||||
contourX = axis === "x",
|
||||
|
|
@ -37,16 +38,15 @@ export class Topo {
|
|||
boundsY = maxY - minY,
|
||||
inside = contour.inside,
|
||||
density = 1 + (contour.reduction || 0),
|
||||
resolution = tolerance ? tolerance : 1 / Math.sqrt(animesh / (boundsX * boundsY)),
|
||||
resolution = (tolerance ? tolerance : 1 / Math.sqrt(animesh / (boundsX * boundsY))).round(5),
|
||||
tool = new Tool(settings, contour.tool),
|
||||
toolOffset = tool.generateProfile(resolution).profile,
|
||||
toolDiameter = tool.fluteDiameter(),
|
||||
toolStep = toolDiameter * contour.step, //tool.contourOffset(contour.step),
|
||||
toolStep = toolDiameter * contour.step,
|
||||
leave = contour.leave || 0,
|
||||
maxangle = contour.angle,
|
||||
curvesOnly = contour.curves,
|
||||
bridge = contour.bridging || 0,
|
||||
// R2A = 180 / Math.PI,
|
||||
stepsX = Math.ceil(boundsX / resolution),
|
||||
stepsY = Math.ceil(boundsY / resolution),
|
||||
widtopo = widget.topo,
|
||||
|
|
@ -75,12 +75,7 @@ export class Topo {
|
|||
clipTo = inside ? shadow : POLY.expand(shadow, toolDiameter / 2 + resolution * 3),
|
||||
partOff = inside ? 0 : toolDiameter / 2 + resolution,
|
||||
gridDelta = Math.floor(partOff / resolution),
|
||||
debug = false,
|
||||
debug_clips = debug && true,
|
||||
debug_topo = debug && true,
|
||||
debug_topo_lines = debug && true,
|
||||
debug_topo_shells = debug && true,
|
||||
time = Date.now;
|
||||
debug_clips = false;
|
||||
|
||||
if (tolerance === 0 && !topoCache) {
|
||||
console.log(widget.id, 'topo auto tolerance', resolution.round(4));
|
||||
|
|
@ -90,7 +85,7 @@ export class Topo {
|
|||
// used by Pocket -> Contour
|
||||
this.tolerance = resolution;
|
||||
|
||||
if (tabs) {
|
||||
if (tabsOn) {
|
||||
clipTab.appendAll(tabs.map(tab => {
|
||||
let ctab = POLY.expand([tab.poly], toolDiameter / 2);
|
||||
ctab.forEach(ct => ct.z = tab.dim.z / 2 + tab.pos.z);
|
||||
|
|
@ -107,6 +102,190 @@ export class Topo {
|
|||
newslices.push(debug);
|
||||
}
|
||||
|
||||
if (webGPU && !contour.nogpu) {
|
||||
// invert tool Z offset for gpu code
|
||||
let toolBounds = new THREE.Box3()
|
||||
.expandByPoint({ x: -toolDiameter/2, y: -toolDiameter/2, z: 0 })
|
||||
.expandByPoint({ x: toolDiameter/2, y: toolDiameter/2, z: 0 });
|
||||
let toolPos = tool.profile.slice();
|
||||
for (let i=0; i<toolPos.length; i+= 3) {
|
||||
// toolPos[i+2] = -toolPos[i+2];
|
||||
toolBounds.expandByPoint({ x: toolPos[i], y: toolPos[i+1], z: toolPos[i+2] });
|
||||
}
|
||||
let toolData = { positions: toolPos, bounds: toolBounds };
|
||||
|
||||
const vertices = widget.getGeoVertices({ unroll: true, translate: true });
|
||||
const wbounds = widget.getBoundingBox();
|
||||
if (!inside) {
|
||||
wbounds.expandByVector({ x: toolDiameter/2, y: toolDiameter/2, z: 0 });
|
||||
}
|
||||
|
||||
if (contourY) {
|
||||
// console.time('swap XY vertices');
|
||||
// swap XY vertices
|
||||
for (let i=0; i<vertices.length; i+= 3) {
|
||||
let tmp = vertices[i+1];
|
||||
vertices[i+1] = vertices[i+0];
|
||||
vertices[i+0] = tmp;
|
||||
}
|
||||
['min','max'].forEach(ext => {
|
||||
ext = wbounds[ext];
|
||||
let tmp = ext.x;
|
||||
ext.x = ext.y;
|
||||
ext.y = tmp;
|
||||
});
|
||||
// console.timeEnd('swap XY vertices');
|
||||
}
|
||||
|
||||
let trace = contour.trace;
|
||||
let gpu = await self.get_raster_gpu({
|
||||
mode: trace ? "tracing" : "planar",
|
||||
resolution
|
||||
});
|
||||
let xStep = density;
|
||||
let yStep = Math.ceil(toolStep / resolution);
|
||||
let epsilon = 10e-4;
|
||||
await gpu.loadTool({
|
||||
sparseData: toolData
|
||||
});
|
||||
let terrain = await gpu.loadTerrain({
|
||||
triangles: vertices,
|
||||
boundsOverride: wbounds
|
||||
});
|
||||
let { gridWidth, positions } = terrain;
|
||||
let output = await gpu.generateToolpaths({
|
||||
xStep,
|
||||
yStep,
|
||||
zFloor: zBottom - 1,
|
||||
onProgress(pct) { console.log({ pct }); onupdate(pct/100, 100) }
|
||||
});
|
||||
gpu.terminate();
|
||||
|
||||
let { numScanlines, pointsPerLine, pathData } = output;
|
||||
let xmult = xStep * resolution;
|
||||
let ymult = yStep * resolution;
|
||||
let xoff = wbounds.min.x;
|
||||
let yoff = wbounds.min.y;
|
||||
let slices = [];
|
||||
function calcPoint(x, y, z, tx, ty) {
|
||||
let p = newPoint(x, y, z);
|
||||
p.tx = tx;
|
||||
p.ty = ty;
|
||||
return p;
|
||||
}
|
||||
for (let i=0; i<numScanlines; i++) {
|
||||
let lineStart = i * pointsPerLine;
|
||||
let lineData = pathData.slice(lineStart, lineStart + pointsPerLine);
|
||||
let points = contourY
|
||||
? Array.from(lineData).map((v,j) => calcPoint(i * ymult + yoff, j * xmult + xoff, v, i * xStep, j * yStep))
|
||||
: Array.from(lineData).map((v,j) => calcPoint(j * xmult + xoff, i * ymult + yoff, v, j * xStep, i * yStep))
|
||||
;
|
||||
let slice = newSlice(i);
|
||||
let poly = newPolygon().setOpen();
|
||||
let lines = [ ];
|
||||
let skip = 0;
|
||||
let lp;
|
||||
for (let p of points) {
|
||||
if (inside && positions[p.ty * gridWidth + p.tx] < zBottom - epsilon) {
|
||||
if (poly.length > 1) {
|
||||
lines.push(poly);
|
||||
poly = newPolygon().setOpen();
|
||||
} else if (poly.length === 1) {
|
||||
poly = newPolygon().setOpen();
|
||||
}
|
||||
lp = undefined;
|
||||
skip = 0;
|
||||
continue;
|
||||
}
|
||||
if (p.z < zBottom - epsilon) {
|
||||
lp = p;
|
||||
if (poly.length === 0) {
|
||||
skip++;
|
||||
continue;
|
||||
}
|
||||
if (poly.length > 1) {
|
||||
skip = 0;
|
||||
p.z = zBottom;
|
||||
poly.push(p);
|
||||
lines.push(poly);
|
||||
poly = newPolygon().setOpen();
|
||||
continue;
|
||||
}
|
||||
} else if (lp && skip && poly.length === 0) {
|
||||
lp.z = zBottom;
|
||||
poly.push(lp);
|
||||
}
|
||||
poly.push(lp = p);
|
||||
skip = 0;
|
||||
}
|
||||
// require two points or more
|
||||
if (poly.length > 1) {
|
||||
lines.push(poly);
|
||||
}
|
||||
// raize output points when inside tab boundaries
|
||||
if (!inside && tabsOn && clipTab.length)
|
||||
for (let poly of lines) {
|
||||
for (let p of poly.points) {
|
||||
for (let clip of clipTab) {
|
||||
if (p.z < clip.z && p.isInPolygon(clip)) {
|
||||
p.z = clip.z;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// drop interior points along a continuous slope
|
||||
for (let poly of lines) {
|
||||
let { points } = poly;
|
||||
let merged = [ points[0] ];
|
||||
let lp = points[1];
|
||||
let dz = points[0].z - lp.z;
|
||||
for (let i=2; i<points.length; i++) {
|
||||
let np = points[i];
|
||||
let ndz = np.z - lp.z;
|
||||
if (Math.abs(dz - ndz) > epsilon) {
|
||||
// slope changed
|
||||
merged.push(lp);
|
||||
dz = ndz;
|
||||
} else if (curvesOnly && Math.abs(ndz) < epsilon) {
|
||||
// add empty points as path separators
|
||||
merged.push(undefined);
|
||||
}
|
||||
lp = np;
|
||||
}
|
||||
if (merged.peek() !== lp) {
|
||||
merged.push(lp);
|
||||
}
|
||||
poly.points = merged;
|
||||
}
|
||||
// drop flat regions when enabled
|
||||
if (curvesOnly) {
|
||||
let nulines = [];
|
||||
for (let poly of lines) {
|
||||
let { points } = poly;
|
||||
let newp = [];
|
||||
for (let p of points) {
|
||||
if (p) {
|
||||
newp.push(p);
|
||||
} else if (newp.length) {
|
||||
nulines.push(newPolygon(newp).setOpen());
|
||||
newp = [];
|
||||
}
|
||||
}
|
||||
nulines.push(newPolygon(newp).setOpen());
|
||||
}
|
||||
lines = nulines.filter(p => p.perimeter() > toolStep);
|
||||
}
|
||||
slice.camLines = lines;
|
||||
slices.push(slice);
|
||||
if (inside) {
|
||||
onupdate(i, numScanlines);
|
||||
}
|
||||
}
|
||||
ondone(slices);
|
||||
return this;
|
||||
}
|
||||
|
||||
const probe = this.probe = new Probe({
|
||||
profile: toolOffset,
|
||||
data,
|
||||
|
|
@ -177,8 +356,7 @@ export class Topo {
|
|||
clipTabZ: clipTab ? clipTab.map(t => t.z) : undefined,
|
||||
tabHeight,
|
||||
newslices,
|
||||
leave,
|
||||
color
|
||||
leave
|
||||
}, (i, l, p) => {
|
||||
onupdate(l / 2 + i / 2, l, p);
|
||||
});
|
||||
|
|
@ -304,7 +482,7 @@ export class Topo {
|
|||
|
||||
const { minX, maxX, minY, maxY, boundsX, boundsY, stepsX, stepsY } = params;
|
||||
const { gridDelta, resolution, density, partOff, toolStep, contourX, contourY } = params;
|
||||
const { clipTo, clipTab, clipTabZ, tabHeight, newslices, color, leave } = params;
|
||||
const { clipTo, clipTab, clipTabZ, tabHeight, newslices, leave } = params;
|
||||
|
||||
let stepsTaken = 0,
|
||||
stepsTotal = 0;
|
||||
|
|
@ -370,11 +548,8 @@ export class Topo {
|
|||
gridy
|
||||
}, segments => {
|
||||
if (segments.length > 0) {
|
||||
let slice = newSlice(gridx);
|
||||
let slice = newSlice(x);
|
||||
slice.camLines = segments;
|
||||
slice.output()
|
||||
.setLayer("contour y", { face: color, line: color })
|
||||
.addPolys(segments);
|
||||
slicesY.push(slice);
|
||||
}
|
||||
onupdate(++stepsTaken, stepsTotal, "contour y");
|
||||
|
|
@ -399,11 +574,8 @@ export class Topo {
|
|||
gridy
|
||||
}, segments => {
|
||||
if (segments.length > 0) {
|
||||
let slice = newSlice(gridy);
|
||||
let slice = newSlice(y);
|
||||
slice.camLines = segments;
|
||||
slice.output()
|
||||
.setLayer("contour x", { face: color, line: color })
|
||||
.addPolys(segments);
|
||||
slicesX.push(slice);
|
||||
}
|
||||
onupdate(++stepsTaken, stepsTotal, "contour x");
|
||||
|
|
@ -534,9 +706,9 @@ export class Trace {
|
|||
console.log(...arguments);
|
||||
}
|
||||
|
||||
const push_point = this.push_point = function (x, y, z) {
|
||||
this.push_point = function (x, y, z) {
|
||||
const newP = newPoint(x, y, z);
|
||||
const lastP = lastPP;//trace.last();
|
||||
const lastP = lastPP;
|
||||
|
||||
if (lastP) {
|
||||
const dl = (x - lastP.x) || (y - lastP.y);
|
||||
|
|
@ -553,7 +725,6 @@ export class Trace {
|
|||
}
|
||||
if (curvesOnly) {
|
||||
const dv = contourX ? Math.abs(lastP.x - x) : Math.abs(lastP.y - y);
|
||||
// const dz = lastPP.z - z;
|
||||
const angle = Math.atan2(Math.abs(dz), dv) * RAD2DEG;
|
||||
if (angle > maxangle) {
|
||||
end_poly();
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ export class Topo {
|
|||
|
||||
async generate(opt = {}) {
|
||||
let { state, op, onupdate, ondone } = opt;
|
||||
let { widget, settings, tabs } = opt.state;
|
||||
let { widget, settings, tabs, color } = state;
|
||||
let { controller, process } = settings;
|
||||
let { webGPU } = controller;
|
||||
|
||||
let axis = op.axis.toLowerCase(),
|
||||
tool = new Tool(settings, op.tool),
|
||||
|
|
@ -38,9 +39,8 @@ export class Topo {
|
|||
x: axis === "x",
|
||||
y: axis === "y"
|
||||
},
|
||||
zMin = min.z + 0.0001,
|
||||
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,
|
||||
angle = this.angle = op.angle || 1;
|
||||
|
||||
|
|
@ -52,31 +52,36 @@ export class Topo {
|
|||
console.log(widget.id, 'topo4 auto tolerance', resolution.round(4));
|
||||
}
|
||||
|
||||
this.zBottom = state.zBottom;
|
||||
this.zBottom = state.zBottom ?? 0;
|
||||
this.resolution = resolution;
|
||||
this.vertices = widget.getGeoVertices({ unroll: true, translate: true });
|
||||
this.tabverts = widget.getTabVertices();
|
||||
this.tool = tool.generateProfile(resolution).profile;
|
||||
this.maxo = tool.profileDim.maxo * resolution;
|
||||
this.diam = tool.fluteDiameter();
|
||||
this.unit = tool.unitScale();
|
||||
this.units = controller.units === 'in' ? 25.4 : 1
|
||||
this.zoff = widget.track.top || 0;
|
||||
this.leave = op.leave || 0;
|
||||
this.linear = op.linear || false;
|
||||
this.lineColor = state.settings.controller.dark ? 0xffff00 : 0x555500;
|
||||
this.lineColor = color;//controller.dark ? 0xffff00 : 0x555500;
|
||||
this.offStart = op.offStart ?? 0;
|
||||
this.offEnd = op.offEnd ?? 0;
|
||||
this.bounds = bounds;
|
||||
this.gpu = webGPU ?? false;
|
||||
|
||||
onupdate(0, "lathe");
|
||||
|
||||
const parts = webGPU ? [ 0.8, 0.2 ] : [ 0.25, 0.75 ];
|
||||
const range = this.range = { min: Infinity, max: -Infinity };
|
||||
const slices = this.sliced = await this.slice(scale(onupdate, 0.25, 0));
|
||||
const slices = this.sliced = await this.slice(scale(onupdate, parts[0], 0));
|
||||
|
||||
for (let slice of slices) {
|
||||
range.min = Math.min(range.min, slice.z);
|
||||
range.max = Math.max(range.max, slice.z);
|
||||
slice.output()
|
||||
.setLayer("lathe", { line: 0x888888 })
|
||||
.addPolys(slice.topPolys());
|
||||
}
|
||||
|
||||
const lathe = await this.lathe(scale(onupdate, 0.75, 0.25));
|
||||
const lathe = await this.lathe(scale(onupdate, parts[1], parts[0]));
|
||||
|
||||
onupdate(1, "lathe");
|
||||
ondone(lathe);
|
||||
|
|
@ -86,10 +91,9 @@ export class Topo {
|
|||
}
|
||||
|
||||
async slice(onupdate) {
|
||||
const { vertices, resolution, tabverts, zoff } = this;
|
||||
|
||||
const { vertices, resolution, tabverts, zoff, offStart, offEnd, units } = this;
|
||||
const { minions } = self.kiri_worker;
|
||||
const range = this.range = { min: Infinity, max: -Infinity };
|
||||
const box = this.box = new THREE.Box2();
|
||||
|
||||
// swap XZ in shared array
|
||||
for (let i = 0, l = vertices.length; i < l; i += 3) {
|
||||
|
|
@ -101,9 +105,10 @@ export class Topo {
|
|||
range.max = Math.max(range.max, x);
|
||||
}
|
||||
|
||||
// add tool radius to slice min/max range ot fully carve part
|
||||
range.min -= this.diam / 2;
|
||||
range.max += this.diam / 2;
|
||||
// add tool diameter to slice min/max range to fully carve part
|
||||
range.min += offStart * units;
|
||||
range.max -= offEnd * units;
|
||||
range.max += resolution * 2;
|
||||
|
||||
// merge in tab vertices here so they don't affect slice range / dimensions
|
||||
for (let i = 0, l = tabverts.length; i < l; i += 3) {
|
||||
|
|
@ -116,24 +121,36 @@ export class Topo {
|
|||
// re-create shared vertex array for workers
|
||||
this.vertices = [].appendAll(vertices).appendAll(tabverts).toFloat32().toShared();
|
||||
|
||||
const shards = Math.ceil(Math.min(25, vertices.length / 27000));
|
||||
const step = (range.max - range.min) / shards;
|
||||
|
||||
let index = 0;
|
||||
let slices = this.slices = [];
|
||||
let slice = { min: range.min, max: range.min + step, index };
|
||||
|
||||
for (let z = range.min; z < range.max; z += resolution) {
|
||||
if (z > slice.max) {
|
||||
slices.push(slice);
|
||||
slice = { min: z, max: z + step, index };
|
||||
}
|
||||
index++;
|
||||
// rp.rasterizeMesh(vertices, resolution).then(rpo => console.log({ rpo }));
|
||||
if (this.gpu) {
|
||||
return await this.sliceGPU(onupdate);
|
||||
}
|
||||
slices.push(slice);
|
||||
// console.log({ shards, range, step, slices });
|
||||
|
||||
const { minions } = self.kiri_worker;
|
||||
const zSpan = range.max - range.min;
|
||||
const shards = Math.ceil(Math.min(25, vertices.length / 27000));
|
||||
const totalSteps = Math.ceil(zSpan / resolution);
|
||||
const stardSteps = Math.ceil(totalSteps / shards);
|
||||
const stepWidth = stardSteps * resolution;
|
||||
|
||||
let slices = this.slices = [];
|
||||
for (let i=0; i<shards; i++) {
|
||||
let s;
|
||||
slices.push(s = {
|
||||
min: range.min + i * stepWidth,
|
||||
max: Math.min(range.max, range.min + (i+1) * stepWidth),
|
||||
index: i * stardSteps
|
||||
});
|
||||
}
|
||||
|
||||
// console.log({
|
||||
// shards,
|
||||
// step: stepWidth,
|
||||
// range,
|
||||
// resolution,
|
||||
// slices: slices.slice(),
|
||||
// minions
|
||||
// });
|
||||
|
||||
if (minions?.running > 1) {
|
||||
return await this.sliceMinions(onupdate);
|
||||
} else {
|
||||
|
|
@ -141,6 +158,96 @@ export class Topo {
|
|||
}
|
||||
}
|
||||
|
||||
async sliceGPU(onupdate) {
|
||||
const { angle, diam, linear, offStart, offEnd, resolution, tool, units, vertices, zBottom } = this;
|
||||
|
||||
// invert tool Z offset for gpu code
|
||||
let toolBounds = new THREE.Box3()
|
||||
.expandByPoint({ x: -this.diam/2, y: -diam/2, z: 0 })
|
||||
.expandByPoint({ x: this.diam/2, y: diam/2, z: 0 });
|
||||
let toolPos = tool.slice();
|
||||
let minz = Infinity;
|
||||
for (let i=0; i<toolPos.length; i += 3) {
|
||||
// toolPos[i+2] = -toolPos[i+2];
|
||||
toolBounds.expandByPoint({ x: 0, y: 0, z: toolPos[i+2] });
|
||||
minz = Math.min(minz,toolPos[i+2]);
|
||||
}
|
||||
let toolData = { positions: toolPos, bounds: toolBounds };
|
||||
|
||||
// console.time('swap XZ vertices');
|
||||
// swap XZ vertices for gpu code
|
||||
for (let i=0; i<vertices.length; i+= 3) {
|
||||
let tmp = vertices[i+2];
|
||||
vertices[i+2] = vertices[i+0];
|
||||
vertices[i+1] = -vertices[i+1];
|
||||
vertices[i+0] = tmp;
|
||||
}
|
||||
// console.timeEnd('swap XZ vertices');
|
||||
|
||||
let gpu = await self.get_raster_gpu({
|
||||
mode: "radial",
|
||||
resolution,
|
||||
rotationStep: angle,
|
||||
radialRotationOffset: 90
|
||||
});
|
||||
let xStep = Math.max(1, Math.round(this.step / resolution));
|
||||
let boundsOverride = this.bounds.clone();
|
||||
boundsOverride.min.x += offStart * units;
|
||||
boundsOverride.max.x -= offEnd * units;
|
||||
await gpu.loadTool({
|
||||
sparseData: toolData
|
||||
});
|
||||
await gpu.loadTerrain({
|
||||
triangles: vertices,
|
||||
boundsOverride,
|
||||
onProgress(pct) { onupdate(pct/100) }
|
||||
});
|
||||
let output = await gpu.generateToolpaths({
|
||||
xStep,
|
||||
yStep: 1,
|
||||
zFloor: zBottom,
|
||||
onProgress(i,j) { onupdate(i/j) }
|
||||
});
|
||||
gpu.terminate();
|
||||
|
||||
let { numStrips, strips } = output;
|
||||
let degPerRow = 360 / numStrips;
|
||||
let slices = this.gpu_slices = [];
|
||||
let xmult = resolution * xStep;
|
||||
let xoff = boundsOverride.min.x;
|
||||
let rows = [];
|
||||
for (let i=0; i<numStrips; i++) {
|
||||
let points = Array.from(strips[i].pathData).map((v,j) => newPoint(j * xmult + xoff, 0, v).setA(-i * degPerRow));
|
||||
rows.push(points);
|
||||
}
|
||||
if (linear) {
|
||||
for (let i=0; i<rows.length; i++) {
|
||||
let slice = newSlice(i);
|
||||
slice.index = i;
|
||||
slice.camLines = [ newPolygon(rows[i]).setOpen() ];
|
||||
if (i % 2 === 1) slice.camLines[0].reverse();
|
||||
slices.push(slice);
|
||||
}
|
||||
} else {
|
||||
let { pointsPerLine } = strips[0];
|
||||
for (let i=0; i<pointsPerLine; i++) {
|
||||
let slice = newSlice(i);
|
||||
let points = rows.map(row => row[i]);
|
||||
if (i % 2 === 1) points.reverse();
|
||||
slice.index = i;
|
||||
slice.camLines = [ newPolygon(points).setOpen() ];
|
||||
slices.push(slice);
|
||||
}
|
||||
}
|
||||
for (let slice of slices) {
|
||||
slice.output()
|
||||
.setLayer("lathe", { line: this.lineColor })
|
||||
.addPoly(slice.camLines[0].clone().applyRotations());
|
||||
}
|
||||
// console.log({ webGPU: output, slices });
|
||||
return slices;
|
||||
}
|
||||
|
||||
async sliceWorker(onupdate) {
|
||||
const { vertices, slices, resolution } = this;
|
||||
|
||||
|
|
@ -205,22 +312,28 @@ export class Topo {
|
|||
|
||||
async lathe(onupdate) {
|
||||
const { minions } = self.kiri_worker;
|
||||
if (minions?.running > 1) {
|
||||
if (this.gpu) {
|
||||
return await this.latheGPU(onupdate);
|
||||
} else if (minions?.running > 1) {
|
||||
return await this.latheMinions(onupdate);
|
||||
} else {
|
||||
return await this.latheWorker(onupdate);
|
||||
}
|
||||
}
|
||||
|
||||
async latheGPU(onupdate) {
|
||||
return this.gpu_slices;
|
||||
}
|
||||
|
||||
lathePath(slices, tool) {
|
||||
const { resolution, step, zBottom, maxo } = this;
|
||||
|
||||
const tlen = tool.length;
|
||||
const slen = slices.length;
|
||||
const sinc = Math.max(1, Math.ceil(step / resolution));
|
||||
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 = [];
|
||||
for (let slice of slices) {
|
||||
const lines = slice.lines;
|
||||
|
|
@ -244,7 +357,9 @@ export class Topo {
|
|||
// iterate over all slices (real x = z)
|
||||
// find max real z using z ray intersect from tool point to slice lines + offset
|
||||
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;
|
||||
let mz = -Infinity;
|
||||
// iterate over tool offsets
|
||||
|
|
@ -281,12 +396,12 @@ export class Topo {
|
|||
}
|
||||
if (mz === -Infinity && xo === 0 && yo === 0) {
|
||||
// tool tip is off the model
|
||||
continue;
|
||||
// continue;
|
||||
}
|
||||
}
|
||||
if (mz === -Infinity) {
|
||||
mz = zBottom; // Math.min(0, lz)
|
||||
} else if (zBottom && mz < zBottom) {
|
||||
mz = zBottom;
|
||||
} else if (mz < zBottom) {
|
||||
mz = zBottom;
|
||||
}
|
||||
heights.push(rx, 0, lz = mz);
|
||||
|
|
|
|||
Loading…
Reference in a new issue