function rubix()
%RUBIX Launch Rubix Cube
%	RUBIX launches a new instance of the Rubix Cube application.  This
%	version is intended for MATLAB 7.2 (R2006a) or later.
%
%	Directions
%
%	BASIC PLAY
%	To rotate a side of the cube simply click on the face of the side you
%	want to rotate.  The side will rotate either clockwise or counter
%	clockwise according to the setting of the radio buttons on the right.
%
%	ROTATE THE ENTIRE CUBE
%	To view the cube from a different perspective click the ROTATE3D toggle
%	button on the left.  You can now click and drag the cube to rotate it to the
%	desired perspective.  Click on the toggle button again to resume basic
%	play.
%
%	UICONTROLS
%
%	Undo Previous Move - Click this button to undo the previous move.
%	Clicking on any face adds this move to the player's move memory.  Undoing  a
%	move removes this move from the player's move memory.  Scrambling, Solving, or
%	Rearranging Stickers clears the player's move memory.
%
%	Scramble Cube - Click this button to set the cube to a randomly
%	scrambled state.  This state is achieved via a random sequence of 18
%	moves beginning from a solved state.  These moves can only be undone
%	using the "Solve Cube" button. Scrambling the cube will clear the
%	player's move memory and reset the cube to a different random state.
%	Any move made by the player after the cube has been scrambled is added
%	to the player's move memory and can be undone using the "Undo Previous
%	Move" button.
%
%	Solve Cube - Click this button to watch the cube solve itself.  The
%	solution retraces all moves made by the player and/or by scrambling the
%	cube.  Solving the cube clears the player's move memory.
%
%	Rearrange Stickers - Click this button to reset the cube to a solved
%	state.  Rearranging stickers clears the player's move memory.

% SETUP THE FIGURE

hf=figure('Visible','off',...
				'Units','normalized',...
				'WindowButtonDownFcn',@WButtonDownFcn,...
				'Renderer','zbuffer',...
				'Color',[.05 .05 .05],...
				'Resize','off',...
				'Name','Rubix Cube',...
				'NumberTitle','off',...
				'DockControls','off',...
				'MenuBar','none',...
				'Interruptible','off',...
				'BusyAction','cancel');		

large_axes=repmat([-6 6],1,3);
small_axes=repmat([-3.2 3.2],1,3);


%SETUP THE CUBE

%       [  red  blue  orange green white yellow black];
map=[.5 0 0;0 0 .6;.8 .4 .125;0 .6 .3;1 1 1;1 1 0;0 0 0];
colormap(map);
c=repmat([1 2 3 4 5 6],27,1);

x1=2*[0 1 1 0 0 0;1 1 0 0 1 1;1 1 0 0 1 1;0 1 1 0 0 0];
y1=2*[0 0 1 1 0 0;0 1 1 0 0 0;0 1 1 0 1 1;0 0 1 1 1 1];
z1=2*[0 0 0 0 0 1;0 0 0 0 0 1;1 1 1 1 0 1;1 1 1 1 0 1];

x2=x1+2;
x3=x2+2;

y2=y1+2;
y3=y2+2;

z2=z1+2;
z3=z2+2;

x(:,:,1)=x1;
x(:,:,2)=x2;
x(:,:,3)=x3;
x=x-3;

y(:,:,1)=y1;
y(:,:,2)=y2;
y(:,:,3)=y3;
y=y-3;

z(:,:,1)=z1;
z(:,:,2)=z2;
z(:,:,3)=z3;
z=z-3;

%Render the cube with 27 patch objects
for k=1:3
	for j=1:3
		for i=1:3
			h(i,j,k)=patch(x(:,:,i),y(:,:,j),z(:,:,k),c(9*(k-1)+3*(j-1)+i,:),'CDataMapping','direct','LineWidth',3);
			cube(i,j,k)=9*(k-1)+3*(j-1)+i;
		end
	end
end

hinit=h;

%Adjust axes limits, view, CLim, and zoom
camorbit(45,-75)
axis(small_axes);
axis square
axis off
zoom(.5);
caxis([.5 7.5]);
xlabel('xlabel');
ylabel('ylabel');
zlabel('zlabel');

% Title text
htitle = uicontrol('Style','text',...
							'String','Rubix Cube',...
							'Units','normalized',...
							'Position',[.27 .8 .5 .1],...
							'FontUnits','normalized',...
							'FontSize',1,...
							'BackgroundColor',[.05 .05 .05],...
							'ForegroundColor',[0 0 1]);

