add optional shadow selection to area op

This commit is contained in:
Stewart Allen 2026-01-01 11:52:20 -05:00
commit 9da2368a7a
5 changed files with 53 additions and 33 deletions

View file

@ -443,6 +443,7 @@ export const conf = {
camAreaExpand: 0,
camAreaRefine: 0,
camAreaSmooth: 1,
camAreaShadow: false,
camAreaDogbones: false,
camAreaRevbones: false,
camAreaOutline: false,

View file

@ -669,6 +669,10 @@ export function createPopOps() {
return env.poppedRec.mode === 'trace';
}
function isShadow() {
return env.poppedRec.shadow;
}
function isSurface() {
return env.poppedRec.mode === 'surface';
}
@ -697,6 +701,7 @@ export function createPopOps() {
follow: 'camAreaFollow',
refine: 'camAreaRefine',
outline: 'camAreaOutline',
shadow: 'camAreaShadow',
tolerance: 'camTolerance',
dogbones: 'camAreaDogbones',
revbones: 'camAreaRevbones',
@ -707,33 +712,39 @@ export function createPopOps() {
tr_type: UC.newSelect(LANG.cc_offs_s, { title: LANG.cc_offs_l, show: isTrace }, "traceoff"),
sr_type: UC.newSelect("pattern", { title: "pattern", show: isSurface }, "surftyp"),
sep: UC.newBlank({ class: "pop-sep" }),
exp: UC.newExpand("area selection", { open }),
menu: UC.newRow([
UC.newButton("edge", traceAdd),
UC.newButton("surface", surfaceAdd),
], { class: "ext-buttons f-row" }),
], { class: "ext-buttons f-row", show: () => !isShadow() }),
shadow: UC.newBoolean(LANG.cp_shad_s, undefined, { title: LANG.cp_shad_l }),
outline: UC.newBoolean(LANG.cp_outl_s, undefined, { title: LANG.cp_outl_l }),
exp_end: UC.endExpand(),
sep: UC.newBlank({ class: "pop-sep" }),
exp: UC.newExpand("area modifiers", { }),
expand: UC.newInput(LANG.cp_xpnd_s, { title: LANG.cp_xpnd_l, convert: toFloat, units }),
smooth: UC.newInput(LANG.cp_smoo_s, { title: LANG.cp_smoo_l, convert: toInt, xshow: isSurface }),
follow: UC.newInput(LANG.cp_foll_s, { title: LANG.cp_foll_l, convert: toFloat }),
tolerance: UC.newInput(LANG.ou_toll_s, { title: LANG.ou_toll_l, convert: toFloat, bound: UC.bound(0, 10.0), units, round: 4, show: isSurface }),
exp_end: UC.endExpand(),
exp: UC.newExpand("tool & stepping", { open }),
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
direction: UC.newSelect(LANG.ou_dire_s, { title: LANG.ou_dire_l }, "direction"),
sr_angle: UC.newInput("step angle", { title: "step angle", convert: toFloat, bound: UC.bound(0, 360), show: isSurfaceLinear }),
sr_angle: UC.newInput(LANG.ca_sang_s, { title: LANG.ca_sang_l, convert: toFloat, bound: UC.bound(0, 360), show: isSurfaceLinear }),
over: UC.newInput(LANG.cc_sovr_s, { title: LANG.cc_sovr_l, convert: toFloat, bound: UC.bound(0.001, 100.0), show: () => isClear() || isSurface() }),
down: UC.newInput(LANG.cc_sdwn_s, { title: LANG.cc_sdwn_l, convert: toFloat, bound: UC.bound(0, 100.0), units, show: () => isClear() || isTrace() }),
refine: UC.newInput(LANG.cp_refi_s, { title: LANG.cp_refi_l, convert: toInt, show: isSurface }),
sr_alter: UC.newBoolean("alternate", undefined, { title: "alternate cutting direction", show: isSurfaceLinear }),
sr_alter: UC.newBoolean(LANG.ca_altr_s, undefined, { title: LANG.ca_altr_l, show: isSurfaceLinear }),
dogbones: UC.newBoolean(LANG.co_dogb_s, undefined, { title: LANG.co_dogb_l, show: isTrace }),
revbones: UC.newBoolean(LANG.co_dogr_s, undefined, { title: LANG.co_dogr_l, show: () => env.poppedRec.dogbones }),
exp_end: UC.endExpand(),
exp: UC.newExpand("feeds & speeds", { open }),
exp: UC.newExpand("feeds & speeds", { }),
sep: UC.newBlank({ class: "pop-sep" }),
spindle: UC.newInput(LANG.cc_spnd_s, { title: LANG.cc_spnd_l, convert: toInt, show: hasSpindle }),
rate: UC.newInput(LANG.cc_feed_s, { title: LANG.cc_feed_l, convert: toInt, units }),
plunge: UC.newInput(LANG.cc_plng_s, { title: LANG.cc_plng_l, convert: toInt, units }),
exp_end: UC.endExpand(),
exp: UC.newExpand("bounds", { open }),
exp: UC.newExpand("bounds", { }),
sep: UC.newBlank({ class: "pop-sep" }),
ov_topz: UC.newInput(LANG.ou_ztop_s, { title: LANG.ou_ztop_l, convert: toFloat, units }),
ov_botz: UC.newInput(LANG.ou_zbot_s, { title: LANG.ou_zbot_l, convert: toFloat, units }),

View file

@ -35,7 +35,7 @@ class OpArea extends CamOp {
let areaTool = new Tool(settings, tool);
let smoothVal = (smooth ?? 0) / 10;
let toolDiam = areaTool.fluteDiameter();
let toolOver = areaTool.getStepSize(over);
let toolOver = this.toolOver = areaTool.getStepSize(over);
let zTop = workarea.top_z;
let zBottom = workarea.bottom_z;
let shadowBase = state.shadow.base;
@ -71,6 +71,7 @@ class OpArea extends CamOp {
}
// gather area selections
if (!op.shadow)
for (let arr of (op.areas[widget.id] ?? [])) {
let poly = newPolygon().fromArray(arr);
aminz = Math.min(aminz, poly.minZ());
@ -86,22 +87,30 @@ class OpArea extends CamOp {
polys = POLY.nest(POLY.reconnect(polys, false));
// gather surface selections
let vert = widget.getGeoVertices({ unroll: true, translate: true }).map(v => v.round(4));
let faces = CAM.surface_find(widget, (op.surfaces[widget.id] ?? []), (follow ?? edgeangle ?? 5) * DEG2RAD);
let fpoly = [];
for (let face of faces) {
let i = face * 9;
fpoly.push(newPolygon()
.add(vert[i++], vert[i++], aminz = Math.min(aminz, vert[i++]))
.add(vert[i++], vert[i++], aminz = Math.min(aminz, vert[i++]))
.add(vert[i++], vert[i++], aminz = Math.min(aminz, vert[i++]))
);
}
// remove invalid edges (eg. when vertical walls are the only selection)
fpoly = fpoly.filter(p => p.area() > 0.001);
if (!op.shadow) {
let vert = widget.getGeoVertices({ unroll: true, translate: true }).map(v => v.round(4));
let faces = CAM.surface_find(widget, (op.surfaces[widget.id] ?? []), (follow ?? edgeangle ?? 5) * DEG2RAD);
let fpoly = [];
for (let face of faces) {
let i = face * 9;
fpoly.push(newPolygon()
.add(vert[i++], vert[i++], aminz = Math.min(aminz, vert[i++]))
.add(vert[i++], vert[i++], aminz = Math.min(aminz, vert[i++]))
.add(vert[i++], vert[i++], aminz = Math.min(aminz, vert[i++]))
);
}
// remove invalid edges (eg. when vertical walls are the only selection)
fpoly = fpoly.filter(p => p.area() > 0.001);
// add in unioned surface areas
polys.push(...POLY.setZ(POLY.union(fpoly, 0.00001, true), aminz));
// add in unioned surface areas
polys.push(...POLY.setZ(POLY.union(fpoly, 0.00001, true), aminz));
}
// use part shadow instead of areas or surfaces
if (op.shadow) {
console.log({ use_shadow_base: op.shadow });
polys.push(...shadowBase.clone(true));
}
// smoothing for jaggies usually caused by vertical walls
if (smoothVal) {
@ -343,7 +352,7 @@ class OpArea extends CamOp {
}
// optional alternating paths
if (sr_alter) {
paths = tip2tipJoin(paths, paths[0].first(), toolDiam * 4);
paths = tip2tipJoin(paths, paths[0].first(), toolOver * 10);
}
} else
if (sr_type === 'offset') {
@ -392,6 +401,7 @@ class OpArea extends CamOp {
raster.terminate();
// convert terrain raster output back to open polylines
// todo: add leave_z support
for (let path of output.paths) {
path = newPolygon().fromArray([1, ...path]);
if (op.refine) path.refine(op.refine);
@ -417,14 +427,14 @@ class OpArea extends CamOp {
}
prepare(ops, progress) {
let { op, state, areas, surfaces } = this;
let { op, state, areas, surfaces, toolOver } = this;
let { newLayer, pocket, polyEmit, printPoint, tip2tipEmit } = ops;
let { setContouring, setNextIsMove } = ops;
let { process } = state.settings;
// process surface paths
if (surfaces.length) {
setContouring(true);
setContouring(true, toolOver * 2);
for (let surface of surfaces) {
for (let poly of surface) {
setNextIsMove();

View file

@ -2,16 +2,12 @@
import { CamOp } from './op.js';
import { OpArea } from './op-area.js';
import { polygons as POLY } from '../../../geo/polygons.js';
import { newPolygon } from '../../../geo/polygon.js';
class OpOutline extends CamOp {
constructor(state, op) {
super(state, op);
}
// todo: wide cutout, dogbones
async slice(progress) {
let { op, state } = this;
let { direction, dogbones, down, inside, omitthru, omitvoid, outside } = op;
@ -26,7 +22,6 @@ class OpOutline extends CamOp {
let ops_list = this.ops_list = [ ];
if (outside) {
// let areas = POLY.flatten(POLY.expand(shadow.base, state.tool.fluteDiameter() / 2 - 0.001));
let areas = shadow.base.clone(true);
ops_list.push(new OpArea(state, {
areas: { [widget.id]: areas.map(p => p.toArray()) },
@ -37,7 +32,6 @@ class OpOutline extends CamOp {
mode: 'trace',
omitinner: omitvoid,
omitthru,
// outline: omitthru,
ov_botz,
ov_topz,
over: op.wide ? op.step : 0,
@ -66,7 +60,6 @@ class OpOutline extends CamOp {
omitinner: omitvoid,
omitouter: inside,
omitthru,
// outline: omitthru,
ov_botz,
ov_topz,
plunge,
@ -80,9 +73,6 @@ class OpOutline extends CamOp {
thru: true,
tr_type: 'outside',
}));
if (!inside) {
// add outline
}
}
for (let op of this.ops_list) {

View file

@ -606,6 +606,8 @@ self.lang['en-us'] = {
cp_cont_l: ["ignore interior voids and features"],
cp_outl_s: "outline only",
cp_outl_l: ["ignore interior voids and features"],
cp_shad_s: "shadow",
cp_shad_l: ["select part shadow as work area. overrides selected edges and surface areas"],
// CNC DRILLING
cd_menu: "drill",
@ -718,6 +720,12 @@ self.lang['en-us'] = {
cl_xpnd_s: "expand",
cl_xpnd_l: ["grow starting radius for step down. radius is defined as the highest Z point measured from the X axis center of the part"],
// CNC AREA OP
ca_altr_s: "alternate",
ca_altr_l: "alternate direction of travel lines (zigzag)",
ca_sang_s: "step angle",
ca_sang_l: "crossing angle on the XY plane for contour lines",
// OUTPUT
ou_menu: "output",