Skip to content

Instantly share code, notes, and snippets.

@ednisley
Last active December 24, 2017 11:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ednisley/71362ebcab0eede5e81f590ef0a1b29a to your computer and use it in GitHub Desktop.
Save ednisley/71362ebcab0eede5e81f590ef0a1b29a to your computer and use it in GitHub Desktop.
GCMC source code: Spirograph-based patterns for MPCNC shakedown
// 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);
@ednisley
Copy link
Author

ednisley commented Dec 15, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment