#MATLAB: 735 bytes - 150 bonus = 585
My program handles the n=2 case and draws a real-time animation.
There are a few differences between the golfed and ungolfed versions:
The ungolfed version has an option to save the animation to a file 'g.gif', by setting savegif = 1
in the code. It is off by default as it can be annoying for a few reasons:
- Creation of an unwanted file
- Possible lag
- An error is generated if you have multiple monitors and the plot window is not on the right one...
The gif saving had to be dropped in the golfed version as it took about 100 bytes, exceeding the size of the bonus.
The ungolfed version draws a circle on the tracer vertex. It also produces more frames and moves faster (though this can be adjusted in the golfed version by changing the numbers).
##Samples:
f(11,5,90,2,99,0)
after program termination
epic(1.3,4,2,6,6,1)
with gif output
##Ungolfed code
%epicyclogon animation outputs to 'g.gif' if savegif=1 as well as animating in real time
function[] = epic(r,r1,r2,n1,n2,dispPoly)
savegif = 0; %set to 1 to write .gif
cs = @(a) [cos(a);sin(a)];
vert = @(r, n, v) r * cs(2*pi*v/n);
polyPt = @(l, s, n, r) vert(r, n, floor(l/s)) + mod(l/s,1)*(vert(r, n, floor(l/s)+1) - vert(r, n, floor(l/s)));
polyPt2 = @(i, f, n, r) vert(r, n, i) + f*(vert(r, n, i+1) - vert(r, n, i));
rotm = @(a) [cos(a) -sin(a);sin(a) cos(a)];
arrpluspt = @(a, p) a + kron(p, ones(1,length(a)));
arg = @(p) atan2(p(2), p(1));
E = 1e-9;
dispPoly = dispPoly / dispPoly;
sgn = sign(-r);
r = abs(r);
s1 = 2*r1*sin(pi/n1);
s2 = 2*r2*sin(pi/n2);
%d1 = (r1*r1 - s1*s1*.25)^.5;
d2 = (r2*r2 - s2*s2*.25)^.5;
plotmax = r1+2*r2;
astep = .05; %determines amount of frames per rotation
delay = .01; % time per frame
l = 0;
lRem = 0;
lr = 0;
P1 = vert(r1, n1, 1:n1+1) * dispPoly;
trace = [];
first = 1;
while 1
if lr %exists while rotating about a corner of the stationary
rotA = 2*pi/n1;
else
rotA = 2*pi/n2;
end
rotPt = polyPt(l, s1, n1, r1);
lb = l + lRem;
side1 = floor(l / s1 - E);
side1up = side1 + lr;
p2cen = polyPt2(side1, lb/s1 -side1 - .5 * s2/s1, n1, r1) + d2 * cs(2*pi*(side1+.5)/n1);
if first
p2cen0 = p2cen;
r = r + arg(p2cen0)/(2*pi);
end
for a = 0:astep:rotA
P2 = vert(r2, n2, 0:n2);
P2 = rotm( pi +pi/n1 -pi/n2 +2*pi*side1/n1) * P2;
P2 = arrpluspt(P2, p2cen);
P2 = arrpluspt(P2, -rotPt);
P2 = rotm(a) * P2;
P2 = arrpluspt(P2, rotPt);
trV = mod(floor(l/s2 + E) + lr, n2) + 1;
cen = rotm(a) * (p2cen - rotPt) + rotPt;
trace = [trace,P2(:,trV)];
plot(P1(1,:), sgn*P1(2,:), P2(1,:)*dispPoly, sgn*P2(2,:)*dispPoly, trace(1,:),sgn*trace(2,:),P2(1,trV), sgn*P2(2,trV),'o');
%plot(P1(1,:), P1(2,:), P2(1,:), P2(2,:), trace(1,:),trace(2,:),...
%[0,p2cen0(1)],[0,p2cen0(2)],[0,cen(1)],[0,cen(2)], P2(1,trV), P2(2,trV),'o');
axis([-plotmax,plotmax,-plotmax,plotmax]);
axis square
figure(1);
if savegif
drawnow
frame = getframe(1); % plot window must be on same monitor!
img = frame2im(frame);
[img1,img2] = rgb2ind(img,256);
end
if first
if savegif
imwrite(img1,img2,'g','gif','DelayTime',2*delay); %control animation speed(but not really)
end
first = 0;
else
if savegif
imwrite(img1,img2,'g','gif','WriteMode','append','DelayTime', 2*delay);
end
end
pause(.01);
adf = mod(arg(cen) - r*2*pi, 2*pi);
if adf < astep & l/(n1*s1) + .5 > r
return
end
end
%cleanup for next iteration
jump = lRem + ~lr * s2;
lnex = l + jump;
if floor(lnex / s1 - E) > side1up
lnex = s1*(side1up+1);
lRem = jump - (lnex - l);
lr = 1;
else
lRem = 0;
lr = 0;
end
l = lnex;
end
##Golfed code
function[]=f(r,h,H,n,N,d)
P=pi;T=2*P;F=@floor;C=@(a)[cos(a);sin(a)];g=@(i,f,n,r)r*C(T*i/n)*(1-f)+f*r*C(T*(i+1)/n);R=@(a)[C(a),C(a+P/2)];W=@(a,p)[a(1,:)+p(1);a(2,:)+p(2)];b=@(p)atan2(p(2),p(1));E=1e-9;d=d/d;S=1-2*(r>0);r=-r*S;x=2*h*sin(P/n);X=2*H*sin(P/N);M=h+2*H;l=0;z=0;L=0;A=h*C(T*(0:n)/n)*d;t=[];while 1
v=l/x;D=F(v-E);q=g(D,v-D,n,h);Z=D+L;c=g(D,v+z/x-D-.5*X/x,n,h)+H*cos(P/N)*C(T*D/n+P/n);r=r+~(l+L)*b(c)/T;for a=0:.1:T/(L*n+~L*N)
O=@(p)W(R(a)*W(p,-q),q);B=O(W(R(P+P/n-P/N+T*D/n)*H*C(T*(0:N)/N),c));t=[t,B(:,mod(F(l/X+E)+L,N)+1)];plot(A(1,:),S*A(2,:),d*B(1,:),d*S*B(2,:),t(1,:),t(2,:)*S)
axis([-M,M,-M,M],'square');pause(.1);if.1>mod(b(O(c))-r*T,T)&v/n+.5>r
return;end;end;j=z+~L*X;J=l+j;L=F(J/x-E)>Z;l=L*x*(Z+1)+~L*J;z=L*(J-l);end
##Instructions:
Save the function to a file with the same name, i.e. epic.m
or f.m
.
Run it by calling the function from the Matlab console.
Usage: epic(r, r1, r2, n1, n2, dispPoly)
where dispPoly
is a Boolean variable (zero if false, a nonzero number if true) determining whether to draw the polygons.