2025-07-11 15:45:57 -06:00
|
|
|
import { arcToPath } from "../../../geo/paths.js";
|
|
|
|
|
import { newPolygon } from "../../../geo/polygon.js";
|
|
|
|
|
import { CamOp } from "./op.js";
|
2025-07-11 22:23:14 -06:00
|
|
|
import { Tool } from "./tool.js";
|
|
|
|
|
import { cylinder_poly_find } from "./slice.js";
|
|
|
|
|
import { newPoint } from "../../../geo/point.js";
|
|
|
|
|
import { newSlice } from "../../core/slice.js";
|
2025-07-10 12:57:56 -06:00
|
|
|
|
|
|
|
|
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 { zBottom, zThru, thruHoles, color } = state;
|
|
|
|
|
let {
|
|
|
|
|
cylinders,
|
|
|
|
|
forceStartAng,
|
|
|
|
|
startAng,
|
|
|
|
|
fromTop,
|
|
|
|
|
offOver,
|
|
|
|
|
offset,
|
|
|
|
|
down,
|
|
|
|
|
thru,
|
2025-07-13 20:26:32 -06:00
|
|
|
finish,
|
2025-07-10 12:57:56 -06:00
|
|
|
} = op;
|
2025-07-11 22:23:14 -06:00
|
|
|
let {stock} = settings
|
|
|
|
|
|
|
|
|
|
|
2025-07-10 12:57:56 -06:00
|
|
|
|
2025-07-11 22:23:14 -06:00
|
|
|
let tool = new Tool(settings, op.tool),
|
2025-07-10 12:57:56 -06:00
|
|
|
toolDiam = tool.fluteDiameter(),
|
|
|
|
|
sliceOut = (this.sliceOut = []),
|
|
|
|
|
startAngle = forceStartAng ? startAng : 0;
|
|
|
|
|
|
|
|
|
|
updateToolDiams(toolDiam);
|
2025-07-13 19:56:04 -06:00
|
|
|
// console.log({cylinders})
|
|
|
|
|
let faces = cylinders[widget.id] ?? [];
|
2025-07-10 12:57:56 -06:00
|
|
|
if (faces.faces) faces = faces.faces;
|
|
|
|
|
let polys = [];
|
|
|
|
|
|
|
|
|
|
//iterate over selected faces
|
|
|
|
|
for (let face of faces) {
|
|
|
|
|
//get poly info for the cylinder
|
2025-07-11 22:23:14 -06:00
|
|
|
let { zmin, zmax, diam, center, interior, faces } = cylinder_poly_find(
|
|
|
|
|
widget,
|
|
|
|
|
face
|
|
|
|
|
);
|
|
|
|
|
// console.log({ zmin, zmax, diam, center, interior });
|
2025-07-10 12:57:56 -06:00
|
|
|
let radius = diam >> 1;
|
2025-07-11 22:23:14 -06:00
|
|
|
zmax = fromTop ? stock.z: zmax;
|
2025-07-10 12:57:56 -06:00
|
|
|
|
|
|
|
|
let radAdd,
|
|
|
|
|
zBottom = zmin - thru,
|
|
|
|
|
clockwise = true,
|
|
|
|
|
numSegs = faces.length / 2,
|
|
|
|
|
poly = newPolygon().setOpen();
|
|
|
|
|
|
|
|
|
|
if (offOver) {
|
|
|
|
|
//apply offset override
|
|
|
|
|
if (offset == "auto") {
|
2025-07-11 22:23:14 -06:00
|
|
|
radAdd = interior ? -offOver : offOver;
|
2025-07-10 12:57:56 -06:00
|
|
|
} else if (offset == "inside") {
|
2025-07-11 22:23:14 -06:00
|
|
|
radAdd = -offOver;
|
2025-07-10 12:57:56 -06:00
|
|
|
} else if (offset == "outside") {
|
2025-07-11 22:23:14 -06:00
|
|
|
radAdd = offOver;
|
2025-07-10 12:57:56 -06:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//otherwise, calculate based on offset type
|
|
|
|
|
if (offset == "auto") {
|
2025-07-11 22:23:14 -06:00
|
|
|
radAdd = (interior ? -toolDiam : toolDiam) / 2;
|
2025-07-10 12:57:56 -06:00
|
|
|
} else if (offset == "inside") {
|
2025-07-11 22:23:14 -06:00
|
|
|
radAdd = -toolDiam / 2;
|
2025-07-10 12:57:56 -06:00
|
|
|
} else if (offset == "outside") {
|
2025-07-11 22:23:14 -06:00
|
|
|
radAdd = toolDiam / 2;
|
2025-07-10 12:57:56 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
radius += radAdd;
|
|
|
|
|
|
2025-07-13 19:56:04 -06:00
|
|
|
if( radius <= 0 ){
|
|
|
|
|
//if negative radius, skip this cylinder
|
|
|
|
|
console.log('negative radius', radius);
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-10 12:57:56 -06:00
|
|
|
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);
|
2025-07-11 22:23:14 -06:00
|
|
|
// console.log(currentZ, bottom, [start.z, end.z]);
|
2025-07-10 12:57:56 -06:00
|
|
|
let path = arcToPath(start, end, numSegs, {
|
|
|
|
|
clockwise,
|
|
|
|
|
radius,
|
|
|
|
|
center,
|
|
|
|
|
});
|
2025-07-11 22:23:14 -06:00
|
|
|
// console.log({ path, start, end, radius, center });
|
2025-07-10 12:57:56 -06:00
|
|
|
poly.addPoints(path);
|
|
|
|
|
} else {
|
|
|
|
|
//if at the bottom, do a partial helix
|
|
|
|
|
|
|
|
|
|
let toBottom = currentZ - zBottom,
|
|
|
|
|
ofFull = toBottom / down,
|
|
|
|
|
ofCircle = ofFull * (2 * Math.PI),
|
|
|
|
|
finalAngle = startAngle + ofCircle,
|
|
|
|
|
finalEnd = center
|
|
|
|
|
.clone()
|
|
|
|
|
.setZ(zBottom)
|
|
|
|
|
.add(
|
|
|
|
|
newPoint(
|
|
|
|
|
Math.cos(finalAngle) * radius,
|
|
|
|
|
Math.sin(finalAngle) * radius,
|
|
|
|
|
0
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
2025-07-14 07:37:37 -06:00
|
|
|
// console.log({down,toBottom,ofFull,ofCircle,finalAngle,finalEnd});
|
2025-07-13 19:56:04 -06:00
|
|
|
|
2025-07-10 12:57:56 -06:00
|
|
|
//circle down to final point
|
|
|
|
|
poly.addPoints(
|
2025-07-13 19:56:04 -06:00
|
|
|
arcToPath(startPoint.setZ(currentZ), finalEnd, numSegs , {
|
2025-07-10 12:57:56 -06:00
|
|
|
clockwise: !clockwise,
|
|
|
|
|
radius,
|
|
|
|
|
center,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
2025-07-13 20:26:32 -06:00
|
|
|
//if settings dictate, do a full circle at the bottom
|
|
|
|
|
if(finish){
|
|
|
|
|
poly.addPoints(
|
|
|
|
|
arcToPath(finalEnd, finalEnd, numSegs, {
|
|
|
|
|
clockwise,
|
|
|
|
|
radius,
|
|
|
|
|
center,
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
poly.append(
|
|
|
|
|
finalEnd.clone().setZ(zBottom),
|
|
|
|
|
)
|
|
|
|
|
}
|
2025-07-10 12:57:56 -06:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
currentZ = bottom;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 22:23:14 -06:00
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
let [polys] = this.sliceOut.map((s) => s.camLines);
|
|
|
|
|
|
|
|
|
|
let pp = getPrintPoint();
|
|
|
|
|
|
2025-07-13 19:56:04 -06:00
|
|
|
// console.log(polys)
|
2025-07-11 22:23:14 -06:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
//emit
|
|
|
|
|
|
|
|
|
|
pp = polyEmit(poly,0,1,poly.points[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setPrintPoint(pp);
|
|
|
|
|
}
|
2025-07-10 12:57:56 -06:00
|
|
|
}
|