241 lines
7.7 KiB
Plaintext
241 lines
7.7 KiB
Plaintext
/*
|
|
* G-code meta compiler
|
|
*
|
|
* Copyright (C) 2014 B. Stultiens
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
* Gear example
|
|
* ------------
|
|
* This example is by no means a complete or correct implementation of gears in
|
|
* any generic form. It is primarily for inspiration and to show what gcmc can
|
|
* do with relatively little coding. You may use this script as inspiration to
|
|
* create a better implementation if you like.
|
|
*
|
|
* Please note: The below parameters will shift the examples with an XY offset
|
|
* as indicated by the options. These are passed on the command-line when the
|
|
* examples are generated.
|
|
*
|
|
* @@@--svg-toolwidth 0.1 --svg-opacity 1 --svg-no-movelayer -x 110 -y 70@@@
|
|
*/
|
|
|
|
|
|
/*
|
|
* Gear terms:
|
|
* N - Number of Teeth
|
|
* Pa - Pressure Angle
|
|
* D - Pitch Diameter - D = N/P = Do - 2/P (Gear radius at center of the teeth)
|
|
* P - Diametral Pitch - P = N/D
|
|
* p - Circular Pitch - p = pi() / P
|
|
* Db - Base Diameter - Db = D * cos(Pa) (Bottom of teeth insertion)
|
|
* Dr - Root Diameter - Dr = D - 2b (Bottom of tooth cutout)
|
|
* Do - Outside Diameter - Do = D + 2a
|
|
* a - Addendum - a = 1/P
|
|
* b - Dedendum - b = ht - a
|
|
* ht - Whole Depth (Pa<20) - 2.157/P
|
|
* ht - Whole Depth (Pa>=20) - 2.2/P + 0.05mm (Total depth from outer dia to bottom)
|
|
* t - Tooth Thickness - t = pi()/(2*P) (Thinckness at Pitch Diameter)
|
|
*/
|
|
|
|
__ang_step = 2.0deg; /* Trace interval for curves */
|
|
|
|
/*
|
|
* Point on involute curve at specified angle, see https://en.wikipedia.org/wiki/Involute
|
|
* Cartesian:
|
|
* x = a * ( cos(t) + t * sin(t))
|
|
* y = a * ( sin(t) - t * cos(t))
|
|
* Polar:
|
|
* r = a * sqrt(1 + t^2) = sqrt(a^2 + (a*t)^2)
|
|
* phi = t - atan(t)
|
|
* where:
|
|
* - a = circle radius
|
|
* - t = angle (radians)
|
|
*
|
|
* For angle from circle radius: t^2 = (r/a)^2 - 1
|
|
*/
|
|
function involute_point(angle, radius)
|
|
{
|
|
angle = to_rad(angle); /* Multiplication must be in radians */
|
|
return radius * [cos(angle) + to_none(angle) * sin(angle), sin(angle) - to_none(angle) * cos(angle)];
|
|
}
|
|
|
|
function involute_angle(radius, outrad)
|
|
{
|
|
return to_rad(sqrt(pow(outrad/radius, 2.0) - 1));
|
|
}
|
|
|
|
/*
|
|
* Make a gear with:
|
|
* - nteeth Number of teeth
|
|
* - pressure_angle Teeth contact pressure angle
|
|
* - diametral_pitch Diametral pitch (teets/length)
|
|
*
|
|
* Return a vectorlist with outer points of the gear centered at [0,0]
|
|
*/
|
|
function gear_P(nteeth, pressure_angle, diametral_pitch)
|
|
{
|
|
/* The routine gets is serious trouble if you make the pressure angle
|
|
* too large or too small. Warn the user if such case occurs.
|
|
*/
|
|
if(pressure_angle > 24.6deg) {
|
|
warning("Pressure angle (", pressure_angle, ") too large, cannot fit teeth inside the set outside diameter");
|
|
}
|
|
if(pressure_angle < 12.0deg) {
|
|
warning("Pressure angle (", pressure_angle, ") too small, teeth may get stuck at pitch radius");
|
|
}
|
|
local i;
|
|
local pitch_diameter = nteeth / diametral_pitch;
|
|
local base_diameter = pitch_diameter * cos(pressure_angle);
|
|
local addendum = 1.0/diametral_pitch;
|
|
local ht = 2.157 / diametral_pitch;
|
|
local dedendum = ht - addendum;
|
|
local outside_diameter = pitch_diameter + 2.0*addendum;
|
|
local root_diameter = base_diameter - 2.0*dedendum;
|
|
local work_diameter = outside_diameter - 4.0*addendum;
|
|
|
|
local tooth = {}; // The curve for one tooth
|
|
|
|
/*
|
|
* message("nteeth=", nteeth, " pressure_angle=", pressure_angle, " diametral_pitch=", diametral_pitch);
|
|
* message("addendum=", addendum, " dedendum=", dedendum, " ht=", ht);
|
|
* message("pitch_diameter=", pitch_diameter);
|
|
* message("base_diameter=", base_diameter);
|
|
* message("outside_diameter=", outside_diameter);
|
|
* message("root_diameter=", root_diameter);
|
|
* message("work_diameter=", work_diameter);
|
|
*/
|
|
|
|
/*
|
|
* Show the different diameters:
|
|
* hole([0, 0], pitch_diameter/2.0);
|
|
* hole([0, 0], base_diameter/2.0);
|
|
* hole([0, 0], outside_diameter/2.0);
|
|
* hole([0, 0], root_diameter/2.0);
|
|
* hole([0, 0], work_diameter/2.0);
|
|
*/
|
|
|
|
// Fillet radius is approx. Will not reach root exactly, but close enough
|
|
// Otherwise need to calculate intersection with root-circle
|
|
local filletrad = (base_diameter - root_diameter)/8.0;
|
|
|
|
// Center of the fillet arc, involute makes a ~240deg angle with fillet arc
|
|
// The fillet arc runs from the root to the working depth of the gear
|
|
local center = rotate_xy([-filletrad, 0.0mm], 60.0deg) + [work_diameter/2.0, 0];
|
|
|
|
// Trace the fillet arc from ~root-circle to working depth at involute arc starting Y-level
|
|
for(i = 180.0deg; i > 60.0deg; i -= __ang_step*2.5) {
|
|
tooth += { [cos(i), sin(i)] * filletrad + center };
|
|
}
|
|
if(i != 60.0deg) {
|
|
// Add the last point if we did not reach the working depth
|
|
tooth += { [cos(60.0deg), sin(60.0deg)] * filletrad + center };
|
|
}
|
|
|
|
// Calculate the maximum involute angle to intersect at the outside radius
|
|
local max_a = involute_angle(base_diameter/2.0, outside_diameter/2.0);
|
|
|
|
// Trace the involute arc from the base up to outside radius
|
|
for(i = 0.0deg; i < max_a; i += __ang_step) {
|
|
tooth += { involute_point(i, base_diameter/2.0)};
|
|
}
|
|
if(i != max_a) {
|
|
// Add the last point if we did not reach the outside radius
|
|
tooth += { involute_point(max_a, base_diameter/2.0)};
|
|
}
|
|
|
|
// We now have one side of the tooth. Rotate to be at tooth-symmetry on X-axis
|
|
tooth = rotate_xy(tooth, -90.0deg / nteeth);
|
|
|
|
// Add the same curve mirrored to make the other side of the tooth
|
|
// Coordinates reverse to have them all in one direction only
|
|
tooth += reverse(scale(tooth, [1, -1]));
|
|
|
|
// Create all teeth of the gear by adding each tooth at correct angle
|
|
local gear = {};
|
|
repeat(nteeth; i) {
|
|
gear += rotate_xy(tooth, 360.0deg * i / nteeth);
|
|
}
|
|
|
|
return gear;
|
|
}
|
|
|
|
/* -------------------- Helper Functions -------------------- */
|
|
|
|
/*
|
|
* Trace a path at given offset
|
|
*/
|
|
function trace(path, offset)
|
|
{
|
|
goto(path[-1] + offset);
|
|
foreach(path; v) {
|
|
move(v + offset);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make a hole at center point with given radius
|
|
*/
|
|
function hole(point, radius)
|
|
{
|
|
goto(point - [radius]);
|
|
circle_cw_r([radius, 0]);
|
|
}
|
|
|
|
/* -------------------- Main Program -------------------- */
|
|
|
|
//ngcgui: info: Involute-gear example
|
|
|
|
//ngcgui: umode = 1; //, units: 1:mm, 0:inch
|
|
//ngcgui: D = 100.0; //, Pitch Dia
|
|
//ngcgui: HD = 6.0; //, Hole Dia
|
|
//ngcgui: N = 9; //, Number of teeth
|
|
//ngcgui: PA = 20.0; //, Pressure Angle (deg)
|
|
//ngcgui: frate = 600; //, Feedrate
|
|
//ngcgui: xoffset = 0;
|
|
//ngcgui: yoffset = 0;
|
|
//ngcgui: howmany = 1; //, howmany (1 | 2)
|
|
//ngcgui: verbose = 0;
|
|
include("ensure_units.gcmc"); //avoid preamble conflict
|
|
|
|
if (umode == 1) {
|
|
zero = 0.0mm;
|
|
} else {
|
|
zero = 0.0in;
|
|
}
|
|
// ngcgui entries are unitless so these additions are used
|
|
// to ensure 1) floatingpoint and 2) units per umode setting
|
|
frate = frate + zero;
|
|
HD = HD + zero;
|
|
D = D + zero;
|
|
PA = PA + 0.0deg;
|
|
|
|
P = N/D; // Diametral pitch
|
|
|
|
feedrate(frate);
|
|
location = [xoffset,yoffset];
|
|
|
|
if (howmany == 1) {
|
|
hole(location, HD/2.0);
|
|
trace(gear_P(N, PA, P), location);
|
|
} elif (howmany == 2) {
|
|
hole(location + [D/2.0, 0.0mm], HD/2.0);
|
|
trace(gear_P(N, PA, P), location + [D/2.0, 0.0mm]);
|
|
hole(location + [-D/2.0, 0.0mm], HD/2.0);
|
|
trace(gear_P(N, PA, P), location + [-D/2, 0.0mm]);
|
|
} else {
|
|
error("howmany must be 1 or 2");
|
|
}
|
|
|