% Create Radio buttons for clockwise/counterclockwise selection
hbg = uibuttongroup('Units','normalized',...
									'Position',[.69 .405 .23 .15],...
									'SelectionChangeFcn',@SChangeFcn,...
									'Interruptible','off',...
									'BusyAction','cancel');
								
hrb1 = uicontrol('Style','Radiobutton',...
							'String','Clockwise',...
							'Units','normalized',...
							'Position',[.05 .55 .9 .35],...
							'Parent',hbg,...
							'Interruptible','off',...
							'BusyAction','cancel');
						
hrb2 = uicontrol('Style','Radiobutton',...
							'String','Counter Clockwise',...
							'Units','normalized',...
							'Position',[.05 .1 .9 .35],...
							'Parent',hbg,...
							'Interruptible','off',...
							'BusyAction','cancel');		

% Rotate3D toggle button

rotate_struct = load([matlabroot,'/toolbox/matlab/icons/rotate.mat']);
rotate_cdata = rotate_struct.cdata;

hrot3d = uicontrol('Style','togglebutton',...
							'Callback',@rot3d,...
							'Tag','mypan',...
							'Units','normalized',...
							'Position',[.275 .44 .07 .07],...
							'CData',rotate_cdata,...
							'Interruptible','off',...
							'BusyAction','cancel');

% Undo button
hundo = uicontrol('style','pushbutton',...
							'Callback',@undo,...
							'String','Undo Previous Move',...
							'Units','normalized',...
							'Position',[.42 .23 .2 .05],...
							'Enable','off',...
							'Interruptible','off',...
							'BusyAction','cancel');

% Scramble Cube button
hscramble = uicontrol('Style','pushbutton',...
									'Callback',@scramble,...
									'String','Scramble Cube',...
									'Units','normalized',...
									'Position',[.42 .18 .2 .05],...
									'Interruptible','off',...
									'BusyAction','cancel');
									
% Solve Cube button
hsolve = uicontrol('Style','pushbutton',...
									'Callback',@solve,...
									'String','Solve Cube',...
									'Units','normalized',...
									'Position',[.42 .13 .2 .05],...
									'Enable','off',...
									'Interruptible','off',...
									'BusyAction','cancel');
									
% Rearrange Stickers button
hstickers = uicontrol('style','pushbutton',...
							'Callback',@stickers,...
							'String','Rearrange Stickers',...
							'Units','normalized',...
							'Position',[.42 .08 .2 .05],...
                            'Enable','off',...
							'Interruptible','off',...
							'BusyAction','cancel');
							
% Initialize variables to be visible to all nested functions.      
currentPoint=zeros(2,3);
currentFaceNbr=[];
oppFaceNbr=[];
faceNumbers=[4 1 5;2 3 6];
limits=[-3.2 -3.2 -3.2;3.2 3.2 3.2];
ax_rotation=[];
TformFactor=[];
ccColorTform=[];
rot_factor=[];
ind=[];
black_ind=[];
direction = 'c';
dirs = {'c' 'cc'};
speed = 1;
moves=[];
playerMoves=[];
		
