/** Copyright Stewart Allen -- All Rights Reserved */ const HPI = Math.PI / 2; const RAD2DEG = 180 / Math.PI; class Tool { constructor(settings, id, number) { if (number >= 0) { this.tool = settings.tools.filter(tool => tool.number == number)[0]; } else { this.tool = settings.tools.filter(tool => tool.id == id)[0]; } if (!this.tool) { this.tool = Object.assign({}, settings.tools[0]); this.tool.number = number >= 0 ? number : this.tool.number; this.tool.id = id >= 0 ? id : this.tool.id; } } getID() { return this.tool.id; } getName() { return this.tool.name; } getType() { return this.tool.type; } getNumber() { return this.tool.number; } isMetric() { return this.tool.metric; } unitScale() { return this.isMetric() ? 1 : 25.4; } fluteLength() { return this.unitScale() * this.tool.flute_len; } fluteDiameter() { return this.unitScale() * this.tool.flute_diam; } tipDiameter() { return this.unitScale() * this.tool.taper_tip; } maxDiameter() { return Math.max(this.fluteDiameter(), this.tipDiameter(), this.shaftDiameter()); } traceOffset() { return (this.hasTaper() ? this.tipDiameter() : this.fluteDiameter()) / 2; } contourOffset(step) { const diam = Math.min(this.hasTaper() ? this.tipDiameter() : this.fluteDiameter()); return diam ? diam * step : step; } setTaperLengthFromAngle(angle) { const rad = (this.flute_diam - this.taper_tip) / 2; return this.flute_len = calcTaperLength(rad, angle); } getTaperAngle() { return calcTaperAngle((this.flute_diam - this.taper_tip) / 2, this.flute_len); } shaftLength() { return this.unitScale() * this.tool.shaft_len; } /** * returns the length of the drill tip as a function of the flute diameter, * given a 118 deg point angle. the result is in workspace units. * * @return {number} length of the drill tip. */ drillTipLength() { const drillAngleRad = 140 * Math.PI / 180; return 0.5 * this.fluteDiameter() * Math.sin(drillAngleRad); } shaftDiameter() { return this.unitScale() * this.tool.shaft_diam; } isBallMill() { return this.tool.type === "ballmill"; } isTaperMill() { return this.tool.type === "tapermill"; } isDrill() { return this.tool.type === "drill"; } hasTaper() { return this.isTaperMill(); } /** * generate tool profile with resolution number of pixels. the result is a * Float32Array stored in `this.profile` and `this.profileDim` containing * dimensions of the profile in pixels and shared array buffer size. * * @param {number} resolution - number of pixels for the tool profile * * @return {Tool} this */ generateProfile(resolution) { // generate tool profile let ball = this.isBallMill(), taper = this.hasTaper(), drill = this.isDrill(), tip_diameter = this.tipDiameter(), drill_tip_length = this.drillTipLength(), shaft_offset = this.fluteLength(), flute_diameter = this.fluteDiameter(), shaft_diameter = Math.max(flute_diameter, this.shaftDiameter()), max_diameter = Math.max(flute_diameter, shaft_diameter), shaft_pix_float = max_diameter / resolution, shaft_pix_int = Math.round(shaft_pix_float), shaft_radius_pix_float = shaft_pix_float / 2, flute_radius = flute_diameter / 2, flute_pix_float = flute_diameter / resolution, flute_radius_pix_float = flute_pix_float / 2, tip_pix_float = tip_diameter / resolution, tip_radius_pix_float = tip_pix_float / 2, tip_max_radius_offset = flute_radius_pix_float - tip_radius_pix_float, profile_pix_iter = shaft_pix_int + (1 - shaft_pix_int % 2), toolCenter = (shaft_pix_int - (shaft_pix_int % 2)) / 2, toolOffset = [], larger_shaft = shaft_diameter - flute_diameter > 0.001, rpixsq = flute_radius_pix_float * flute_radius_pix_float, maxo = -Infinity; // for each point in tool profile, check inside radius for (let x = 0; x < profile_pix_iter; x++) { for (let y = 0; y < profile_pix_iter; y++) { let dx = x - toolCenter, dy = y - toolCenter, dist_from_center = Math.sqrt(dx * dx + dy * dy); if (dist_from_center <= flute_radius_pix_float) { // if xy point inside flute radius maxo = Math.max(maxo, dx, dy); // flute offset points let z_offset = 0; if (ball) { let rd = dist_from_center * dist_from_center; z_offset = Math.sqrt(rpixsq - rd) * resolution - flute_radius; // z_offset = (1 - Math.cos((dist_from_center / flute_radius_pix_float) * HPI)) * -flute_radius; } else if (taper && dist_from_center >= tip_radius_pix_float) {// if tapered and not in the flat tip radius z_offset = ((dist_from_center - tip_radius_pix_float) / tip_max_radius_offset) * -shaft_offset; } else if (drill) { z_offset = -dist_from_center / 45; } toolOffset.push(dx, dy, z_offset); } else if (shaft_offset && larger_shaft && dist_from_center <= shaft_radius_pix_float) { // shaft offset points toolOffset.push(dx, dy, -shaft_offset); } } } // convert to shared array for use with minions const profile = new Float32Array(new SharedArrayBuffer(toolOffset.length * 4)); profile.set(toolOffset); this.profile = profile; this.profileDim = { size: shaft_diameter, pix: profile_pix_iter + 2, maxo }; return this; } } function calcTaperAngle(rad, len) { return (Math.atan(rad / len) * RAD2DEG); }; function calcTaperLength(rad, angle) { return (rad / Math.tan(angle)); }; function getToolDiameter(settings, id) { return new Tool(settings, id).fluteDiameter(); }; export { Tool, calcTaperAngle, calcTaperLength, getToolDiameter };