Merge pull request #379 from GridSpace/drill-improvments
Drill improvments
This commit is contained in:
commit
9098f790ca
7 changed files with 114 additions and 62 deletions
|
|
@ -1184,7 +1184,7 @@ CAM.init = function(kiri, api) {
|
|||
return func.selectHolesDone();
|
||||
}
|
||||
func.clearPops();
|
||||
alert = api.show.alert("analyzing parts...", 1000);
|
||||
let alert = api.show.alert("analyzing parts...");
|
||||
holeSelOn = hoveredOp;
|
||||
holeSelOn.classList.add("editing");
|
||||
api.feature.hover = true;
|
||||
|
|
@ -1192,9 +1192,7 @@ CAM.init = function(kiri, api) {
|
|||
func.hover = func.selectHolesHover;
|
||||
func.hoverUp = func.selectHolesHoverUp;
|
||||
|
||||
let settings = API.conf.get();
|
||||
const {tool,mark} = poppedRec //TODO: display some visual difference if mark is selected
|
||||
let diam = new CAM.Tool(settings,tool).fluteDiameter()
|
||||
|
||||
|
||||
|
||||
const widgets = kiri.api.widgets.all()
|
||||
|
|
@ -1205,7 +1203,7 @@ CAM.init = function(kiri, api) {
|
|||
* @returns {Mesh} the created mesh
|
||||
*/
|
||||
function createHoleMesh(widget,drill){
|
||||
let {depth,selected} = drill
|
||||
let {depth,selected, diam} = drill
|
||||
let color = selected ? 0xFF0000:0x39e366
|
||||
let geo = new THREE.CylinderGeometry(diam/2,diam/2,depth,20);
|
||||
const material = new THREE.MeshPhongMaterial( { color } );
|
||||
|
|
@ -1221,10 +1219,10 @@ CAM.init = function(kiri, api) {
|
|||
widget.adds.push(mesh); //for click detection
|
||||
return mesh
|
||||
}
|
||||
|
||||
let meshesCached = widgets.every(widget=>poppedRec.drills[widget.id] != undefined)
|
||||
if( individual && meshesCached ){ // if any widget already has cached holes
|
||||
// console.log("already has cached holes",poppedRec.drills)
|
||||
api.hide.alert(alert)
|
||||
kiri.api.widgets.for(widget => {
|
||||
if(widget.adds){
|
||||
let drills = poppedRec.drills[widget.id]
|
||||
|
|
@ -1245,33 +1243,52 @@ CAM.init = function(kiri, api) {
|
|||
})
|
||||
|
||||
}else{ // if no widget has cached holes
|
||||
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 ?? []
|
||||
// list of all hole centers and if they are selected
|
||||
kiri.api.widgets.for(widget => {
|
||||
const {holes} = centers.find(center=>center.id = widget.id)
|
||||
// console.log(holes)
|
||||
if (!holes.length) unselectHoles(holes);
|
||||
holes.forEach(hole => {
|
||||
createHoleMesh(widget, hole)
|
||||
let alert = api.show.alert("");
|
||||
await CAM.holes(
|
||||
individual,
|
||||
poppedRec,
|
||||
(progress,msg)=>{
|
||||
alert[0]=msg
|
||||
api.show.progress(progress,msg)
|
||||
api.alerts.update()
|
||||
},
|
||||
async centers => {
|
||||
api.show.progress(0)
|
||||
if(! Array.isArray(centers) ){
|
||||
console.log("worker returned a malformed drills response")
|
||||
return
|
||||
}
|
||||
let shadow = centers.some(c=>c.shadowed)
|
||||
api.hide.alert(alert)
|
||||
if(shadow){
|
||||
alert = api.show.alert("Some holes are shadowed by part and are not shown.");
|
||||
}
|
||||
centers = centers ?? []
|
||||
// list of all hole centers and if they are selected
|
||||
kiri.api.widgets.for(widget => {
|
||||
const {holes} = centers.find(center=>center.id = widget.id)
|
||||
// console.log(holes)
|
||||
if (!holes.length) unselectHoles(holes);
|
||||
holes.forEach(hole => {
|
||||
createHoleMesh(widget, hole)
|
||||
})
|
||||
//add hole data to record
|
||||
poppedRec.drills = poppedRec.drills ?? {}
|
||||
poppedRec.drills[widget.id] = holes
|
||||
//give widget access to an array of drill records that refrence it
|
||||
//so that it can be cleared when widget is rotated or mirrored etc.
|
||||
if(!widget.drills){widget.drills = []}
|
||||
widget.drills.push(holes)
|
||||
})
|
||||
//add hole data to record
|
||||
poppedRec.drills = poppedRec.drills ?? {}
|
||||
poppedRec.drills[widget.id] = holes
|
||||
//give widget access to an array of drill records that refrence it
|
||||
//so that it can be cleared when widget is rotated or mirrored etc.
|
||||
if(!widget.drills){widget.drills = []}
|
||||
widget.drills.push(holes)
|
||||
})
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
//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");
|
||||
// api.hide.alert(alert);
|
||||
let escAlert = api.show.alert("[esc] cancels drill editing",1000);
|
||||
setTimeout(() => {
|
||||
api.hide.alert(escAlert);
|
||||
}, 5000);
|
||||
kiri.api.widgets.opacity(0.8);
|
||||
}
|
||||
|
||||
|
|
@ -1782,10 +1799,10 @@ CAM.init = function(kiri, api) {
|
|||
dwell: 'camDrillDwell',
|
||||
lift: 'camDrillLift',
|
||||
mark: 'camDrillMark',
|
||||
precision:'camDrillPrecision',
|
||||
thru: 'camDrillThru',
|
||||
|
||||
})
|
||||
drillOp.inputs = {
|
||||
}).inputs = {
|
||||
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
|
||||
sep: UC.newBlank({class:"pop-sep"}),
|
||||
spindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, show:hasSpindle}),
|
||||
|
|
@ -1795,8 +1812,9 @@ CAM.init = function(kiri, api) {
|
|||
lift: UC.newInput(LANG.cd_lift_s, {title:LANG.cd_lift_l, convert:UC.toFloat, units:true, show:() => !poppedRec.mark}),
|
||||
mark: UC.newBoolean(LANG.cd_mark_s, undefined, {title:LANG.cd_mark_l}),
|
||||
sep: UC.newBlank({class:"pop-sep"}),
|
||||
thru: UC.newInput(LANG.cd_dtru_s, {title:LANG.cd_dtru_l, convert:UC.toFloat, units:true}),
|
||||
sep: UC.newBlank({class:"pop-sep"}),
|
||||
thru: UC.newInput(LANG.cd_dtru_s, {title:LANG.cd_dtru_l, convert:UC.toFloat, units:true,show:() => !poppedRec.mark}),
|
||||
precision:UC.newInput(LANG.cd_prcn_s, {title:LANG.cd_prcn_l, convert:UC.toFloat, units:true,show:() => !poppedRec.mark}),
|
||||
sep: UC.newBlank({class:"pop-sep", }),
|
||||
actions: UC.newRow([
|
||||
UC.newButton(LANG.select, ()=>func.selectHoles(true), {title:LANG.cd_seli_l}),
|
||||
UC.newButton(LANG.cd_sela_s, ()=>func.selectHoles(false), {title:LANG.cd_sela_l})
|
||||
|
|
|
|||
|
|
@ -82,15 +82,23 @@ kiri.load(api => {
|
|||
});
|
||||
};
|
||||
|
||||
//0 or less diameter means select all holes
|
||||
CAM.holes = function(diam,onDone) {
|
||||
|
||||
CAM.holes = function(indiv,rec,onProgress,onDone) {
|
||||
kiri.client.sync();
|
||||
const settings = api.conf.get();
|
||||
const widgets = api.widgets.map();
|
||||
kiri.client.send("cam_holes", { settings, diam }, output => {
|
||||
let res = kiri.codec.decode(output)
|
||||
onDone(res)
|
||||
});
|
||||
return new Promise((res,rej)=>{
|
||||
kiri.client.send("cam_holes", { settings, rec, indiv }, output => {
|
||||
let out = kiri.codec.decode(output)
|
||||
if(out.progress != undefined){
|
||||
//if a progress message,
|
||||
onProgress(out.progress,out.msg)
|
||||
}else{
|
||||
api.hide.alert(alert)
|
||||
onDone(out)
|
||||
res(out)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,11 +164,14 @@ kiri.load(api => {
|
|||
};
|
||||
|
||||
kiri.worker.cam_holes = async function(data, send) {
|
||||
const { settings, diam } = data;
|
||||
const { settings, indiv, rec } = data;
|
||||
const widgets = Object.values(kiri.worker.cache);
|
||||
const fresh = [];
|
||||
for (let widget of widgets) {
|
||||
if (await CAM.holes(settings, widget, diam)) {
|
||||
|
||||
for (let [i,widget] of widgets.entries() ) {
|
||||
if (await CAM.holes(settings, widget, indiv, rec,
|
||||
( prog, msg )=>{ send.data({progress: (i/widgets.length)+(prog/widgets.length),msg})}
|
||||
)){
|
||||
fresh.push(widget);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -441,10 +441,16 @@ CAM.traces = async function(settings, widget, single) {
|
|||
*
|
||||
* @param {Object} settings - settings object
|
||||
* @param {Object} widget - widget object
|
||||
* @param {number} diam - diameter of the drill bit
|
||||
* @param {boolean} individual - if true, drill holes individually
|
||||
* @param {Object} rec - DrillOp record
|
||||
* @param {Function} onProgress - callback function to report progress
|
||||
* @returns {Array} list of hole centers as objects with `x`, `y`, `z`, `depth`, and `selected` properties.
|
||||
*/
|
||||
CAM.holes = async function(settings, widget, diam) {
|
||||
CAM.holes = async function(settings, widget, individual, rec,onProgress) {
|
||||
|
||||
let {tool,mark,precision} = rec //TODO: display some visual difference if mark is selected
|
||||
let toolDiam = new CAM.Tool(settings,tool).fluteDiameter()
|
||||
let diam = individual ? 1 : toolDiam; // sets default diameter when select individual used
|
||||
|
||||
let proc = settings.process,
|
||||
stock = settings.stock || {},
|
||||
|
|
@ -462,23 +468,23 @@ CAM.holes = async function(settings, widget, diam) {
|
|||
zBottom = isIndexed ? camZBottom : camZBottom - zbOff;
|
||||
|
||||
|
||||
let slicerOpts = {flatoff: 0.01}
|
||||
let slicerOpts = {flatoff: 0.001}
|
||||
let slicer = new kiri.cam_slicer(widget,slicerOpts);
|
||||
let zFlats = Object.keys(slicer.zFlat).map(Number).map(z=>[z]).flat()
|
||||
let zFlats = Object.keys(slicer.zFlat).map(Number).map(z=>[z,z-0.002]).flat()
|
||||
|
||||
let intervals = slicer.interval(1,{
|
||||
fit: false, off: -0.01, flats: true
|
||||
})
|
||||
precision = Math.max( 0, precision )
|
||||
let intervals = (precision == 0) ? [] : slicer.interval(
|
||||
precision,
|
||||
{
|
||||
fit: false, off: -0.01, flats: true
|
||||
}
|
||||
)
|
||||
|
||||
let zees = [...zFlats,...intervals]
|
||||
let indices = [...new Set(zees
|
||||
.map(kv => parseFloat(kv).round(5))
|
||||
.filter(z => z !== null)
|
||||
)]
|
||||
let individual = (diam <= 0);
|
||||
|
||||
diam = individual ? 1 : diam; // sets default diameter when select individual used
|
||||
|
||||
let centerDiff = diam * 0.1,
|
||||
area = (diam/2) * (diam/2) * Math.PI,
|
||||
circles = [],
|
||||
|
|
@ -487,14 +493,14 @@ CAM.holes = async function(settings, widget, diam) {
|
|||
function onEach(slice) {
|
||||
slices.push(slice);
|
||||
}
|
||||
let opts = { each: onEach };
|
||||
let opts = { each: onEach, progress: (num,total)=> onProgress(num/total*0.5,"slice") };
|
||||
await slicer.slice(indices, opts);
|
||||
let shadowedDrills = false;
|
||||
// console.log("slices",slices)
|
||||
for (let slice of slices) {
|
||||
for (let [i,slice] of slices.entries()) {
|
||||
for(let top of slice.tops){
|
||||
// console.log("slicing",slice.z,top)
|
||||
slice.shadow = CAM.shadowAt(widget,slice.z, 0)
|
||||
slice.shadow = CAM.shadowAt(widget, slice.z, 0)
|
||||
let inner = top.inner;
|
||||
if (!inner) { //no holes
|
||||
continue;
|
||||
|
|
@ -532,13 +538,13 @@ CAM.holes = async function(settings, widget, diam) {
|
|||
if (!overlap) circles.push(center);
|
||||
}
|
||||
}
|
||||
onProgress(0.5+(i/slices.length*0.25),"recognize circles")
|
||||
}
|
||||
|
||||
let drills = []
|
||||
|
||||
for (let c of circles) {
|
||||
for (let [i,c] of circles.entries()) {
|
||||
let overlapping = c.overlapping
|
||||
.sort((a,b) => b.z - a.z)
|
||||
|
||||
let last = overlapping.shift();
|
||||
while (overlapping.length) {
|
||||
|
|
@ -555,10 +561,11 @@ CAM.holes = async function(settings, widget, diam) {
|
|||
}
|
||||
}
|
||||
if (last.depth != 0) drills.push(last) //add last circle
|
||||
onProgress(0.75+(i/circles.length*0.25),"assemble holes")
|
||||
}
|
||||
|
||||
drills.forEach( h=>{
|
||||
delete h.overlapping //for encoding
|
||||
h.diam = toolDiam // for mesh generation
|
||||
h.selected = (!individual && Math.abs(h.area - area) <= area * 0.05 ); //for same size selection
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -135,9 +135,21 @@ class Slicer {
|
|||
return this;
|
||||
}
|
||||
|
||||
// slice through points at given Z and return polygons
|
||||
/**
|
||||
* Given an array of z-slice positions, generate a list of polygons
|
||||
* representing the intersection of the mesh with the given z-slice
|
||||
* positions.
|
||||
*
|
||||
* @param {number[]} zs - array of z-slice positions
|
||||
* @param {Object} options - slicing parameters
|
||||
* @param {number} [options.flatoff=0.01] - z offset when slicing on a flat surface
|
||||
* @param {boolean} [options.progress=false] - callback with slice progress
|
||||
* @param {function} [options.each] - callback with each slice result
|
||||
* @returns {Promise<Object[]>} - list of slice objects with z and tops (polygons)
|
||||
*/
|
||||
async slice(zs, options) {
|
||||
const opt = this.setOptions(options);
|
||||
// slice through points at given Z and return polygons
|
||||
|
||||
if (!Array.isArray(zs)) {
|
||||
throw "slice zs parameter must be ab array";
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ function show(message, time) {
|
|||
api.event.emit('alert', rec);
|
||||
} else {
|
||||
alerts.push(rec);
|
||||
setTimeout( update, time+0.02);
|
||||
update();
|
||||
}
|
||||
return rec;
|
||||
|
|
|
|||
|
|
@ -521,6 +521,7 @@ const conf = exports({
|
|||
camDrillLift: 2,
|
||||
camDrillMark: false,
|
||||
camDrillThru: 5,
|
||||
camDrillPrecision: 1,
|
||||
camDrillingOn: false,
|
||||
camRegisterSpeed: 1000,
|
||||
camRegisterThru: 5,
|
||||
|
|
|
|||
|
|
@ -599,6 +599,8 @@ self.kiri.lang['en-us'] = {
|
|||
cd_dtru_l: ["additional drill depth below part bottom","overrides global z thru","0 to disable"],
|
||||
cd_mark_s: "marking",
|
||||
cd_mark_l: ["only mark holes, do not drill out","the step down setting is used","to determine how deep to mark"],
|
||||
cd_prcn_s: "precision",
|
||||
cd_prcn_l: ["distance between slices","to search for holes","in workspace units","lower is slower and","uses more memory","0 = only flats"],
|
||||
|
||||
cd_seli_l: ["individually select holes","to drill"],
|
||||
cd_sela_s: "Select Matching Diameter",
|
||||
|
|
|
|||
Loading…
Reference in a new issue