No BSD License  

Analog clock

by

 

01 Jul 2008 (Updated )

Analog clock, may be used as a component in a larger application

scottsclock
classdef scottsclock < handle
    % Based on Scott Frasso's scottsclock
    % Extended by Aleksander Veksler of Kongsberg Maritime to put a clock
    % on the current axes, and be updated on demand instead of by timer,
    % and by any time instead of real time.
    methods
        function o = scottsclock(x0, y0, dia, ah)
            if ~exist('x0', 'var'), x0=0; end;
            if ~exist('y0', 'var'), y0=0; end;
            if ~exist('dia', 'var'), dia=20; end;
            if ~exist('ah', 'var'), ah = gca; end;
            o.ah = ah;
            set(get(ah, 'Parent'), 'CurrentAxes', ah); % Make sure that the program works on correct axes;
            
            dia = dia/20; % In original code, clock had diameter of 20;
            
            
            %PreAllocate arrays for faster startup
            
            handles = o.handles; % Convenience
            
            HourHandData = zeros(1,4);
            MinuteHandData = zeros(1,4);
            SecondHandData = zeros(1,4);
            pDx = zeros(1,5);
            pDy = zeros(1,5);

            %Draw the perimeter of the clock and position the figure
            R=linspace(0,2*pi,1000);
            x1=9*cos(R);
            y1=9*sin(R);
            h = plot(x1*dia+x0,y1*dia+y0,'b','linewidth',8*dia,'color','k'); %Draws a thick black circle
            handles(end+1) = h;
            hold on
            %axis off
            %axis([-10 10 -10 10]) %Draws the figure with +/-10 for [Xmin Xmin Ymin Ymax]
            %axis equal
            
            % Plot the numbers 1-12 on the screen
            % --Declare variables to be used in the plotting the clock numbers
            Clk_fSize = 12;                                  % controls the font size of the numbers of the clock
            Clk_fTheta = (pi/3:-2*pi/12:-3*pi/2)';           % sets the Theta for each number position
            Clk_fRad = 7*dia;                                 % sets the Raduis for each number position
            Clk_numbas = (1:1:12)';
            Clk_nData = [Clk_fRad*cos(Clk_fTheta) Clk_fRad*sin(Clk_fTheta) Clk_numbas];
            h = text(Clk_nData(:,1)+x0,Clk_nData(:,2) + y0,num2str(Clk_nData(:,3)),...
                'horizontalAlignment','center','verticalAlignment','middle','FontSize',Clk_fSize);
            handles(end+1:end + length(h)) = h;
            
            %== Tic Marks ==
            % Define the Length of the Tic marks
            TLenStart = 8.1;    % Start of the Tick mark (distance from origin)
            TLenStop = 8.5;     % End of the Tick mark (distance from origin)
            [STX,STY,TTX,TTY] = ticMark(TLenStart,TLenStop);
            % Plot Skinny and Thick Tick marks on the clock face
            h = plot(STX*dia+x0,STY*dia+y0, 'linewidth',1,'color','k');
            handles(end+1: end + length(h)) = h;
            h= plot(TTX*dia+x0,TTY*dia+y0, 'linewidth',5,'color','k');
            handles(end+1: end + length(h)) = h;
            time = clock;
            [HpDx,HpDy,MpDx,MpDy,SpDx,SpDy] = GetPolyData(time);
            %Plot/Fill the 3 polygon hands for initial view
            o.hourhand = fill(HpDx*dia+x0,HpDy*dia+y0,'k');
            handles(end+1) = o.hourhand;
            o.minhand = fill(MpDx*dia +x0,MpDy*dia+y0,'k');
            handles(end+1) = o.minhand;
            o.sechand = fill(SpDx*dia+x0,SpDy*dia +y0,'r', 'EdgeColor', 'none');
            handles(end+1) = o.sechand;
            hold off
            o.handles = handles;
            o.x0 = x0; o.y0=y0; o.dia = dia;
        end
        
        function updateClock(o, time)
            % time should be in format [year month day hour minute seconds]
            if ~exist('time', 'var'), time = clock; end
            
            x0 = o.x0; y0 = o.y0; dia = o.dia; % Convenience;

            [HpDx,HpDy,MpDx,MpDy,SpDx,SpDy] = GetPolyData(time);
            set(o.hourhand,'xdata',HpDx*dia+x0,'ydata',HpDy*dia + y0);
            set(o.minhand,'xdata',MpDx*dia +x0,'ydata',MpDy*dia + y0);
            set(o.sechand,'xdata',SpDx*dia +x0,'ydata',SpDy*dia + y0);
        end%end of the timerFcn
        function startRT(o)
            % Lets it run as a real-time clock, i.e. usual clock
            o.datimer = timer('timerfcn',{@updateClock, o},'period',.1,'executionmode','fixedrate');
            %start the timer
            start(o.datimer)
        end
        function hide(o)
            if o.hidden, return; end;
            for i = 1:length(o.handles)
                set(o.handles(i), 'Visible', 'off');
            end
            o.hidden = true;
        end
        function unhide(o)
            if ~o.hidden, return; end;
            for i = 1:length(o.handles)
                set(o.handles(i), 'Visible', 'on');
            end
            o.hidden = false;
        end
            
            

    end
    properties (Access = private)
        hourhand;
        minhand;
        sechand;
        datimer;
        x0;
        y0;
        dia;
        ah; %Axis handle where the clock is to be drawn
        handles = [];
        hidden = false;
        
    end
