Code covered by the BSD License  

Highlights from
Date Odometer class

image thumbnail

Date Odometer class

by

 

12 Apr 2011 (Updated )

Add an odometer-like object to a plot to show the progression of time.

date_odometer
classdef date_odometer < handle
% classdef date_odometer < handle
%
% handle = date_odometer(figure_handle, datetime, position)

% "date_odometer" objects contain odometer digits for month, day and year.

% Kevin J. Delaney, BMT Scientific Marine Services
% April 11, 2011

properties (Access = private, Transient = true) 
    datetime                    % Date/time in datenum format
    daybox_10_handle            % Uicontrol for day digits
    daybox_1_handle             % Uicontrol for day digits
    handles_being_incremented   % Cell array of handles being incremented.
    monthbox_handle             % Uicontrol for month text
    parent                      % Figure object
    position                    % Vector [x, y, width, height] in normalized units
    step_fraction               % Typically 1, but can be a fraction for animation
                                %  Example: setting = 0.25 rolls the odometer
                                %  one quarter of the way
    substrate_frame             % Handle to black frame underlying the digits.
	total_rotation_fraction     % Used to keep track of when enough partial
                                %  rotations have been accomplished.
    style                       % String like 'mmm-dd-yyyy'
    yearbox_1000_handle         % Uicontrol for year digits
    yearbox_100_handle          % Uicontrol for year digits
    yearbox_10_handle           % Uicontrol for year digits
    yearbox_1_handle            % Uicontrol for year digits
end

methods
    %
    %   OBJECT CONSTRUCTOR
    %
    function handle = date_odometer(varargin)
        % handle = date_odometer(figure_handle, datetime, position)
        %
        % Instantiates a 'date_odometer' object.
        %
        % Inputs:
        %   figure_handle   Parent object
        %   datetime        Initial value
        %   position        Position vector in normalized units
        
        switch nargin

            case 3
                handle.parent   = varargin{1};
                handle.datetime = varargin{2};
                handle.position = varargin{3};
                
                odometer_width = handle.position(3);
                digit_width = odometer_width / 10;
                odometer_height = handle.position(4);
                digit_height = 0.95 * odometer_height;
%                 lower_left_hand_corner = [handle.position(1:2), 0, 0];
%                 frame_width = 0.5 * digit_width;
                
                %   Create axes to hold the digits.
                figure_color = get(handle.parent, 'Color');
                axes_handle = axes('Color',    figure_color, ...
                                   'HandleVisibility', 'off', ...
                                   'Parent',   handle.parent, ...
                                   'Position', handle.position, ...
                                   'XColor',   figure_color, ...
                                   'YColor',   figure_color);
%                 
%                 %   Add opaque frame underneath the displayed text.
%                 handle.substrate_frame      = uicontrol('Units', 'normalized', ...
%                                                         'BackgroundColor', get(handle.parent, 'Color'), ...
%                                                         'Parent',  handle.parent, ...
%                                                         'Position', handle.position + [-frame_width, 0, 2*frame_width, 0], ...
%                                                         'Style', 'text'); 
                
                %   The month text digits.
                [year_value, month_index, day_value] = datevec(handle.datetime);
                position_vector = [0, 0, 3 * digit_width, digit_height];
                handle.monthbox_handle = odometer_text(axes_handle, position_vector, ...
                                                       {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', ...
                                                        'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'}, month_index);
                set(axes_handle, 'XLim', [0, odometer_width], 'YLim', [0, odometer_height]);
                
                %   The day digits.
                [tens_digit, ones_digit] = break_into_digits(day_value, 2);
                position_vector = [3.5 * digit_width, 0, digit_width, digit_height];
                handle.daybox_10_handle = odometer_text(axes_handle, position_vector, ...
                                                        {'1', '2', '3', '0'}, tens_digit);
                position_vector = [4.5 * digit_width, 0, digit_width, digit_height];
                handle.daybox_1_handle = odometer_digit(axes_handle, position_vector, ...
                                                        ones_digit);

                %   The year digits.
                [thousands_digit, hundreds_digit, tens_digit, ones_digit] = break_into_digits(year_value, 4);
                position_vector = [6.0 * digit_width, 0, digit_width, digit_height];
                handle.yearbox_1000_handle = odometer_digit(axes_handle, position_vector, ...
                                                            thousands_digit);
                position_vector = [7.0 * digit_width, 0, digit_width, digit_height];
                handle.yearbox_100_handle = odometer_digit(axes_handle, position_vector, ...
                                                           hundreds_digit);
                position_vector = [8.0 * digit_width, 0, digit_width, digit_height];
                handle.yearbox_10_handle = odometer_digit(axes_handle, position_vector, ...
                                                          tens_digit);
                position_vector = [9.0 * digit_width, 0, digit_width, digit_height];
                handle.yearbox_1_handle = odometer_digit(axes_handle, position_vector, ...
                                                         ones_digit);

                %   Defaults
                handle.handles_being_incremented = {};
                handle.step_fraction = 0.25;
                handle.style = 'mmm-dd-yyyy';
                handle.total_rotation_fraction = 0;

            otherwise
                errordlg(['Unexpected number of inputs in "date_odometer" constructor: ', ...
                    num2str(nargin)], mfilename);
                return
        end
           
    end %object constructor
    
    function set.datetime(obj, datetime)
        if isempty(datetime) || isdatenum(datetime)
            obj.datetime = datetime;
        else
            errordlg('Input "datetime" is not valid datenum".', mfilename);
            return
        end
    end
                
    function set.position(obj, position_vector)
        if isempty(position_vector) || ...
           (isnumeric(position_vector) && length(position_vector) == 4)
            obj.position = position_vector;
        else
            errordlg('Input "position_vector" is not a [1 X 4] numeric vector.', ...
                mfilename);
            return
        end
    end
        
    function datetime = get.datetime(obj)
        datetime = obj.datetime;
    end
        
    function handles = get.monthbox_handle(obj)
        handles = obj.monthbox_handle;
    end
        
    function handles = get.daybox_10_handle(obj)
        handles = obj.daybox_10_handle;
    end
        
    function handles = get.daybox_1_handle(obj)
        handles = obj.daybox_1_handle;
    end

    function handles = get.yearbox_1000_handle(obj)
        handles = obj.yearbox_1000_handle;
    end

    function handles = get.yearbox_100_handle(obj)
        handles = obj.yearbox_100_handle;
    end

    function handles = get.yearbox_10_handle(obj)
        handles = obj.yearbox_10_handle;
    end

    function handles = get.yearbox_1_handle(obj)
        handles = obj.yearbox_1_handle;
    end
        
    function set.parent(obj, parent_figure)
        if isempty(parent_figure) || ...
           (ishandle(parent_figure) && strcmpi(get(parent_figure, 'Type'), 'figure'))
            obj.parent = parent_figure;
        else
            errordlg('Input "parent_figure" is not a "figure" object.', ...
                mfilename);
            return
        end
    end
        
    function increment(obj, step_fraction)

        %   Default: Animate in four steps.
        if ~exist('step_fraction', 'var') || isempty(step_fraction) || ~isnumeric(step_fraction)
            step_fraction = 0.25;
            num_steps     = 4;
        else
            if (step_fraction < -1) || (step_fraction > 1)
                errordlg(['Invalid step size: ', num2str(step_fraction), '.'], ...
                    mfilename);
                return
            end

            %   Make sure it all results in exactly one whole rotation.
            total_num_steps = round(1 / abs(step_fraction));
            step_fraction = sign(step_fraction) / total_num_steps;
            obj.step_fraction = step_fraction;

            %   Perform ONE step, then return to calling routine.
            num_steps = 1;
        end
        
        if isempty(obj.handles_being_incremented)
            %   Then what handles will be affected by this change?
            digits_changed = which_digits_change(obj.datetime, obj.datetime + 1, obj.style);
            all_object_handles = {obj.monthbox_handle; ...
                                  obj.daybox_10_handle; ...
                                  obj.daybox_1_handle; ...
                                  obj.yearbox_1000_handle; ...
                                  obj.yearbox_100_handle; ...
                                  obj.yearbox_10_handle; ...
                                  obj.yearbox_1_handle};
            obj.handles_being_incremented = all_object_handles(digits_changed);
        end

        for step_num = 1:num_steps
            cellfun(@(x) x.increment(step_fraction), obj.handles_being_incremented);
            pause(0.2);
            
            %   Remember how far we've rotated.
            obj.total_rotation_fraction = obj.total_rotation_fraction + step_fraction;
        end
        
        %   Are we done?
        if abs(obj.total_rotation_fraction) >= 1
            obj.handles_being_incremented = [];
            obj.total_rotation_fraction = 0;
            obj.datetime = obj.datetime + 1;
        end
    end
    
    function delete(obj)
        if ishandle(obj.daybox_10_handle)
            delete(obj.daybox_10_handle);
        end
        
        if ishandle(obj.daybox_1_handle)
            delete(obj.daybox_1_handle);
        end
        
        if ishandle(obj.monthbox_handle)
            delete(obj.monthbox_handle);
        end
        
        if ishandle(obj.substrate_frame)
            delete(obj.substrate_frame);
        end
        
        if ishandle(obj.yearbox_1000_handle)
            delete(obj.yearbox_1000_handle);
        end
        
        if ishandle(obj.yearbox_100_handle)
            delete(obj.yearbox_100_handle);
        end
        
        if ishandle(obj.yearbox_10_handle)
            delete(obj.yearbox_10_handle);
        end
        
        if ishandle(obj.yearbox_1_handle)
            delete(obj.yearbox_1_handle);
        end
    end
end

end %classdef

Contact us