% MAKECOLORCET - Constructs the COLORCET function
%
% Usage: makecolorcet
%
% The colourmaps to include within the function are hard-wired in the
% code, edit to suit
%
% See also: COLORCET, CMAP

% 2018 Peter Kovesi
% Centre for Exploration Targeting
% The University of Western Australia
% peter.kovesi at uwa edu au

% PK March 2017
%   August 2017 - Added colour maps for the colour blind
%   April  2018 - Added L16 black-blue-green-yellow and L17, L18,
%                 L19 decreasong lightness, increasing chroma maps.
%   Nov    2020 - Added C10, C11 and R4.  Allowed for 2-digit naming
%   Dec    2020 - Add L20

function makecolorcet
    
    mapnames = {{'L1' 'L01' 'GREY' 'GRAY'}
                {'L2' 'L02' 'REDUCEDGREY' }
                {'L3' 'L03' 'KRYW', 'HEAT' 'FIRE'}
                {'L4' 'L04' 'KRY', 'YELLOWHEAT' }
                {'L5' 'L05' 'KGY'}
                {'L6' 'L06' 'KBC'}
                {'L7' 'L07' 'BMW'}
                {'L8' 'L08' 'BMY' }
                {'L9' 'L09' 'BGYW' }
                {'L10' 'GEOGRAPHIC' }
                {'L11' 'GEOGRAPHIC2' }
                {'L12' 'DEPTH' }
                {'L13' 'REDTERNARY'}
                {'L14' 'GREENTERNARY'}
                {'L15' 'BLUETERNARY'}
                {'L16' 'KBGYW'}
                {'L17' 'WORB'}
                {'L18' 'WYOR'}
                {'L19' 'WCMR'}
                {'L20' 'GOULDIAN'}
                {'D1' 'D01' 'BWR', 'COOLWARM'}
                {'D1A' 'D01A' 'BWRA'}
                {'D2' 'D02' 'GWV'}
                {'D3' 'D03' 'GWR'}
                {'D4' 'D04' 'BKR'}
                {'D5' 'D05' 'GKR'}
                {'D6' 'D06' 'BKY'}
                {'D7' 'D07' 'BJY','DIVBJY'}
                {'D8' 'D08' 'BJR'}
                {'D9' 'D09'}
                {'D10' }
                {'D11' }
                {'D12' }
                {'D13', 'BWG'}
                {'C1' 'C01'}	    
                {'C2' 'C02' 'PHASE4'}
                {'C3' 'C03' }
                {'C4' 'C04' 'PHASE2'}
                {'C5' 'C05' 'CYCLICGREY' }
                {'C6' 'C06'}
                {'C7' 'C07'}
                {'C8' 'C08'}
                {'C9' 'C09'}
                {'C10'}
                {'C11'}
                {'R1' 'R01' 'RAINBOW'}
                {'R2' 'R02' 'RAINBOW2'}
                {'R3' 'R03' 'RAINBOW3'}
                {'R4' 'R04' 'RAINBOW4'}
                {'I1' 'I01'}
                {'I2' 'I02'}
                {'I3' 'I03'}
                {'CBL1' }
                {'CBL2' }
                {'CBL3' }
                {'CBL4' }
                {'CBD1' }
                {'CBD2' }
                {'CBC1' }
                {'CBC2' }
                {'CBTL1' }
                {'CBTL2' }
                {'CBTL3' }
                {'CBTL4' }
                {'CBTD1' }
                {'CBTC1' }
                {'CBTC2' }};
    
    space4 = '    ';
    space8 = '        ';
    N = 256;
    
    % If colorcet.m exists back it up.  Use the mv command so this will
    % only work on Linux or OSX
    if exist('colorcet.m', 'file')
        backupname = ['colorcet' '_backup_' date '.m'];
        system(['mv colorcet.m ' backupname]);
    end
    
    [fid, msg] = fopen('colorcet.m', 'wt');
    error(msg);
    
    writecelltext(fid, helptext)
    
    fprintf(fid, '%% Generated by MAKECOLORCET %s\n\n', date);
    
    fprintf(fid, 'function varargout = colorcet(varargin)\n');
    
    fprintf(fid, '\n[mapname, reverse, shift, N] = parseinputs(varargin{:});\n');
    
    % Write the list of mapnames to colorcet so that it can generate full
    % help data
    fprintf(fid, '\nmapnames = {\n');
    for n = 1:length(mapnames)
        fprintf(fid, '{');
        for m = 1:length(mapnames{n})
            fprintf(fid, ' ''%s'' ', upper(mapnames{n}{m}));
        end
        fprintf(fid, '}\n');        
    end
    fprintf(fid, '};\n\n');

    writecelltext(fid, listalltext)    
    
    fprintf(fid, 'switch upper(mapname) \n');
    
    for n = 1:length(mapnames)
        [map, descriptorname, description] = cmap(mapnames{n}{1}, 'N', N);

        fprintf(fid, 'case {');
        for m = 1:length(mapnames{n})
            fprintf(fid, ' ''%s'' ', upper(mapnames{n}{m}));
        end
        fprintf(fid, '}\n');        

        
        fprintf(fid, 'descriptorname = ''%s'';\n', descriptorname);
        fprintf(fid, 'description = ''%s'';\n', description);
        
        fprintf(fid, 'map = [%f %f %f\n', map(1,1), map(1,2), map(1,3));
        for m = 2:N-1
            fprintf(fid, '       %f %f %f\n', map(m,1), map(m,2), map(m,3));
        end
        
        fprintf(fid, '       %f %f %f];\n\n', map(N,1), map(N,2), map(N,3));
        
        
    end % for n = 1:length(mapnames)
    
    fprintf(fid, 'otherwise\n');
    fprintf(fid, 'error(''Unrecognised colour map name'')\n');
    fprintf(fid, 'end \n\n');  % end of switch
    
    writecelltext(fid, endoffunctiontext)
    writecelltext(fid, parseinputstext)
    
