From f3174b681dc94db096cdc6a53f1d6a9d07eec8fd Mon Sep 17 00:00:00 2001 From: bakedpotatolord Date: Fri, 4 Jul 2025 17:41:22 -0600 Subject: [PATCH] added cylinder selection --- src/kiri-mode/cam/client.js | 78 ++++++++++++++++++++++++++++--------- src/kiri-mode/cam/driver.js | 62 ++++++++++++++--------------- src/kiri-mode/cam/slice.js | 10 +++++ src/mesh/tool.js | 64 ++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 49 deletions(-) diff --git a/src/kiri-mode/cam/client.js b/src/kiri-mode/cam/client.js index ff341819..96cdd164 100644 --- a/src/kiri-mode/cam/client.js +++ b/src/kiri-mode/cam/client.js @@ -421,7 +421,11 @@ CAM.init = function(kiri, api) { }; func.opAddHelical = () => { - func.opAdd(popOp.helical.new()); + func.traceDone(); + func.surfaceDone(); + let rec = popOp.helical.new(); + rec.cylinders = { /* widget.id: [ faces... ] */ }; + func.opAdd(rec); }; // TAB/TRACE BUTTON HANDLERS @@ -1086,6 +1090,7 @@ CAM.init = function(kiri, api) { func.traceDone(); func.surfaceDone(); func.selectHolesDone(); + func.helicalDone(); }; api.event.on("cam.trace.clear", func.traceClear = () => { func.traceDone(); @@ -1355,27 +1360,64 @@ CAM.init = function(kiri, api) { }); }; + let helicalOn = false; func.selectHelical = () => { - - console.log("select helical") - - - CAM.cylinderFaces((progress,msg)=>{ - api.show.progress(progress,msg) - },(data)=>{ - - console.log(data) - - func.selectHelicalDone(data); - }) - - + console.log("select helical",{helicalOn}) + if (helicalOn) { + return func.surfaceDone(); + } + func.clearPops(); + alert = api.show.alert("analyzing surfaces...", 1000); + let cylinders = poppedRec.cylinders; + CAM.surface_prep(currentIndex * RAD2DEG, () => { + api.hide.alert(alert); + alert = api.show.alert("[esc] cancels surface selection"); + for (let [wid, arr] of Object.entries(cylinders)) { + let widget = api.widgets.forid(wid); + if (widget && arr.length) + for (let faceid of arr) { + CAM.cylinderToggle(widget, faceid, faceids => { + cylinders[widget.id] = faceids; + }); + } + } + }); + helicalOn = hoveredOp; + helicalOn.classList.add("editing"); + api.feature.on_mouse_up = (obj, ev) => { + let { face } = obj; + let min = Math.min(face.a, face.b, face.c); + let faceid = min / 3; + let widget = lastWidget = obj.object.widget; + CAM.cylinderToggle(widget, faceid, ({faces,error}) => { + if (error) { + api.show.alert(error,3000); + return; + } + cylinders[widget.id] = faces; + }); + }; } - func.selectHelicalDone = () =>{ - - console.log("select helical done") + func.helicalDone = () =>{ + if (!(helicalOn && poppedRec && poppedRec.cylinders)) { + return; + } + let {cylinders} = poppedRec; + for (let wid of Object.keys(cylinders)) { + let widget = api.widgets.forid(wid); + if (widget) { + CAM.cylinderClear(widget); + } else { + delete cylinders[wid]; + } + } + api.hide.alert(alert); + api.feature.on_mouse_up = undefined; + helicalOn.classList.remove("editing"); + helicalOn = false; + console.log("helicalOn set to false") } // COMMON TAB/TRACE EVENT HANDLERS diff --git a/src/kiri-mode/cam/driver.js b/src/kiri-mode/cam/driver.js index 2f50574a..81edcfc7 100644 --- a/src/kiri-mode/cam/driver.js +++ b/src/kiri-mode/cam/driver.js @@ -37,6 +37,11 @@ kiri.load(api => { widget.selectFaces(Object.values(widget._surfaces).flat()); }; + CAM.cylinder_show = function(widget) { + widget.selectFaces(Object.values(widget._cylinders).flat()); + }; + + CAM.surface_toggle = function(widget, face, radians, ondone) { let surfaces = widget._surfaces = widget._surfaces || {}; for (let [root, faces] of Object.entries(surfaces)) { @@ -108,28 +113,31 @@ kiri.load(api => { } - CAM.cylinderToggle = (widget, face, radians, onDone) => { - let surfaces = widget._cylinders = widget._cylinders || {}; - for (let [root, faces] of Object.entries(surfaces)) { - if (faces.contains(face)) { + CAM.cylinderToggle = (widget, face, onDone) => { + let cyls = widget._cylinders = widget._cylinders || {}; + for (let [root, faces] of Object.entries(cyls)) { + if (faces.contains(face)) { // if a face group contains the selected face // delete this face group - delete surfaces[root]; - CAM.surface_show(widget); - ondone(Object.keys(surfaces).map(v => parseInt(v))); + delete cyls[root]; + CAM.cylinder_show(widget); + onDone(Object.keys(cyls).map(v => parseInt(v))); return; } } - kiri.client.send("cam_surface_find", { id: widget.id, face, radians }, faces => { - if (faces.length) { - surfaces[face] = faces; - CAM.surface_show(widget); + //send + kiri.client.send("cam_cylinder_find", { id: widget.id, face }, ({faces,error}) => { + if (faces?.length) { + cyls[face] = faces; + CAM.cylinder_show(widget); } - ondone(Object.keys(surfaces).map(v => parseInt(v))); + + onDone({faces:Object.keys(cyls).map(v => parseInt(v)),error}); }); } - CAM.cylinderClear = (onProgress, onDone) => { - + CAM.cylinderClear = (widget) => { + widget.selectFaces([]); + widget._cylinders = {}; } } @@ -143,10 +151,7 @@ kiri.load(api => { } }; - CAM.surface_find = function(widget, faces, radians) { - CAM.surface_prep(widget); - return widget.tool.findConnectedSurface(faces, radians || 0, 0.0); - }; + kiri.worker.cam_surfaces = function(data, send) { const { settings, index } = data; @@ -215,19 +220,14 @@ kiri.load(api => { } } ))); } - kiri.worker.cam_cylinder_faces = async function(data,send){ - const { settings, rec } = data; - const widgets = Object.values(kiri.worker.cache); - console.log("hi from the worker") - - for (let [i,widget] of widgets.entries() ) { - CAM.surface_prep(widget); - if (await CAM.cylinders(settings, widget,)) { - - - - } - + kiri.worker.cam_cylinder_find = async function(data,send){ + const { id, face, settings } = data; + const widget = kiri.worker.cache[id]; + try{ + const faces = CAM.cylinder_find(widget, face); + send.done({faces}); + }catch(error){ + send.done({error}); } } } diff --git a/src/kiri-mode/cam/slice.js b/src/kiri-mode/cam/slice.js index 472d57d7..ca2ed27d 100644 --- a/src/kiri-mode/cam/slice.js +++ b/src/kiri-mode/cam/slice.js @@ -568,6 +568,16 @@ CAM.cylinders = async (settings, widget, opts) => { return Array.from(cylinderTriangles.values()) } +CAM.surface_find = function(widget, faces, radians) { + CAM.surface_prep(widget); + return widget.tool.findConnectedSurface(faces, radians || 0, 0.0); +}; + +CAM.cylinder_find = (widget, face)=>{ + CAM.surface_prep(widget); + return widget.tool.findCylinderSurface(face); +} + /** * Generate a list of holes in the model based on the given diameter. * diff --git a/src/mesh/tool.js b/src/mesh/tool.js index 851058bd..2dc1492b 100644 --- a/src/mesh/tool.js +++ b/src/mesh/tool.js @@ -238,6 +238,8 @@ mesh.tool = class MeshTool { fn++; } } + //faces are the normals in groups of 6 [nx,] + this.indexed = { faces, sides, sideExt }; @@ -304,6 +306,68 @@ mesh.tool = class MeshTool { return faces; } + findCylinderSurface(face){ + face = Number(face) + + const radianTolerance = 8 * (Math.PI/180); + //helper function + const dissimilar = (a,b,c) => a!==b && a!==c && b!==c + + let found = {} + let out = [face] + const {vertices}= this + const {faces} = this.getIndex(); + + const zs = [vertices[face*3+2],vertices[face*3+5],vertices[face*3+8]] + .map(z => Math.round(z*1000)/1000) + + if(dissimilar(...zs)){ + throw "face must have only 2 Z values" + }else if(Math.abs( faces[face * 6 + 2] > 1e-6 )){ + throw "face's normal must be perpendicular to Z axis" + } + const lowZ = Math.min(zs) + const highZ = Math.max(zs) + + const checked = {}; + const check = [face] + found[face] = 1; + while (check.length) { + const face = check.shift(); + const froot = face * 6; + const vroot = face * 9 + + // filter faces to those perpendicular to +z axis + if ( Math.abs( faces[froot + 2] > 1e-6 )) continue; + //filter faces with verts don't share the z values of the first face + if( + !(vertices[vroot + 2] !== lowZ || vertices[vroot + 2] !== highZ) && + !(vertices[vroot + 5] !== lowZ || vertices[vroot + 5] !== highZ) && + !(vertices[vroot + 8] !== lowZ || vertices[vroot + 8] !== highZ) + ) continue; + + const fadj = this.getAdjacentFaces(face); + for (let f of fadj) { + if (found[f] || checked[f]) { + continue; + } + const aroot = f * 6; + let sum = 0; + for (let i=0; i<3; i++) { + sum += Math.pow(faces[froot + i] - faces[aroot + i], 2); + } + const fn = Math.sqrt(sum); + if (fn <= radianTolerance) { + out.push(f); + check.push(f) + checked[f] = 1; + found[f] = 1; + } + } + } + return out; + } + // given a list of faces (indices), return an array of closed // polylines that represent the outlines of each discrete island generateOutlines(list) {