add milling direction per op

This commit is contained in:
Stewart Allen 2025-12-12 12:46:43 -05:00
commit a1880f7dc3
11 changed files with 26 additions and 33 deletions

View file

@ -200,7 +200,6 @@ const renamed = {
finishingTool: "camOutlineTool", finishingTool: "camOutlineTool",
finishingXOn: "camContourXOn", finishingXOn: "camContourXOn",
finishingYOn: "camContourYOn", finishingYOn: "camContourYOn",
outputClockwise: "camConventional",
roughingDown: "camRoughDown", roughingDown: "camRoughDown",
roughingOn: "camRoughOn", roughingOn: "camRoughOn",
roughingOver: "camRoughOver", roughingOver: "camRoughOver",
@ -430,7 +429,6 @@ export const conf = {
camAreaMode: "clear", camAreaMode: "clear",
camAreaTrace: "none", camAreaTrace: "none",
camAreaSurface: "linear", camAreaSurface: "linear",
camAreaDirection: "climb",
camAreaEdgeOnly: false, camAreaEdgeOnly: false,
camAreaAngle: 0, camAreaAngle: 0,
camAreaOver: 0.4, camAreaOver: 0.4,
@ -457,7 +455,6 @@ export const conf = {
camContourTool: 1000, camContourTool: 1000,
camContourXOn: true, camContourXOn: true,
camContourYOn: true, camContourYOn: true,
camConventional: false, // outputClockwise
camDepthFirst: false, camDepthFirst: false,
camDrillDown: 5, camDrillDown: 5,
camDrillDownSpeed: 250, camDrillDownSpeed: 250,
@ -529,6 +526,7 @@ export const conf = {
camLevelStepZ: 0, camLevelStepZ: 0,
camLevelStock: true, camLevelStock: true,
camLevelTool: 1000, camLevelTool: 1000,
camMillDirection: "climb",
camOriginCenter: false, camOriginCenter: false,
camOriginOffX: 0, camOriginOffX: 0,
camOriginOffY: 0, camOriginOffY: 0,

View file

@ -134,7 +134,7 @@ const LISTS = {
direction: [ direction: [
{ name: "climb" }, { name: "climb" },
{ name: "conventional" }, { name: "conventional" },
{ name: "alternating" }, // { name: "alternating" },
], ],
camtool: [ camtool: [
{ name: "flat end", id: "endmill" }, { name: "flat end", id: "endmill" },

View file

@ -218,6 +218,7 @@ export function createPopOps() {
createPopOp('rough', { createPopOp('rough', {
tool: 'camRoughTool', tool: 'camRoughTool',
direction: 'camMillDirection',
spindle: 'camRoughSpindle', spindle: 'camRoughSpindle',
down: 'camRoughDown', down: 'camRoughDown',
step: 'camRoughOver', step: 'camRoughOver',
@ -230,9 +231,9 @@ export function createPopOps() {
omitthru: 'camRoughOmitThru', omitthru: 'camRoughOmitThru',
ov_topz: 0, ov_topz: 0,
ov_botz: 0, ov_botz: 0,
ov_conv: '~camConventional',
}).inputs = { }).inputs = {
tool: UC.newSelect(LANG.cc_tool, {}, "tools"), tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
direction: UC.newSelect(LANG.ou_dire_s, { title: LANG.ou_dire_l }, "direction"),
sep: UC.newBlank({ class: "pop-sep" }), sep: UC.newBlank({ class: "pop-sep" }),
spindle: UC.newInput(LANG.cc_spnd_s, { title: LANG.cc_spnd_l, convert: toInt, show: hasSpindle }), 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 }), rate: UC.newInput(LANG.cc_feed_s, { title: LANG.cc_feed_l, convert: toInt, units }),
@ -250,12 +251,12 @@ export function createPopOps() {
exp: UC.newExpand("overrides"), exp: UC.newExpand("overrides"),
ov_topz: UC.newInput(LANG.ou_ztop_s, { title: LANG.ou_ztop_l, convert: toFloat, units }), 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 }), ov_botz: UC.newInput(LANG.ou_zbot_s, { title: LANG.ou_zbot_l, convert: toFloat, units }),
ov_conv: UC.newBoolean(LANG.ou_conv_s, undefined, { title: LANG.ou_conv_l }),
exp_end: UC.endExpand(), exp_end: UC.endExpand(),
}; };
createPopOp('outline', { createPopOp('outline', {
tool: 'camOutlineTool', tool: 'camOutlineTool',
direction: 'camMillDirection',
spindle: 'camOutlineSpindle', spindle: 'camOutlineSpindle',
step: 'camOutlineOver', step: 'camOutlineOver',
steps: 'camOutlineOverCount', steps: 'camOutlineOverCount',
@ -271,9 +272,9 @@ export function createPopOps() {
wide: 'camOutlineWide', wide: 'camOutlineWide',
ov_topz: 0, ov_topz: 0,
ov_botz: 0, ov_botz: 0,
ov_conv: '~camConventional',
}).inputs = { }).inputs = {
tool: UC.newSelect(LANG.cc_tool, {}, "tools"), tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
direction: UC.newSelect(LANG.ou_dire_s, { title: LANG.ou_dire_l }, "direction"),
sep: UC.newBlank({ class: "pop-sep" }), sep: UC.newBlank({ class: "pop-sep" }),
spindle: UC.newInput(LANG.cc_spnd_s, { title: LANG.cc_spnd_l, convert: toInt, show: hasSpindle }), 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 }), rate: UC.newInput(LANG.cc_feed_s, { title: LANG.cc_feed_l, convert: toInt, units }),
@ -294,7 +295,6 @@ export function createPopOps() {
exp: UC.newExpand("overrides"), exp: UC.newExpand("overrides"),
ov_topz: UC.newInput(LANG.ou_ztop_s, { title: LANG.ou_ztop_l, convert: toFloat, units }), 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 }), ov_botz: UC.newInput(LANG.ou_zbot_s, { title: LANG.ou_zbot_l, convert: toFloat, units }),
ov_conv: UC.newBoolean(LANG.ou_conv_s, undefined, { title: LANG.ou_conv_l }),
exp_end: UC.endExpand(), exp_end: UC.endExpand(),
}; };
@ -385,6 +385,7 @@ export function createPopOps() {
mode: 'camTraceType', mode: 'camTraceType',
offset: 'camTraceOffset', offset: 'camTraceOffset',
spindle: 'camTraceSpindle', spindle: 'camTraceSpindle',
direction: 'camMillDirection',
tool: 'camTraceTool', tool: 'camTraceTool',
step: 'camTraceOver', step: 'camTraceOver',
down: 'camTraceDown', down: 'camTraceDown',
@ -402,6 +403,7 @@ export function createPopOps() {
tool: UC.newSelect(LANG.cc_tool, {}, "tools"), tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
mode: UC.newSelect(LANG.cu_type_s, { title: LANG.cu_type_l }, "trace"), mode: UC.newSelect(LANG.cu_type_s, { title: LANG.cu_type_l }, "trace"),
offset: UC.newSelect(LANG.cc_offs_s, { title: LANG.cc_offs_l, show: () => (env.poppedRec.mode === 'follow') }, "traceoff"), offset: UC.newSelect(LANG.cc_offs_s, { title: LANG.cc_offs_l, show: () => (env.poppedRec.mode === 'follow') }, "traceoff"),
direction: UC.newSelect(LANG.ou_dire_s, { title: LANG.ou_dire_l }, "direction"),
sep: UC.newBlank({ class: "pop-sep" }), sep: UC.newBlank({ class: "pop-sep" }),
spindle: UC.newInput(LANG.cc_spnd_s, { title: LANG.cc_spnd_l, convert: toInt, show: hasSpindle }), 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 }), rate: UC.newInput(LANG.cc_feed_s, { title: LANG.cc_feed_l, convert: toInt, units }),
@ -419,13 +421,13 @@ export function createPopOps() {
sep: UC.newBlank({ class: "pop-sep" }), sep: UC.newBlank({ class: "pop-sep" }),
ov_topz: UC.newInput(LANG.ou_ztop_s, { title: LANG.ou_ztop_l, convert: toFloat, units }), 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 }), ov_botz: UC.newInput(LANG.ou_zbot_s, { title: LANG.ou_zbot_l, convert: toFloat, units }),
ov_conv: UC.newBoolean(LANG.ou_conv_s, undefined, { title: LANG.ou_conv_l }),
exp_end: UC.endExpand(), exp_end: UC.endExpand(),
sep: UC.newBlank({ class: "pop-sep" }), sep: UC.newBlank({ class: "pop-sep" }),
menu: UC.newRow([UC.newButton("select", traceAdd)], { class: "ext-buttons f-row" }), menu: UC.newRow([UC.newButton("select", traceAdd)], { class: "ext-buttons f-row" }),
}; };
createPopOp('pocket', { createPopOp('pocket', {
direction: 'camMillDirection',
spindle: 'camPocketSpindle', spindle: 'camPocketSpindle',
tool: 'camPocketTool', tool: 'camPocketTool',
step: 'camPocketOver', step: 'camPocketOver',
@ -444,6 +446,7 @@ export function createPopOps() {
tolerance: 'camTolerance', tolerance: 'camTolerance',
}).inputs = { }).inputs = {
tool: UC.newSelect(LANG.cc_tool, {}, "tools"), tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
direction: UC.newSelect(LANG.ou_dire_s, { title: LANG.ou_dire_l }, "direction"),
sep: UC.newBlank({ class: "pop-sep" }), sep: UC.newBlank({ class: "pop-sep" }),
spindle: UC.newInput(LANG.cc_spnd_s, { title: LANG.cc_spnd_l, convert: toInt, show: hasSpindle }), 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 }), rate: UC.newInput(LANG.cc_feed_s, { title: LANG.cc_feed_l, convert: toInt, units }),
@ -464,7 +467,6 @@ export function createPopOps() {
sep: UC.newBlank({ class: "pop-sep" }), sep: UC.newBlank({ class: "pop-sep" }),
ov_topz: UC.newInput(LANG.ou_ztop_s, { title: LANG.ou_ztop_l, convert: toFloat, units }), 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 }), ov_botz: UC.newInput(LANG.ou_zbot_s, { title: LANG.ou_zbot_l, convert: toFloat, units }),
ov_conv: UC.newBoolean(LANG.ou_conv_s, undefined, { title: LANG.ou_conv_l }),
exp_end: UC.endExpand(), exp_end: UC.endExpand(),
sep: UC.newBlank({ class: "pop-sep" }), sep: UC.newBlank({ class: "pop-sep" }),
menu: UC.newRow([UC.newButton("select", surfaceAdd)], { class: "ext-buttons f-row" }), menu: UC.newRow([UC.newButton("select", surfaceAdd)], { class: "ext-buttons f-row" }),
@ -667,6 +669,7 @@ export function createPopOps() {
spindle: 'camAreaSpindle', spindle: 'camAreaSpindle',
tool: 'camAreaTool', tool: 'camAreaTool',
mode: 'camAreaMode', mode: 'camAreaMode',
direction: 'camMillDirection',
tr_type: 'camAreaTrace', tr_type: 'camAreaTrace',
sr_type: 'camAreaSurface', sr_type: 'camAreaSurface',
sr_angle: 'camAreaAngle', sr_angle: 'camAreaAngle',
@ -684,7 +687,6 @@ export function createPopOps() {
revbones: 'camAreaRevbones', revbones: 'camAreaRevbones',
ov_topz: 0, ov_topz: 0,
ov_botz: 0, ov_botz: 0,
direction: 'camAreaDirection',
}).inputs = { }).inputs = {
mode: UC.newSelect(LANG.mo_menu, { post: opRender }, "opmode"), mode: UC.newSelect(LANG.mo_menu, { post: opRender }, "opmode"),
tr_type: UC.newSelect(LANG.cc_offs_s, { title: LANG.cc_offs_l, show: isTrace }, "traceoff"), tr_type: UC.newSelect(LANG.cc_offs_s, { title: LANG.cc_offs_l, show: isTrace }, "traceoff"),

View file

@ -126,7 +126,6 @@ export function menu() {
camFastFeed: newInput(LANG.cc_rapd_s, {title:LANG.cc_rapd_l, convert:toFloat, units}), camFastFeed: newInput(LANG.cc_rapd_s, {title:LANG.cc_rapd_l, convert:toFloat, units}),
camFastFeedZ: newInput(LANG.cc_rzpd_s, {title:LANG.cc_rzpd_l, convert:toFloat, units}), camFastFeedZ: newInput(LANG.cc_rzpd_s, {title:LANG.cc_rzpd_l, convert:toFloat, units}),
_____: newGroup(LANG.ou_menu, $('cam-output'), { modes:CAM, driven, separator, group:"cam-output" }), _____: newGroup(LANG.ou_menu, $('cam-output'), { modes:CAM, driven, separator, group:"cam-output" }),
camConventional: newBoolean(LANG.ou_conv_s, onBooleanClick, {title:LANG.ou_conv_l}),
camEaseDown: newBoolean(LANG.cr_ease_s, onBooleanClick, {title:LANG.cr_ease_l}), camEaseDown: newBoolean(LANG.cr_ease_s, onBooleanClick, {title:LANG.cr_ease_l}),
camDepthFirst: newBoolean(LANG.ou_depf_s, onBooleanClick, {title:LANG.ou_depf_l}), camDepthFirst: newBoolean(LANG.ou_depf_s, onBooleanClick, {title:LANG.ou_depf_l}),
camInnerFirst: newBoolean(LANG.ou_inrf_s, onBooleanClick, {title:LANG.ou_inrf_l}), camInnerFirst: newBoolean(LANG.ou_inrf_s, onBooleanClick, {title:LANG.ou_inrf_l}),

View file

@ -28,10 +28,9 @@ class OpArea extends CamOp {
async slice(progress) { async slice(progress) {
let { op, state } = this; let { op, state } = this;
let { tool, mode, down, over, follow, expand, outline, smooth } = op; let { direction, down, expand, follow, mode, outline, over, rename, smooth, tool } = op;
let { ov_topz, ov_botz, direction, rename } = op; let { addSlices, color, cutTabs, settings } = state;
let { settings, widget, tabs, color } = state; let { shadowAt, setToolDiam, tabs, widget, workarea } = state;
let { addSlices, setToolDiam, cutTabs, shadowAt, workarea } = state;
let areaTool = new Tool(settings, tool); let areaTool = new Tool(settings, tool);
let smoothVal = (smooth ?? 0) / 10; let smoothVal = (smooth ?? 0) / 10;
@ -185,6 +184,7 @@ class OpArea extends CamOp {
let tab_shadows = tabs.filter(t => t.top >= z).map(t => t.poly); let tab_shadows = tabs.filter(t => t.top >= z).map(t => t.poly);
if (tab_shadows) tool_shadow.push(...tab_shadows); if (tab_shadows) tool_shadow.push(...tab_shadows);
} }
POLY.setWinding(outs, direction === 'climb');
// store travel boundary that triggers up and over moves // store travel boundary that triggers up and over moves
slice.tool_shadow = [ area, ...shadow, ...tool_shadow ]; slice.tool_shadow = [ area, ...shadow, ...tool_shadow ];
slice.camLines = outs; slice.camLines = outs;
@ -252,6 +252,7 @@ class OpArea extends CamOp {
outs = omitInner(outs); outs = omitInner(outs);
} }
slice.camLines = outs; slice.camLines = outs;
POLY.setWinding(outs, direction === 'climb');
// store travel boundary that triggers up and over moves // store travel boundary that triggers up and over moves
slice.tool_shadow = [ slice.tool_shadow = [
area, area,

View file

@ -14,8 +14,8 @@ class OpOutline extends CamOp {
async slice(progress) { async slice(progress) {
let { op, state } = this; let { op, state } = this;
let { dogbones, down, inside, omitthru, omitvoid, outside, ov_botz, ov_topz } = op; let { direction, dogbones, down, inside, omitthru, omitvoid, outside } = op;
let { plunge, rate, rename, revbones, spindle, tool } = op; let { ov_botz, ov_topz, plunge, rate, rename, revbones, spindle, tool } = op;
let { shadow, widget } = state; let { shadow, widget } = state;
let shadow_base = shadow.base; let shadow_base = shadow.base;
@ -30,6 +30,7 @@ class OpOutline extends CamOp {
let areas = shadow.base.clone(true); let areas = shadow.base.clone(true);
ops_list.push(new OpArea(state, { ops_list.push(new OpArea(state, {
areas: { [widget.id]: areas.map(p => p.toArray()) }, areas: { [widget.id]: areas.map(p => p.toArray()) },
direction,
dogbones, dogbones,
down, down,
expand: 0, expand: 0,
@ -54,6 +55,7 @@ class OpOutline extends CamOp {
let areas = shadow.base.clone(true); let areas = shadow.base.clone(true);
ops_list.push(new OpArea(state, { ops_list.push(new OpArea(state, {
areas: { [widget.id]: areas.map(p => p.toArray()) }, areas: { [widget.id]: areas.map(p => p.toArray()) },
direction,
dogbones, dogbones,
down, down,
drape: true, drape: true,

View file

@ -10,10 +10,11 @@ class OpPocket extends CamOp {
async slice(progress) { async slice(progress) {
let { op, state } = this; let { op, state } = this;
let { contour, down, expand, follow, outline, ov_botz, ov_topz } = op; let { contour, direction, down, expand, follow, outline, ov_botz, ov_topz } = op;
let { plunge, rate, refine, smooth, spindle, surfaces, tolerance, tool } = op; let { plunge, rate, refine, smooth, spindle, surfaces, tolerance, tool } = op;
let pocket = { let pocket = {
areas: {}, areas: {},
direction,
down, down,
expand, expand,
follow, follow,

View file

@ -35,6 +35,7 @@ class OpRough extends CamOp {
ops_list.push(new OpArea(state, { ops_list.push(new OpArea(state, {
rename: op.rename ?? "rough", rename: op.rename ?? "rough",
spindle: op.spindle, spindle: op.spindle,
direction: op.direction,
tool: op.tool, tool: op.tool,
rate: op.rate, rate: op.rate,
plunge: op.plunge, plunge: op.plunge,
@ -57,6 +58,7 @@ class OpRough extends CamOp {
ops_list.push(new OpArea(state, { ops_list.push(new OpArea(state, {
rename: op.rename ?? "rough", rename: op.rename ?? "rough",
spindle: op.spindle, spindle: op.spindle,
direction: op.direction === 'climb' ? 'conventional' : 'climb',
tool: op.tool, tool: op.tool,
rate: op.rate, rate: op.rate,
plunge: op.plunge, plunge: op.plunge,

View file

@ -12,10 +12,11 @@ class OpTrace extends CamOp {
async slice(progress) { async slice(progress) {
let { op, state } = this; let { op, state } = this;
let { areas, down, expand, follow, offset, outline, mode, ov_botz, ov_topz } = op; let { areas, direction, down, expand, follow, offset, outline, mode, ov_botz, ov_topz } = op;
let { plunge, rate, refine, smooth, spindle, step, steps, thru, tolerance, tool } = op; let { plunge, rate, refine, smooth, spindle, step, steps, thru, tolerance, tool } = op;
let trace = { let trace = {
areas, areas,
direction,
dogbones: op.dogbone, dogbones: op.dogbone,
down, down,
expand, expand,

View file

@ -73,22 +73,11 @@ class Tool {
return diam ? diam * step : step; return diam ? diam * step : step;
} }
// setTaperLengthFromAngle(angle) {
// const rad = (this.flute_diam - this.taper_tip) / 2;
// return this.flute_len = calcTaperLength(rad, angle);
// }
getTaperAngle() { getTaperAngle() {
let { flute_diam, flute_len, taper_tip } = this.tool; let { flute_diam, flute_len, taper_tip } = this.tool;
return calcTaperAngle((flute_diam - taper_tip) / 2, flute_len); return calcTaperAngle((flute_diam - taper_tip) / 2, flute_len);
} }
// getTaperBallExtent() {
// let rad = this.tipDiameter() / 2;
// let ang = this.getTaperAngle() * DEG2RAD;
// return calcTaperBallExtent(rad, ang);
// }
shaftLength() { shaftLength() {
return this.unitScale() * this.tool.shaft_len; return this.unitScale() * this.tool.shaft_len;
} }

View file

@ -808,8 +808,6 @@ self.lang['en-us'] = {
ou_zclr_l: ["safe travel offset","from top of part","in workspace units"], ou_zclr_l: ["safe travel offset","from top of part","in workspace units"],
ou_ztru_s: "z thru", ou_ztru_s: "z thru",
ou_ztru_l: ["extend cutout pass down","in workspace units"], ou_ztru_l: ["extend cutout pass down","in workspace units"],
ou_conv_s: "conventional",
ou_conv_l: ["milling direction","uncheck for 'climb'"],
ou_depf_s: "depth first", ou_depf_s: "depth first",
ou_depf_l: ["optimize pocket cuts","with depth priority"], ou_depf_l: ["optimize pocket cuts","with depth priority"],
ou_inrf_s: "inner first", ou_inrf_s: "inner first",