end

function updateClock(varargin)
    o = varargin{3};
    if ~ishandle(o.ah)
        % The user has probably closed the window, stopping timer to avoid
        % "invalid handle object" error. The try/catch has been added in case the
        % timer has never started it will just end without causing errors.
        try
            stop(o.datimer)
            delete(o.datimer)
        catch ME %#ok<NASGU>
        end
    else
        varargin{3}.updateClock();
    end
end

function [HpDx,HpDy,MpDx,MpDy,SpDx,SpDy] = GetPolyData(time)
        %GetPolyData is given the time in a vector and returns
        % the points that make up the polygons relative to the
        % time(in a vector) given to it. This angle of each 
        % polygon is calculated by the Initial angle of each hand
        % then the dimensions are specified through the _HandData
        % shown below. Finally the function PolyEngine is
        % called where it will return the data points that make up
        % the polygon. This is done to allow the polygons to be 
        % updated later without having to redifine there 
        % specifications here in GetPolyData.  
        %Initial Angle of Each Hand
        hoursTheta = (((time(4)*30)+(time(5)/2))-90)*(-pi/180);
        minsTheta = (((time(5)*6)-90)+time(6)/10)*(-pi/180);
        secsTheta= ((time(6)*6)-90)*(-pi/180);
        %Data Set for each hand
        %HandData = 'Front Length' 'Back Length' 'Front Width' 'Back Width'
        HourHandData = [5 5/3 .1 .3];
        % X     Y data for this polygon
        [HpDx,HpDy] = PolyEngine(hoursTheta,HourHandData);
        MinuteHandData  = [7 7/3 .1 .3];
        [MpDx,MpDy] = PolyEngine(minsTheta,MinuteHandData);
        SecondHandData = [7 7/3 .05 .15];
        [SpDx,SpDy] = PolyEngine(secsTheta,SecondHandData);
end %end GetPolyData

function [STX,STY,TTX,TTY] = ticMark(TLenStart,TLenStop)
            %ticMark is given the distance from center to start the tick
            % marks (TLenStart) and the distance from origin to stop the
            % tick marks (TLenStop).
            %STTTheta 60 point array going clockwise skinny ticmarks
            STTheta = pi/2:-2*pi/60:-3*pi/2;
            %Calculates X Y coordinates for all 60 skinny tick marks
            STX = [TLenStart*cos(STTheta') TLenStop*cos(STTheta')]'; 
            STY = [TLenStart*sin(STTheta') TLenStop*sin(STTheta')]';
            %TTTheta 12 point array going around clockwise thick tic marks
            TTTheta = pi/2:-2*pi/12:-3*pi/2;
            %Calculates X Y coordinates for all 12 thick tic marks
            TTX = [TLenStart*cos(TTTheta') TLenStop*cos(TTTheta')]'; 
            TTY = [TLenStart*sin(TTTheta') TLenStop*sin(TTTheta')]';
end %end ticmark function
   
function [pDx,pDy] = PolyEngine(Theta,HanData)
        %PolyEngine is given the initial angle and specifications for each
        % polygon it is to generate. It then calculates the data points
        % that will makeup the polygon and passes it back in two variables
        % making up a set of X and Y coordinates that corelate to the
        % current angle. This makes it easy to plot the points as well as
        % easy to change the polygons shape. At this time it is specified
        % with 4 data points but a 5th could be added easily with only
        % changing the data in this PolyEngine function.
    
        %-Hand Polygon Equations
        %==================================================================
        %-Calculate the length from origin to points A and B.
        oA = sqrt(HanData(1)^2+HanData(3)^2);
        %-Calculate the length from origin to points C and D.
        oB = sqrt(HanData(2)^2+HanData(4)^2);
        %-Calculate X Y coordinates of points A B C and D.
        %-Prepare the X Y data points to be easily passed back and plotted.
        pDx = [oA*cos(Theta+atan(HanData(3)/HanData(1))), ...
              (HanData(1)+HanData(3)*5)*cos(Theta),...
              oA*cos(Theta-atan(HanData(3)/HanData(1))),...
              oB*cos(Theta+atan(HanData(4)/HanData(2))+pi),...
              oB*cos(Theta-atan(HanData(4)/HanData(2))+pi)];
        pDy = [oA*sin(Theta+atan(HanData(3)/HanData(1))) (HanData(1)+HanData(3)*5)*sin(Theta) oA*sin(Theta-atan(HanData(3)/HanData(1))) oB*sin(Theta+atan(HanData(4)/HanData(2))+pi) oB*sin(Theta-atan(HanData(4)/HanData(2))+pi)];
end%end PolyEngine function
    
    

Contact us