% The idea here is to produce an animation of a "Tonnetz" torus (see
% http://en.wiki.x.io/wiki/Neo-Riemannian_theory), possibly for upload
% to Wikimedia.
% FIRSTLY CREATE THE TEXTURE.
% Regrettably, no screen to which I have access is anywhere near large
% enough to contain the whole texture (at sufficient resolution to look
% good after texture-mapping the torus' surface). I'm not certain it's
% impossible to draw the whole figure to a larger-than-screen invisible
% buffer & getframe from there, but I don't know how to do it. Instead I'll
% build the texture out of sections of the plot, using xlim & ylim to
% scroll in between. Gawd help me.
% First determine texture's pixel dimensions:
TextureWidth=2702; % A spike in this plot: n=1:5000; h=abs(round(n/sqrt(27))./n-1/sqrt(27)); plot(n, 1./h);
TextureHeight=round(TextureWidth/sqrt(27));
ScreenSize = get(0, 'ScreenSize');
PatternRect = [9*sqrt(3)/2, 1/2, 12*sqrt(3), 4]; % left, bottom, width, height
notes = {'C', 'C$\sharp$', 'D', 'D$\sharp$', 'E', 'F', 'F$\sharp$', 'G', 'G$\sharp$', 'A', 'A$\sharp$', 'B'};
gap = .3;
xoffset = [0,0,0;sqrt(3)/2,0,-sqrt(3)/2];
yoffset = [0,0,0;.5,1,.5];
xoffset=(1-gap)*xoffset+gap*flipud(xoffset);
yoffset=(1-gap)*yoffset+gap*flipud(yoffset);
FontPts = round(TextureWidth/92);
close all
figure(1);
set(1, 'Position', [64, 64, ScreenSize(3:4)-128], 'Color', 'w');
FigureRect = get(1, 'Position'); % should be the same but who knows
TilesAcross = ceil(TextureWidth/FigureRect(3));
TilesDown = ceil(TextureHeight/FigureRect(4));
hold on
%rectangle('Position', PatternRect, 'EdgeColor', 'none', 'FaceColor', [.2,.2,.2]);%[1,.5,0]);%[.65, .6, .55]);
for j=0:9,
for k=0:16,
x = sqrt(3)*(k+j/2);
y = j/2;
text(x, y, notes(1+mod(7*j-k, 12)), 'Interpreter', 'latex', 'HorizontalAlignment', 'center', 'fontsize', FontPts);
plot(x+xoffset, y+yoffset, 'LineWidth', TextureWidth/600);
end
end
axis equal
axis off
set(gca, 'Position', [0, 0, 1, 1]);
texture = zeros(FigureRect(4)*TilesDown, FigureRect(3)*TilesAcross, 3);
for j=1:TilesDown,
for k=1:TilesAcross,
xlim(PatternRect(1)+[k-1,k]*FigureRect(3)/TextureWidth*PatternRect(3));
ylim(PatternRect(2)+[j-1,j]*FigureRect(4)/TextureHeight*PatternRect(4));
[tile, map] = frame2im(getframe(gcf));
texture(1+(TilesDown-j)*FigureRect(4):(TilesDown-j+1)*FigureRect(4), 1+(k-1)*FigureRect(3):k*FigureRect(3), :) = tile;
end
end
texture = texture(1+end-TextureHeight:end, 1:TextureWidth, :);
background = 220+floor(35*rand(size(texture))); % mottle the surface a little
isblank = repmat(min(texture, [], 3)==255, [1,1,3]);
texture = isblank.*background + (1-isblank).*texture;
close(1);
% NOW CREATE THE TORUS:
sx = size(texture, 2)-1;
sy = size(texture, 1)-1;
close all
figure(1);
set(1, 'Position', [1, 31, 1366, 662]);
[phi, theta] = meshgrid(2*pi*(0:sx)/sx, 2*pi*(0:sy)/sy);
h=surf(cos(phi).*(100-20*sin(theta)), sin(phi).*(100-20*sin(theta)), -20*cos(theta), texture);
set(h, 'CDataMapping', 'direct', 'EdgeColor' ,'none', 'FaceColor', 'interp', 'AmbientStrength', 0.003, 'DiffuseStrength', 0.003, 'SpecularStrength', 0.9, 'SpecularExponent', 25);
light('Position', [2000, -2000, 1000], 'Color', [1, 1, .7]);
light('Position', [-2000, 2000, 1000], 'Color', [.7, .7, 1]);
set(gca, 'AmbientLightColor', [1, .9, .9]);
axis equal
axis off
campos([-870, -1266, 749]);
lighting gouraud
% NOW SPIN IT, SAVING THE FRAMES:
NumFrames = 23;
delete('MovieFrames/Frame*.eps');
delete('MovieFrames/Frame*.bmp');
for t=1:NumFrames;
twist = t*pi/4/NumFrames;
set(h, 'XData', cos(phi+twist).*(100-20*sin(theta+3*twist)));
set(h, 'YData', sin(phi+twist).*(100-20*sin(theta+3*twist)));
set(h, 'ZData', -20*cos(theta+3*twist));
drawnow;
fprintf('Writing frame number %d to disk.\n', t);
FileName = sprintf('MovieFrames/Frame%03d.bmp', t);
print(gcf, '-dbmp', FileName, '-r200')
system(sprintf('convert %s -crop 900x490+378+333 %s', FileName, FileName)); % This relies on having installed "ImageMagick," but it's free
end
% NOW MAKE THE GIF (ImageMagick needed again)
fprintf('Compiling frames into a gif ...');
system('convert -antialias -loop 0 -delay 12 -compress LZW MovieFrames/Frame* TonnetzTorus.gif');
fprintf(' complete.\n');