added cylinder selection

This commit is contained in:
bakedpotatolord 2025-07-04 17:41:22 -06:00
commit f3174b681d
4 changed files with 165 additions and 49 deletions

View file

@ -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

View file

@ -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});
}
}
}

View file

@ -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.
*

View file

@ -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) {