CAM routing refactor, area operation, and taper ball tool implementation
Major routing improvements: - Refactor depth-first routing algorithm for better path optimization - Reimplemented outline, roughing, pocket, and trace as area operation wrappers - Add pocket shadow travel routing with optimized descent using travel bounds - Improve contour routing with arc detection and polyEmit arc strategy - Add area smoothing and ease-down options - Fix outline handling with thru holes and surface reversal on tip2tip - Move widget shadow methods into widget class for faster hole detection - Better camInnerFirst support and proper feed/plunge defaults Tool system improvements: - Implement taper ball tool type with full support - Add renderTool2() for profile-based visualization - Convert tool menu to init code with bound vars - Fix auto tool and auto spindle for prep operations - Update tool profile generation for taperball geometry - Add calcTaperBallExtent() for proper ball tangency calculations Code cleanup: - Remove duplicated slicer code from specialized slicers - Asyncify shadowAt operations - Deprecate pocket engrave operation - Simplify upNover implementation - Fix animation with new tool+settings requirements
This commit is contained in:
parent
761568b533
commit
e0b1c4e31f
44 changed files with 1607 additions and 2894 deletions
|
|
@ -1,18 +1,16 @@
|
|||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||
|
||||
import { base } from '../../../geo/base.js';
|
||||
import { newPoint } from '../../../geo/point.js';
|
||||
import { newOrderedLine } from '../../../geo/line.js';
|
||||
|
||||
const { config } = base;
|
||||
import {
|
||||
checkOverUnderOn,
|
||||
makeZLine,
|
||||
removeDuplicateLines,
|
||||
intersectPoints
|
||||
} from '../../../geo/slicer.js';
|
||||
|
||||
/** Slicer used in Topo3 and Topo4 to find contour lines */
|
||||
export class Slicer {
|
||||
|
||||
static intersectPoints = intersectPoints;
|
||||
static checkOverUnderOn = checkOverUnderOn;
|
||||
static removeDuplicateLines = removeDuplicateLines;
|
||||
|
||||
constructor(index = 0) {
|
||||
this.min = Infinity;
|
||||
this.max = -Infinity;
|
||||
|
|
@ -143,177 +141,3 @@ export class Slicer {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* given a point, append to the correct
|
||||
* 'where' objec tarray (on, over or under)
|
||||
*
|
||||
* @param {Point} p
|
||||
* @param {number} z offset
|
||||
* @param {Obejct} where
|
||||
*/
|
||||
function checkOverUnderOn(p, z, where) {
|
||||
let delta = p.z - z;
|
||||
if (Math.abs(delta) < config.precision_slice_z) { // on
|
||||
where.on.push(p);
|
||||
} else if (delta < 0) { // under
|
||||
where.under.push(p);
|
||||
} else { // over
|
||||
where.over.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a point over and under a z offset, calculate
|
||||
* and return the intersection point on that z plane
|
||||
*
|
||||
* @param {Point} over
|
||||
* @param {Point} under
|
||||
* @param {number} z offset
|
||||
* @returns {Point} intersection point
|
||||
*/
|
||||
function intersectPoints(over, under, z) {
|
||||
let ip = [];
|
||||
for (let i = 0; i < over.length; i++) {
|
||||
for (let j = 0; j < under.length; j++) {
|
||||
ip.push(over[i].intersectZ(under[j], z));
|
||||
}
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure points are unique with a cache/key algorithm
|
||||
*/
|
||||
function getCachedPoint(phash, p) {
|
||||
let cached = phash[p.key];
|
||||
if (!cached) {
|
||||
phash[p.key] = p;
|
||||
return p;
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two points and hints about their edges,
|
||||
* return a new Line object with points sorted
|
||||
* lexicographically by key. This allows for future
|
||||
* line de-duplication and joins.
|
||||
*
|
||||
* @param {Object} phash
|
||||
* @param {Point} p1
|
||||
* @param {Point} p2
|
||||
* @param {boolean} [coplanar]
|
||||
* @param {boolean} [edge]
|
||||
* @returns {Line}
|
||||
*/
|
||||
function makeZLine(phash, p1, p2, coplanar, edge) {
|
||||
p1 = getCachedPoint(phash, p1);
|
||||
p2 = getCachedPoint(phash, p2);
|
||||
let line = newOrderedLine(p1, p2);
|
||||
line.coplanar = coplanar || false;
|
||||
line.edge = edge || false;
|
||||
return line;
|
||||
}
|
||||
|
||||
/**
|
||||
* eliminate duplicate lines and interior-only lines (coplanar)
|
||||
*
|
||||
* lines are sorted using lexicographic point keys such that
|
||||
* they are comparable even if their points are reversed. hinting
|
||||
* for deletion, co-planar and suspect shared edge is detectable at
|
||||
* this time.
|
||||
*
|
||||
* @param {Line[]} lines
|
||||
* @returns {Line[]}
|
||||
*/
|
||||
function removeDuplicateLines(lines, debug) {
|
||||
let output = [],
|
||||
tmplines = [],
|
||||
points = [],
|
||||
pmap = {};
|
||||
|
||||
function cachePoint(p) {
|
||||
let cp = pmap[p.key];
|
||||
if (cp) return cp;
|
||||
points.push(p);
|
||||
pmap[p.key] = p;
|
||||
return p;
|
||||
}
|
||||
|
||||
function addLinesToPoint(point, line) {
|
||||
cachePoint(point);
|
||||
if (!point.group) point.group = [line];
|
||||
else point.group.push(line);
|
||||
}
|
||||
|
||||
// mark duplicates for deletion preserving edges
|
||||
lines.sort(function (l1, l2) {
|
||||
if (l1.key === l2.key) {
|
||||
l1.del = !l1.edge;
|
||||
l2.del = !l2.edge;
|
||||
if (debug && (l1.del || l2.del)) {
|
||||
console.log('dup', l1, l2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return l1.key < l2.key ? -1 : 1;
|
||||
});
|
||||
|
||||
// associate points with their lines, cull deleted
|
||||
lines.forEach(function (line) {
|
||||
if (!line.del) {
|
||||
tmplines.push(line);
|
||||
addLinesToPoint(line.p1, line);
|
||||
addLinesToPoint(line.p2, line);
|
||||
}
|
||||
});
|
||||
|
||||
// merge collinear lines
|
||||
points.forEach(function (point) {
|
||||
if (point.group.length != 2) return;
|
||||
let l1 = point.group[0],
|
||||
l2 = point.group[1];
|
||||
if (l1.isCollinear(l2)) {
|
||||
l1.del = true;
|
||||
l2.del = true;
|
||||
// find new endpoints that are not shared point
|
||||
let p1 = l1.p1 != point ? l1.p1 : l1.p2,
|
||||
p2 = l2.p1 != point ? l2.p1 : l2.p2,
|
||||
newline = newOrderedLine(p1, p2);
|
||||
// remove deleted lines from associated points
|
||||
p1.group.remove(l1);
|
||||
p1.group.remove(l2);
|
||||
p2.group.remove(l1);
|
||||
p2.group.remove(l2);
|
||||
// associate new line with points
|
||||
p1.group.push(newline);
|
||||
p2.group.push(newline);
|
||||
// add new line to lines array
|
||||
newline.edge = l1.edge || l2.edge;
|
||||
tmplines.push(newline);
|
||||
}
|
||||
});
|
||||
|
||||
// mark duplicates for deletion
|
||||
// but preserve one if it's an edge
|
||||
tmplines.sort(function (l1, l2) {
|
||||
if (l1.key === l2.key) {
|
||||
l1.del = true;
|
||||
l2.del = !l2.edge;
|
||||
return 0;
|
||||
}
|
||||
return l1.key < l2.key ? -1 : 1;
|
||||
});
|
||||
|
||||
// create new line array culling deleted
|
||||
tmplines.forEach(function (line) {
|
||||
if (!line.del) {
|
||||
output.push(line);
|
||||
line.p1.group = null;
|
||||
line.p2.group = null;
|
||||
}
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue