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) {
|
||||
refresh(opt.trigger === 1 || opt.trigger === true);
|
||||
}
|
||||
if (opt.post) {
|
||||
opt.post();
|
||||
}
|
||||
});
|
||||
if (opt.units) {
|
||||
addUnits(ip, opt.round || 3);
|
||||
|
|
|
|||
|
|
@ -661,6 +661,14 @@ export function createPopOps() {
|
|||
|
||||
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() {
|
||||
return env.poppedRec.mode === 'clear';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,6 +165,10 @@ const opAddLevel = () => {
|
|||
opAdd(env.popOp.level.new());
|
||||
};
|
||||
|
||||
const opAddLoop = () => {
|
||||
opAdd(env.popOp.loop.new());
|
||||
};
|
||||
|
||||
const opAddRough = () => {
|
||||
opAdd(env.popOp.rough.new());
|
||||
};
|
||||
|
|
@ -287,8 +291,33 @@ export function opRender() {
|
|||
oplist.forEach((rec, i) => {
|
||||
let title = '';
|
||||
let clock = rec.type === '|';
|
||||
let isLoop = rec.type === 'loop';
|
||||
let label = clock ? `` : (rec.mode ? rec.type + ' ' + rec.mode : rec.type);
|
||||
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;
|
||||
if (clock) { clazz.push('clock'); title = ` title="end of ops chain\ndrag/drop like an op\nops after this are disabled"` }
|
||||
if (notable?.length) {
|
||||
|
|
@ -755,6 +784,7 @@ export function init() {
|
|||
case "laser off": return opAddLaserOff();
|
||||
case "gcode": return opAddGCode();
|
||||
case "level": return opAddLevel();
|
||||
case "loop": return opAddLoop();
|
||||
case "rough": return opAddRough();
|
||||
case "outline": return opAddOutline();
|
||||
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 { OpLathe } from './op-lathe.js';
|
||||
import { OpLevel } from './op-level.js';
|
||||
import { OpLoop } from './op-loop.js';
|
||||
import { OpOutline } from './op-outline.js';
|
||||
import { OpPocket } from './op-pocket.js';
|
||||
import { OpRegister } from './op-register.js';
|
||||
|
|
@ -31,6 +32,7 @@ export const ops = {
|
|||
"laser on": OpLaserOn,
|
||||
"lathe": OpLathe,
|
||||
"level": OpLevel,
|
||||
"loop": OpLoop,
|
||||
"outline": OpOutline,
|
||||
"pocket": OpPocket,
|
||||
"register": OpRegister,
|
||||
|
|
|
|||
|
|
@ -990,6 +990,10 @@ export async function prepare_one(widget, settings, print, firstPoint, update) {
|
|||
|
||||
// pre-flight check of ops
|
||||
for (let op of widget.camops) {
|
||||
// Skip loop operations (they're expanded during slice)
|
||||
if (op.op.type === 'loop') {
|
||||
continue;
|
||||
}
|
||||
opTot += op.weight();
|
||||
// ensure tool related parameters are available
|
||||
// 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) {
|
||||
// Skip loop operations (they're expanded during slice)
|
||||
if (op.op.type === 'loop') {
|
||||
continue;
|
||||
}
|
||||
contouring = false;
|
||||
lasering = false;
|
||||
let cop = currentOp = op.op;
|
||||
|
|
|
|||
|
|
@ -324,6 +324,52 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
|||
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
|
||||
for (let op of activeOps) {
|
||||
if (op.type === '|') {
|
||||
|
|
|
|||
|
|
@ -1448,6 +1448,21 @@ details[open] summary::after {
|
|||
#oplist .clock:hover {
|
||||
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 {
|
||||
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="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 id="loop" title="loop next N operations M times">loop</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue