GCMC source code: Spirograph-based patterns for MPCNC shakedown
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Spirograph simulator for MPCNC plotter | |
// Ed Nisley KE4ZNU - 2017-12 | |
// Spirograph equations: | |
// https://en.wikipedia.org/wiki/Spirograph | |
// Loosely based on GCMC cycloids.gcmc demo: | |
// https://gitlab.com/gcmc/gcmc/tree/master/example/cycloids.gcmc | |
include("tracepath.inc.gcmc"); | |
//----- | |
// Greatest Common Divisor | |
// https://en.wikipedia.org/wiki/Greatest_common_divisor#Using_Euclid's_algorithm | |
// Inputs = integers without units | |
function gcd(a,b) { | |
local d=0; | |
// message("gcd(" + a + "," + b + ") = "); | |
if (!isnone(a) || isfloat(a) || !isnone(b) || isfloat(b)) { | |
warning("Values must be dimensionless integers"); | |
} | |
while (!((a | b) & 1)) { // remove and tally common factors of two | |
a >>= 1; | |
b >>= 1; | |
d++; | |
} | |
while (a != b) { | |
if (!(a & 1)) {a >>=1;} // discard non-common factor of 2 | |
elif (!(b & 1)) {b >>= 1;} // ... likewise | |
elif (a > b) {a = (a - b) >> 1;} // gcd(a,b) also divides a-b | |
else {b = (b - a) >> 1;} // ... likewise | |
} | |
g = a*(1 << d); // form gcd | |
// message(" " + g); | |
return g; | |
} | |
//----- | |
// Max and min functions | |
function max(x,y) { | |
return (x > y) ? x : y; | |
} | |
function min(x,y) { | |
return (x < y) ? x : y; | |
} | |
//----- | |
// Spirograph tooth counts mooched from: | |
// http://nathanfriend.io/inspirograph/ | |
Stators = [96, 105, 144, 150]; | |
Rotors = [24, 30, 32, 36, 40, 45, 48, 50, 52, 56, 60, 63, 64, 72, 75, 80, 84]; | |
//----- | |
// Set up gearing | |
s = 1; // index values should be randomized | |
r = 6; | |
StatorTeeth = Stators[s]; // from the universe of possible teeth | |
RotorTeeth = Rotors[r]; | |
message("Stator: ", StatorTeeth); | |
message("Rotor: ", RotorTeeth); | |
L = 0.90; // normalized pen offset in rotor | |
message("Pen offset: ", L); | |
g = gcd(StatorTeeth,RotorTeeth); // reduce teeth to ratio of least integers | |
StatorN = StatorTeeth / g; | |
RotorM = RotorTeeth / g; | |
K = to_float(RotorM) / to_float(StatorN); // normalized rotor dia | |
Lobes = StatorN; // having removed all common factors | |
Turns = RotorM; | |
message("Lobes: ", Lobes); | |
message("Turns: ", Turns); | |
AngleStep = 2.0deg; | |
//----- | |
// Crank out a list of points in normalized coordinates | |
Path = {}; | |
Xmax = 0.0; | |
Xmin = 0.0; | |
Ymax = 0.0; | |
Ymin = 0.0; | |
for (a=0.0deg ; a <= Turns*360deg ; a += AngleStep) { | |
x = (1 - K)*cos(a) + L*K*cos(a*(1 - K)/K); | |
if (x > Xmax) {Xmax = x;} | |
elif (x < Xmin) {Xmin = x;} | |
y = (1 - K)*sin(a) - L*K*sin(a*(1 - K)/K); | |
if (y > Ymax) {Ymax = y;} | |
elif (y < Ymin) {Ymin = y;} | |
Path += {[x,y]}; | |
} | |
message("Max X: ", Xmax, " Y: ", Ymax); | |
message("Min X: ", Xmin, " Y: ", Ymin); // min will always be negative | |
Xmax = max(Xmax,-Xmin); // odd lobes can cause min != max | |
Ymax = max(Ymax,-Ymin); // ... need really truly absolute maximum | |
//----- | |
// Scale points to actual plot size | |
TableSize = [25in,18in]; // largest possible plot area | |
PaperSize = 0 ? [17.0in,11.0in] : TableSize; | |
Margins = [0.5in,0.5in] * 2; | |
Boundary = PaperSize - Margins; | |
message("Boundary: ",Boundary); | |
PlotScale = [Boundary.x / (2*Xmax), Boundary.y / (2*Ymax)]; | |
message("Plot scale: ", PlotScale); | |
Points = scale(Path,PlotScale); // fill page, origin at center | |
//----- | |
// Produce G-Code | |
feedrate(6000.0mm); | |
safe_z = [-,-,25.0mm]; | |
plotz = -1.0mm; | |
goto([0,0,10.0mm]); | |
tracepath(Points, plotz); | |
goto(safe_z); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
More details on my blog at https://wp.me/poZKh-7e8 and https://wp.me/poZKh-7ei