Browse Source

Upload files to 'code'

Arrenberg_Lab 3 years ago
parent
commit
2e8174b033

+ 771 - 0
code/Fig6 cone code/figureCone20190604.m

@@ -0,0 +1,771 @@
+function hd = coneAnalysis
+  
+%   datachoice = 'visual field';  % for main figure
+  datachoice = 'retina';  % for supplementary figure
+
+
+  warning('off','MATLAB:interp1:UsePCHIP')
+  % deactivating the following cbrewer warning:
+  % Warning: INTERP1(...,'CUBIC') will change in a future release. Use INTERP1(...,'PCHIP') instead. 
+  % > In interp1>sanitycheckmethod (line 265)
+  %   In interp1>parseinputs (line 401)
+  %   In interp1 (line 80)
+  %   In interpolate_cbrewer (line 31)
+  %   In cbrewer (line 101)
+  %   In heatmap20180823 (line 21) 
+  
+%   global globalparam
+  close all
+  
+  cd(['C:\Users\dehmelt\Dropbox\Documents\MATLAB\', ...
+      '201808 SphereAnalysis\fromTakeshiBadenlab_180615\5dpf_180628_correctedbug\'])
+  
+    
+  % Define which character sequences identify the different types of data file.
+  filestring.all   = 'allCones';
+  filestring.blue  = 'Blue';
+  filestring.green = 'Green';
+  filestring.red   = 'Red';
+  filestring.uv    = 'UV';
+  
+  
+  % Manually set max. cone density per square mm, as in Zimmermann et al.
+  maxdensity = 35000;
+  
+  % Manually select data files, and automatically check their names.
+  switch datachoice
+    case 'visual field'
+      fileselection = {'VisualFieldProjection_Red_8dpf.txt', ...
+                       'VisualFieldProjection_Green_8dpf.txt','VisualFieldProjection_Blue_8dpf.txt', ...
+                       'VisualFieldProjection_UV_8dpf.txt','VisualFieldProjection_allCones_8dpf.txt'};
+    case 'retina'
+      fileselection = {'DensityinRetina_Red_8dpf.txt', ...
+                       'DensityinRetina_Green_8dpf.txt','DensityinRetina_Blue_8dpf.txt', ...
+                       'DensityinRetina_UV_8dpf.txt','DensityinRetina_allCones_8dpf.txt'};
+  end
+  occurrence = struct('all',[],'blue',[],'green',[],'red',[],'uv',[]);
+  for file = 1:numel(fileselection)
+    occurrence.all   = [occurrence.all,   ~isempty(strfind(fileselection{file},filestring.all))];
+    occurrence.blue  = [occurrence.blue,  ~isempty(strfind(fileselection{file},filestring.blue))];
+    occurrence.green = [occurrence.green, ~isempty(strfind(fileselection{file},filestring.green))];
+    occurrence.red   = [occurrence.red,   ~isempty(strfind(fileselection{file},filestring.red))];
+    occurrence.uv    = [occurrence.uv,    ~isempty(strfind(fileselection{file},filestring.uv))];
+  end
+  
+  
+  % Check whether each type of file was selected exactly once.
+  if sum(occurrence.all) == 0
+    disp(['None of the file names contain the characters ''',filestring.all,'''.'])
+  elseif sum(occurrence.all) > 1
+    error(['More than one file name contains the characters ''',filestring.all,'''!'])
+  else
+    datafile.all = fileselection{find(occurrence.all)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.blue) == 0
+    disp(['None of the file names contain the characters ''',filestring.blue,'''.'])
+  elseif sum(occurrence.blue) > 1
+    error(['More than one file name contains the characters ''',filestring.blue,'''!'])
+  else
+    datafile.blue = fileselection{find(occurrence.blue)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.green) == 0
+    disp(['None of the file names contain the characters ''',filestring.green,'''.'])
+  elseif sum(occurrence.green) > 1
+    error(['More than one file name contains the characters ''',filestring.green,'''!'])
+  else
+    datafile.green = fileselection{find(occurrence.green)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.red) == 0
+    disp(['None of the file names contain the characters ''',filestring.red,'''.'])
+  elseif sum(occurrence.red) > 1
+    error(['More than one file name contains the characters ''',filestring.red,'''!'])
+  else
+    datafile.red = fileselection{find(occurrence.red)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.uv) == 0
+    disp(['None of the file names contain the characters ''',filestring.uv,'''.'])
+  elseif sum(occurrence.uv) > 1
+    error(['More than one file name contains the characters ''',filestring.uv,'''!'])
+  else
+    datafile.uv = fileselection{find(occurrence.uv)}; %#ok<FNDSB>
+  end
+
+
+
+
+%   % Fit centres from FitAzimElev20181217.txt:
+%   leftnormmaxazim = 74.1675;
+%   leftnormmaxelev = 5.9175;
+%   leftnormmeanazim = 74.9488;
+%   leftnormmeanelev = 5.7009;
+%   leftnormratioazim = 74.9490;
+%   leftnormratioelev = 5.7007;
+%   rightnormmaxazim = -72.0633;
+%   rightnormmaxelev = 7.3858;
+%   rightnormmeanazim = -71.1931;
+%   rightnormmeanelev = 7.5766;
+%   rightnormratioazim = -71.1933;
+%   rightnormratioelev = 7.5767;
+  leftazim_body = -80.2647;
+  leftelev_body = 6.0557;
+  rightazim_body = +77.0306;
+  rightelev_body = -1.9776;
+
+  % Median eye position, pooled once, from positionAnalysis20181217.m:
+  leftmedianazim =  +6.2450;
+  rightmedianazim = -9.8760;
+  leftmeanazim =  +5.2455;
+  rightmeanazim = -9.8840;
+  leftstdazim = 6.1703;
+  rightstdazim = 6.5227;
+  leftsemazim = 0.2472;
+  rightsemazim = 0.2613;
+
+  % Median vertical eye position, from figureVertical20190225.m:
+  leftmeanelev = 3.4937;  % meanmeanposition.UUH(:,:,1);
+  rightmeanelev = 4.8735; % meanmeanposition.UUH(:,:,2);
+  
+  % Corrected to eye-centred visual field angles:
+  leftazim_eye = abs(leftazim_body - leftmeanazim);
+  leftelev_eye = leftelev_body - leftmeanelev;
+  rightazim_eye = abs(rightazim_body - rightmeanazim);
+  rightelev_eye = rightelev_body - rightmeanelev;
+
+
+
+
+  % Create one figure panel per file, and all plots on it.
+  datapresent = fieldnames(datafile);
+  % Reorder field names to reflect desired arrangement of panels on figure.
+  datapresent = {datapresent{[4 3 2 5 1]}};
+  
+  for typeindex = 1:numel(datapresent)
+    
+    conetype = datapresent{typeindex};
+    textfile = datafile.(conetype);
+
+    switch datachoice
+      case 'visual field'
+        conedata = importdata(textfile) * maxdensity;
+      case 'retina'
+        conedata = importdata(textfile);
+    end
+    switch datachoice
+      case 'visual field'
+        conedata = rot90(conedata,3); % so up is at top, anterior to the left
+      case 'retina'
+        conedata = rot90(conedata,1); % so up is at bottom, anterior to the right  
+    end
+    conedata(conedata==0) = NaN; % consider nonzero data only, i.e. from the true area of the retina
+
+    
+    switch datachoice
+      case 'visual field'
+        % Undo approximate correction by Takeshi, then apply a more precise one:
+        offsetAzimTakeshi = 8;
+        offsetElevTakeshi = 5;
+        offsetAzimAccurate = abs(leftmedianazim);
+        offsetElevAccurate = leftmeanelev;
+        resolutionAzim = size(conedata,2)/360;
+        resolutionElev = size(conedata,1)/180;
+        discreteShiftAzim = round((offsetAzimTakeshi-offsetAzimAccurate)/resolutionAzim);
+        discreteShiftElev = round((offsetElevTakeshi-offsetElevAccurate)/resolutionElev);
+        nandata = NaN(size(conedata));
+        % Execute shift (min/max sanitise indices in case shift is negative):
+        nandata(max(1,1-discreteShiftElev):min(end,end-discreteShiftElev), ...
+                max(1,1-discreteShiftAzim):min(end,end-discreteShiftAzim)) = ...
+          conedata(max(1,1+discreteShiftElev):min(end,end+discreteShiftElev), ...
+                   max(1,1+discreteShiftAzim):min(end,end+discreteShiftAzim));
+        % Overwrite original data:
+        conedata = nandata;
+      case 'retina'
+        % Cannot apply correction, as VF filter used by Takeshi is unknown.
+    end
+
+
+  %   cd(['C:\Users\dehmelt\Dropbox\Documents\MATLAB\', ...
+  %       '201808 SphereAnalysis'])
+  % 
+  %   numelevation = size(conedata,1);
+  %   numazimuth = size(conedata,2);
+  % 
+  %   % The following four values represent the coordinates of the four corners
+  %   % of the 2D data sets provided by Baden lab (and are currently unknown).
+  %   elevation.max = 90;
+  %   elevation.min = -90;
+  %   azimuth.max = 180;
+  %   azimuth.min = -180;
+
+  %   % Build an (NX*NY x 2) array, where NX is the number of azimuth values,
+  %   % NY is the number of elevation values, NX*NY is the total number of data
+  %   % points, and for each data point (each row), the azimuth (column 1) and
+  %   % elevation (column 2) are listed.
+  %   elevation.step = (elevation.max-elevation.min)/(numelevation-1);
+  %   azimuth.step = (azimuth.max-azimuth.min)/(numazimuth-1);
+  %   elevationlist = elevation.max:-elevation.step:elevation.min;
+  %   azimuthlist = azimuth.max:-azimuth.step:azimuth.min;
+  %   predictor = [];
+  %   for k = 1:numazimuth
+  %     predictor = [predictor; ...
+  %                  repmat(azimuthlist(k),[numelevation 1]), elevationlist'];
+  %   end
+
+  %   % One raw data value per data point, might have to be reformatted to match
+  %   % the order in which the list of data azimuths/elevations in "predictor" is
+  %   % being created. Predictor goes top-to-bottom, left-to-right.
+  %   response = conedata(:);
+  % 
+  %   offset = 0;
+  %   amplitude = 1;
+  %   meanazimuth = 90;
+  %   meanelevation = 0;
+  %   concentration = 1;
+  %   
+  %   globalparam.offset = offset;
+  %   globalparam.amplitude = amplitude;
+  %   globalparam.concentration = concentration;
+  % 
+  % 
+  %   figure(1)
+  % %   imagesc(conedata)
+  %   surf(conedata)
+  %   shading flat
+  %   axis square
+  %   colormap(cbrewer('div','PRGn',100))
+  %   hold on
+  %   plot([1,numazimuth],[numelevation/2 numelevation/2],'--w','LineWidth',2)
+  %   plot([numazimuth/2, numazimuth/2],[1,numelevation],'--w','LineWidth',2)
+  %   hold off
+  %   set(gca,'XTick',[1,numazimuth*[1/4,1/2,3/4,1]],'XTickLabel',{180 90 0 -90 -180})
+  %   set(gca,'YTick',[1,numelevation*[1/4,1/2,3/4,1]],'YTickLabel',{90 45 0 -45 -90})
+  %   xlabel('azimuth'),ylabel('elevation'),set(gcf,'Color',[1 1 1])
+  %   dataclim = get(gca,'CLim');
+
+
+    hd.cone.fg = figure(1);
+    hd.cone.fg.Position = [400 160 1000 700];
+%     hd.cone.fg.Position = [500 50 540 380];
+    hd.cone.fg.Color = [1 1 1];
+
+    hd.cone.ax(typeindex,1) = axes;
+    if typeindex < 4
+      hd.cone.ax(typeindex,1).Position = [.11+(typeindex-1)*.29 .61 hd.cone.fg.Position([4 3])/3500];
+    else
+      hd.cone.ax(typeindex,1).Position = [.19+(typeindex-4)*.42 .11 hd.cone.fg.Position([4 3])/3500];
+    end
+    
+    az = 0:180/(size(conedata,1)-1):180;
+    el = -90:180/(size(conedata,2)-1):90;
+  %   conedatanonnan = fliplr(flipud(conedata));
+    conedatanonnan = conedata;
+    conedatanonnan(isnan(conedatanonnan)) = 0;
+    density = conv2(conedatanonnan,fspecial('gaussian',[100 100],3),'same');
+  %   surf(az,el,conv2(conedatanonnan,fspecial('gaussian',[100 100],10),'same'))
+  %   shading flat
+    hold on
+    if strcmp(datachoice,'retina') && typeindex == 5 % FD20190829 added as simple correction
+      density = fliplr(density)
+    end
+    imagesc(az,el,density)
+%     if strcmp(conetype,'all')
+%       contour(az,el,density,'LevelStep',1e-1,'Color',.5*[1 1 1])
+%     else
+%       contour(az,el,density,'LevelStep',3e-2,'Color',.5*[1 1 1])
+%     end
+    contourlevels = (.1:.1:1) * max(max(density));  % increments = 10%-of-max.
+    contour(az,el,density,contourlevels,'Color',.5*[1 1 1]); 
+    hold off
+%     axis square
+%     uistack(hd.cone.ax(typeindex,2),'bottom')
+%     hd.cone.ax(2,2) = copyobj(hd.cone.ax(2,1),hd.cone.fg)
+
+    hd.cone.ax(typeindex,1).XLim = [0,180];
+    hd.cone.ax(typeindex,1).YLim = [-90,90];
+    oldclim = hd.cone.ax(typeindex,1).CLim;
+%     climcutoff = .8;
+    climcutoff = .60;
+%     climcutoff = 0;
+    hd.cone.ax(typeindex,1).CLim = [min(oldclim)+climcutoff*(max(oldclim)-min(oldclim)), ...
+                            max(oldclim)];
+    switch datachoice
+      case 'visual field'
+        hd.cone.ax(typeindex,1).XLabel.String = 'azimuth';
+        hd.cone.ax(typeindex,1).YLabel.String = 'elevation';
+      case 'retina'
+        hd.cone.ax(typeindex,1).XLabel.String = 'x  (a.u.)';
+        hd.cone.ax(typeindex,1).YLabel.String = 'y  (a.u.)';
+    end
+    textfilesplit = strsplit(textfile,'_');
+    switch textfilesplit{2}
+      case 'allCones'
+        densitylabel = 'all cones combined';
+      case 'Green'
+        densitylabel = 'green cones (mm^{-2})';
+      case 'Red'
+        densitylabel = 'red cones (mm^{-2})';
+      case 'Blue'
+        densitylabel = 'blue cones (mm^{-2})';
+      case 'UV'
+        densitylabel = 'ultraviolet cones';
+    end
+%     switch textfilesplit{2}
+%       case 'allCones'
+%         densitylabel = {'retinal density','(any cones)'};
+%       case 'Green'
+%         densitylabel = {'retinal density / mm^{-2}','(green cones)'};
+%       case 'Red'
+%         densitylabel = {'retinal density','(red cones)'};
+%       case 'Blue'
+%         densitylabel = {'retinal density','(blue cones)'};
+%       case 'UV'
+%         densitylabel = {'retinal density','(ultraviolet cones)'};
+%     end
+%     switch textfilesplit{2}
+%       case 'allCones'
+%         densitylabel = 'density (any cones)';
+%       case 'Green'
+%         densitylabel = 'density (green cones)';
+%       case 'Red'
+%         densitylabel = 'density (red cones)';
+%       case 'Blue'
+%         densitylabel = 'density (blue cones)';
+%       case 'UV'
+%         densitylabel = 'density (ultraviolet cones)';
+%     end
+
+    hd.cone.ax(typeindex,1).Title.String = densitylabel;
+    hd.cone.ax(typeindex,1).Box = 'on';
+%     hd.cone.ax(typeindex,1).XTick = 0:15:180;
+%     hd.cone.ax(typeindex,1).YTick = -90:15:90;
+%     hd.cone.ax(typeindex,1).XTickLabel = {0,[],[],45,[],[],90,[],[],135,[],[],180};
+%     hd.cone.ax(typeindex,1).YTickLabel = {-90,[],[],-45,[],[],0,[],[],45,[],[],90};
+%     hd.cone.ax(typeindex,1).XTick = 0:45:180;
+%     hd.cone.ax(typeindex,1).YTick = -90:45:90;
+%     hd.cone.ax(typeindex,1).XTickLabel = {0,45,90,135,180};
+%     hd.cone.ax(typeindex,1).YTickLabel = {-90,-45,0,45,90};
+    hd.cone.ax(typeindex,1).XTick = 0:30:180;
+    hd.cone.ax(typeindex,1).YTick = -90:30:90;
+    switch datachoice
+      case 'visual field'
+        hd.cone.ax(typeindex,1).XTickLabel = {0,[],[],90,[],[],180};
+        hd.cone.ax(typeindex,1).YTickLabel = {-90,[],[],0,[],[],90};
+      case 'retina'
+        hd.cone.ax(typeindex,1).XTickLabel = {0,[],[],[],[],[],1};
+        hd.cone.ax(typeindex,1).YTickLabel = {0,[],[],[],[],[],1};
+    end
+    hd.cone.ax(typeindex,1).FontSize = 14;
+    hd.cone.ax(typeindex,1).TitleFontSizeMultiplier = 1;
+    hd.cone.ax(typeindex,1).TitleFontWeight = 'bold';
+%     hd.cone.ax(typeindex,1).Title.Interpreter = 'none';
+    hd.cone.ax(typeindex,1).Title.Interpreter = 'tex';
+  %   [hd.cone.ax(typeindex,1).XLabel.FontSize, ...
+  %    hd.cone.ax(typeindex,1).YLabel.FontSize, ...
+  %    hd.cone.ax(typeindex,1).Title.FontSize, ...
+  %    ha.cone.ax(typeindex,1).FontSize,
+    hd.cone.ax(typeindex,1).XGrid = 'on';
+    hd.cone.ax(typeindex,1).YGrid = 'on';
+    hd.cone.ax(typeindex,1).Layer = 'top';
+    
+    
+    hold on
+    plot([0,180],[0,0],'--k')
+    plot([90,90],[-90,90],'--k')
+    hold off
+
+   
+    
+% %     leftnormmaxazimVF = abs(leftnormmaxazim - leftmedianazim);
+% %     leftnormmeanazimVF = abs(leftnormmeanazim - leftmedianazim);
+% %     leftnormratioazimVF = abs(leftnormratioazim - leftmedianazim);
+% %     rightnormmaxazimVF = abs(rightnormmaxazim - rightmedianazim);
+% %     rightnormmeanazimVF = abs(rightnormmeanazim - rightmedianazim);
+% %     rightnormratioazimVF = abs(rightnormratioazim - rightmedianazim);
+
+
+
+    hold on
+    hd.cone.ob(1,1) = plot([153,165],[-65,-65],'k');
+    hd.cone.ob(1,2) = plot([159,159],[-59,-71],'k');
+    switch datachoice
+      case 'visual field'
+        hd.cone.ob(1,3) = text(153,-64.5,'A','HorizontalAlignment','right','VerticalAlignment','middle');
+        hd.cone.ob(1,4) = text(166,-64.5,'P','HorizontalAlignment','left','VerticalAlignment','middle');
+        hd.cone.ob(1,5) = text(159.2,-59.5,'U','HorizontalAlignment','center','VerticalAlignment','bottom');
+        hd.cone.ob(1,6) = text(159.2,-69.5,'D','HorizontalAlignment','center','VerticalAlignment','top');
+      case 'retina'
+        hd.cone.ob(1,3) = text(153,-64.5,'P','HorizontalAlignment','right','VerticalAlignment','middle');
+        hd.cone.ob(1,4) = text(166,-64.5,'A','HorizontalAlignment','left','VerticalAlignment','middle');
+        hd.cone.ob(1,5) = text(159.2,-59.5,'D','HorizontalAlignment','center','VerticalAlignment','bottom');
+        hd.cone.ob(1,6) = text(159.2,-69.5,'U','HorizontalAlignment','center','VerticalAlignment','top');
+    end
+    hold off
+    
+%     circle.radius = 40/2;
+%     circle.xcentre = leftnormmaxazimVF; % pick preferred measure here
+%     circle.ycentre = leftnormmaxelev; % pick preferred measure here
+%     circle.x = -circle.radius:.1:circle.radius;
+%     circle.y = sqrt(circle.x(1)^2-circle.x.^2);
+%     hold on
+%     plot([circle.xcentre + circle.x, fliplr(circle.xcentre + circle.x)], ...
+%          [circle.ycentre + circle.y, circle.ycentre - circle.y],'-','LineWidth',3,'Color',0*[1 1 1])    
+%     plot([circle.xcentre + circle.x, fliplr(circle.xcentre + circle.x)], ...
+%          [circle.ycentre + circle.y, circle.ycentre - circle.y],'-','LineWidth',2,'Color',1*[1 1 1])    
+%     hold off
+    
+%     boundary.angle = 64;
+% %     centre.azimuth = leftnormmaxazimVF;
+% %     centre.elevation = leftnormmaxelev;
+%     centre.azimuth = rightazim;
+%     centre.elevation = rightelev;
+%     boundary.resolution = .01;
+%     [boundary.azimuth,boundary.elevation] = ...
+%       diskBoundary(centre.azimuth,centre.elevation,boundary.angle,boundary.resolution);
+%     hold on
+%     plot(boundary.azimuth,boundary.elevation,'-','LineWidth',3,'Color',0*[1 1 1])    
+%     plot(boundary.azimuth,boundary.elevation,'-','LineWidth',2,'Color',1*[1 1 1])    
+%     hold off
+
+    boundary.angle = 40;
+%     centre.azimuth = leftnormmaxazimVF;
+%     centre.elevation = leftnormmaxelev;
+%     centre.azimuth = leftazim_body;
+%     centre.elevation = leftelev_body;
+    centre.azimuth = abs(leftazim_body);
+    centre.elevation = leftelev_body;
+    switch datachoice
+      case 'retina'
+        centre.azimuth = 180-centre.azimuth;
+        centre.elevation = -centre.elevation;
+    end
+    switch datachoice
+      case 'visual field'
+        boundary.resolution = .01;
+        [boundary.azimuth,boundary.elevation] = ...
+          diskBoundary(centre.azimuth,centre.elevation,boundary.angle,boundary.resolution);
+        hold on
+        plot(boundary.azimuth,boundary.elevation,'-','LineWidth',3,'Color',0*[1 1 1])    
+        plot(boundary.azimuth,boundary.elevation,'-','LineWidth',2,'Color',1*[1 1 1])    
+        hold off
+    end
+
+
+    mtp = 'o';
+    msz = 100;
+    mec = [0 0 0];
+%     mfc = [.2 .8 .2];
+%     mfa = .2;
+    mfc = 1*[1 1 1];
+    mfa = .5;
+    hold on
+%     hd.cone.ob(1,7) = scatter(rightazim,rightelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+    hd.cone.ob(1,8) = scatter(centre.azimuth,centre.elevation,msz,mtp, ...
+                               'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',.8);
+%     hd.cone.ob(1,9) = scatter(leftnormratioazimVF,leftnormratioelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+%     hd.cone.ob(1,10) = scatter(rightnormmaxazimVF,rightnormmaxelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+%     hd.cone.ob(1,11) = scatter(rightnormmeanazimVF,rightnormmeanelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+%     hd.cone.ob(1,12) = scatter(rightnormratioazimVF,rightnormratioelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+    hold off
+    
+    
+    
+    if typeindex == 2 || typeindex == 3
+      hd.cone.ax(typeindex,1).YLabel.String = [];
+      hd.cone.ax(typeindex,1).YTickLabel = [];
+    end
+    switch conetype
+      case 'blue'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Blues',100))
+      case 'green'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Greens',100))
+      case 'red'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Reds',100))
+      case 'uv'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Purples',100))
+      case 'all'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Greys',100))
+    end
+    
+    hd.cone.ax(typeindex,2) = axes;
+    hd.cone.ax(typeindex,2).Position = hd.cone.ax(typeindex,1).Position + ...
+                                       hd.cone.ax(typeindex,1).Position(3) * .28*[1 0 0 0];
+    hd.cone.cbar(typeindex) = colorbar;
+
+    hd.cone.ax(typeindex,2).Colormap = hd.cone.ax(typeindex,1).Colormap;
+    hd.cone.ax(typeindex,2).CLim     = hd.cone.ax(typeindex,1).CLim;
+    hd.cone.ax(typeindex,2).FontSize = hd.cone.ax(typeindex,1).FontSize;
+    hd.cone.ax(typeindex,2).Color = 'none';
+    [hd.cone.ax(typeindex,2).XAxis.Visible,hd.cone.ax(typeindex,2).YAxis.Visible] = deal('off');
+        
+        
+%     colormap(cbrewer('div','PRGn',100))
+
+
+
+  end
+  
+  hd.cone.ax(1,3) = axes;
+  hd.cone.ax(1,3).Position = [8 0 1 1];
+  hd.cone.ax(1,3).Color = 'none';
+  [hd.cone.ax(1,3).XAxis.Visible,hd.cone.ax(1,3).YAxis.Visible] = deal('off');
+  for typeindex = 1:numel(datapresent)
+    parentpos = hd.cone.ax(typeindex,1).Position;
+    xpos = parentpos(1)+parentpos(3)/2;
+    ypos = parentpos(2)+parentpos(4)*1.128;
+    hd.cone.ob(3,typeindex) = text(xpos, ypos, 'retinal density / mm^{-2}');
+%     hd.cone.ob(3,typeindex) = text(xpos, ypos, 'retinal density (µm^{-2})');
+    hd.cone.ob(3,typeindex).FontWeight = 'normal';
+    hd.cone.ob(3,typeindex).Interpreter = 'tex';
+    hd.cone.ob(3,typeindex).FontSize = hd.cone.ax(typeindex,1).FontSize;
+    hd.cone.ob(3,typeindex).HorizontalAlignment = 'center';
+    hd.cone.ob(3,typeindex).VerticalAlignment = 'bottom';
+  end
+  uistack(hd.cone.ax(1,3),'bottom')
+
+  hd.cone.ax(1,4) = axes;
+  hd.cone.ax(1,4).Position = [0 0 1 1];
+  hd.cone.ax(1,4).Color = 'none';
+  [hd.cone.ax(1,4).XAxis.Visible,hd.cone.ax(1,4).YAxis.Visible] = deal('off');
+  for typeindex = 1:numel(datapresent)
+    parentpos = hd.cone.ax(typeindex,1).Position;
+%     xpos = parentpos(1)+parentpos(3)*-.18;
+    xpos = parentpos(1)+parentpos(3)*-.23;
+    ypos = parentpos(2)+parentpos(4)*1.30;
+%     hd.cone.ob(3,typeindex) = text(xpos, ypos, 'retinal density / mm^{-2}');
+    switch typeindex
+      case 1
+        letterstring = 'a';
+      case 4
+        letterstring = 'b';
+      case 5
+        letterstring = 'c';
+      otherwise
+        letterstring = '';  
+    end
+    hd.cone.ob(4,typeindex) = text(xpos, ypos, letterstring);
+    hd.cone.ob(4,typeindex).FontWeight = 'bold';
+    hd.cone.ob(4,typeindex).Interpreter = 'tex';
+    hd.cone.ob(4,typeindex).FontSize = hd.cone.ax(typeindex,1).FontSize * 18/14;
+    hd.cone.ob(4,typeindex).HorizontalAlignment = 'right';
+    hd.cone.ob(4,typeindex).VerticalAlignment = 'top';
+  end
+  uistack(hd.cone.ax(1,3),'bottom')
+
+  [hd.cone.ax(:,1).TickLength] = deal([0 0]);
+
+  
+%   for k = 1:5, hd.cone.cbar(k,1).Position(1) = hd.cone.ax(k,1).Position(1) + .83*hd.cone.ax(k,1).Position(3); end
+  
+    runtime = clock;
+    timestring = ['_', ...
+                num2str(runtime(1),'%.4u'), ...
+                num2str(runtime(2),'%.2u'), ...
+                num2str(runtime(3),'%.2u'), ...
+                '_', ...
+                num2str(runtime(4),'%.2u'), ...
+                num2str(runtime(5),'%.2u'), ...
+                num2str(runtime(6),'%02.0f')];
+                            
+%   savefig(2,['conefigure/',textfilesplit{2},timestring])
+%   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-depsc')
+% %   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-dtiff')
+%   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-dtiffn')
+%   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-dpng')
+  
+end
+
+
+
+function [azimuthOut, elevationOut] = diskBoundary(azimuthIn, elevationIn, angleIn, resolutionIn)
+
+  centre.azimuth = azimuthIn;
+  centre.elevation = elevationIn;
+  border.angle = angleIn / 2; % convert solid angle ("diameter") to "radius"
+  border.resolution = resolutionIn;
+
+  % The following equation describes orthodromic (or "great circle") distance as an angle:
+  % arccos(sin(centre.elevation)*sin(border.elevation) + ...
+  %        cos(centre.elevation)*cos(border.elevation)*cos(border.azimuth-centre.azimuth)) = angle;
+
+%   border.elevation = centre.elevation-border.angle/2 : .1 : centre.elevation+border.angle/2;
+  intended  = -90 : border.resolution : 90;
+%   intended  = (centre.elevation-border.angle) : border.resolution : (centre.elevation+border.angle);
+  % "auxiliary" elevations are explicitly added to guarantee coverage near top/bottom of circle:
+  auxiliary = centre.elevation + border.angle*[-1 1];
+%   border.elevation = unique([intended,auxiliary]);
+  
+  limit = cosd([centre.elevation-border.angle centre.elevation+border.angle]);
+%   intended = acosd(min(limit) : border.resolution : max(limit));
+
+  %   % XXX Not sure why the following line leads to half of the curve segments not being computed...
+%   intended = [-acosd(0:border.resolution:1),acosd(1-border.resolution:-border.resolution:0)];
+%   auxiliary = centre.elevation + border.angle*[-1 1];
+
+  border.elevation = unique([intended,auxiliary]);
+%   figure(6)
+%   plot(intended)
+
+  border.azimuth = centre.azimuth + ...
+                   acosd((cosd(border.angle) - sind(centre.elevation)*sind(border.elevation)) ./ ...
+                         (cosd(centre.elevation)*cosd(border.elevation)));
+
+
+  % Eliminate complex elements...
+  border.azimuth(imag(border.azimuth)~=0) = NaN; % This statement sometimes fails horribly.
+%   border.azimuth(imag(border.azimuth)>1e-5) = NaN; % This statement is silly, but more stable.
+
+  % ...and compute corresponding values for the second half of the boundary.
+  border.azimuth = [border.azimuth, 2*centre.azimuth-border.azimuth];
+  border.elevation = [border.elevation, border.elevation];
+  
+  border.azimuth = mod(border.azimuth+180,360)-180;
+%   jump = diff(border.azimuth) > 10*border.resolution;
+%   border.azimuth(jump)   = NaN;
+%   border.elevation(jump) = NaN;
+  
+  azimuthOut   = border.azimuth;
+  elevationOut = border.elevation;
+
+end
+
+
+
+% function combinedvalue = vonmisesfisher2param_centre(param,predictor)
+% 
+%   global globalparam
+%   
+%   convention = 'CCW';
+%   dimension = 3;
+%   radius = 1;
+%   if size(predictor,2) == 2
+%     predictor(:,3) = radius*ones(size(predictor(:,1)));
+%   end
+%   [x,y,z] = geo2car(predictor(:,1),predictor(:,2),predictor(:,3),convention);
+%   direction = [x,y,z];
+%   
+%   numberofdistributions = 1; % <---
+%   value = NaN(size(predictor,1),numberofdistributions);
+%   
+%   for k = 1:numberofdistributions
+%     
+%     meanazimuth   = param(2*k-1);
+%     meanelevation = param(2*k);
+%     
+%     offset        = globalparam.offset(k);
+%     amplitude     = globalparam.amplitude(k);
+%     concentration = globalparam.concentration(k);
+%     
+%     [meanx,meany,meanz] = geo2car(meanazimuth,meanelevation,radius,convention);
+%     meandirection = [meanx,meany,meanz];
+% 
+%     bessel = concentration / ...
+%              (2*pi * (exp(concentration) - exp(-concentration)));
+% 
+%     normalisation = concentration^(dimension/2 - 1) / ...
+%                     ((2*pi)^(dimension/2) * bessel);
+%                 
+%     value(:,k) = normalisation * exp(concentration*meandirection*direction')';
+%     value(:,k) = amplitude * value(:,k) + offset;
+%    
+%     badness = fitpenalty(meanazimuth,-180,180) + ...
+%               fitpenalty(meanelevation,-90,90) + ...
+%               fitpenalty(amplitude,0,Inf)      + ...
+%               fitpenalty(concentration,0,Inf);
+%             
+%     value(:,k) = value(:,k) + 1e3*badness;
+%     
+%   end
+%   
+%   combinedvalue = sum(value,2);
+%   
+% end
+
+
+
+% function penalty = fitpenalty(value,lowerlimit,upperlimit)
+%   
+%   factor = 1;
+%   
+%   if value < lowerlimit
+%     
+%     penalty = factor*(lowerlimit-value).^1;
+%     
+%   elseif value > upperlimit
+%     
+%     penalty = factor*(value-upperlimit).^1;
+%     
+%   else
+%     
+%     penalty = 0;
+%     
+%   end
+%   
+% end
+
+
+
+% function combinedvalue = oneVonMisesFisher(params,predictor)
+% %TWOVONMISESFISHER Evaluate two overlapping von Mises-Fisher distributions.
+% %
+% %   VALUE = VONMISESFISHER computes the value of only one(!) 
+% %   spherical (p=3) von Mises-Fisher distributions at the chosen location.
+% %
+% %   It is formatted to match the input/output structure required by the
+% %   built-in non-linear regression function nlinfit.
+% 
+%   convention = 'CW';
+%   dimension = 3;
+%   radius = 1;
+%   if size(predictor,2) == 2
+%     predictor(:,3) = radius*ones(size(predictor(:,1)));
+%   end
+%   [x,y,z] = geo2car(predictor(:,1),predictor(:,2),predictor(:,3),convention);
+%   direction = [x,y,z];
+%   
+%   numberofdistributions = 1; % <-- This is the only difference to "twoVonMisesFisher.m".
+%   value = NaN(size(predictor,1),numberofdistributions);
+%   
+%   for k = 1:numberofdistributions
+%     
+%     offset        = params(5*k-4);
+%     amplitude     = params(5*k-3);
+%     meanazimuth   = params(5*k-2);
+%     meanelevation = params(5*k-1);
+%     
+%         meanelevation = 25; % HACK!
+% 
+%     
+%     [meanx,meany,meanz] = geo2car(meanazimuth,meanelevation,radius,convention);
+%     meandirection = [meanx,meany,meanz];
+% %     meandirection = geo2car([params((-2:-1)+4*k),radius]);
+%     concentration = params(5*k);
+%     
+%     bessel = concentration / ...
+%              (2*pi * (exp(concentration) - exp(-concentration)));
+% 
+%     normalisation = concentration^(dimension/2 - 1) / ...
+%                     ((2*pi)^(dimension/2) * bessel);
+%                 
+%     value(:,k) = normalisation * exp(concentration*meandirection*direction')';
+%     value(:,k) = amplitude * value(:,k) + offset;
+%     
+%     % HACK to constrain values
+%     if abs(meanazimuth) > 180  || ...
+%        abs(meanelevation) > 90 || ...
+%        amplitude < 0           || ...
+%        concentration < 0
+%      
+%       value(:,k) = 1e10*ones(size(value(:,k)));
+%       
+%     end
+% 
+%   end
+%   
+%   combinedvalue = sum(value,2);
+%   
+% end
+

+ 770 - 0
code/Fig6 cone code/figureCone20190604gin.m

@@ -0,0 +1,770 @@
+function hd = coneAnalysis
+  
+  datachoice = 'visual field';  % for main figure
+%   datachoice = 'retina';  % for supplementary figure
+
+
+  warning('off','MATLAB:interp1:UsePCHIP')
+  % deactivating the following cbrewer warning:
+  % Warning: INTERP1(...,'CUBIC') will change in a future release. Use INTERP1(...,'PCHIP') instead. 
+  % > In interp1>sanitycheckmethod (line 265)
+  %   In interp1>parseinputs (line 401)
+  %   In interp1 (line 80)
+  %   In interpolate_cbrewer (line 31)
+  %   In cbrewer (line 101)
+  %   In heatmap20180823 (line 21) 
+  
+%   global globalparam
+  close all
+  
+  cd(['..\..\data\Fig6 cone data\'])
+  
+    
+  % Define which character sequences identify the different types of data file.
+  filestring.all   = 'allCones';
+  filestring.blue  = 'Blue';
+  filestring.green = 'Green';
+  filestring.red   = 'Red';
+  filestring.uv    = 'UV';
+  
+  
+  % Manually set max. cone density per square mm, as in Zimmermann et al.
+  maxdensity = 35000;
+  
+  % Manually select data files, and automatically check their names.
+  switch datachoice
+    case 'visual field'
+      fileselection = {'VisualFieldProjection_Red_8dpf.txt', ...
+                       'VisualFieldProjection_Green_8dpf.txt','VisualFieldProjection_Blue_8dpf.txt', ...
+                       'VisualFieldProjection_UV_8dpf.txt','VisualFieldProjection_allCones_8dpf.txt'};
+    case 'retina'
+      fileselection = {'DensityinRetina_Red_8dpf.txt', ...
+                       'DensityinRetina_Green_8dpf.txt','DensityinRetina_Blue_8dpf.txt', ...
+                       'DensityinRetina_UV_8dpf.txt','DensityinRetina_allCones_8dpf.txt'};
+  end
+  occurrence = struct('all',[],'blue',[],'green',[],'red',[],'uv',[]);
+  for file = 1:numel(fileselection)
+    occurrence.all   = [occurrence.all,   ~isempty(strfind(fileselection{file},filestring.all))];
+    occurrence.blue  = [occurrence.blue,  ~isempty(strfind(fileselection{file},filestring.blue))];
+    occurrence.green = [occurrence.green, ~isempty(strfind(fileselection{file},filestring.green))];
+    occurrence.red   = [occurrence.red,   ~isempty(strfind(fileselection{file},filestring.red))];
+    occurrence.uv    = [occurrence.uv,    ~isempty(strfind(fileselection{file},filestring.uv))];
+  end
+  
+  
+  % Check whether each type of file was selected exactly once.
+  if sum(occurrence.all) == 0
+    disp(['None of the file names contain the characters ''',filestring.all,'''.'])
+  elseif sum(occurrence.all) > 1
+    error(['More than one file name contains the characters ''',filestring.all,'''!'])
+  else
+    datafile.all = fileselection{find(occurrence.all)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.blue) == 0
+    disp(['None of the file names contain the characters ''',filestring.blue,'''.'])
+  elseif sum(occurrence.blue) > 1
+    error(['More than one file name contains the characters ''',filestring.blue,'''!'])
+  else
+    datafile.blue = fileselection{find(occurrence.blue)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.green) == 0
+    disp(['None of the file names contain the characters ''',filestring.green,'''.'])
+  elseif sum(occurrence.green) > 1
+    error(['More than one file name contains the characters ''',filestring.green,'''!'])
+  else
+    datafile.green = fileselection{find(occurrence.green)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.red) == 0
+    disp(['None of the file names contain the characters ''',filestring.red,'''.'])
+  elseif sum(occurrence.red) > 1
+    error(['More than one file name contains the characters ''',filestring.red,'''!'])
+  else
+    datafile.red = fileselection{find(occurrence.red)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.uv) == 0
+    disp(['None of the file names contain the characters ''',filestring.uv,'''.'])
+  elseif sum(occurrence.uv) > 1
+    error(['More than one file name contains the characters ''',filestring.uv,'''!'])
+  else
+    datafile.uv = fileselection{find(occurrence.uv)}; %#ok<FNDSB>
+  end
+
+
+
+
+%   % Fit centres from FitAzimElev20181217.txt:
+%   leftnormmaxazim = 74.1675;
+%   leftnormmaxelev = 5.9175;
+%   leftnormmeanazim = 74.9488;
+%   leftnormmeanelev = 5.7009;
+%   leftnormratioazim = 74.9490;
+%   leftnormratioelev = 5.7007;
+%   rightnormmaxazim = -72.0633;
+%   rightnormmaxelev = 7.3858;
+%   rightnormmeanazim = -71.1931;
+%   rightnormmeanelev = 7.5766;
+%   rightnormratioazim = -71.1933;
+%   rightnormratioelev = 7.5767;
+  leftazim_body = -80.2647;
+  leftelev_body = 6.0557;
+  rightazim_body = +77.0306;
+  rightelev_body = -1.9776;
+
+  % Median eye position, pooled once, from positionAnalysis20181217.m:
+  leftmedianazim =  +6.2450;
+  rightmedianazim = -9.8760;
+  leftmeanazim =  +5.2455;
+  rightmeanazim = -9.8840;
+  leftstdazim = 6.1703;
+  rightstdazim = 6.5227;
+  leftsemazim = 0.2472;
+  rightsemazim = 0.2613;
+
+  % Median vertical eye position, from figureVertical20190225.m:
+  leftmeanelev = 3.4937;  % meanmeanposition.UUH(:,:,1);
+  rightmeanelev = 4.8735; % meanmeanposition.UUH(:,:,2);
+  
+  % Corrected to eye-centred visual field angles:
+  leftazim_eye = abs(leftazim_body - leftmeanazim);
+  leftelev_eye = leftelev_body - leftmeanelev;
+  rightazim_eye = abs(rightazim_body - rightmeanazim);
+  rightelev_eye = rightelev_body - rightmeanelev;
+
+
+
+
+  % Create one figure panel per file, and all plots on it.
+  datapresent = fieldnames(datafile);
+  % Reorder field names to reflect desired arrangement of panels on figure.
+  datapresent = {datapresent{[4 3 2 5 1]}};
+  
+  for typeindex = 1:numel(datapresent)
+    
+    conetype = datapresent{typeindex};
+    textfile = datafile.(conetype);
+
+    switch datachoice
+      case 'visual field'
+        conedata = importdata(textfile) * maxdensity;
+      case 'retina'
+        conedata = importdata(textfile);
+    end
+    switch datachoice
+      case 'visual field'
+        conedata = rot90(conedata,3); % so up is at top, anterior to the left
+      case 'retina'
+        conedata = rot90(conedata,1); % so up is at bottom, anterior to the right  
+    end
+    conedata(conedata==0) = NaN; % consider nonzero data only, i.e. from the true area of the retina
+
+    
+    switch datachoice
+      case 'visual field'
+        % Undo approximate correction by Takeshi, then apply a more precise one:
+        offsetAzimTakeshi = 8;
+        offsetElevTakeshi = 5;
+        offsetAzimAccurate = abs(leftmedianazim);
+        offsetElevAccurate = leftmeanelev;
+        resolutionAzim = size(conedata,2)/360;
+        resolutionElev = size(conedata,1)/180;
+        discreteShiftAzim = round((offsetAzimTakeshi-offsetAzimAccurate)/resolutionAzim);
+        discreteShiftElev = round((offsetElevTakeshi-offsetElevAccurate)/resolutionElev);
+        nandata = NaN(size(conedata));
+        % Execute shift (min/max sanitise indices in case shift is negative):
+        nandata(max(1,1-discreteShiftElev):min(end,end-discreteShiftElev), ...
+                max(1,1-discreteShiftAzim):min(end,end-discreteShiftAzim)) = ...
+          conedata(max(1,1+discreteShiftElev):min(end,end+discreteShiftElev), ...
+                   max(1,1+discreteShiftAzim):min(end,end+discreteShiftAzim));
+        % Overwrite original data:
+        conedata = nandata;
+      case 'retina'
+        % Cannot apply correction, as VF filter used by Takeshi is unknown.
+    end
+
+
+  %   cd(['C:\Users\dehmelt\Dropbox\Documents\MATLAB\', ...
+  %       '201808 SphereAnalysis'])
+  % 
+  %   numelevation = size(conedata,1);
+  %   numazimuth = size(conedata,2);
+  % 
+  %   % The following four values represent the coordinates of the four corners
+  %   % of the 2D data sets provided by Baden lab (and are currently unknown).
+  %   elevation.max = 90;
+  %   elevation.min = -90;
+  %   azimuth.max = 180;
+  %   azimuth.min = -180;
+
+  %   % Build an (NX*NY x 2) array, where NX is the number of azimuth values,
+  %   % NY is the number of elevation values, NX*NY is the total number of data
+  %   % points, and for each data point (each row), the azimuth (column 1) and
+  %   % elevation (column 2) are listed.
+  %   elevation.step = (elevation.max-elevation.min)/(numelevation-1);
+  %   azimuth.step = (azimuth.max-azimuth.min)/(numazimuth-1);
+  %   elevationlist = elevation.max:-elevation.step:elevation.min;
+  %   azimuthlist = azimuth.max:-azimuth.step:azimuth.min;
+  %   predictor = [];
+  %   for k = 1:numazimuth
+  %     predictor = [predictor; ...
+  %                  repmat(azimuthlist(k),[numelevation 1]), elevationlist'];
+  %   end
+
+  %   % One raw data value per data point, might have to be reformatted to match
+  %   % the order in which the list of data azimuths/elevations in "predictor" is
+  %   % being created. Predictor goes top-to-bottom, left-to-right.
+  %   response = conedata(:);
+  % 
+  %   offset = 0;
+  %   amplitude = 1;
+  %   meanazimuth = 90;
+  %   meanelevation = 0;
+  %   concentration = 1;
+  %   
+  %   globalparam.offset = offset;
+  %   globalparam.amplitude = amplitude;
+  %   globalparam.concentration = concentration;
+  % 
+  % 
+  %   figure(1)
+  % %   imagesc(conedata)
+  %   surf(conedata)
+  %   shading flat
+  %   axis square
+  %   colormap(cbrewer('div','PRGn',100))
+  %   hold on
+  %   plot([1,numazimuth],[numelevation/2 numelevation/2],'--w','LineWidth',2)
+  %   plot([numazimuth/2, numazimuth/2],[1,numelevation],'--w','LineWidth',2)
+  %   hold off
+  %   set(gca,'XTick',[1,numazimuth*[1/4,1/2,3/4,1]],'XTickLabel',{180 90 0 -90 -180})
+  %   set(gca,'YTick',[1,numelevation*[1/4,1/2,3/4,1]],'YTickLabel',{90 45 0 -45 -90})
+  %   xlabel('azimuth'),ylabel('elevation'),set(gcf,'Color',[1 1 1])
+  %   dataclim = get(gca,'CLim');
+
+
+    hd.cone.fg = figure(1);
+    hd.cone.fg.Position = [400 160 1000 700];
+%     hd.cone.fg.Position = [500 50 540 380];
+    hd.cone.fg.Color = [1 1 1];
+
+    hd.cone.ax(typeindex,1) = axes;
+    if typeindex < 4
+      hd.cone.ax(typeindex,1).Position = [.11+(typeindex-1)*.29 .61 hd.cone.fg.Position([4 3])/3500];
+    else
+      hd.cone.ax(typeindex,1).Position = [.19+(typeindex-4)*.42 .11 hd.cone.fg.Position([4 3])/3500];
+    end
+    
+    az = 0:180/(size(conedata,1)-1):180;
+    el = -90:180/(size(conedata,2)-1):90;
+  %   conedatanonnan = fliplr(flipud(conedata));
+    conedatanonnan = conedata;
+    conedatanonnan(isnan(conedatanonnan)) = 0;
+    density = conv2(conedatanonnan,fspecial('gaussian',[100 100],3),'same');
+  %   surf(az,el,conv2(conedatanonnan,fspecial('gaussian',[100 100],10),'same'))
+  %   shading flat
+    hold on
+    if strcmp(datachoice,'retina') && typeindex == 5 % FD20190829 added as simple correction
+      density = fliplr(density);
+    end
+    imagesc(az,el,density)
+%     if strcmp(conetype,'all')
+%       contour(az,el,density,'LevelStep',1e-1,'Color',.5*[1 1 1])
+%     else
+%       contour(az,el,density,'LevelStep',3e-2,'Color',.5*[1 1 1])
+%     end
+    contourlevels = (.1:.1:1) * max(max(density));  % increments = 10%-of-max.
+    contour(az,el,density,contourlevels,'Color',.5*[1 1 1]); 
+    hold off
+%     axis square
+%     uistack(hd.cone.ax(typeindex,2),'bottom')
+%     hd.cone.ax(2,2) = copyobj(hd.cone.ax(2,1),hd.cone.fg)
+
+    hd.cone.ax(typeindex,1).XLim = [0,180];
+    hd.cone.ax(typeindex,1).YLim = [-90,90];
+    oldclim = hd.cone.ax(typeindex,1).CLim;
+%     climcutoff = .8;
+    climcutoff = .60;
+%     climcutoff = 0;
+    hd.cone.ax(typeindex,1).CLim = [min(oldclim)+climcutoff*(max(oldclim)-min(oldclim)), ...
+                            max(oldclim)];
+    switch datachoice
+      case 'visual field'
+        hd.cone.ax(typeindex,1).XLabel.String = 'azimuth';
+        hd.cone.ax(typeindex,1).YLabel.String = 'elevation';
+      case 'retina'
+        hd.cone.ax(typeindex,1).XLabel.String = 'x  (a.u.)';
+        hd.cone.ax(typeindex,1).YLabel.String = 'y  (a.u.)';
+    end
+    textfilesplit = strsplit(textfile,'_');
+    switch textfilesplit{2}
+      case 'allCones'
+        densitylabel = 'all cones combined';
+      case 'Green'
+        densitylabel = 'green cones (mm^{-2})';
+      case 'Red'
+        densitylabel = 'red cones (mm^{-2})';
+      case 'Blue'
+        densitylabel = 'blue cones (mm^{-2})';
+      case 'UV'
+        densitylabel = 'ultraviolet cones';
+    end
+%     switch textfilesplit{2}
+%       case 'allCones'
+%         densitylabel = {'retinal density','(any cones)'};
+%       case 'Green'
+%         densitylabel = {'retinal density / mm^{-2}','(green cones)'};
+%       case 'Red'
+%         densitylabel = {'retinal density','(red cones)'};
+%       case 'Blue'
+%         densitylabel = {'retinal density','(blue cones)'};
+%       case 'UV'
+%         densitylabel = {'retinal density','(ultraviolet cones)'};
+%     end
+%     switch textfilesplit{2}
+%       case 'allCones'
+%         densitylabel = 'density (any cones)';
+%       case 'Green'
+%         densitylabel = 'density (green cones)';
+%       case 'Red'
+%         densitylabel = 'density (red cones)';
+%       case 'Blue'
+%         densitylabel = 'density (blue cones)';
+%       case 'UV'
+%         densitylabel = 'density (ultraviolet cones)';
+%     end
+
+    hd.cone.ax(typeindex,1).Title.String = densitylabel;
+    hd.cone.ax(typeindex,1).Box = 'on';
+%     hd.cone.ax(typeindex,1).XTick = 0:15:180;
+%     hd.cone.ax(typeindex,1).YTick = -90:15:90;
+%     hd.cone.ax(typeindex,1).XTickLabel = {0,[],[],45,[],[],90,[],[],135,[],[],180};
+%     hd.cone.ax(typeindex,1).YTickLabel = {-90,[],[],-45,[],[],0,[],[],45,[],[],90};
+%     hd.cone.ax(typeindex,1).XTick = 0:45:180;
+%     hd.cone.ax(typeindex,1).YTick = -90:45:90;
+%     hd.cone.ax(typeindex,1).XTickLabel = {0,45,90,135,180};
+%     hd.cone.ax(typeindex,1).YTickLabel = {-90,-45,0,45,90};
+    hd.cone.ax(typeindex,1).XTick = 0:30:180;
+    hd.cone.ax(typeindex,1).YTick = -90:30:90;
+    switch datachoice
+      case 'visual field'
+        hd.cone.ax(typeindex,1).XTickLabel = {0,[],[],90,[],[],180};
+        hd.cone.ax(typeindex,1).YTickLabel = {-90,[],[],0,[],[],90};
+      case 'retina'
+        hd.cone.ax(typeindex,1).XTickLabel = {0,[],[],[],[],[],1};
+        hd.cone.ax(typeindex,1).YTickLabel = {0,[],[],[],[],[],1};
+    end
+    hd.cone.ax(typeindex,1).FontSize = 14;
+    hd.cone.ax(typeindex,1).TitleFontSizeMultiplier = 1;
+    hd.cone.ax(typeindex,1).TitleFontWeight = 'bold';
+%     hd.cone.ax(typeindex,1).Title.Interpreter = 'none';
+    hd.cone.ax(typeindex,1).Title.Interpreter = 'tex';
+  %   [hd.cone.ax(typeindex,1).XLabel.FontSize, ...
+  %    hd.cone.ax(typeindex,1).YLabel.FontSize, ...
+  %    hd.cone.ax(typeindex,1).Title.FontSize, ...
+  %    ha.cone.ax(typeindex,1).FontSize,
+    hd.cone.ax(typeindex,1).XGrid = 'on';
+    hd.cone.ax(typeindex,1).YGrid = 'on';
+    hd.cone.ax(typeindex,1).Layer = 'top';
+    
+    
+    hold on
+    plot([0,180],[0,0],'--k')
+    plot([90,90],[-90,90],'--k')
+    hold off
+
+   
+    
+% %     leftnormmaxazimVF = abs(leftnormmaxazim - leftmedianazim);
+% %     leftnormmeanazimVF = abs(leftnormmeanazim - leftmedianazim);
+% %     leftnormratioazimVF = abs(leftnormratioazim - leftmedianazim);
+% %     rightnormmaxazimVF = abs(rightnormmaxazim - rightmedianazim);
+% %     rightnormmeanazimVF = abs(rightnormmeanazim - rightmedianazim);
+% %     rightnormratioazimVF = abs(rightnormratioazim - rightmedianazim);
+
+
+
+    hold on
+    hd.cone.ob(1,1) = plot([153,165],[-65,-65],'k');
+    hd.cone.ob(1,2) = plot([159,159],[-59,-71],'k');
+    switch datachoice
+      case 'visual field'
+        hd.cone.ob(1,3) = text(153,-64.5,'A','HorizontalAlignment','right','VerticalAlignment','middle');
+        hd.cone.ob(1,4) = text(166,-64.5,'P','HorizontalAlignment','left','VerticalAlignment','middle');
+        hd.cone.ob(1,5) = text(159.2,-59.5,'U','HorizontalAlignment','center','VerticalAlignment','bottom');
+        hd.cone.ob(1,6) = text(159.2,-69.5,'D','HorizontalAlignment','center','VerticalAlignment','top');
+      case 'retina'
+        hd.cone.ob(1,3) = text(153,-64.5,'P','HorizontalAlignment','right','VerticalAlignment','middle');
+        hd.cone.ob(1,4) = text(166,-64.5,'A','HorizontalAlignment','left','VerticalAlignment','middle');
+        hd.cone.ob(1,5) = text(159.2,-59.5,'D','HorizontalAlignment','center','VerticalAlignment','bottom');
+        hd.cone.ob(1,6) = text(159.2,-69.5,'U','HorizontalAlignment','center','VerticalAlignment','top');
+    end
+    hold off
+    
+%     circle.radius = 40/2;
+%     circle.xcentre = leftnormmaxazimVF; % pick preferred measure here
+%     circle.ycentre = leftnormmaxelev; % pick preferred measure here
+%     circle.x = -circle.radius:.1:circle.radius;
+%     circle.y = sqrt(circle.x(1)^2-circle.x.^2);
+%     hold on
+%     plot([circle.xcentre + circle.x, fliplr(circle.xcentre + circle.x)], ...
+%          [circle.ycentre + circle.y, circle.ycentre - circle.y],'-','LineWidth',3,'Color',0*[1 1 1])    
+%     plot([circle.xcentre + circle.x, fliplr(circle.xcentre + circle.x)], ...
+%          [circle.ycentre + circle.y, circle.ycentre - circle.y],'-','LineWidth',2,'Color',1*[1 1 1])    
+%     hold off
+    
+%     boundary.angle = 64;
+% %     centre.azimuth = leftnormmaxazimVF;
+% %     centre.elevation = leftnormmaxelev;
+%     centre.azimuth = rightazim;
+%     centre.elevation = rightelev;
+%     boundary.resolution = .01;
+%     [boundary.azimuth,boundary.elevation] = ...
+%       diskBoundary(centre.azimuth,centre.elevation,boundary.angle,boundary.resolution);
+%     hold on
+%     plot(boundary.azimuth,boundary.elevation,'-','LineWidth',3,'Color',0*[1 1 1])    
+%     plot(boundary.azimuth,boundary.elevation,'-','LineWidth',2,'Color',1*[1 1 1])    
+%     hold off
+
+    boundary.angle = 40;
+%     centre.azimuth = leftnormmaxazimVF;
+%     centre.elevation = leftnormmaxelev;
+%     centre.azimuth = leftazim_body;
+%     centre.elevation = leftelev_body;
+    centre.azimuth = abs(leftazim_body);
+    centre.elevation = leftelev_body;
+    switch datachoice
+      case 'retina'
+        centre.azimuth = 180-centre.azimuth;
+        centre.elevation = -centre.elevation;
+    end
+    switch datachoice
+      case 'visual field'
+        boundary.resolution = .01;
+        [boundary.azimuth,boundary.elevation] = ...
+          diskBoundary(centre.azimuth,centre.elevation,boundary.angle,boundary.resolution);
+        hold on
+        plot(boundary.azimuth,boundary.elevation,'-','LineWidth',3,'Color',0*[1 1 1])    
+        plot(boundary.azimuth,boundary.elevation,'-','LineWidth',2,'Color',1*[1 1 1])    
+        hold off
+    end
+
+
+    mtp = 'o';
+    msz = 100;
+    mec = [0 0 0];
+%     mfc = [.2 .8 .2];
+%     mfa = .2;
+    mfc = 1*[1 1 1];
+    mfa = .5;
+    hold on
+%     hd.cone.ob(1,7) = scatter(rightazim,rightelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+    hd.cone.ob(1,8) = scatter(centre.azimuth,centre.elevation,msz,mtp, ...
+                               'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',.8);
+%     hd.cone.ob(1,9) = scatter(leftnormratioazimVF,leftnormratioelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+%     hd.cone.ob(1,10) = scatter(rightnormmaxazimVF,rightnormmaxelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+%     hd.cone.ob(1,11) = scatter(rightnormmeanazimVF,rightnormmeanelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+%     hd.cone.ob(1,12) = scatter(rightnormratioazimVF,rightnormratioelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+    hold off
+    
+    
+    
+    if typeindex == 2 || typeindex == 3
+      hd.cone.ax(typeindex,1).YLabel.String = [];
+      hd.cone.ax(typeindex,1).YTickLabel = [];
+    end
+    switch conetype
+      case 'blue'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Blues',100))
+      case 'green'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Greens',100))
+      case 'red'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Reds',100))
+      case 'uv'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Purples',100))
+      case 'all'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Greys',100))
+    end
+    
+    hd.cone.ax(typeindex,2) = axes;
+    hd.cone.ax(typeindex,2).Position = hd.cone.ax(typeindex,1).Position + ...
+                                       hd.cone.ax(typeindex,1).Position(3) * .28*[1 0 0 0];
+    hd.cone.cbar(typeindex) = colorbar;
+
+    hd.cone.ax(typeindex,2).Colormap = hd.cone.ax(typeindex,1).Colormap;
+    hd.cone.ax(typeindex,2).CLim     = hd.cone.ax(typeindex,1).CLim;
+    hd.cone.ax(typeindex,2).FontSize = hd.cone.ax(typeindex,1).FontSize;
+    hd.cone.ax(typeindex,2).Color = 'none';
+    [hd.cone.ax(typeindex,2).XAxis.Visible,hd.cone.ax(typeindex,2).YAxis.Visible] = deal('off');
+        
+        
+%     colormap(cbrewer('div','PRGn',100))
+
+
+
+  end
+  
+  hd.cone.ax(1,3) = axes;
+  hd.cone.ax(1,3).Position = [8 0 1 1];
+  hd.cone.ax(1,3).Color = 'none';
+  [hd.cone.ax(1,3).XAxis.Visible,hd.cone.ax(1,3).YAxis.Visible] = deal('off');
+  for typeindex = 1:numel(datapresent)
+    parentpos = hd.cone.ax(typeindex,1).Position;
+    xpos = parentpos(1)+parentpos(3)/2;
+    ypos = parentpos(2)+parentpos(4)*1.128;
+    hd.cone.ob(3,typeindex) = text(xpos, ypos, 'retinal density / mm^{-2}');
+%     hd.cone.ob(3,typeindex) = text(xpos, ypos, 'retinal density (µm^{-2})');
+    hd.cone.ob(3,typeindex).FontWeight = 'normal';
+    hd.cone.ob(3,typeindex).Interpreter = 'tex';
+    hd.cone.ob(3,typeindex).FontSize = hd.cone.ax(typeindex,1).FontSize;
+    hd.cone.ob(3,typeindex).HorizontalAlignment = 'center';
+    hd.cone.ob(3,typeindex).VerticalAlignment = 'bottom';
+  end
+  uistack(hd.cone.ax(1,3),'bottom')
+
+  hd.cone.ax(1,4) = axes;
+  hd.cone.ax(1,4).Position = [0 0 1 1];
+  hd.cone.ax(1,4).Color = 'none';
+  [hd.cone.ax(1,4).XAxis.Visible,hd.cone.ax(1,4).YAxis.Visible] = deal('off');
+  for typeindex = 1:numel(datapresent)
+    parentpos = hd.cone.ax(typeindex,1).Position;
+%     xpos = parentpos(1)+parentpos(3)*-.18;
+    xpos = parentpos(1)+parentpos(3)*-.23;
+    ypos = parentpos(2)+parentpos(4)*1.30;
+%     hd.cone.ob(3,typeindex) = text(xpos, ypos, 'retinal density / mm^{-2}');
+    switch typeindex
+      case 1
+        letterstring = 'a';
+      case 4
+        letterstring = 'b';
+      case 5
+        letterstring = 'c';
+      otherwise
+        letterstring = '';  
+    end
+    hd.cone.ob(4,typeindex) = text(xpos, ypos, letterstring);
+    hd.cone.ob(4,typeindex).FontWeight = 'bold';
+    hd.cone.ob(4,typeindex).Interpreter = 'tex';
+    hd.cone.ob(4,typeindex).FontSize = hd.cone.ax(typeindex,1).FontSize * 18/14;
+    hd.cone.ob(4,typeindex).HorizontalAlignment = 'right';
+    hd.cone.ob(4,typeindex).VerticalAlignment = 'top';
+  end
+  uistack(hd.cone.ax(1,3),'bottom')
+
+  [hd.cone.ax(:,1).TickLength] = deal([0 0]);
+
+  
+%   for k = 1:5, hd.cone.cbar(k,1).Position(1) = hd.cone.ax(k,1).Position(1) + .83*hd.cone.ax(k,1).Position(3); end
+  
+    runtime = clock;
+    timestring = ['_', ...
+                num2str(runtime(1),'%.4u'), ...
+                num2str(runtime(2),'%.2u'), ...
+                num2str(runtime(3),'%.2u'), ...
+                '_', ...
+                num2str(runtime(4),'%.2u'), ...
+                num2str(runtime(5),'%.2u'), ...
+                num2str(runtime(6),'%02.0f')];
+                            
+%   savefig(2,['conefigure/',textfilesplit{2},timestring])
+%   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-depsc')
+% %   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-dtiff')
+%   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-dtiffn')
+%   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-dpng')
+  
+end
+
+
+
+function [azimuthOut, elevationOut] = diskBoundary(azimuthIn, elevationIn, angleIn, resolutionIn)
+
+  centre.azimuth = azimuthIn;
+  centre.elevation = elevationIn;
+  border.angle = angleIn / 2; % convert solid angle ("diameter") to "radius"
+  border.resolution = resolutionIn;
+
+  % The following equation describes orthodromic (or "great circle") distance as an angle:
+  % arccos(sin(centre.elevation)*sin(border.elevation) + ...
+  %        cos(centre.elevation)*cos(border.elevation)*cos(border.azimuth-centre.azimuth)) = angle;
+
+%   border.elevation = centre.elevation-border.angle/2 : .1 : centre.elevation+border.angle/2;
+  intended  = -90 : border.resolution : 90;
+%   intended  = (centre.elevation-border.angle) : border.resolution : (centre.elevation+border.angle);
+  % "auxiliary" elevations are explicitly added to guarantee coverage near top/bottom of circle:
+  auxiliary = centre.elevation + border.angle*[-1 1];
+%   border.elevation = unique([intended,auxiliary]);
+  
+  limit = cosd([centre.elevation-border.angle centre.elevation+border.angle]);
+%   intended = acosd(min(limit) : border.resolution : max(limit));
+
+  %   % XXX Not sure why the following line leads to half of the curve segments not being computed...
+%   intended = [-acosd(0:border.resolution:1),acosd(1-border.resolution:-border.resolution:0)];
+%   auxiliary = centre.elevation + border.angle*[-1 1];
+
+  border.elevation = unique([intended,auxiliary]);
+%   figure(6)
+%   plot(intended)
+
+  border.azimuth = centre.azimuth + ...
+                   acosd((cosd(border.angle) - sind(centre.elevation)*sind(border.elevation)) ./ ...
+                         (cosd(centre.elevation)*cosd(border.elevation)));
+
+
+  % Eliminate complex elements...
+  border.azimuth(imag(border.azimuth)~=0) = NaN; % This statement sometimes fails horribly.
+%   border.azimuth(imag(border.azimuth)>1e-5) = NaN; % This statement is silly, but more stable.
+
+  % ...and compute corresponding values for the second half of the boundary.
+  border.azimuth = [border.azimuth, 2*centre.azimuth-border.azimuth];
+  border.elevation = [border.elevation, border.elevation];
+  
+  border.azimuth = mod(border.azimuth+180,360)-180;
+%   jump = diff(border.azimuth) > 10*border.resolution;
+%   border.azimuth(jump)   = NaN;
+%   border.elevation(jump) = NaN;
+  
+  azimuthOut   = border.azimuth;
+  elevationOut = border.elevation;
+
+end
+
+
+
+% function combinedvalue = vonmisesfisher2param_centre(param,predictor)
+% 
+%   global globalparam
+%   
+%   convention = 'CCW';
+%   dimension = 3;
+%   radius = 1;
+%   if size(predictor,2) == 2
+%     predictor(:,3) = radius*ones(size(predictor(:,1)));
+%   end
+%   [x,y,z] = geo2car(predictor(:,1),predictor(:,2),predictor(:,3),convention);
+%   direction = [x,y,z];
+%   
+%   numberofdistributions = 1; % <---
+%   value = NaN(size(predictor,1),numberofdistributions);
+%   
+%   for k = 1:numberofdistributions
+%     
+%     meanazimuth   = param(2*k-1);
+%     meanelevation = param(2*k);
+%     
+%     offset        = globalparam.offset(k);
+%     amplitude     = globalparam.amplitude(k);
+%     concentration = globalparam.concentration(k);
+%     
+%     [meanx,meany,meanz] = geo2car(meanazimuth,meanelevation,radius,convention);
+%     meandirection = [meanx,meany,meanz];
+% 
+%     bessel = concentration / ...
+%              (2*pi * (exp(concentration) - exp(-concentration)));
+% 
+%     normalisation = concentration^(dimension/2 - 1) / ...
+%                     ((2*pi)^(dimension/2) * bessel);
+%                 
+%     value(:,k) = normalisation * exp(concentration*meandirection*direction')';
+%     value(:,k) = amplitude * value(:,k) + offset;
+%    
+%     badness = fitpenalty(meanazimuth,-180,180) + ...
+%               fitpenalty(meanelevation,-90,90) + ...
+%               fitpenalty(amplitude,0,Inf)      + ...
+%               fitpenalty(concentration,0,Inf);
+%             
+%     value(:,k) = value(:,k) + 1e3*badness;
+%     
+%   end
+%   
+%   combinedvalue = sum(value,2);
+%   
+% end
+
+
+
+% function penalty = fitpenalty(value,lowerlimit,upperlimit)
+%   
+%   factor = 1;
+%   
+%   if value < lowerlimit
+%     
+%     penalty = factor*(lowerlimit-value).^1;
+%     
+%   elseif value > upperlimit
+%     
+%     penalty = factor*(value-upperlimit).^1;
+%     
+%   else
+%     
+%     penalty = 0;
+%     
+%   end
+%   
+% end
+
+
+
+% function combinedvalue = oneVonMisesFisher(params,predictor)
+% %TWOVONMISESFISHER Evaluate two overlapping von Mises-Fisher distributions.
+% %
+% %   VALUE = VONMISESFISHER computes the value of only one(!) 
+% %   spherical (p=3) von Mises-Fisher distributions at the chosen location.
+% %
+% %   It is formatted to match the input/output structure required by the
+% %   built-in non-linear regression function nlinfit.
+% 
+%   convention = 'CW';
+%   dimension = 3;
+%   radius = 1;
+%   if size(predictor,2) == 2
+%     predictor(:,3) = radius*ones(size(predictor(:,1)));
+%   end
+%   [x,y,z] = geo2car(predictor(:,1),predictor(:,2),predictor(:,3),convention);
+%   direction = [x,y,z];
+%   
+%   numberofdistributions = 1; % <-- This is the only difference to "twoVonMisesFisher.m".
+%   value = NaN(size(predictor,1),numberofdistributions);
+%   
+%   for k = 1:numberofdistributions
+%     
+%     offset        = params(5*k-4);
+%     amplitude     = params(5*k-3);
+%     meanazimuth   = params(5*k-2);
+%     meanelevation = params(5*k-1);
+%     
+%         meanelevation = 25; % HACK!
+% 
+%     
+%     [meanx,meany,meanz] = geo2car(meanazimuth,meanelevation,radius,convention);
+%     meandirection = [meanx,meany,meanz];
+% %     meandirection = geo2car([params((-2:-1)+4*k),radius]);
+%     concentration = params(5*k);
+%     
+%     bessel = concentration / ...
+%              (2*pi * (exp(concentration) - exp(-concentration)));
+% 
+%     normalisation = concentration^(dimension/2 - 1) / ...
+%                     ((2*pi)^(dimension/2) * bessel);
+%                 
+%     value(:,k) = normalisation * exp(concentration*meandirection*direction')';
+%     value(:,k) = amplitude * value(:,k) + offset;
+%     
+%     % HACK to constrain values
+%     if abs(meanazimuth) > 180  || ...
+%        abs(meanelevation) > 90 || ...
+%        amplitude < 0           || ...
+%        concentration < 0
+%      
+%       value(:,k) = 1e10*ones(size(value(:,k)));
+%       
+%     end
+% 
+%   end
+%   
+%   combinedvalue = sum(value,2);
+%   
+% end
+

+ 770 - 0
code/Fig6FigSupp1 cone code/figureConeSupp20190604gin.m

@@ -0,0 +1,770 @@
+function hd = coneAnalysis
+  
+%   datachoice = 'visual field';  % for main figure
+  datachoice = 'retina';  % for supplementary figure
+
+
+  warning('off','MATLAB:interp1:UsePCHIP')
+  % deactivating the following cbrewer warning:
+  % Warning: INTERP1(...,'CUBIC') will change in a future release. Use INTERP1(...,'PCHIP') instead. 
+  % > In interp1>sanitycheckmethod (line 265)
+  %   In interp1>parseinputs (line 401)
+  %   In interp1 (line 80)
+  %   In interpolate_cbrewer (line 31)
+  %   In cbrewer (line 101)
+  %   In heatmap20180823 (line 21) 
+  
+%   global globalparam
+  close all
+  
+  cd(['..\..\data\Fig6 cone data\'])
+  
+    
+  % Define which character sequences identify the different types of data file.
+  filestring.all   = 'allCones';
+  filestring.blue  = 'Blue';
+  filestring.green = 'Green';
+  filestring.red   = 'Red';
+  filestring.uv    = 'UV';
+  
+  
+  % Manually set max. cone density per square mm, as in Zimmermann et al.
+  maxdensity = 35000;
+  
+  % Manually select data files, and automatically check their names.
+  switch datachoice
+    case 'visual field'
+      fileselection = {'VisualFieldProjection_Red_8dpf.txt', ...
+                       'VisualFieldProjection_Green_8dpf.txt','VisualFieldProjection_Blue_8dpf.txt', ...
+                       'VisualFieldProjection_UV_8dpf.txt','VisualFieldProjection_allCones_8dpf.txt'};
+    case 'retina'
+      fileselection = {'DensityinRetina_Red_8dpf.txt', ...
+                       'DensityinRetina_Green_8dpf.txt','DensityinRetina_Blue_8dpf.txt', ...
+                       'DensityinRetina_UV_8dpf.txt','DensityinRetina_allCones_8dpf.txt'};
+  end
+  occurrence = struct('all',[],'blue',[],'green',[],'red',[],'uv',[]);
+  for file = 1:numel(fileselection)
+    occurrence.all   = [occurrence.all,   ~isempty(strfind(fileselection{file},filestring.all))];
+    occurrence.blue  = [occurrence.blue,  ~isempty(strfind(fileselection{file},filestring.blue))];
+    occurrence.green = [occurrence.green, ~isempty(strfind(fileselection{file},filestring.green))];
+    occurrence.red   = [occurrence.red,   ~isempty(strfind(fileselection{file},filestring.red))];
+    occurrence.uv    = [occurrence.uv,    ~isempty(strfind(fileselection{file},filestring.uv))];
+  end
+  
+  
+  % Check whether each type of file was selected exactly once.
+  if sum(occurrence.all) == 0
+    disp(['None of the file names contain the characters ''',filestring.all,'''.'])
+  elseif sum(occurrence.all) > 1
+    error(['More than one file name contains the characters ''',filestring.all,'''!'])
+  else
+    datafile.all = fileselection{find(occurrence.all)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.blue) == 0
+    disp(['None of the file names contain the characters ''',filestring.blue,'''.'])
+  elseif sum(occurrence.blue) > 1
+    error(['More than one file name contains the characters ''',filestring.blue,'''!'])
+  else
+    datafile.blue = fileselection{find(occurrence.blue)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.green) == 0
+    disp(['None of the file names contain the characters ''',filestring.green,'''.'])
+  elseif sum(occurrence.green) > 1
+    error(['More than one file name contains the characters ''',filestring.green,'''!'])
+  else
+    datafile.green = fileselection{find(occurrence.green)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.red) == 0
+    disp(['None of the file names contain the characters ''',filestring.red,'''.'])
+  elseif sum(occurrence.red) > 1
+    error(['More than one file name contains the characters ''',filestring.red,'''!'])
+  else
+    datafile.red = fileselection{find(occurrence.red)}; %#ok<FNDSB>
+  end
+  if sum(occurrence.uv) == 0
+    disp(['None of the file names contain the characters ''',filestring.uv,'''.'])
+  elseif sum(occurrence.uv) > 1
+    error(['More than one file name contains the characters ''',filestring.uv,'''!'])
+  else
+    datafile.uv = fileselection{find(occurrence.uv)}; %#ok<FNDSB>
+  end
+
+
+
+
+%   % Fit centres from FitAzimElev20181217.txt:
+%   leftnormmaxazim = 74.1675;
+%   leftnormmaxelev = 5.9175;
+%   leftnormmeanazim = 74.9488;
+%   leftnormmeanelev = 5.7009;
+%   leftnormratioazim = 74.9490;
+%   leftnormratioelev = 5.7007;
+%   rightnormmaxazim = -72.0633;
+%   rightnormmaxelev = 7.3858;
+%   rightnormmeanazim = -71.1931;
+%   rightnormmeanelev = 7.5766;
+%   rightnormratioazim = -71.1933;
+%   rightnormratioelev = 7.5767;
+  leftazim_body = -80.2647;
+  leftelev_body = 6.0557;
+  rightazim_body = +77.0306;
+  rightelev_body = -1.9776;
+
+  % Median eye position, pooled once, from positionAnalysis20181217.m:
+  leftmedianazim =  +6.2450;
+  rightmedianazim = -9.8760;
+  leftmeanazim =  +5.2455;
+  rightmeanazim = -9.8840;
+  leftstdazim = 6.1703;
+  rightstdazim = 6.5227;
+  leftsemazim = 0.2472;
+  rightsemazim = 0.2613;
+
+  % Median vertical eye position, from figureVertical20190225.m:
+  leftmeanelev = 3.4937;  % meanmeanposition.UUH(:,:,1);
+  rightmeanelev = 4.8735; % meanmeanposition.UUH(:,:,2);
+  
+  % Corrected to eye-centred visual field angles:
+  leftazim_eye = abs(leftazim_body - leftmeanazim);
+  leftelev_eye = leftelev_body - leftmeanelev;
+  rightazim_eye = abs(rightazim_body - rightmeanazim);
+  rightelev_eye = rightelev_body - rightmeanelev;
+
+
+
+
+  % Create one figure panel per file, and all plots on it.
+  datapresent = fieldnames(datafile);
+  % Reorder field names to reflect desired arrangement of panels on figure.
+  datapresent = {datapresent{[4 3 2 5 1]}};
+  
+  for typeindex = 1:numel(datapresent)
+    
+    conetype = datapresent{typeindex};
+    textfile = datafile.(conetype);
+
+    switch datachoice
+      case 'visual field'
+        conedata = importdata(textfile) * maxdensity;
+      case 'retina'
+        conedata = importdata(textfile);
+    end
+    switch datachoice
+      case 'visual field'
+        conedata = rot90(conedata,3); % so up is at top, anterior to the left
+      case 'retina'
+        conedata = rot90(conedata,1); % so up is at bottom, anterior to the right  
+    end
+    conedata(conedata==0) = NaN; % consider nonzero data only, i.e. from the true area of the retina
+
+    
+    switch datachoice
+      case 'visual field'
+        % Undo approximate correction by Takeshi, then apply a more precise one:
+        offsetAzimTakeshi = 8;
+        offsetElevTakeshi = 5;
+        offsetAzimAccurate = abs(leftmedianazim);
+        offsetElevAccurate = leftmeanelev;
+        resolutionAzim = size(conedata,2)/360;
+        resolutionElev = size(conedata,1)/180;
+        discreteShiftAzim = round((offsetAzimTakeshi-offsetAzimAccurate)/resolutionAzim);
+        discreteShiftElev = round((offsetElevTakeshi-offsetElevAccurate)/resolutionElev);
+        nandata = NaN(size(conedata));
+        % Execute shift (min/max sanitise indices in case shift is negative):
+        nandata(max(1,1-discreteShiftElev):min(end,end-discreteShiftElev), ...
+                max(1,1-discreteShiftAzim):min(end,end-discreteShiftAzim)) = ...
+          conedata(max(1,1+discreteShiftElev):min(end,end+discreteShiftElev), ...
+                   max(1,1+discreteShiftAzim):min(end,end+discreteShiftAzim));
+        % Overwrite original data:
+        conedata = nandata;
+      case 'retina'
+        % Cannot apply correction, as VF filter used by Takeshi is unknown.
+    end
+
+
+  %   cd(['C:\Users\dehmelt\Dropbox\Documents\MATLAB\', ...
+  %       '201808 SphereAnalysis'])
+  % 
+  %   numelevation = size(conedata,1);
+  %   numazimuth = size(conedata,2);
+  % 
+  %   % The following four values represent the coordinates of the four corners
+  %   % of the 2D data sets provided by Baden lab (and are currently unknown).
+  %   elevation.max = 90;
+  %   elevation.min = -90;
+  %   azimuth.max = 180;
+  %   azimuth.min = -180;
+
+  %   % Build an (NX*NY x 2) array, where NX is the number of azimuth values,
+  %   % NY is the number of elevation values, NX*NY is the total number of data
+  %   % points, and for each data point (each row), the azimuth (column 1) and
+  %   % elevation (column 2) are listed.
+  %   elevation.step = (elevation.max-elevation.min)/(numelevation-1);
+  %   azimuth.step = (azimuth.max-azimuth.min)/(numazimuth-1);
+  %   elevationlist = elevation.max:-elevation.step:elevation.min;
+  %   azimuthlist = azimuth.max:-azimuth.step:azimuth.min;
+  %   predictor = [];
+  %   for k = 1:numazimuth
+  %     predictor = [predictor; ...
+  %                  repmat(azimuthlist(k),[numelevation 1]), elevationlist'];
+  %   end
+
+  %   % One raw data value per data point, might have to be reformatted to match
+  %   % the order in which the list of data azimuths/elevations in "predictor" is
+  %   % being created. Predictor goes top-to-bottom, left-to-right.
+  %   response = conedata(:);
+  % 
+  %   offset = 0;
+  %   amplitude = 1;
+  %   meanazimuth = 90;
+  %   meanelevation = 0;
+  %   concentration = 1;
+  %   
+  %   globalparam.offset = offset;
+  %   globalparam.amplitude = amplitude;
+  %   globalparam.concentration = concentration;
+  % 
+  % 
+  %   figure(1)
+  % %   imagesc(conedata)
+  %   surf(conedata)
+  %   shading flat
+  %   axis square
+  %   colormap(cbrewer('div','PRGn',100))
+  %   hold on
+  %   plot([1,numazimuth],[numelevation/2 numelevation/2],'--w','LineWidth',2)
+  %   plot([numazimuth/2, numazimuth/2],[1,numelevation],'--w','LineWidth',2)
+  %   hold off
+  %   set(gca,'XTick',[1,numazimuth*[1/4,1/2,3/4,1]],'XTickLabel',{180 90 0 -90 -180})
+  %   set(gca,'YTick',[1,numelevation*[1/4,1/2,3/4,1]],'YTickLabel',{90 45 0 -45 -90})
+  %   xlabel('azimuth'),ylabel('elevation'),set(gcf,'Color',[1 1 1])
+  %   dataclim = get(gca,'CLim');
+
+
+    hd.cone.fg = figure(1);
+    hd.cone.fg.Position = [400 160 1000 700];
+%     hd.cone.fg.Position = [500 50 540 380];
+    hd.cone.fg.Color = [1 1 1];
+
+    hd.cone.ax(typeindex,1) = axes;
+    if typeindex < 4
+      hd.cone.ax(typeindex,1).Position = [.11+(typeindex-1)*.29 .61 hd.cone.fg.Position([4 3])/3500];
+    else
+      hd.cone.ax(typeindex,1).Position = [.19+(typeindex-4)*.42 .11 hd.cone.fg.Position([4 3])/3500];
+    end
+    
+    az = 0:180/(size(conedata,1)-1):180;
+    el = -90:180/(size(conedata,2)-1):90;
+  %   conedatanonnan = fliplr(flipud(conedata));
+    conedatanonnan = conedata;
+    conedatanonnan(isnan(conedatanonnan)) = 0;
+    density = conv2(conedatanonnan,fspecial('gaussian',[100 100],3),'same');
+  %   surf(az,el,conv2(conedatanonnan,fspecial('gaussian',[100 100],10),'same'))
+  %   shading flat
+    hold on
+    if strcmp(datachoice,'retina') && typeindex == 5 % FD20190829 added as simple correction
+      density = fliplr(density);
+    end
+    imagesc(az,el,density)
+%     if strcmp(conetype,'all')
+%       contour(az,el,density,'LevelStep',1e-1,'Color',.5*[1 1 1])
+%     else
+%       contour(az,el,density,'LevelStep',3e-2,'Color',.5*[1 1 1])
+%     end
+    contourlevels = (.1:.1:1) * max(max(density));  % increments = 10%-of-max.
+    contour(az,el,density,contourlevels,'Color',.5*[1 1 1]); 
+    hold off
+%     axis square
+%     uistack(hd.cone.ax(typeindex,2),'bottom')
+%     hd.cone.ax(2,2) = copyobj(hd.cone.ax(2,1),hd.cone.fg)
+
+    hd.cone.ax(typeindex,1).XLim = [0,180];
+    hd.cone.ax(typeindex,1).YLim = [-90,90];
+    oldclim = hd.cone.ax(typeindex,1).CLim;
+%     climcutoff = .8;
+    climcutoff = .60;
+%     climcutoff = 0;
+    hd.cone.ax(typeindex,1).CLim = [min(oldclim)+climcutoff*(max(oldclim)-min(oldclim)), ...
+                            max(oldclim)];
+    switch datachoice
+      case 'visual field'
+        hd.cone.ax(typeindex,1).XLabel.String = 'azimuth';
+        hd.cone.ax(typeindex,1).YLabel.String = 'elevation';
+      case 'retina'
+        hd.cone.ax(typeindex,1).XLabel.String = 'x  (a.u.)';
+        hd.cone.ax(typeindex,1).YLabel.String = 'y  (a.u.)';
+    end
+    textfilesplit = strsplit(textfile,'_');
+    switch textfilesplit{2}
+      case 'allCones'
+        densitylabel = 'all cones combined';
+      case 'Green'
+        densitylabel = 'green cones (mm^{-2})';
+      case 'Red'
+        densitylabel = 'red cones (mm^{-2})';
+      case 'Blue'
+        densitylabel = 'blue cones (mm^{-2})';
+      case 'UV'
+        densitylabel = 'ultraviolet cones';
+    end
+%     switch textfilesplit{2}
+%       case 'allCones'
+%         densitylabel = {'retinal density','(any cones)'};
+%       case 'Green'
+%         densitylabel = {'retinal density / mm^{-2}','(green cones)'};
+%       case 'Red'
+%         densitylabel = {'retinal density','(red cones)'};
+%       case 'Blue'
+%         densitylabel = {'retinal density','(blue cones)'};
+%       case 'UV'
+%         densitylabel = {'retinal density','(ultraviolet cones)'};
+%     end
+%     switch textfilesplit{2}
+%       case 'allCones'
+%         densitylabel = 'density (any cones)';
+%       case 'Green'
+%         densitylabel = 'density (green cones)';
+%       case 'Red'
+%         densitylabel = 'density (red cones)';
+%       case 'Blue'
+%         densitylabel = 'density (blue cones)';
+%       case 'UV'
+%         densitylabel = 'density (ultraviolet cones)';
+%     end
+
+    hd.cone.ax(typeindex,1).Title.String = densitylabel;
+    hd.cone.ax(typeindex,1).Box = 'on';
+%     hd.cone.ax(typeindex,1).XTick = 0:15:180;
+%     hd.cone.ax(typeindex,1).YTick = -90:15:90;
+%     hd.cone.ax(typeindex,1).XTickLabel = {0,[],[],45,[],[],90,[],[],135,[],[],180};
+%     hd.cone.ax(typeindex,1).YTickLabel = {-90,[],[],-45,[],[],0,[],[],45,[],[],90};
+%     hd.cone.ax(typeindex,1).XTick = 0:45:180;
+%     hd.cone.ax(typeindex,1).YTick = -90:45:90;
+%     hd.cone.ax(typeindex,1).XTickLabel = {0,45,90,135,180};
+%     hd.cone.ax(typeindex,1).YTickLabel = {-90,-45,0,45,90};
+    hd.cone.ax(typeindex,1).XTick = 0:30:180;
+    hd.cone.ax(typeindex,1).YTick = -90:30:90;
+    switch datachoice
+      case 'visual field'
+        hd.cone.ax(typeindex,1).XTickLabel = {0,[],[],90,[],[],180};
+        hd.cone.ax(typeindex,1).YTickLabel = {-90,[],[],0,[],[],90};
+      case 'retina'
+        hd.cone.ax(typeindex,1).XTickLabel = {0,[],[],[],[],[],1};
+        hd.cone.ax(typeindex,1).YTickLabel = {0,[],[],[],[],[],1};
+    end
+    hd.cone.ax(typeindex,1).FontSize = 14;
+    hd.cone.ax(typeindex,1).TitleFontSizeMultiplier = 1;
+    hd.cone.ax(typeindex,1).TitleFontWeight = 'bold';
+%     hd.cone.ax(typeindex,1).Title.Interpreter = 'none';
+    hd.cone.ax(typeindex,1).Title.Interpreter = 'tex';
+  %   [hd.cone.ax(typeindex,1).XLabel.FontSize, ...
+  %    hd.cone.ax(typeindex,1).YLabel.FontSize, ...
+  %    hd.cone.ax(typeindex,1).Title.FontSize, ...
+  %    ha.cone.ax(typeindex,1).FontSize,
+    hd.cone.ax(typeindex,1).XGrid = 'on';
+    hd.cone.ax(typeindex,1).YGrid = 'on';
+    hd.cone.ax(typeindex,1).Layer = 'top';
+    
+    
+    hold on
+    plot([0,180],[0,0],'--k')
+    plot([90,90],[-90,90],'--k')
+    hold off
+
+   
+    
+% %     leftnormmaxazimVF = abs(leftnormmaxazim - leftmedianazim);
+% %     leftnormmeanazimVF = abs(leftnormmeanazim - leftmedianazim);
+% %     leftnormratioazimVF = abs(leftnormratioazim - leftmedianazim);
+% %     rightnormmaxazimVF = abs(rightnormmaxazim - rightmedianazim);
+% %     rightnormmeanazimVF = abs(rightnormmeanazim - rightmedianazim);
+% %     rightnormratioazimVF = abs(rightnormratioazim - rightmedianazim);
+
+
+
+    hold on
+    hd.cone.ob(1,1) = plot([153,165],[-65,-65],'k');
+    hd.cone.ob(1,2) = plot([159,159],[-59,-71],'k');
+    switch datachoice
+      case 'visual field'
+        hd.cone.ob(1,3) = text(153,-64.5,'A','HorizontalAlignment','right','VerticalAlignment','middle');
+        hd.cone.ob(1,4) = text(166,-64.5,'P','HorizontalAlignment','left','VerticalAlignment','middle');
+        hd.cone.ob(1,5) = text(159.2,-59.5,'U','HorizontalAlignment','center','VerticalAlignment','bottom');
+        hd.cone.ob(1,6) = text(159.2,-69.5,'D','HorizontalAlignment','center','VerticalAlignment','top');
+      case 'retina'
+        hd.cone.ob(1,3) = text(153,-64.5,'P','HorizontalAlignment','right','VerticalAlignment','middle');
+        hd.cone.ob(1,4) = text(166,-64.5,'A','HorizontalAlignment','left','VerticalAlignment','middle');
+        hd.cone.ob(1,5) = text(159.2,-59.5,'D','HorizontalAlignment','center','VerticalAlignment','bottom');
+        hd.cone.ob(1,6) = text(159.2,-69.5,'U','HorizontalAlignment','center','VerticalAlignment','top');
+    end
+    hold off
+    
+%     circle.radius = 40/2;
+%     circle.xcentre = leftnormmaxazimVF; % pick preferred measure here
+%     circle.ycentre = leftnormmaxelev; % pick preferred measure here
+%     circle.x = -circle.radius:.1:circle.radius;
+%     circle.y = sqrt(circle.x(1)^2-circle.x.^2);
+%     hold on
+%     plot([circle.xcentre + circle.x, fliplr(circle.xcentre + circle.x)], ...
+%          [circle.ycentre + circle.y, circle.ycentre - circle.y],'-','LineWidth',3,'Color',0*[1 1 1])    
+%     plot([circle.xcentre + circle.x, fliplr(circle.xcentre + circle.x)], ...
+%          [circle.ycentre + circle.y, circle.ycentre - circle.y],'-','LineWidth',2,'Color',1*[1 1 1])    
+%     hold off
+    
+%     boundary.angle = 64;
+% %     centre.azimuth = leftnormmaxazimVF;
+% %     centre.elevation = leftnormmaxelev;
+%     centre.azimuth = rightazim;
+%     centre.elevation = rightelev;
+%     boundary.resolution = .01;
+%     [boundary.azimuth,boundary.elevation] = ...
+%       diskBoundary(centre.azimuth,centre.elevation,boundary.angle,boundary.resolution);
+%     hold on
+%     plot(boundary.azimuth,boundary.elevation,'-','LineWidth',3,'Color',0*[1 1 1])    
+%     plot(boundary.azimuth,boundary.elevation,'-','LineWidth',2,'Color',1*[1 1 1])    
+%     hold off
+
+    boundary.angle = 40;
+%     centre.azimuth = leftnormmaxazimVF;
+%     centre.elevation = leftnormmaxelev;
+%     centre.azimuth = leftazim_body;
+%     centre.elevation = leftelev_body;
+    centre.azimuth = abs(leftazim_body);
+    centre.elevation = leftelev_body;
+    switch datachoice
+      case 'retina'
+        centre.azimuth = 180-centre.azimuth;
+        centre.elevation = -centre.elevation;
+    end
+    switch datachoice
+      case 'visual field'
+        boundary.resolution = .01;
+        [boundary.azimuth,boundary.elevation] = ...
+          diskBoundary(centre.azimuth,centre.elevation,boundary.angle,boundary.resolution);
+        hold on
+        plot(boundary.azimuth,boundary.elevation,'-','LineWidth',3,'Color',0*[1 1 1])    
+        plot(boundary.azimuth,boundary.elevation,'-','LineWidth',2,'Color',1*[1 1 1])    
+        hold off
+    end
+
+
+    mtp = 'o';
+    msz = 100;
+    mec = [0 0 0];
+%     mfc = [.2 .8 .2];
+%     mfa = .2;
+    mfc = 1*[1 1 1];
+    mfa = .5;
+    hold on
+%     hd.cone.ob(1,7) = scatter(rightazim,rightelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+    hd.cone.ob(1,8) = scatter(centre.azimuth,centre.elevation,msz,mtp, ...
+                               'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',.8);
+%     hd.cone.ob(1,9) = scatter(leftnormratioazimVF,leftnormratioelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+%     hd.cone.ob(1,10) = scatter(rightnormmaxazimVF,rightnormmaxelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+%     hd.cone.ob(1,11) = scatter(rightnormmeanazimVF,rightnormmeanelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+%     hd.cone.ob(1,12) = scatter(rightnormratioazimVF,rightnormratioelev,msz,mtp, ...
+%                                 'MarkerEdgeColor',mec,'MarkerFaceColor',mfc,'MarkerFaceAlpha',mfa);
+    hold off
+    
+    
+    
+    if typeindex == 2 || typeindex == 3
+      hd.cone.ax(typeindex,1).YLabel.String = [];
+      hd.cone.ax(typeindex,1).YTickLabel = [];
+    end
+    switch conetype
+      case 'blue'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Blues',100))
+      case 'green'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Greens',100))
+      case 'red'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Reds',100))
+      case 'uv'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Purples',100))
+      case 'all'
+        colormap(hd.cone.ax(typeindex,1),cbrewer('seq','Greys',100))
+    end
+    
+    hd.cone.ax(typeindex,2) = axes;
+    hd.cone.ax(typeindex,2).Position = hd.cone.ax(typeindex,1).Position + ...
+                                       hd.cone.ax(typeindex,1).Position(3) * .28*[1 0 0 0];
+    hd.cone.cbar(typeindex) = colorbar;
+
+    hd.cone.ax(typeindex,2).Colormap = hd.cone.ax(typeindex,1).Colormap;
+    hd.cone.ax(typeindex,2).CLim     = hd.cone.ax(typeindex,1).CLim;
+    hd.cone.ax(typeindex,2).FontSize = hd.cone.ax(typeindex,1).FontSize;
+    hd.cone.ax(typeindex,2).Color = 'none';
+    [hd.cone.ax(typeindex,2).XAxis.Visible,hd.cone.ax(typeindex,2).YAxis.Visible] = deal('off');
+        
+        
+%     colormap(cbrewer('div','PRGn',100))
+
+
+
+  end
+  
+  hd.cone.ax(1,3) = axes;
+  hd.cone.ax(1,3).Position = [8 0 1 1];
+  hd.cone.ax(1,3).Color = 'none';
+  [hd.cone.ax(1,3).XAxis.Visible,hd.cone.ax(1,3).YAxis.Visible] = deal('off');
+  for typeindex = 1:numel(datapresent)
+    parentpos = hd.cone.ax(typeindex,1).Position;
+    xpos = parentpos(1)+parentpos(3)/2;
+    ypos = parentpos(2)+parentpos(4)*1.128;
+    hd.cone.ob(3,typeindex) = text(xpos, ypos, 'retinal density / mm^{-2}');
+%     hd.cone.ob(3,typeindex) = text(xpos, ypos, 'retinal density (µm^{-2})');
+    hd.cone.ob(3,typeindex).FontWeight = 'normal';
+    hd.cone.ob(3,typeindex).Interpreter = 'tex';
+    hd.cone.ob(3,typeindex).FontSize = hd.cone.ax(typeindex,1).FontSize;
+    hd.cone.ob(3,typeindex).HorizontalAlignment = 'center';
+    hd.cone.ob(3,typeindex).VerticalAlignment = 'bottom';
+  end
+  uistack(hd.cone.ax(1,3),'bottom')
+
+  hd.cone.ax(1,4) = axes;
+  hd.cone.ax(1,4).Position = [0 0 1 1];
+  hd.cone.ax(1,4).Color = 'none';
+  [hd.cone.ax(1,4).XAxis.Visible,hd.cone.ax(1,4).YAxis.Visible] = deal('off');
+  for typeindex = 1:numel(datapresent)
+    parentpos = hd.cone.ax(typeindex,1).Position;
+%     xpos = parentpos(1)+parentpos(3)*-.18;
+    xpos = parentpos(1)+parentpos(3)*-.23;
+    ypos = parentpos(2)+parentpos(4)*1.30;
+%     hd.cone.ob(3,typeindex) = text(xpos, ypos, 'retinal density / mm^{-2}');
+    switch typeindex
+      case 1
+        letterstring = 'a';
+      case 4
+        letterstring = 'b';
+      case 5
+        letterstring = 'c';
+      otherwise
+        letterstring = '';  
+    end
+    hd.cone.ob(4,typeindex) = text(xpos, ypos, letterstring);
+    hd.cone.ob(4,typeindex).FontWeight = 'bold';
+    hd.cone.ob(4,typeindex).Interpreter = 'tex';
+    hd.cone.ob(4,typeindex).FontSize = hd.cone.ax(typeindex,1).FontSize * 18/14;
+    hd.cone.ob(4,typeindex).HorizontalAlignment = 'right';
+    hd.cone.ob(4,typeindex).VerticalAlignment = 'top';
+  end
+  uistack(hd.cone.ax(1,3),'bottom')
+
+  [hd.cone.ax(:,1).TickLength] = deal([0 0]);
+
+  
+%   for k = 1:5, hd.cone.cbar(k,1).Position(1) = hd.cone.ax(k,1).Position(1) + .83*hd.cone.ax(k,1).Position(3); end
+  
+    runtime = clock;
+    timestring = ['_', ...
+                num2str(runtime(1),'%.4u'), ...
+                num2str(runtime(2),'%.2u'), ...
+                num2str(runtime(3),'%.2u'), ...
+                '_', ...
+                num2str(runtime(4),'%.2u'), ...
+                num2str(runtime(5),'%.2u'), ...
+                num2str(runtime(6),'%02.0f')];
+                            
+%   savefig(2,['conefigure/',textfilesplit{2},timestring])
+%   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-depsc')
+% %   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-dtiff')
+%   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-dtiffn')
+%   figure(2),print(['conefigure/',textfilesplit{2},timestring],'-dpng')
+  
+end
+
+
+
+function [azimuthOut, elevationOut] = diskBoundary(azimuthIn, elevationIn, angleIn, resolutionIn)
+
+  centre.azimuth = azimuthIn;
+  centre.elevation = elevationIn;
+  border.angle = angleIn / 2; % convert solid angle ("diameter") to "radius"
+  border.resolution = resolutionIn;
+
+  % The following equation describes orthodromic (or "great circle") distance as an angle:
+  % arccos(sin(centre.elevation)*sin(border.elevation) + ...
+  %        cos(centre.elevation)*cos(border.elevation)*cos(border.azimuth-centre.azimuth)) = angle;
+
+%   border.elevation = centre.elevation-border.angle/2 : .1 : centre.elevation+border.angle/2;
+  intended  = -90 : border.resolution : 90;
+%   intended  = (centre.elevation-border.angle) : border.resolution : (centre.elevation+border.angle);
+  % "auxiliary" elevations are explicitly added to guarantee coverage near top/bottom of circle:
+  auxiliary = centre.elevation + border.angle*[-1 1];
+%   border.elevation = unique([intended,auxiliary]);
+  
+  limit = cosd([centre.elevation-border.angle centre.elevation+border.angle]);
+%   intended = acosd(min(limit) : border.resolution : max(limit));
+
+  %   % XXX Not sure why the following line leads to half of the curve segments not being computed...
+%   intended = [-acosd(0:border.resolution:1),acosd(1-border.resolution:-border.resolution:0)];
+%   auxiliary = centre.elevation + border.angle*[-1 1];
+
+  border.elevation = unique([intended,auxiliary]);
+%   figure(6)
+%   plot(intended)
+
+  border.azimuth = centre.azimuth + ...
+                   acosd((cosd(border.angle) - sind(centre.elevation)*sind(border.elevation)) ./ ...
+                         (cosd(centre.elevation)*cosd(border.elevation)));
+
+
+  % Eliminate complex elements...
+  border.azimuth(imag(border.azimuth)~=0) = NaN; % This statement sometimes fails horribly.
+%   border.azimuth(imag(border.azimuth)>1e-5) = NaN; % This statement is silly, but more stable.
+
+  % ...and compute corresponding values for the second half of the boundary.
+  border.azimuth = [border.azimuth, 2*centre.azimuth-border.azimuth];
+  border.elevation = [border.elevation, border.elevation];
+  
+  border.azimuth = mod(border.azimuth+180,360)-180;
+%   jump = diff(border.azimuth) > 10*border.resolution;
+%   border.azimuth(jump)   = NaN;
+%   border.elevation(jump) = NaN;
+  
+  azimuthOut   = border.azimuth;
+  elevationOut = border.elevation;
+
+end
+
+
+
+% function combinedvalue = vonmisesfisher2param_centre(param,predictor)
+% 
+%   global globalparam
+%   
+%   convention = 'CCW';
+%   dimension = 3;
+%   radius = 1;
+%   if size(predictor,2) == 2
+%     predictor(:,3) = radius*ones(size(predictor(:,1)));
+%   end
+%   [x,y,z] = geo2car(predictor(:,1),predictor(:,2),predictor(:,3),convention);
+%   direction = [x,y,z];
+%   
+%   numberofdistributions = 1; % <---
+%   value = NaN(size(predictor,1),numberofdistributions);
+%   
+%   for k = 1:numberofdistributions
+%     
+%     meanazimuth   = param(2*k-1);
+%     meanelevation = param(2*k);
+%     
+%     offset        = globalparam.offset(k);
+%     amplitude     = globalparam.amplitude(k);
+%     concentration = globalparam.concentration(k);
+%     
+%     [meanx,meany,meanz] = geo2car(meanazimuth,meanelevation,radius,convention);
+%     meandirection = [meanx,meany,meanz];
+% 
+%     bessel = concentration / ...
+%              (2*pi * (exp(concentration) - exp(-concentration)));
+% 
+%     normalisation = concentration^(dimension/2 - 1) / ...
+%                     ((2*pi)^(dimension/2) * bessel);
+%                 
+%     value(:,k) = normalisation * exp(concentration*meandirection*direction')';
+%     value(:,k) = amplitude * value(:,k) + offset;
+%    
+%     badness = fitpenalty(meanazimuth,-180,180) + ...
+%               fitpenalty(meanelevation,-90,90) + ...
+%               fitpenalty(amplitude,0,Inf)      + ...
+%               fitpenalty(concentration,0,Inf);
+%             
+%     value(:,k) = value(:,k) + 1e3*badness;
+%     
+%   end
+%   
+%   combinedvalue = sum(value,2);
+%   
+% end
+
+
+
+% function penalty = fitpenalty(value,lowerlimit,upperlimit)
+%   
+%   factor = 1;
+%   
+%   if value < lowerlimit
+%     
+%     penalty = factor*(lowerlimit-value).^1;
+%     
+%   elseif value > upperlimit
+%     
+%     penalty = factor*(value-upperlimit).^1;
+%     
+%   else
+%     
+%     penalty = 0;
+%     
+%   end
+%   
+% end
+
+
+
+% function combinedvalue = oneVonMisesFisher(params,predictor)
+% %TWOVONMISESFISHER Evaluate two overlapping von Mises-Fisher distributions.
+% %
+% %   VALUE = VONMISESFISHER computes the value of only one(!) 
+% %   spherical (p=3) von Mises-Fisher distributions at the chosen location.
+% %
+% %   It is formatted to match the input/output structure required by the
+% %   built-in non-linear regression function nlinfit.
+% 
+%   convention = 'CW';
+%   dimension = 3;
+%   radius = 1;
+%   if size(predictor,2) == 2
+%     predictor(:,3) = radius*ones(size(predictor(:,1)));
+%   end
+%   [x,y,z] = geo2car(predictor(:,1),predictor(:,2),predictor(:,3),convention);
+%   direction = [x,y,z];
+%   
+%   numberofdistributions = 1; % <-- This is the only difference to "twoVonMisesFisher.m".
+%   value = NaN(size(predictor,1),numberofdistributions);
+%   
+%   for k = 1:numberofdistributions
+%     
+%     offset        = params(5*k-4);
+%     amplitude     = params(5*k-3);
+%     meanazimuth   = params(5*k-2);
+%     meanelevation = params(5*k-1);
+%     
+%         meanelevation = 25; % HACK!
+% 
+%     
+%     [meanx,meany,meanz] = geo2car(meanazimuth,meanelevation,radius,convention);
+%     meandirection = [meanx,meany,meanz];
+% %     meandirection = geo2car([params((-2:-1)+4*k),radius]);
+%     concentration = params(5*k);
+%     
+%     bessel = concentration / ...
+%              (2*pi * (exp(concentration) - exp(-concentration)));
+% 
+%     normalisation = concentration^(dimension/2 - 1) / ...
+%                     ((2*pi)^(dimension/2) * bessel);
+%                 
+%     value(:,k) = normalisation * exp(concentration*meandirection*direction')';
+%     value(:,k) = amplitude * value(:,k) + offset;
+%     
+%     % HACK to constrain values
+%     if abs(meanazimuth) > 180  || ...
+%        abs(meanelevation) > 90 || ...
+%        amplitude < 0           || ...
+%        concentration < 0
+%      
+%       value(:,k) = 1e10*ones(size(value(:,k)));
+%       
+%     end
+% 
+%   end
+%   
+%   combinedvalue = sum(value,2);
+%   
+% end
+