%----------------------------------------------------------
% Function to write text stored in a cell array to a fileid

function writecelltext(fid, txt)
    
    for n = 1:length(txt)
        fprintf(fid,'%s\n', txt{n});
    end
    
%----------------------------------------------------------
% Function to define help text

function txt = helptext
    
    txt = {...
        '% COLORCET - Perceptually uniform color maps'
        '%'
        '% Usage 1: Generate a colour map and apply it to the current figure.'
        '%  >> colorcet(name);'
        '%  >> colorcet(name, keyword, arg, ...);'
        '%'
        '% Usage 2: Return a colour map and its description.'
        '%  >> [map, descriptorname, description] = colorcet(name);'
        '%'
        '% Usage 3: Get a list of all possible colour maps.'
        '%  >> colorcet'
        '%'
        '% Arguments:'
        '%       name - A string label indicating the colour map to be generated.'
        '%' 
        '%     names:  ''L1'' - ''L20''  for linear maps'
        '%             ''D1'' - ''D13''  for diverging maps'
        '%             ''C1'' - ''C11''  for cyclic maps'
        '%             ''R1'' - ''R4''   for rainbow maps'
        '%             ''I1'' - ''I3''   for isoluminant maps'
        '%                                                    '
        '%     maps for the red-green colour blind (Protanopia/Deuteranopia)' 
        '%             ''CBL1'' - ''CBL4'' '
        '%             ''CBD1'' - ''CBD2'' '
        '%             ''CBC1'' - ''CBC2'' '
        '%     maps for the blue-yellow colour blind (Tritanopia)' 
        '%             ''CBTL1'' - ''CBTL4'' '
        '%             ''CBTD1'' '
        '%             ''CBTC1'' - ''CBTC2'' '
        '%'
        '% Keyword - argument options'
        '%           ''N'' - Number of values in the colour map. Defaults to 256.'
        '%       ''shift'' - Fraction of the colour map length N that the colour map is'
        '%                 to be cyclically rotated, may be negative.  (Should only be'
        '%                 applied to cyclic colour maps!). Defaults to 0.'
        '%     ''reverse'' - If set to 1/true reverses the colour map. Defaults to 0.'
        '%'
        '%'
        '% Returns:'
        '%            map - Nx3 rgb colour map'
        '% descriptorname - A string giving a systematic descriptor name for the colour map.'
        '%    description - A string giving a brief description of the colour map.'
        '%'
        '%'
        '% Some colour maps have alternate names for convenience and readability.'
        '% Run colorcet with no arguments to get a full list of names'
        '% For example:'
        '%   >> map = colorcet(''L3'');  or map = colorcet(''heat'');  will produce a linear heat map.'
        '%'
        '% Examples of use:'
        '% Rotate a cyclic colour map by half a cycle'
        '%   >> map = colorcet(''C2'', ''shift'', 0.5);'
        '%'
        '% Generate a 64 level reversed blue-green-yellow map'
        '%   >> map = colorcet(''L9'', ''N'', 64, ''reverse'', true);'
        '%'
        '% Load up the SINERAMP test image and render it with MATLAB''s hot map'
        '%   >> figure; imagesc(sineramp); colormap(hot);'
        '%'
        '% Now repeat using the colorcet perceptually uniform heat map'
        '%   >> figure; imagesc(sineramp);  colorcet(''heat'');'
        '%'
        '% Notice the difference! No preceptual dead spots across the top of the' 
        '% test image and no false features across the bottom.'
        '%'
        '% Some colour maps likely to be of interest:'
        '%   L1/GREY/GRAY  - Standard grey colour map '
        '%   L3/HEAT/FIRE  - A good heat colour map with no perceptual dead spots.'
        '%   L16/KBGYW     - Black-Blue-Green-Yellow-White, quite nice. '
        '%   L20/GOULDIAN  - A replacement for Parula, it works much better! '
        '%   D1/COOLWARM   - Standard blue-white-red diverging colour map with'
        '%                   end colours matched for lightness and chroma.'
        '%   D1A/BWRA      - Similar to D1 but with greater chroma and darker '
        '%                   end colours.'
        '%   D13/BWG       - Blue-White-Green diverging map, easy on the eye'
        '%   R2/RAINBOW2   - The least worst rainbow colour map I could devise,'
        '%                   actually it''s not bad.'
        '%   R3/RAINBOW3   - Rainbow map that can also be used as a diverging map'
        '%                   with yellow as the reference centre colour.'
        '%   C2/PHASE4     - Good cyclic map with four distinct colours allowing'
        '%                   four orientations or phase angles to be visualised.'
        '%   C4/PHASE2     - A blue - white - red cyclic map for when you are only'
        '%                   interested in phase being +ve or -ve.'
        '%   C6            - A modified colour circle with 6 distinct colours.'
        '%   C7            - Another good cyclic map with 4 distinct colours.'
        '%'
        '% If you are colour blind...'
        '% The following maps have been constructed to lie within a 2D model of'
        '% protanopic/deuteranopic colour space. Hopefully by working within this'
        '% colour space people who are colour blind will be able to share a common'
        '% perceptual interpretation of data with those who have normal colour vision.'
        '%   CBL1 to CBL3 - Linear maps for protanopes and deuteranopes'
        '%   CBD1         - Diverging maps for protanopes and deuteranopes'
        '%   CBC1 to CBC2 - Cyclic maps for protanopes and deuteranopes'
        '%  There are also maps for tritanopes'
        '%'
        '% The colour map arrays in this function have been generated by the function CMAP.'
        '%'
        '% For more information about the design of these colour maps see:'
        '% Peter Kovesi. Good Colour Maps: How to Design Them.'
        '% https://arxiv.org/abs/1509.03700'
        '% See also:'
        '% https://peterkovesi.com/projects/colourmaps/usersguide'
        '%'
        '% See also: CMAP, MAKECOLORCET, SINERAMP, SHOW'
        ''
        '% These colour maps are released under the Creative Commons BY License.'
        '% A summary of the conditions can be found at'
        '% https://creativecommons.org/licenses/by/4.0/'
        '% Basically, you are free to use these colour maps in anyway you wish'
        '% as long as you give appropriate credit.'
        '%'
        '% Reference:'
        '% Peter Kovesi. Good Colour Maps: How to Design Them.'
        '% arXiv:1509.03700 [cs.GR] 2015'
        '% https://arxiv.org/abs/1509.03700'
        ''
        '% Copyright (c) 2014-2020 Peter Kovesi'
        '% Centre for Exploration Targeting'
        '% The University of Western Australia'
        '% peter.kovesi at uwa edu au'
        '%'
        '% Permission is hereby granted, free of charge, to any person obtaining a copy'
        '% of this software and associated documentation files (the "Software"), to deal'
        '% in the Software without restriction, subject to the following conditions:'
        '%'
        '% The above copyright notice and this permission notice shall be included in'
        '% all copies or substantial portions of the Software.'
        '%'
        '% The Software is provided "as is", without warranty of any kind.'
        ''};
    
