stub in loop operation
This commit is contained in:
parent
1bda9ecfd4
commit
f25e75f557
9 changed files with 130 additions and 0 deletions
|
|
@ -794,6 +794,9 @@ function newInput(label, opt = {}) {
|
||||||
if (opt.trigger) {
|
if (opt.trigger) {
|
||||||
refresh(opt.trigger === 1 || opt.trigger === true);
|
refresh(opt.trigger === 1 || opt.trigger === true);
|
||||||
}
|
}
|
||||||
|
if (opt.post) {
|
||||||
|
opt.post();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (opt.units) {
|
if (opt.units) {
|
||||||
addUnits(ip, opt.round || 3);
|
addUnits(ip, opt.round || 3);
|
||||||
|
|
|
||||||
|
|
@ -661,6 +661,14 @@ export function createPopOps() {
|
||||||
|
|
||||||
createPopOp('|', {}).inputs = {};
|
createPopOp('|', {}).inputs = {};
|
||||||
|
|
||||||
|
createPopOp('loop', {
|
||||||
|
repeat: 2,
|
||||||
|
count: 1
|
||||||
|
}).inputs = {
|
||||||
|
repeat: UC.newInput('repeat', { title: "repeat count", convert: toInt, bound: UC.bound(1, 100), post: opRender }),
|
||||||
|
count: UC.newInput('operations', { title: "num operations", convert: toInt, bound: UC.bound(1, 20), post: opRender })
|
||||||
|
};
|
||||||
|
|
||||||
function isClear() {
|
function isClear() {
|
||||||
return env.poppedRec.mode === 'clear';
|
return env.poppedRec.mode === 'clear';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,10 @@ const opAddLevel = () => {
|
||||||
opAdd(env.popOp.level.new());
|
opAdd(env.popOp.level.new());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const opAddLoop = () => {
|
||||||
|
opAdd(env.popOp.loop.new());
|
||||||
|
};
|
||||||
|
|
||||||
const opAddRough = () => {
|
const opAddRough = () => {
|
||||||
opAdd(env.popOp.rough.new());
|
opAdd(env.popOp.rough.new());
|
||||||
};
|
};
|
||||||
|
|
@ -287,8 +291,33 @@ export function opRender() {
|
||||||
oplist.forEach((rec, i) => {
|
oplist.forEach((rec, i) => {
|
||||||
let title = '';
|
let title = '';
|
||||||
let clock = rec.type === '|';
|
let clock = rec.type === '|';
|
||||||
|
let isLoop = rec.type === 'loop';
|
||||||
let label = clock ? `` : (rec.mode ? rec.type + ' ' + rec.mode : rec.type);
|
let label = clock ? `` : (rec.mode ? rec.type + ' ' + rec.mode : rec.type);
|
||||||
let clazz = notime ? ["draggable", "notime"] : ["draggable"];
|
let clazz = notime ? ["draggable", "notime"] : ["draggable"];
|
||||||
|
|
||||||
|
// Loop operation label and styling
|
||||||
|
if (isLoop) {
|
||||||
|
label = `loop ${rec.repeat || 2}x`;
|
||||||
|
clazz.push('loop');
|
||||||
|
title = ` title="loop: repeat next ${rec.count || 1} operation(s) ${rec.repeat || 2} time(s)"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this operation is within N operations after a loop
|
||||||
|
let inLoopRange = false;
|
||||||
|
for (let j = i - 1; j >= 0; j--) {
|
||||||
|
if (oplist[j].type === 'loop') {
|
||||||
|
let loopCount = oplist[j].count || 1;
|
||||||
|
let distanceFromLoop = i - j;
|
||||||
|
if (distanceFromLoop <= loopCount && distanceFromLoop > 0) {
|
||||||
|
inLoopRange = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inLoopRange) {
|
||||||
|
clazz.push('in-loop');
|
||||||
|
}
|
||||||
|
|
||||||
let notable = rec.note ? rec.note.split(' ').filter(v => v.charAt(0) === '#') : undefined;
|
let notable = rec.note ? rec.note.split(' ').filter(v => v.charAt(0) === '#') : undefined;
|
||||||
if (clock) { clazz.push('clock'); title = ` title="end of ops chain\ndrag/drop like an op\nops after this are disabled"` }
|
if (clock) { clazz.push('clock'); title = ` title="end of ops chain\ndrag/drop like an op\nops after this are disabled"` }
|
||||||
if (notable?.length) {
|
if (notable?.length) {
|
||||||
|
|
@ -755,6 +784,7 @@ export function init() {
|
||||||
case "laser off": return opAddLaserOff();
|
case "laser off": return opAddLaserOff();
|
||||||
case "gcode": return opAddGCode();
|
case "gcode": return opAddGCode();
|
||||||
case "level": return opAddLevel();
|
case "level": return opAddLevel();
|
||||||
|
case "loop": return opAddLoop();
|
||||||
case "rough": return opAddRough();
|
case "rough": return opAddRough();
|
||||||
case "outline": return opAddOutline();
|
case "outline": return opAddOutline();
|
||||||
case "contour":
|
case "contour":
|
||||||
|
|
|
||||||
17
src/kiri/mode/cam/op-loop.js
Normal file
17
src/kiri/mode/cam/op-loop.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
import { CamOp } from './op.js';
|
||||||
|
|
||||||
|
export class OpLoop extends CamOp {
|
||||||
|
weight() {
|
||||||
|
return 0; // No weight, acts as marker
|
||||||
|
}
|
||||||
|
|
||||||
|
async slice() {
|
||||||
|
// No-op: expansion happens in slice.js
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
// No-op: ignored during g-code generation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import { OpLaserOff } from './op-laser-off.js';
|
||||||
import { OpLaserOn } from './op-laser-on.js';
|
import { OpLaserOn } from './op-laser-on.js';
|
||||||
import { OpLathe } from './op-lathe.js';
|
import { OpLathe } from './op-lathe.js';
|
||||||
import { OpLevel } from './op-level.js';
|
import { OpLevel } from './op-level.js';
|
||||||
|
import { OpLoop } from './op-loop.js';
|
||||||
import { OpOutline } from './op-outline.js';
|
import { OpOutline } from './op-outline.js';
|
||||||
import { OpPocket } from './op-pocket.js';
|
import { OpPocket } from './op-pocket.js';
|
||||||
import { OpRegister } from './op-register.js';
|
import { OpRegister } from './op-register.js';
|
||||||
|
|
@ -31,6 +32,7 @@ export const ops = {
|
||||||
"laser on": OpLaserOn,
|
"laser on": OpLaserOn,
|
||||||
"lathe": OpLathe,
|
"lathe": OpLathe,
|
||||||
"level": OpLevel,
|
"level": OpLevel,
|
||||||
|
"loop": OpLoop,
|
||||||
"outline": OpOutline,
|
"outline": OpOutline,
|
||||||
"pocket": OpPocket,
|
"pocket": OpPocket,
|
||||||
"register": OpRegister,
|
"register": OpRegister,
|
||||||
|
|
|
||||||
|
|
@ -990,6 +990,10 @@ export async function prepare_one(widget, settings, print, firstPoint, update) {
|
||||||
|
|
||||||
// pre-flight check of ops
|
// pre-flight check of ops
|
||||||
for (let op of widget.camops) {
|
for (let op of widget.camops) {
|
||||||
|
// Skip loop operations (they're expanded during slice)
|
||||||
|
if (op.op.type === 'loop') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
opTot += op.weight();
|
opTot += op.weight();
|
||||||
// ensure tool related parameters are available
|
// ensure tool related parameters are available
|
||||||
// for the first index call when no tool is specified
|
// for the first index call when no tool is specified
|
||||||
|
|
@ -999,6 +1003,10 @@ export async function prepare_one(widget, settings, print, firstPoint, update) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let op of widget.camops) {
|
for (let op of widget.camops) {
|
||||||
|
// Skip loop operations (they're expanded during slice)
|
||||||
|
if (op.op.type === 'loop') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
contouring = false;
|
contouring = false;
|
||||||
lasering = false;
|
lasering = false;
|
||||||
let cop = currentOp = op.op;
|
let cop = currentOp = op.op;
|
||||||
|
|
|
||||||
|
|
@ -324,6 +324,52 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
||||||
opTot += opList.peek().weight();
|
opTot += opList.peek().weight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LOOP EXPANSION - duplicate next N operations M times
|
||||||
|
let expandedOps = [];
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
while (i < activeOps.length) {
|
||||||
|
let op = activeOps[i];
|
||||||
|
|
||||||
|
if (op.type === 'loop') {
|
||||||
|
let repeatCount = op.repeat || 2; // M times
|
||||||
|
let opCount = op.count || 1; // N operations
|
||||||
|
|
||||||
|
// Validate we have enough operations
|
||||||
|
if (i + opCount >= activeOps.length) {
|
||||||
|
return error(`Loop operation requires ${opCount} following operation(s), but only ${activeOps.length - i - 1} available`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract next N operations
|
||||||
|
let loopOps = activeOps.slice(i + 1, i + 1 + opCount);
|
||||||
|
|
||||||
|
// Check for nested loops
|
||||||
|
for (let loopOp of loopOps) {
|
||||||
|
if (loopOp.type === 'loop') {
|
||||||
|
return error("Nested loops are not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate operations M times
|
||||||
|
for (let r = 0; r < repeatCount; r++) {
|
||||||
|
for (let loopOp of loopOps) {
|
||||||
|
let clonedOp = Object.clone(loopOp);
|
||||||
|
expandedOps.push(clonedOp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the loop operation itself and the N operations
|
||||||
|
i += 1 + opCount;
|
||||||
|
} else if (op.type === '|') {
|
||||||
|
break; // Clock operation
|
||||||
|
} else {
|
||||||
|
expandedOps.push(op);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeOps = expandedOps;
|
||||||
|
|
||||||
// determine # of steps and step weighting for progress bar
|
// determine # of steps and step weighting for progress bar
|
||||||
for (let op of activeOps) {
|
for (let op of activeOps) {
|
||||||
if (op.type === '|') {
|
if (op.type === '|') {
|
||||||
|
|
|
||||||
|
|
@ -1448,6 +1448,21 @@ details[open] summary::after {
|
||||||
#oplist .clock:hover {
|
#oplist .clock:hover {
|
||||||
background-color: var(--blue-2);
|
background-color: var(--blue-2);
|
||||||
}
|
}
|
||||||
|
#oplist .loop:hover {
|
||||||
|
background-color: rgba(100, 150, 255, 0.35);
|
||||||
|
}
|
||||||
|
#oplist .in-loop {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
#oplist .in-loop::before {
|
||||||
|
content: "";
|
||||||
|
border: 1px solid var(--blue-2);
|
||||||
|
border-radius: 3px;
|
||||||
|
width: 2px;
|
||||||
|
height: 80%;
|
||||||
|
position: absolute;
|
||||||
|
left: -10px;
|
||||||
|
}
|
||||||
#oplist > div {
|
#oplist > div {
|
||||||
padding: 4px 20px 4px 7px !important;
|
padding: 4px 20px 4px 7px !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -471,6 +471,7 @@
|
||||||
<div title="remove an area of material inside a defined boundary sometimes called pocketing">rough</div>
|
<div title="remove an area of material inside a defined boundary sometimes called pocketing">rough</div>
|
||||||
<div title="cutout parts using their shadow outline">outline</div>
|
<div title="cutout parts using their shadow outline">outline</div>
|
||||||
<div title="perform operations on using outlines or surfaces many operations are simple wrappers around this">area</div>
|
<div title="perform operations on using outlines or surfaces many operations are simple wrappers around this">area</div>
|
||||||
|
<div id="loop" title="loop next N operations M times">loop</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue