function R2_D2_14(arg) % R2_D2, version 1.4, March 5, 2023. % Rotations and Dilations in Two dimensions. % % See: https://blogs.mathworks.com/ ... % cleve/2023/03/03/r2-d2-rotations-and-dilations-in-two-dimensions/ % Copyright 2016-2023 Cleve Moler ishouse = nargin == 0 || strcmp(arg,'house'); if ishouse H = house; else H = hand; end theta = 0; sigma = 1; U = [cosd(theta) -sind(theta); sind(theta) cosd(theta)]; S = sigma*eye(2,2); A = U*S; X = A*H; Y = X; initialize_graphics(ishouse); Ut = framed_matrix(.1); St = framed_matrix(.4); At = framed_matrix(.7); d2d = dot2dot(X); % End of main program. Motion functions take charge. % ------------------------------------------------------ function initialize_graphics(ishouse) clf shg set(gcf, ... 'position',get(0,'screensize'), ... 'windowbuttondownfcn',@down, ... 'windowbuttonupfcn',@up) pushbut( 'exit',[.86 .90 .12 .06],'close(gcf)') pushbut( 'info',[.86 .82 .12 .06],@infocb) pushbut( 'start',[.86 .74 .12 .06],@(~,~) R2_D2_14) toggbut( 'tbd',[.86 .66 .12 .06],@house_hand); toggbut('rotate',[.86 .58 .12 .06],@action); toggbut('dilate',[.86 .50 .12 .06],@action); toggbut( 'both',[.86 .42 .12 .06],@action); pushbut( '-',[.86 .34 .03 .06],@dtcb) pushbut( '3',[.90 .34 .04 .06],[]) pushbut( '+',[.95 .34 .03 .06],@dtcb) hh = findobj('string','tbd'); if ishouse hh.String = 'house'; hh.Value = 0; hh.BackgroundColor = gray; else hh.String = 'hand'; hh.Value = 1; hh.BackgroundColor = bluegreen; end axes('position',[0 0 1 1],'vis','off') text(.35,.15,'*','fontsize',30,'fontweight','bold'); text(.65,.15,'=','fontsize',30,'fontweight','bold'); end % initialize graphics function house_hand(arg,~) if arg.Value == 1 arg.BackgroundColor = bluegreen; arg.String = 'hand'; R2_D2_14('hand') else arg.BackgroundColor = gray; arg.String = 'house'; R2_D2_14('house') end end function Mt = framed_matrix(xmin) txt = @(x,y,t) text(x,y,t, ... 'fontsize',20, ... 'fontweight','bold', ... 'horiz','left'); framed_axis([xmin .02 .2 .25]); Mt = [txt(.12,.65,'1.00') txt(.12,.35,' 0 ') txt(.62,.65,' 0 ') txt(.62,.35,'1.00')]; end % framed_matrix function d2d = dot2dot(X,d2d) % DOT2DOT Connect the points from a 2-by-n array. X(:,end+1) = X(:,1); if nargin == 1 axes('position',[.18 .34 .62 .62]) d2d = plot(X(1,:),X(2,:),'.-', ... 'markersize',18, ... 'linewidth',2); axis(10*[-1 1 -1 1]) axis off axis square set(gca,'clipping','off') else set(d2d,'xdata',X(1,:),'ydata',X(2,:)) end end % dot2dot function action(arg,~) job = arg.String; thetamax = 90; t = 0; tag = findobj('tag','3'); dt = str2num(tag.String); if want_gif uic = get(gcf,'children'); set(uic(1:10),'vis','off') end while arg.Value == 1 arg.BackgroundColor = bluegreen; if strcmp(job,'rotate') || strcmp(job,'both') U = [cosd(t) -sind(t); sind(t) cosd(t)]; end if strcmp(job,'dilate') || strcmp(job,'both') S = (3/4)^(t/45)*eye(2,2); end A = U*S; Y = A*X; update(U,Ut); update(S,St); update(A,At) dot2dot(Y,d2d); drawnow if want_gif gif_frame end if abs(t+dt) > thetamax pause(0.1) dt = -dt; end dt = sign(dt)*str2num(tag.String); t = t + dt; if want_gif && t == 0 && dt > 0 break end end if want_gif gif_frame('wrap') set(uic(1:10),'vis','on') end arg.BackgroundColor = 'w'; end function dtcb(arg,~) tag = findobj('tag','3'); dt = str2num(tag.String); if dt > 1 if arg.String == '+' dt = dt+1; else dt = dt-1; end tag.String = num2str(dt); elseif dt < 1 if arg.String == '+' dt = dt/(1-dt); else dt = dt/(1+dt); end tag.String = ['1/' num2str(1/dt)]; elseif arg.String == '+' tag.String = '2'; else tag.String = '1/2'; end end function infocb(~,~) web('https://blogs.mathworks.com/cleve/2023/03/03/r2-d2-rotations-and-dilations-in-two-dimensions/') end function motion(varargin) % WindowsButtonMotionFunction z = read_mouse; w = U'*z; if inregion(w(1),w(2),Y(1,:),Y(2,:)) == 0 theta = 90 - atan2d(z(2),z(1)); U = [cosd(theta) -sind(theta); sind(theta) cosd(theta)]; else sigma = norm(w)/4; S = sigma*eye(2,2); end A = U*S; X = A*H; dot2dot(X,d2d); update(U,Ut) update(S,St) update(A,At) end % motion function update(M,Mt) for k = 1:4 Mt(k).String = sprintf('%5.2f',M(k)); end end % update function down(varargin) % Called at the start of mouse movement. % Activate the motion function. set(gcf,'windowbuttonmotionfcn',@motion) end % down function up(varargin) % Called at the end of mouse movement. % Deactivate motion function. set(gcf,'windowbuttonmotionfcn',[]) set(gcf,'windowbuttondownfcn',@down) end % up function z = read_mouse % Current horizontal and vertical coordinates of the mouse. cp = get(gca,'currentpoint'); z = cp(1,1:2)'; end % read_mouse function H = house H = [ -6 -6 -7 0 7 6 6 -3 -3 0 0 -7 2 1 8 1 2 -7 -7 -2 -2 -7 ]; end function H = hand H = ... [3.15 4.43 6.13 7.65 7.52 6.75 5.60 4.45 3.07 2.60 ... 2.73 3.22 3.39 2.84 2.10 1.48 0.68 0.81 0.66 -0.33 ... -0.99 -1.04 -1.17 -1.81 -2.63 -3.39 -3.86 -3.49 -3.13 -4.78 ... -6.00 -6.95 -7.12 -5.82 -4.76 -4.52 -4.00; -9.00 -6.55 -4.39 -2.21 -1.26 -0.95 -1.56 -2.85 -3.00 -1.70 ... 0.59 3.66 6.79 7.92 7.41 4.25 0.79 4.96 8.36 9.06 ... 8.09 4.68 0.90 4.15 7.45 7.80 6.97 3.35 -0.44 1.87 ... 3.81 3.92 3.04 -0.07 -2.29 -5.62 -9.00]; end function pushbut(string,position,callback) uicontrol(Style = "pushbutton", ... String = string, ... Tag = string, ... Units = "normalized", ... Position = position, ... Callback = callback, ... Fontsize = 20, ... Fontweight = "bold", ... BackgroundColor = gray); end function tb = toggbut(str,position,callback) tb = uicontrol(Style = "toggle", ... String = str, ... Units = "normalized", ... Position = position, ... Callback = callback, ... Value = 0, ... Fontsize = 20, ... Fontweight = "bold", ... BackgroundColor = gray); end function wg = want_gif d = dir; ds = string({d.name})'; wg = any(contains(ds,"xx.gif")); end end