%----------------------------------------------------------
% Function to define code for printing all the colour map names and descriptions

function txt = listalltext    

    txt = {...
        'spaces = ''                                                           '';'
        ''
        'if ~exist(''mapname'', ''var'') || isempty(mapname)'
        ''
        'fprintf(''\nName(s):         Description:\n'');'
        'fprintf(''------------------------------------------------------\n'');'
        ''
        'for n = 1:length(mapnames)'
        '    [map, descriptorname, description] = colorcet(mapnames{n}{1});'
        ''
        '    len = 0;'
        '    for m = 1:length(mapnames{n})'
        '        len = len + fprintf(''%s'', upper(mapnames{n}{m}));'
        '        if m < length(mapnames{n})'
        '            len = len+fprintf(''/'');'
        '        end'
        '    end'
        ''
        '    fprintf(''%s'', spaces(1:16-len));'
        '    fprintf('' %s \n'', description);'
        'end'
        ''
        'return'
        'end'
        ''};
    
    
%----------------------------------------------------------
% Function returning the text defining the end of the function

function txt = endoffunctiontext

    txt = {...    
        '    % If specified apply a cyclic shift to the colour map'
        '    if shift'
        '        map = circshift(map, round(size(map,1)*shift));'
        '    end'
        ''
        '    if reverse'
        '       map = flipud(map);'
        '    end'
        ''    
        '    % Subsample if specified'
        '    if N < size(map,1)'
        '        tmap = map;'
        '        tN = size(tmap,1);'
        '        map = zeros(N,3);'
        '        for j = 1:3'
        '            map(:,j) = interp1(0:(tN-1), tmap(:,j), (0:(N-1))*(tN-1)/(N-1));'
        '        end'
        '    end'
        ''    
        ''
        '    if nargout == 0'
        '        colormap(map);'
        '    elseif nargout == 1'
        '        varargout = {map};'
        '    elseif nargout == 2'
        '        varargout = {map, descriptorname};'
        '    elseif nargout == 3'
        '        varargout = {map, descriptorname, description};'
        '    end'
        ''};
    
    
%----------------------------------------------------------
% Function returning the text to define the parseinputs() function

function txt = parseinputstext

    txt = {...
        '%---------------------------------------------------------'
        'function [mapname, reverse, shift, N] = parseinputs(varargin)'
        ''    
        '    p = inputParser;'
        '    numericORlogical = @(x) isnumeric(x) || islogical(x);'
        ''    
        '    addOptional(p, ''mapname'', '''', @ischar);'
        '    addParameter(p, ''shift'',   0, @isnumeric);'
        '    addParameter(p, ''reverse'', 0, numericORlogical);'
        '    addParameter(p, ''N'', 256, @isnumeric);'
        ''
        '    parse(p, varargin{:});'
        ''    
        '    mapname = strtrim(upper(p.Results.mapname));'
        '    shift   = p.Results.shift;'
        '    reverse = p.Results.reverse;'
        '    N = p.Results.N;'
        ''    
        '    if abs(shift) > 1'
        '        error(''Cyclic shift fraction magnitude cannot be larger than 1'');'
        '    end'
        ''};