Merge branch 'rel-4.1' of https://github.com/bakedPotatoLord/grid-apps into outline-order

This commit is contained in:
bakedpotatolord 2025-05-06 20:51:44 -06:00
commit 96b36adbb3
11 changed files with 225 additions and 48 deletions

52
.github/workflows/build-test.yml vendored Normal file
View file

@ -0,0 +1,52 @@
name: Test Electron App Production Build
on:
# Runs on pushes targeting the default branch
push:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
build-ubuntu:
runs-on: "ubuntu-latest"
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm install
- name: Build Electron app
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
run: npm run build
build-windows:
runs-on: "windows-latest"
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm install
- name: Build Electron app
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
run: npm run build

27
.github/workflows/prod-test.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Test NPM Production Build
on:
# Runs on pushes targeting the default branch
push:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
build:
runs-on: "ubuntu-latest"
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup And Install dependencies
run: npm run setup
- name: dry run production build
run: npm run prod-dryrun

View file

@ -126,7 +126,12 @@ self.kiri.load(api => {
}, },
drain() { drain() {
while (socket.open && socket.q.length) { while (socket.open && socket.q.length) {
socket.ws.send(JSON.stringify(socket.q.shift())); let data = socket.q.shift();
if (data instanceof ArrayBuffer) {
socket.ws.send(data);
} else {
socket.ws.send(JSON.stringify(data));
}
} }
}, },
send(msg) { send(msg) {
@ -614,6 +619,16 @@ self.kiri.load(api => {
} }
} }
function file_send(file, data) {
if (selected?.rec?.host) {
let { rec } = selected;
host = rec.host;
serial = rec.serial;
password = rec.code;
send(file.name, data, false);
}
}
function calcFG(bg) { function calcFG(bg) {
let r = parseInt(bg.substring(0, 2), 16); let r = parseInt(bg.substring(0, 2), 16);
let g = parseInt(bg.substring(2, 4), 16); let g = parseInt(bg.substring(2, 4), 16);
@ -1007,7 +1022,7 @@ self.kiri.load(api => {
]) ])
]), ]),
h.div({ class: "t-body t-inset f-col gap3 pad4 grow" }, [ h.div({ id: "bbl_drop", class: "t-body t-inset f-col gap3 pad4 grow" }, [
h.div({ class: "set-header", onclick() { h.div({ class: "set-header", onclick() {
file_list(); file_list();
} }, h.a({ class: "flex f-row grow a-center" }, [ } }, h.a({ class: "flex f-row grow a-center" }, [
@ -1050,7 +1065,10 @@ self.kiri.load(api => {
file_print(selected.file.path); file_print(selected.file.path);
api.alerts.show(`printing: ${selected.file.path}`,2); api.alerts.show(`printing: ${selected.file.path}`,2);
}}), }}),
]) ]),
h.div({ id: "bbl_drop_zone", class: "full hide allpe", style: "background-color: rgba(0,255,0,0.75); font-weight: bold" }, [
h.label('drop to upload')
]),
]), ]),
h.div({ class: "t-body t-inset f-col gap3 pad4" }, [ h.div({ class: "t-body t-inset f-col gap3 pad4" }, [
h.label({ class: "set-header dev-sel" }, [ h.label({ class: "set-header dev-sel" }, [
@ -1122,6 +1140,41 @@ self.kiri.load(api => {
$('bbl_file_delete').disabled = $('bbl_file_delete').disabled =
$('bbl_file_print').disabled = false; $('bbl_file_print').disabled = false;
}); });
let drop = modal.bbl_drop;
let drop_zone = modal.bbl_drop_zone;
let drop_timer;
drop.ondragenter = (ev) => {
if (!selected?.rec?.serial) {
return;
}
drop.classList.add('nope');
drop_zone.classList.remove('hide');
clearTimeout(drop_timer);
};
drop_zone.ondragleave = (ev) => {
clearTimeout(drop_timer);
drop_timer = setTimeout(() => {
drop.classList.remove('nope');
drop_zone.classList.add('hide');
}, 50);
};
drop_zone.ondrop = (ev) => {
drop.classList.remove('nope');
drop_zone.classList.add('hide');
ev.preventDefault();
ev.stopPropagation();
let files = ev.dataTransfer.files;
console.log('filedrop', files);
for (let file of files) {
let reader = new FileReader();
// reader.file = file;
reader.onloadend = (ev) => {
let data = ev.target.result;
file_send(file, data);
};
reader.readAsArrayBuffer(file);
}
};
}); });
api.event.on("modal.show", which => { api.event.on("modal.show", which => {
@ -1251,6 +1304,7 @@ self.kiri.load(api => {
console.log('Bambu Send', res); console.log('Bambu Send', res);
if (res.sent) { if (res.sent) {
api.alerts.show('File Sent', 3); api.alerts.show('File Sent', 3);
file_list();
} else { } else {
api.alerts.show('File Send Error', 3); api.alerts.show('File Send Error', 3);
} }

View file

@ -349,9 +349,13 @@ module.exports = async (server) => {
wsopen.push(ws); wsopen.push(ws);
debug && util.log('ws open', req.url, wsopen.length); debug && util.log('ws open', req.url, wsopen.length);
wsend({ found }); wsend({ found });
ws.on('message', msg => { let binaryData;
msg = JSON.parse(msg); ws.on('message', (msg, isBinary) => {
let { cmd, host, code, serial, path, amsmap, direct, frames } = msg; if (isBinary) {
return binaryData = msg;
}
msg = isBinary ? msg : JSON.parse(msg.toString());
let { cmd, host, code, serial, path, amsmap, direct, frames, file, data } = msg;
switch (cmd) { switch (cmd) {
case "monitor": case "monitor":
get_mqtt(host, code, serial, message => { get_mqtt(host, code, serial, message => {

View file

@ -1,6 +1,6 @@
{ {
"name": "grid-apps", "name": "grid-apps",
"version": "4.1.6", "version": "4.1.7",
"description": "grid.space 3d slicing & modeling tools", "description": "grid.space 3d slicing & modeling tools",
"author": "Stewart Allen <sa@grid.space>", "author": "Stewart Allen <sa@grid.space>",
"license": "MIT", "license": "MIT",
@ -36,7 +36,7 @@
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^6.1.1", "@fortawesome/fontawesome-free": "^6.1.1",
"@gridspace/app-server": "^0.0.15", "@gridspace/app-server": "^0.0.16",
"@gridspace/net-level-client": "^0.2.3", "@gridspace/net-level-client": "^0.2.3",
"@tracespace/parser": "^5.0.0-next.0", "@tracespace/parser": "^5.0.0-next.0",
"@tweenjs/tween.js": "^16.6.0", "@tweenjs/tween.js": "^16.6.0",
@ -55,7 +55,7 @@
"three-mesh-bvh": "^0.7.6", "three-mesh-bvh": "^0.7.6",
"uglify-js": "3.14.5", "uglify-js": "3.14.5",
"validator": ">=13.7.0", "validator": ">=13.7.0",
"ws": "^7.5.5" "ws": "^8.18.2"
}, },
"devDependencies": { "devDependencies": {
"@electron/notarize": "^3.0.0", "@electron/notarize": "^3.0.0",
@ -72,6 +72,7 @@
"setup": "npm i && cd mods && npm i", "setup": "npm i && cd mods && npm i",
"dev": "gs-app-server --debug", "dev": "gs-app-server --debug",
"prod": "gs-app-server", "prod": "gs-app-server",
"prod-dryrun": "gs-app-server --dryrun",
"start": "npm run prebuild && electron .", "start": "npm run prebuild && electron .",
"start-dev": "npm run prebuild && electron . --devel", "start-dev": "npm run prebuild && electron . --devel",
"start-dbg": "npm run prebuild && electron . --debugg", "start-dbg": "npm run prebuild && electron . --debugg",

View file

@ -1190,9 +1190,7 @@ CAM.init = function(kiri, api) {
const {tool,mark} = poppedRec //TODO: display some visual difference if mark is selected const {tool,mark} = poppedRec //TODO: display some visual difference if mark is selected
let diam = new CAM.Tool(settings,tool).fluteDiameter() let diam = new CAM.Tool(settings,tool).fluteDiameter()
api.hide.alert(alert);
alert = api.show.alert("[esc] cancels drill editing");
kiri.api.widgets.opacity(0.8);
const widgets = kiri.api.widgets.all() const widgets = kiri.api.widgets.all()
/** /**
* creates a mesh for a hole and adds it to a widget * creates a mesh for a hole and adds it to a widget
@ -1242,10 +1240,16 @@ CAM.init = function(kiri, api) {
}else{ // if no widget has cached holes }else{ // if no widget has cached holes
await CAM.holes(individual?0:diam,(centers)=>{ await CAM.holes(individual?0:diam,(centers)=>{
let shadow = centers.some(c=>c.shadowed)
if(shadow){
alert = api.show.alert("Some holes are shadowed by part and are not shown.");
}
centers = centers ?? [] centers = centers ?? []
//flattened list of all hole centers and if they are selected //flattened list of all hole centers and if they are selected
kiri.api.widgets.for(widget => { kiri.api.widgets.for(widget => {
const {holes} = centers.find(center=>center.id = widget.id) const {holes} = centers.find(center=>center.id = widget.id)
console.log(holes)
if (!holes.length) unselectHoles(holes); if (!holes.length) unselectHoles(holes);
holes.forEach(hole => { holes.forEach(hole => {
createHoleMesh(widget, hole) createHoleMesh(widget, hole)
@ -1260,6 +1264,10 @@ CAM.init = function(kiri, api) {
}) })
}); });
} }
//hide the alert once hole meshes are calculated on the worker, and then added to the scene
api.hide.alert(alert);
alert = api.show.alert("[esc] cancels drill editing");
kiri.api.widgets.opacity(0.8);
} }
func.selectHolesHover = function(data) { func.selectHolesHover = function(data) {

View file

@ -168,6 +168,7 @@ kiri.load(api => {
send.done(kiri.codec.encode(fresh.map(widget => { return { send.done(kiri.codec.encode(fresh.map(widget => { return {
id: widget.id, id: widget.id,
holes: widget.holes, holes: widget.holes,
shadowed:widget.shadowedDrills
} } ))); } } )));
} }
} }

View file

@ -460,14 +460,20 @@ CAM.holes = async function(settings, widget, diam) {
// keeps the z bottom relative to the part when z align changes // keeps the z bottom relative to the part when z align changes
zBottom = isIndexed ? camZBottom : camZBottom - zbOff; zBottom = isIndexed ? camZBottom : camZBottom - zbOff;
let slicer = new kiri.cam_slicer(widget);
let zees = Object.assign({}, slicer.zFlat); let slicerOpts = {flatoff: 0.01}
let indices = [...new Set(Object.keys(zees) let slicer = new kiri.cam_slicer(widget,slicerOpts);
let zFlats = Object.keys(slicer.zFlat).map(Number).map(z=>[z]).flat()
let intervals = slicer.interval(1,{
fit: false, off: -0.01, flats: true
})
let zees = [...zFlats,...intervals]
let indices = [...new Set(zees
.map(kv => parseFloat(kv).round(5)) .map(kv => parseFloat(kv).round(5))
.filter(z => z !== null) .filter(z => z !== null)
)] )]
let centerDiff = diam * 0.1, let centerDiff = diam * 0.1,
area = (diam/2) * (diam/2) * Math.PI, area = (diam/2) * (diam/2) * Math.PI,
areaDelta = area * 0.05, areaDelta = area * 0.05,
@ -478,45 +484,52 @@ CAM.holes = async function(settings, widget, diam) {
function onEach(slice) { function onEach(slice) {
slices.push(slice); slices.push(slice);
} }
let opts = { each: onEach };
let opts = { each: onEach, over: false, flatoff: 0, edges: true, openok: false, lines: false };
await slicer.slice(indices, opts); await slicer.slice(indices, opts);
const tslices = slices.map(s=>s.tops).flat() let shadowedDrills = false;
// console.log("tslices",tslices) // console.log("slices",slices)
for (let slice of tslices) { for (let slice of slices) {
slice.shadow = CAM.shadowAt(widget,slice.z, 0) for(let top of slice.tops){
let inner = slice.inner; // console.log("slicing",slice.z,top)
if (!inner) { //no holes slice.shadow = CAM.shadowAt(widget,slice.z, 0)
continue; let inner = top.inner;
} if (!inner) { //no holes
for (let poly of inner) {
let center = poly.calcCircleCenter();
center.selected = (!individual && poly.circularity() >= 0.985 && Math.abs(poly.area() - area) <= areaDelta );
if (center.isInPolygon(slice.shadow)) {
//TODO: this does not work yet. needs to be implemented
continue; continue;
} }
let overlap = false; for (let poly of inner) {
for (let [i,drill] of drills.entries()) { let center = poly.calcCircleCenter();
let dist = drill.distTo2D(center) center.area = poly.area();
if (dist <= centerDiff) { // too close // console.log("center",center)
// console.log("overlap",center,drill); center.selected = (!individual && poly.circularity() >= 0.985 && Math.abs(center.area - area) <= areaDelta );
if(center.z > drill.z) { //if current is higher than old if (center.isInPolygon(slice.shadow)) {
drills[i] = center; //replace with top point // if shadowed, don't add, and inform client
drills[i].depth = center.z-drill.z shadowedDrills = true;
}else if(center.z <= drill.z-drill.depth){// if current is lower or same as old
drills[i].depth = drill.z-center.z
}
// if overlapping, don't add and continue
overlap = true;
continue; continue;
} }
let overlap = false;
for (let [i,drill] of drills.entries()) {
let dist = drill.distTo2D(center)
//if on the same xy point, and both have the same selection value
if (dist <= centerDiff && drill.selected == center.selected) {
// console.log("overlap",center,drill);
if(center.z > drill.z) { //if current is higher than old
drills[i] = center; //replace with top point
drills[i].depth = center.z-drill.z
}else if(center.z <= drill.z-drill.depth){// if current is lower or same as old
drills[i].depth = drill.z-center.z
}
// if overlapping, don't add and continue
overlap = true;
continue;
}
}
center.depth = 0;
if(!overlap) drills.push(center);
} }
center.depth = 0;
if(!overlap) drills.push(center);
} }
} }
drills = drills.filter(drill => drill.depth > 0) drills = drills.filter(drill => drill.depth > 0)
widget.shadowedDrills = shadowedDrills
widget.holes = drills; widget.holes = drills;
return drills; return drills;
} }

View file

@ -5,7 +5,7 @@
let terms = { let terms = {
COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved", COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved",
LICENSE: "See the license.md file included with the source distribution", LICENSE: "See the license.md file included with the source distribution",
VERSION: (is_self ? self : this).debug_version || "4.1.6" VERSION: (is_self ? self : this).debug_version || "4.1.7"
}; };
if (typeof(module) === 'object') { if (typeof(module) === 'object') {

View file

@ -646,6 +646,23 @@ details[open] summary::after {
.contents { .contents {
display: contents; display: contents;
} }
.full {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.nope, .nope * {
pointer-events: none;
}
.allpe {
pointer-events: auto !important;
}
.hide { .hide {
display: none !important; display: none !important;
} }

View file

@ -1,5 +1,5 @@
{ {
"name": "Kiri:Moto 4.1.6", "name": "Kiri:Moto 4.1.7",
"short_name": "Kiri:Moto", "short_name": "Kiri:Moto",
"description": "Slicer for 3D printers, CNC mills, laser cutters and more", "description": "Slicer for 3D printers, CNC mills, laser cutters and more",
"start_url": "/kiri/", "start_url": "/kiri/",