Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ const rec = {
define: { 'process.env.NODE_ENV': `"${mode}"` },
external: [
'module',
'node:module',
'./constants',
'./voronoi_structures',
'./voronoi_ctypes',
Expand Down
7 changes: 5 additions & 2 deletions src/dock/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
FROM node:18-slim
FROM node:22-slim
WORKDIR /grid
COPY package.json /grid
COPY app.js /grid
COPY apps /grid/apps
COPY src /grid/src
COPY web /grid/web
COPY bin /grid/bin
RUN apt-get -y update; apt-get -y install curl
RUN apt-get -y update; apt-get -y install curl git
EXPOSE 8080
RUN npm i
RUN npm run webpack-ext
RUN npm install -g @gridspace/app-server
RUN npm run pack-prod
RUN rm -rf /grid/bin
CMD gs-app-server

1 change: 1 addition & 0 deletions src/kiri/app/conf/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ export const conf = {
camRoughTop: true,
camRoundCorners: true,
camStockClipTo: false,
camStockCylinder: false,
camStockIndexed: false,
camStockIndexGrid: true,
camStockOffset: true,
Expand Down
54 changes: 33 additions & 21 deletions src/kiri/mode/cam/app/cl-stock.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,43 +102,55 @@ export function updateStock() {
}
if (x && y && z) {
UI.func.animate.classList.remove('disabled');
// cylindrical stock is only valid in indexed (rotary) mode
const cylindrical = process.camStockIndexed && process.camStockCylinder;
{
let geo = new THREE.BoxGeometry(1, 1, 1);
let mat = new THREE.MeshBasicMaterial({
color: 0x777777,
opacity: 0.05,
transparent: true,
side: THREE.DoubleSide
});
let geo, lines;
if (cylindrical) {
// cylinder runs along the rotary (X) axis, diameter = height (z)
geo = new THREE.CylinderGeometry(0.5, 0.5, 1, 60);
geo.rotateZ(Math.PI / 2);
let ligeo = new THREE.EdgesGeometry(geo, 30);
let limat = new THREE.LineBasicMaterial({ color: 0xaaaaaa });
lines = new THREE.LineSegments(ligeo, limat);
} else {
geo = new THREE.BoxGeometry(1, 1, 1);
let lo = 0.5;
let lidat = [
lo, lo, lo, lo, lo, -lo,
lo, lo, lo, lo, -lo, lo,
lo, lo, lo, -lo, lo, lo,
-lo, -lo, -lo, -lo, -lo, lo,
-lo, -lo, -lo, -lo, lo, -lo,
-lo, -lo, -lo, lo, -lo, -lo,
lo, lo, -lo, -lo, lo, -lo,
lo, lo, -lo, lo, -lo, -lo,
lo, -lo, -lo, lo, -lo, lo,
lo, -lo, lo, -lo, -lo, lo,
-lo, -lo, lo, -lo, lo, lo,
-lo, lo, lo, -lo, lo, -lo
];
let ligeo = new THREE.BufferGeometry();
ligeo.setAttribute('position', new THREE.BufferAttribute(lidat.toFloat32(), 3));
let limat = new THREE.LineBasicMaterial({ color: 0xaaaaaa });
lines = new THREE.LineSegments(ligeo, limat);
}
env.camStock = new THREE.Mesh(geo, mat);
env.camStock.renderOrder = 2;
let lo = 0.5;
let lidat = [
lo, lo, lo, lo, lo, -lo,
lo, lo, lo, lo, -lo, lo,
lo, lo, lo, -lo, lo, lo,
-lo, -lo, -lo, -lo, -lo, lo,
-lo, -lo, -lo, -lo, lo, -lo,
-lo, -lo, -lo, lo, -lo, -lo,
lo, lo, -lo, -lo, lo, -lo,
lo, lo, -lo, lo, -lo, -lo,
lo, -lo, -lo, lo, -lo, lo,
lo, -lo, lo, -lo, -lo, lo,
-lo, -lo, lo, -lo, lo, lo,
-lo, lo, lo, -lo, lo, -lo
];
let ligeo = new THREE.BufferGeometry();
ligeo.setAttribute('position', new THREE.BufferAttribute(lidat.toFloat32(), 3));
let limat = new THREE.LineBasicMaterial({ color: 0xaaaaaa });
let lines = new THREE.LineSegments(ligeo, limat);
env.camStock.lines = lines;
env.camStock.add(lines);
SPACE.world.add(env.camStock);
}
// fight z fighting in threejs
const { scale, position, lines } = env.camStock;
scale.x = x + 0.005;
scale.y = y + 0.005;
scale.y = (cylindrical ? z : y) + 0.005;
scale.z = z + 0.005;
position.x = center.x;
position.y = center.y;
Expand Down
1 change: 1 addition & 0 deletions src/kiri/mode/cam/app/init-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export function menu() {
camStockX: newInput(LANG.cs_wdth_s, {title:LANG.cs_wdth_l, convert:toFloat, bound:bound(0,9999), units}),
camStockY: newInput(LANG.cs_dpth_s, {title:LANG.cs_dpth_l, convert:toFloat, bound:bound(0,9999), units}),
camStockZ: newInput(LANG.cs_hght_s, {title:LANG.cs_hght_l, convert:toFloat, bound:bound(0,9999), units}),
camStockCylinder: newBoolean(LANG.cs_cyli_s, onBooleanClick, {title:LANG.cs_cyli_l, show:() => ui.camStockIndexed.checked}),
separator: newBlank({ class:"set-sep", driven }),
camStockOffset: newBoolean(LANG.cs_offs_s, onBooleanClick, {title:LANG.cs_offs_l}),
camStockIndexed: newBoolean(LANG.cs_indx_s, onBooleanClick, {title:LANG.cs_indx_l}),
Expand Down
47 changes: 46 additions & 1 deletion src/kiri/mode/cam/work/op-area.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class OpArea extends CamOp {
let { direction, down, expand, flats, flatOff, follow } = op;
let { mode, outline, over, rename, smooth, tool } = op;
let { addSlices, axisIndex, color, cutTabs, settings } = state;
let { shadowAt, setToolDiam, tabs, widget, workarea } = state;
let { shadowAt, setToolDiam, stock, tabs, widget, workarea } = state;

let areaTool = new Tool(settings, tool);
let smoothVal = (smooth ?? 0) / 10;
Expand Down Expand Up @@ -137,6 +137,23 @@ class OpArea extends CamOp {
// filter out invalid polys
polys = polys.filter(p => p && p.length > 2);

// for cylindrical (round) indexed stock, clearing must be limited to
// the circular cross-section present at each Z, otherwise the tool
// air-cuts the corners of the (non-existent) rectangular bounding box
let cylinder;
if (stock?.cylindrical && mode === 'clear') {
let radius = stock.radius;
cylinder = {
r2: radius * radius,
// top of the round stock sits one radius above the rotary axis
cz: workarea.top_stock - radius,
cx: stock.center.x,
cy: stock.center.y,
// pad in X so the full length along the rotary axis is preserved
hx: stock.x / 2 + toolDiam
};
}

// process each area separately
let proc = 0;
let pinc = 1 / polys.length;
Expand Down Expand Up @@ -176,6 +193,23 @@ class OpArea extends CamOp {

outer: for (;;)
for (let z of zs) {
// round-stock cross-section (chord) at this cutting height
let band;
if (cylinder) {
let dz = z - cylinder.cz;
let h2 = cylinder.r2 - dz * dz;
// above or below the round stock: no material to clear
if (h2 <= 0) {
if (z === zs.peek()) break outer;
continue;
}
let h = Math.sqrt(h2);
band = newPolygon()
.add(cylinder.cx - cylinder.hx, cylinder.cy - h, z)
.add(cylinder.cx + cylinder.hx, cylinder.cy - h, z)
.add(cylinder.cx + cylinder.hx, cylinder.cy + h, z)
.add(cylinder.cx - cylinder.hx, cylinder.cy + h, z);
}
let slice = newLayer(z);
let layers = slice.output();
let shadow = await shadowAt(z + 0.01);
Expand All @@ -197,6 +231,10 @@ class OpArea extends CamOp {
} else {
POLY.subtract([ area ], shadow, clip, undefined, undefined, 0);
}
// restrict clearing to the round stock cross-section
if (band) {
clip = POLY.trimTo(clip, [ band ]) ?? [];
}
//generate offsets to use
let offsets = [ firstOff ];
//if we need a finish cut, add it
Expand All @@ -212,6 +250,13 @@ class OpArea extends CamOp {
});
// if we see no offsets, re-check the mesh bottom Z then exit
if (outs.length === 0) {
// round stock: thin upper slices can yield no cut even
// though wider slices below still have material. keep
// descending instead of terminating the clear early.
if (cylinder) {
if (z === zs.peek()) break outer;
continue;
}
if (bounds && lzo > bounds.min.z) {
// try a bottom layer matching bottom of selection
zs = [ bounds.min.z ];
Expand Down
7 changes: 7 additions & 0 deletions src/kiri/mode/cam/work/slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
z: camStockZ,
center: newPoint(pos.x, pos.y, pos.z)
};
// cylindrical stock is symmetric about the X (rotary) axis, so its
// radius (= half the stock height) is rotation-invariant. capture it
// here, before any YZ rotation is applied to the stock dimensions.
if (isIndexed && proc.camStockCylinder) {
stock.cylindrical = true;
stock.radius = stock.z / 2;
}
if (!camStockOffset && axisIndex && isIndexed) {
if (axisIndex === 0 || axisIndex === 180) {
// do nothing
Expand Down
2 changes: 2 additions & 0 deletions web/kiri/lang/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,8 @@ self.lang['en-us'] = {
cs_ishg_l: ["show platform grid in indexed mode"],
cs_indx_s: "indexed",
cs_indx_l: ["stock is mounted to a rotatary indexer"],
cs_cyli_s: "cylindrical",
cs_cyli_l: ["diameter = height","reduces air cutting when","using cylindrical stock"],
cs_offe_s: "enable",
cs_offe_l: "enable milling stock",

Expand Down