% Make the figure visible
set(hf,'Visible','on');

	function rotate()
        %Avoid cube shrinkage upon rotation caused by rotate3d.m
        set(gca,'CameraPositionMode','manual');
        
		% hh are the cubes of the face to be rotated
 		hh=h(ind);
		t=hgtransform('Parent',gca);
		set(hh,'Parent',t);

		axis(large_axes);
        
		% Set inner faces of middle cubes to black for effect during rotation
		hblack =  h(black_ind);
		mid_cdata = get(hblack,'CData');
		for num = 1:9
			mid_cdata_black = mid_cdata{num};
			mid_cdata_black(currentFaceNbr) = 7;
			set(hblack(num),'CData',mid_cdata_black);
		end
		
		% Set inner faces of rotating cubes to black for effect dur rotation
		opp_cdata = get(hh,'CData');
		for num = 1:9
			opp_cdata_black = opp_cdata{num};
			opp_cdata_black(oppFaceNbr) = 7;
			set(hh(num),'CData',opp_cdata_black);
		end
		
		%%%%%%%%%%
		% Rotate face
		for r=speed:15
			R=makehgtform(ax_rotation,TformFactor*r*pi/30);
			set(t,'Matrix',R);
			drawnow
		end
		%%%%%%%%%%

		% Set inner faces of middle cubes from black to original color
		for num = 1:9
			set(hblack(num),'CData',mid_cdata{num});
		end
		
		% Set inner faces of rotating cubes from black to original color
		for num = 1:9
			set(hh(num),'CData',opp_cdata{num});
		end

		% Rotate CData values and apply ccColorTform
		temp=h;
		cdata=get(temp(rot90(ind,rot_factor)),'CData');
		for num=1:9
			set(hh(num),'CData',cdata{num}(ccColorTform));
		end

		set(hh,'Parent',gca);
		axis(small_axes);
		
	end

	function setRotationParameters()
		switch direction
				case 'c'
					switch currentFaceNbr
						case 1
							ind = squeeze(cube(:,1,:));
							black_ind = squeeze(cube(:,2,:));
							oppFaceNbr = 3;
							ax_rotation = 'yrotate';
							TformFactor = 1;
							ccColorTform = [ 1 6 3 5 2 4 ];
							rot_factor = -1;
						case 2
							ind = squeeze(cube(3,:,:));
							black_ind = squeeze(cube(2,:,:));
							oppFaceNbr = 4;
							ax_rotation = 'xrotate';
							TformFactor = -1;
							ccColorTform = [ 5 2 6 4 3 1 ];
							rot_factor = -1;
						case 3
							ind = squeeze(cube(:,3,:));
							black_ind = squeeze(cube(:,2,:));
							oppFaceNbr = 1;
							ax_rotation = 'yrotate';
							TformFactor = -1;
							ccColorTform = [ 1 5 3 6 4 2 ];
							rot_factor = 1;
						case 4
							ind = squeeze(cube(1,:,:));
							black_ind = squeeze(cube(2,:,:));
							oppFaceNbr = 2;
							ax_rotation = 'xrotate';
							TformFactor = 1;
							ccColorTform = [ 6 2 5 4 1 3 ];
							rot_factor = 1;
						case 5
							ind = squeeze(cube(:,:,1));
							black_ind = squeeze(cube(:,:,2));
							oppFaceNbr = 6;
							ax_rotation = 'zrotate';
							TformFactor = 1;
							ccColorTform = [ 4 1 2 3 5 6 ];
							rot_factor = 1;
						case 6
							ind = squeeze(cube(:,:,3));
							black_ind = squeeze(cube(:,:,2));
							oppFaceNbr = 5;
							ax_rotation = 'zrotate';
							TformFactor = -1;
							ccColorTform = [ 2 3 4 1 5 6 ];
							rot_factor = -1;
					end
				case 'cc'
					switch currentFaceNbr
						case 1
							ind = squeeze(cube(:,1,:));
							black_ind = squeeze(cube(:,2,:));
							oppFaceNbr = 3;
							ax_rotation = 'yrotate';
							TformFactor = -1;
							ccColorTform=[ 1 5 3 6 4 2 ];
							rot_factor = 1;
						case 2
							ind = squeeze(cube(3,:,:));
							black_ind = squeeze(cube(2,:,:));
							oppFaceNbr = 4;
							ax_rotation = 'xrotate';
							TformFactor = 1;
							ccColorTform=[ 6 2 5 4 1 3 ];
							rot_factor = 1;
						case 3
							ind = squeeze(cube(:,3,:));
							black_ind = squeeze(cube(:,2,:));
							oppFaceNbr = 1;
							ax_rotation = 'yrotate';
							TformFactor = 1;
							ccColorTform=[ 1 6 3 5 2 4 ];
							rot_factor = -1;
						case 4
							ind = squeeze(cube(1,:,:));
							black_ind = squeeze(cube(2,:,:));
							oppFaceNbr = 2;
							ax_rotation = 'xrotate';
							TformFactor = -1;
							ccColorTform=[ 5 2 6 4 3 1 ];
							rot_factor = -1;
						case 5
							ind = squeeze(cube(:,:,1));
							black_ind = squeeze(cube(:,:,2));
							oppFaceNbr = 6;
							ax_rotation = 'zrotate';
							TformFactor = -1;
							ccColorTform=[ 2 3 4 1 5 6 ];
							rot_factor = -1;
						case 6
							ind = squeeze(cube(:,:,3));
							black_ind = squeeze(cube(:,:,2));
							oppFaceNbr = 5;
							ax_rotation = 'zrotate';
							TformFactor = 1;
							ccColorTform=[ 4 1 2 3 5 6 ];
							rot_factor = 1;
					end
		end
	end

	function varargout = WButtonDownFcn(varargin)
		if any(find(h==hittest))
			currentPoint=get(gca,'CurrentPoint');
			currentFaceNbr=faceNumbers(repmat(currentPoint(1,:),2,1)==limits);
			setRotationParameters;
			rotate;
			playerMoves(end+1).face = currentFaceNbr;
			playerMoves(end).dir = direction;
			moves(end+1).face = currentFaceNbr;
			moves(end).dir = direction;
			set([hundo, hsolve, hstickers],'Enable','on');
		end
	end

	function varargout = undo(varargin)
        if isempty(getappdata(hf,'Rotate3dOnState'))
            set(hf,'WindowButtonDownFcn',[]);
        end
		currentDir = direction;
		switch playerMoves(end).dir
			case 'c'
				direction = 'cc';
				currentFaceNbr = playerMoves(end).face;
				setRotationParameters;
				rotate;
				direction = currentDir;
			case 'cc'
				direction = 'c';
				currentFaceNbr = playerMoves(end).face;
				setRotationParameters;
				rotate;
				direction = currentDir;
		end
		playerMoves = playerMoves(1:end-1);
		moves = moves(1:end-1);
		if isempty(playerMoves)
			set(hundo,'Enable','off');
		end
		if isempty(moves)
			set(hsolve,'Enable','off');
        end
        if isempty(getappdata(hf,'Rotate3dOnState'))
            set(hf,'WindowButtonDownFcn',@WButtonDownFcn);
        end
	end

	function varargout = scramble(varargin)
		if isempty(getappdata(hf,'Rotate3dOnState'))
            set(hf,'WindowButtonDownFcn',[]);
        end
		stickers;
		speed = 15;
		currentDir = direction;
		rand('state',sum(100*clock));
		scr_faces = [randperm(6) randperm(6) randperm(6)];
		scr_dir = round(rand(1,length(scr_faces))) + 1;
		set(h,'Visible','off');
		for m = 1:length(scr_faces)
			currentFaceNbr = scr_faces(m);
			direction = dirs{scr_dir(m)};
			setRotationParameters;
			rotate;
			moves(m).face = currentFaceNbr;
			moves(m).dir = direction;
		end
		speed = 1;
		direction = currentDir;
		set([hsolve, hstickers],'Enable','on');
		set(h,'Visible','on');
		if isempty(getappdata(hf,'Rotate3dOnState'))
            set(hf,'WindowButtonDownFcn',@WButtonDownFcn);
        end
	end

	function varargout = solve(varargin)
		if isempty(getappdata(hf,'Rotate3dOnState'))
            set(hf,'WindowButtonDownFcn',[]);
        end
		playerMoves = [];
		set([hundo,hscramble,hsolve,hstickers],'Enable','off');
		currentDir = direction;
		while ~isempty(moves)
			switch moves(end).dir
				case 'c'
					direction = 'cc';
					currentFaceNbr = moves(end).face;
					setRotationParameters;
					rotate;
					direction = currentDir;
				case 'cc'
					direction = 'c';
					currentFaceNbr = moves(end).face;
					setRotationParameters;
					rotate;
					direction = currentDir;
			end
			moves=moves(1:end-1);
			pause(.2);
		end
		set(hsolve,'Enable','off');
        set(hscramble,'Enable','on');
		if isempty(getappdata(hf,'Rotate3dOnState'))
            set(hf,'WindowButtonDownFcn',@WButtonDownFcn);
        end
	end

	function varargout = stickers(varargin)
		set(h,'Visible','off');
		set(h,'CData',[1 2 3 4 5 6]);
		moves=[];
		playerMoves=[];
		set([hundo,hsolve,hstickers],'Enable','off');
		set(h,'Visible','on');
	end

	function varargout = rot3d(varargin)
		val = get(hrot3d,'Value');
		switch val
			case 0
				rotate3d off;
				set([hrb1,hrb2],'Enable','on');
			case 1
				rotate3d on;
				set([hrb1,hrb2],'Enable','off');
		end
    end

	function varargout = SChangeFcn(varargin)
		selected=get(hbg,'SelectedObject');
		switch selected
			case hrb1
				direction = 'c';
			case hrb2
				direction = 'cc';
		end
	end

end
