reformat helical

This commit is contained in:
Stewart Allen 2025-11-26 12:58:46 -05:00
commit c20c3ae7e5
4 changed files with 253 additions and 253 deletions

View file

@ -3,7 +3,9 @@ import { CAM } from "./driver-fe.js";
import { clearPops, env } from "./client.js";
let alert, lastWidget;
export let helicalOn = false;
export function selectHelical() {
if (helicalOn) {
return helicalDone();
@ -25,6 +27,7 @@ export function selectHelical() {
}
}
});
helicalOn = env.hoveredOp;
helicalOn.classList.add("editing");
api.feature.on_mouse_up = (obj, ev) => {

View file

@ -104,7 +104,6 @@ function cylinderShow(onProgress,onDone){
cyls[face] = faces;
CAM.cylinder_show(widget);
}
onDone({faces:Object.keys(cyls).map(v => parseInt(v)),error});
});
}

View file

@ -7,278 +7,277 @@ import { newPoint } from "../../../geo/point.js";
import { newSlice } from "../../core/slice.js";
export class OpHelical extends CamOp {
constructor(state, op) {
super(state, op);
}
async slice(progress) {
let { op, state } = this;
let { settings, addSlices, widget, updateToolDiams, slicer } = state;
let { color } = state;
let {
cylinders,
forceStartAng,
startAng,
fromTop,
offOver,
offset,
down,
thru,
finish,
clockwise,
reverse,
entry,
entryOffset,
} = op;
let {stock} = settings
constructor(state, op) {
super(state, op);
}
let tool = new Tool(settings, op.tool),
toolDiam = tool.fluteDiameter(),
sliceOut = (this.sliceOut = []),
startAngle = forceStartAng ? startAng : 0;
updateToolDiams(toolDiam);
// console.log({cylinders})
let faces = cylinders[widget.id] ?? [];
if (faces.faces) faces = faces.faces;
let polys = [];
//iterate over selected faces
for (let [i,face] of faces.entries()) {
//get poly info for the cylinder
let res
try{
res = cylinder_poly_find(
widget,
face
);
}catch(error){
// don't quit if a cylinder errors out, just skip
console.error(error)
continue
}
let { zmin, zmax, diam, center, interior, faces } = res;
// console.log({ zmin, zmax, diam, center, interior });
let radius = diam /2;
zmax = fromTop ? stock.z: zmax;
let radAdd,
zBottom = zmin - thru,
numSegs = faces.length / 2,
poly = newPolygon().setOpen();
if (offOver) {
//apply offset override
if (offset == "auto") {
radAdd = interior ? -offOver : offOver;
} else if (offset == "inside") {
radAdd = -offOver;
} else if (offset == "outside") {
radAdd = offOver;
}
} else {
//otherwise, calculate based on offset type
if (offset == "auto") {
radAdd = (interior ? -toolDiam : toolDiam) / 2;
} else if (offset == "inside") {
radAdd = -toolDiam / 2;
} else if (offset == "outside") {
radAdd = toolDiam / 2;
}
}
radius += radAdd;
if( radius <= 0 ){
//if negative radius, skip this cylinder
console.log('negative radius', radius);
continue
}
let startPoint = center
.clone()
.setZ(zmax)
.add(
newPoint(
Math.cos(startAngle) * radius,
Math.sin(startAngle) * radius,
0
)
);
let currentZ = zmax;
for (;;) {
let bottom = currentZ - down;
if ( bottom > zBottom ) {
//if not at the bottom, do a whole helix
let start = startPoint.setZ(currentZ),
end = startPoint.clone().setZ(bottom);
// console.log(currentZ, bottom, [start.z, end.z]);
let path = arcToPath(start, end, numSegs, {
async slice(progress) {
let { op, state } = this;
let { settings, addSlices, widget, updateToolDiams } = state;
let {
cylinders,
forceStartAng,
startAng,
fromTop,
offOver,
offset,
down,
thru,
finish,
clockwise,
radius,
center,
});
// console.log({ path, start, end, radius, center });
poly.addPoints(path);
} else {
//if at the bottom, do a partial helix
let toBottom = currentZ - zBottom,
ofFull = toBottom / down,
ofCircle = ofFull * (2 * Math.PI),
cwMultiplier = clockwise ? -1 : 1,
finalAngle = startAngle + (ofCircle*cwMultiplier),
finalEnd = center
.clone()
.setZ(zBottom)
.add(
newPoint(
Math.cos(finalAngle) * radius,
Math.sin(finalAngle) * radius,
0
)
);
// console.log({down,toBottom,ofFull,ofCircle,startAngle,finalAngle,finalEnd});
reverse,
entry,
entryOffset,
} = op;
let { color } = state;
let { stock } = settings
//if not already very close to final point
if( ofCircle >= 0.001 ){
//circle down to final point
poly.addPoints(
arcToPath(startPoint.setZ(currentZ), finalEnd, numSegs , {
clockwise,
radius,
center,
})
);
}
let tool = new Tool(settings, op.tool),
toolDiam = tool.fluteDiameter(),
sliceOut = (this.sliceOut = []),
startAngle = forceStartAng ? startAng : 0;
//if starting at the bottom, reverse poly
if ( reverse ){
poly.reverse();
}
updateToolDiams(toolDiam);
// console.log({cylinders});
let faces = cylinders[widget.id] ?? [];
if (faces.faces) faces = faces.faces;
let polys = [];
if ( entry ){
//get first z of poly to use for entry
let firstZ = poly.first().z;
//create new pointArray
let entry =[newPoint(0,0,0)];
//add center point
let entryCenter = center.clone().setZ(firstZ)
//if doing a curved enter, add more
if ( entryOffset > 0 ){
if ( entryOffset > radius ){
console.error("entryOffset must be less than radius of helix");
}else{
//calculate entry center
let lineOut = radius - entryOffset;
let centerOffset = (entryOffset**2 + 2*entryOffset*lineOut) / (2*(entryOffset + lineOut));
//add entry points
entry.push(
newPoint(0,-lineOut,0),
...arcToPath(
newPoint(0,-lineOut,0),
newPoint(radius,0,0),
128,
{
clockwise:false,
radius:lineOut,
center:newPoint(centerOffset,0,0)
}
),
newPoint(radius,0,0),
)
}
//iterate over selected faces
for (let [i, face] of faces.entries()) {
//get poly info for the cylinder
let res
try {
res = cylinder_poly_find(
widget,
face
);
} catch (error) {
// don't quit if a cylinder errors out, just skip
console.error(error)
continue
}
//for each point
//rotate by start angle
// add to hole center
let invert = ((clockwise && !reverse ) || (!clockwise && reverse) ) ? -1 : 1
let entryAngle = reverse ? finalAngle : startAngle
entry = entry.map(p => {
// TODO: there's totally a way to do this with a matrix
// logical XOR
p.y *= invert
p.rotate(entryAngle)
return p.add(entryCenter)
})
//append to start of poly
poly = newPolygon().setOpen().addPoints(entry).addPoints(poly.points);
}
//if doing a finish pass, do a full circle at the bottom
if ( finish && !reverse ){
poly.addPoints(
arcToPath(finalEnd, finalEnd, numSegs, {
clockwise,
radius,
center,
}),
);
poly.append(
finalEnd.clone().setZ(zBottom),
)
}
break;
}
currentZ = bottom;
}
let { zmin, zmax, diam, center, interior, faces } = res;
// console.log({ zmin, zmax, diam, center, interior });
let radius = diam / 2;
zmax = fromTop ? stock.z : zmax;
progress(i/faces.length, "Helical intrerpolation");
polys.push(poly);
let radAdd,
zBottom = zmin - thru,
numSegs = faces.length / 2,
poly = newPolygon().setOpen();
if (offOver) {
//apply offset override
if (offset == "auto") {
radAdd = interior ? -offOver : offOver;
} else if (offset == "inside") {
radAdd = -offOver;
} else if (offset == "outside") {
radAdd = offOver;
}
} else {
//otherwise, calculate based on offset type
if (offset == "auto") {
radAdd = (interior ? -toolDiam : toolDiam) / 2;
} else if (offset == "inside") {
radAdd = -toolDiam / 2;
} else if (offset == "outside") {
radAdd = toolDiam / 2;
}
}
radius += radAdd;
if (radius <= 0) {
//if negative radius, skip this cylinder
console.log('negative radius', radius);
continue
}
let startPoint = center
.clone()
.setZ(zmax)
.add(
newPoint(
Math.cos(startAngle) * radius,
Math.sin(startAngle) * radius,
0
)
);
let currentZ = zmax;
for (; ;) {
let bottom = currentZ - down;
if (bottom > zBottom) {
//if not at the bottom, do a whole helix
let start = startPoint.setZ(currentZ),
end = startPoint.clone().setZ(bottom);
// console.log(currentZ, bottom, [start.z, end.z]);
let path = arcToPath(start, end, numSegs, {
clockwise,
radius,
center,
});
// console.log({ path, start, end, radius, center });
poly.addPoints(path);
} else {
//if at the bottom, do a partial helix
let toBottom = currentZ - zBottom,
ofFull = toBottom / down,
ofCircle = ofFull * (2 * Math.PI),
cwMultiplier = clockwise ? -1 : 1,
finalAngle = startAngle + (ofCircle * cwMultiplier),
finalEnd = center
.clone()
.setZ(zBottom)
.add(
newPoint(
Math.cos(finalAngle) * radius,
Math.sin(finalAngle) * radius,
0
)
);
// console.log({down,toBottom,ofFull,ofCircle,startAngle,finalAngle,finalEnd});
//if not already very close to final point
if (ofCircle >= 0.001) {
//circle down to final point
poly.addPoints(
arcToPath(startPoint.setZ(currentZ), finalEnd, numSegs, {
clockwise,
radius,
center,
})
);
}
//if starting at the bottom, reverse poly
if (reverse) {
poly.reverse();
}
if (entry) {
//get first z of poly to use for entry
let firstZ = poly.first().z;
//create new pointArray
let entry = [newPoint(0, 0, 0)];
//add center point
let entryCenter = center.clone().setZ(firstZ)
//if doing a curved enter, add more
if (entryOffset > 0) {
if (entryOffset > radius) {
console.error("entryOffset must be less than radius of helix");
} else {
//calculate entry center
let lineOut = radius - entryOffset;
let centerOffset = (entryOffset ** 2 + 2 * entryOffset * lineOut) / (2 * (entryOffset + lineOut));
//add entry points
entry.push(
newPoint(0, -lineOut, 0),
...arcToPath(
newPoint(0, -lineOut, 0),
newPoint(radius, 0, 0),
128,
{
clockwise: false,
radius: lineOut,
center: newPoint(centerOffset, 0, 0)
}
),
newPoint(radius, 0, 0),
)
}
}
//for each point
//rotate by start angle
// add to hole center
let invert = ((clockwise && !reverse) || (!clockwise && reverse)) ? -1 : 1
let entryAngle = reverse ? finalAngle : startAngle
entry = entry.map(p => {
// TODO: there's totally a way to do this with a matrix
// logical XOR
p.y *= invert
p.rotate(entryAngle)
return p.add(entryCenter)
})
//append to start of poly
poly = newPolygon().setOpen().addPoints(entry).addPoints(poly.points);
}
//if doing a finish pass, do a full circle at the bottom
if (finish && !reverse) {
poly.addPoints(
arcToPath(finalEnd, finalEnd, numSegs, {
clockwise,
radius,
center,
}),
);
poly.append(
finalEnd.clone().setZ(zBottom),
)
}
break;
}
currentZ = bottom;
}
progress(i / faces.length, "Helical intrerpolation");
polys.push(poly);
}
let slice = newSlice(0);
slice.camTrace = { tool: op.tool, rate: op.feed, plunge: op.rate };
slice.camLines = polys;
slice
.output()
.setLayer("Helical", { face: color, line: color })
.addPolys(slice.camLines);
addSlices(slice);
sliceOut.push(slice);
}
let slice = newSlice(0);
async prepare(ops, progress) {
let {
setTool,
setSpindle,
polyEmit,
setPrintPoint,
getPrintPoint,
} = ops;
let { tool, spindle, rate, feed } = this.op;
slice.camTrace = { tool: op.tool, rate: op.feed, plunge: op.rate };
slice.camLines = polys;
setTool(tool, feed, rate);
setSpindle(spindle);
slice
.output()
.setLayer("Helical", { face: color, line: color })
.addPolys(slice.camLines);
addSlices(slice);
sliceOut.push(slice);
}
let [polys] = this.sliceOut.map((s) => s.camLines);
let pp = getPrintPoint();
polys = polys.slice();
async prepare(ops, progress) {
let { op, state } = this;
let { settings, widget, addSlices, updateToolDiams } = state;
let {
setTool,
setSpindle,
polyEmit,
camOut,
setPrintPoint,
getPrintPoint,
} = ops;
let { tool, spindle, rate, feed } = op;
setTool(tool, feed, rate);
setSpindle(spindle);
for (;;) {
let closestDist = Infinity,
closestI,
closest = null,
dist,
poly;
let [polys] = this.sliceOut.map((s) => s.camLines);
let pp = getPrintPoint();
for (let i = 0; i < polys.length; i++) {
if (!polys[i]) continue;
if ((dist = polys[i].first().distTo2D(pp)) < closestDist) {
closestDist = dist;
closest = polys[i];
closestI = i;
}
}
polys = polys.slice();
for (;;) {
let closestDist = Infinity,
closestI,
closest = null,
dist,
poly;
for (let i = 0; i < polys.length; i++) {
if (!polys[i]) continue;
if ((dist = polys[i].first().distTo2D(pp)) < closestDist) {
closestDist = dist;
closest = polys[i];
closestI = i;
if (!closest) break;
poly = polys[closestI]
polys[closestI] = null;
pp = polyEmit(poly, 0, 1, poly.points[0])
}
}
if (!closest) break;
poly = polys[closestI]
polys[closestI] = null;
//emit
pp = polyEmit(poly,0,1,poly.points[0])
setPrintPoint(pp);
}
setPrintPoint(pp);
}
}

View file

@ -87,7 +87,7 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
bottom_z, bottom_cut
}, 3);
// console.log({ track, bounds, stock, workarea });
// console.log({ bounds, stock, track, workarea });
return structuredClone(workarea);
};
@ -96,7 +96,8 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
var_compute();
if (tabs) {
// make tab polygons
// create tab polygons. defer offset to each op
// since it's dependent on selected tool diameter
tabs.forEach(tab => {
let zero = newPoint(0, 0, 0),
point = newPoint(tab.pos.x, tab.pos.y, tab.pos.z),
@ -110,8 +111,6 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
.map(v => newPoint(v.x, v.y, v.z));
poly.move(point);
tab.poly = poly;
// tslice.output().setLayer("tabs", 0xff0000).addPoly(poly);
// sliceAll.push(tslice);
});
}