add gear generator to mesh tool
This commit is contained in:
parent
7988abcb37
commit
cc93c9a6e9
6 changed files with 143 additions and 2 deletions
|
|
@ -992,6 +992,20 @@ class Polygon {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {int[]} verts flat array of x,y,z vertices
|
||||
*/
|
||||
addVerts(verts) {
|
||||
for (let i=0; i<verts.length; ) {
|
||||
this.add(
|
||||
verts[i++],
|
||||
verts[i++],
|
||||
verts[i++]
|
||||
);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* append point to polygon and return point
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -318,6 +318,40 @@ let add = {
|
|||
});
|
||||
api.modal.bound.gencyl.focus();
|
||||
}
|
||||
},
|
||||
|
||||
gear(opt = {}) {
|
||||
function gengear(teeth, module, angle) {
|
||||
api.modal.hide();
|
||||
worker.model_gen_gear({
|
||||
teeth: parseInt(teeth),
|
||||
module: parseFloat(module),
|
||||
angle: parseFloat(angle)
|
||||
}).then(gear => {
|
||||
const nmdl = new mesh.model({ file: "gear", mesh: gear.toFloat32() });
|
||||
const ngrp = group.new([ nmdl ]);
|
||||
ngrp.floor();
|
||||
});
|
||||
}
|
||||
api.modal.dialog({
|
||||
title: "gear generator",
|
||||
body: [ h.div({ class: "addgear" }, [
|
||||
h.label('number of teeth'),
|
||||
h.input({ value: opt.teeth || 20, size: 5, id: "gteeth" }),
|
||||
h.label('module (diam / teeth)'),
|
||||
h.input({ value: opt.module || 3, size: 5, id: "gmodule" }),
|
||||
h.label('pressure angle'),
|
||||
h.input({ value: opt.angle || 20, size: 5, id: "gangle" }),
|
||||
h.button({ _: "create", onclick() {
|
||||
gengear(
|
||||
api.modal.bound.gteeth.value,
|
||||
api.modal.bound.gmodule.value,
|
||||
api.modal.bound.gangle.value,
|
||||
)
|
||||
} })
|
||||
]) ]
|
||||
});
|
||||
api.modal.bound.gteeth.focus();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -373,6 +373,7 @@ function ui_build() {
|
|||
h.div({ class: "pop"}, [
|
||||
h.button({ _: 'cylinder', onclick: add.cylinder }),
|
||||
h.button({ _: 'cube', onclick: add.cube }),
|
||||
h.button({ _: 'gear', onclick: add.gear }),
|
||||
devel ? h.button({ _: 'input', onclick: add.input }) : undefined
|
||||
])
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -737,6 +737,79 @@ mesh.tool = class MeshTool {
|
|||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
generateGear(numTeeth, module, pressureAngle) {
|
||||
// Adapted from: Public Domain Parametric Involute Spur Gear by Leemon Baird, 2011, Leemon@Leemon.com http://www.thingiverse.com/thing:5505
|
||||
// see also http://grabcad.com/questions/tutorial-how-to-model-involute-gears-in-solidworks-and-show-design-intent
|
||||
|
||||
const pi = Math.PI;
|
||||
|
||||
// degrees to radians
|
||||
function degrees_to_radians(theta) { return theta / 180 * pi; }
|
||||
|
||||
// polar to cartesian
|
||||
function polar(r, theta) { return [r * Math.sin(theta), r * Math.cos(theta)]; }
|
||||
|
||||
// point on involute curve
|
||||
function q6(b, s, t, d) { return polar(d, s * (iang(b, d) + t)); }
|
||||
|
||||
// unwind this many degrees to go from r1 to r2
|
||||
function iang(r1, r2) { return Math.sqrt((r2 / r1) * (r2 / r1) - 1) - Math.acos(r1 / r2); }
|
||||
|
||||
// radius a fraction f up the curved side of the tooth
|
||||
function q7(f, r, b, r2, t, s) { return q6(b, s, t, (1 - f) * Math.max(b, r) + f * r2); }
|
||||
|
||||
// rotate an array of 2d points
|
||||
function rotate(points_array, angle) {
|
||||
let answer = [];
|
||||
for (let i = 0; i < points_array.length; i++) {
|
||||
let x = points_array[i][0];
|
||||
let y = points_array[i][1];
|
||||
let xr = x * Math.cos(angle) - y * Math.sin(angle);
|
||||
let yr = y * Math.cos(angle) + x * Math.sin(angle);
|
||||
answer.push([xr, yr]);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
// involute gear maker
|
||||
function build_gear(number_of_teeth) {
|
||||
let p = mm_per_tooth * number_of_teeth / pi / 2; // radius of pitch circle
|
||||
let c = p + mm_per_tooth / pi - clearance; // radius of outer circle
|
||||
let b = p * Math.cos(pressure_angle); // radius of base circle
|
||||
let r = p - (c - p) - clearance; // radius of root circle
|
||||
let t = mm_per_tooth / 2 - backlash / 2; // tooth thickness at pitch circle
|
||||
let k = -iang(b, p) - t / 2 / p; // angle where involute meets base circle on side of tooth
|
||||
|
||||
// here is the magic - a set of [x,y] points to create a single gear tooth
|
||||
let points = [polar(r, -3.142 / number_of_teeth), polar(r, r < b ? k : -pi / number_of_teeth),
|
||||
q7(0 / 5, r, b, c, k, 1), q7(1 / 5, r, b, c, k, 1), q7(2 / 5, r, b, c, k, 1), q7(3 / 5, r, b, c, k, 1), q7(4 / 5, r, b, c, k, 1), q7(5 / 5, r, b, c, k, 1),
|
||||
q7(5 / 5, r, b, c, k, -1), q7(4 / 5, r, b, c, k, -1), q7(3 / 5, r, b, c, k, -1), q7(2 / 5, r, b, c, k, -1), q7(1 / 5, r, b, c, k, -1), q7(0 / 5, r, b, c, k, -1),
|
||||
polar(r, r < b ? -k : pi / number_of_teeth), polar(r, 3.142 / number_of_teeth)];
|
||||
|
||||
let answer = [];
|
||||
|
||||
// create every gear tooth by rotating the first tooth
|
||||
for (var i = 0; i < number_of_teeth; i++) {
|
||||
answer = answer.concat(rotate(points, -i * 2 * pi / number_of_teeth));
|
||||
}
|
||||
|
||||
return answer; // returns an array of [x,y] points
|
||||
}
|
||||
|
||||
// gear parameter setup
|
||||
// number_of_teeth = numTeeth; // number of teeth (typically the only parameter to change)
|
||||
// note: rest of parameters must be unchanged if you want gears to fit.
|
||||
let mm_per_tooth = 9 * module * pi; // pixel size of one gear tooth (even though it says millimeters, it's pixels) must be same for two gears to fit each other
|
||||
// let pressure_angle = pressureAngle; // in degrees, determines gear shape, range is 10 to 40 degrees, most common is 20 degrees
|
||||
let clearance = 4; // freedom between two gear centers
|
||||
let backlash = 4; // freedom between two gear contact points
|
||||
let axle_radius = 20; // center hole radius in pixels
|
||||
let pressure_angle = degrees_to_radians(pressureAngle); // convet degrees to radians
|
||||
|
||||
return build_gear(numTeeth);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,9 +21,10 @@
|
|||
gapp.main("mesh.work", [], (root) => {
|
||||
|
||||
const { Matrix4, Vector3, BufferGeometry, BufferAttribute, computeFaceNormal } = THREE;
|
||||
const { mesh, moto } = root;
|
||||
const { base, mesh, moto } = root;
|
||||
const { client, worker } = moto;
|
||||
const { util } = mesh;
|
||||
const { newPolygon } = base;
|
||||
const cache = {};
|
||||
|
||||
// add scoped access to cache
|
||||
|
|
@ -397,6 +398,14 @@ let model = {
|
|||
log(`${id} | rebuild complete`);
|
||||
send.done({ lines: layers });
|
||||
});;
|
||||
},
|
||||
|
||||
gen_gear(data, send) {
|
||||
const { teeth, module, angle, z } = data;
|
||||
let points = new mesh.tool().generateGear(teeth, module, angle);
|
||||
let verts = points.map(v => [...v, 0]).flat();
|
||||
let poly = newPolygon().addVerts(verts).extrude(z || 10);
|
||||
send.done(poly);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -225,10 +225,20 @@ button:hover {
|
|||
gap: 4px;
|
||||
}
|
||||
|
||||
.addact input {
|
||||
.addact input, .addgear input {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.addgear {
|
||||
gap: 5px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 50px;
|
||||
}
|
||||
|
||||
.addgear button {
|
||||
column-span: all;
|
||||
}
|
||||
|
||||
.image-import {
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
|
|
|
|||
Loading…
Reference in a new issue