Analyze_Chromatic_Integration_Stimulus.m 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. function Analyze_Chromatic_Integration_Stimulus(varargin)
  2. %
  3. %
  4. %%% Analyze_Chromatic_Integration_Stimulus %%%
  5. %
  6. %
  7. % This function analyzes the response of the ganglion cells to the chromatic
  8. % integration stimulus. This is a supplement code for the Khani & Gollisch
  9. % (2021) study of chromatic integration in the mouse retina.
  10. % This function first loads the data in a format that is provided in by the
  11. % supplementary database of the study. It then generate rasters and PSTHs for
  12. % all the 22 contrast combinations that was shown to each retina. The order
  13. % of the contrasts were randomized and it is recreated using Fisher-Yates
  14. % random permutation algorithem. Check fisher_Yates_shuffle_order and
  15. % reorder_contrast_spikes functions below for more information.
  16. % The contrast values of both colors range from -20 to 20 in steps of 2% change.
  17. % They are categorized in two groups of Green-On-UV-Off and Green-Off-UV-On.
  18. %-------------------------------Green-On-UV-Off-----------------------------
  19. % Green => 20 18 16 14 12 10 8 6 4 2 0
  20. % UV => 0 -2 -4 -6 -8 -10 -12 -14 -16 -18 -20
  21. %-------------------------------Green-Off-UV-On-----------------------------
  22. % Green => -20 -18 -16 -14 -12 -10 -8 -6 -4 -2 0
  23. % UV => 0 2 4 6 8 10 12 14 16 18 20
  24. %
  25. %
  26. % ===============================Inputs====================================
  27. %
  28. % datapath : path to the folder in the database.
  29. % stimulusnumber : number to select correct stimulus.
  30. % cellnumber : an example cell number from the list of good cells.
  31. % clusternumber : a cluster number for the selected cell.
  32. %
  33. %================================Output====================================
  34. %
  35. % chromatic integration curves and PSTHs for the selected cell.
  36. %
  37. % written by Mohammad, 12.02.2021
  38. %--------------------------------------------------------------------------------------------------%
  39. %---------- main-function ----------%
  40. %--------------------------------------------------------------------------------------------------%
  41. % first load the data for an example cell
  42. [dp, stimulusnumber, cellnumber, clusternumber] = select_example_cell(varargin{:});
  43. % get the parameters for the selected example cell
  44. expdata = ci_parameters(dp, stimulusnumber, cellnumber, clusternumber);
  45. % calculate the rasters and PSTH for the stimulus and the gray frame before the stimulus
  46. ci_data = ci_analysis(expdata);
  47. % plot chromatic integration curves and all the psths
  48. plot_chromatic_integration(ci_data, cellnumber, clusternumber, expdata.stimpara);
  49. end
  50. %--------------------------------------------------------------------------------------------------%
  51. %---------- sub-functions ----------%
  52. %--------------------------------------------------------------------------------------------------%
  53. function expdata = ci_parameters(dp, stimulusnum, cellnum, clusternum,varargin)
  54. %
  55. expdata = load_data_for_selected_cell(dp, stimulusnum, cellnum, clusternum);
  56. para = expdata.stimpara;
  57. % loading clusters and frametimings
  58. ft = expdata.frametimes; % work in seconds
  59. % getting contrasts of the experiment (-20:2:20)
  60. contgr = round([para.mincontrast:para.contrastdiff:0,0:para.contrastdiff:para.maxcontrast]*100);
  61. contuv = round([0:para.contrastdiff:para.maxcontrast,para.mincontrast:para.contrastdiff:0]*100);
  62. % re-ordering stimulus for plotting and presentation purpose, here we start
  63. % with on green and reverse off green instead of off to on green as in stimulus program.
  64. para.contrastIdx = [length(contgr) : -1 : 1 + length(contgr)/2, 1 : length(contuv)/2];
  65. para.contrastgreen = contgr(para.contrastIdx);
  66. para.contrastuv = contuv(para.contrastIdx);
  67. % generating contrast values
  68. para.numtrials = floor( length( ft(2 : 2 : end)) / length(contgr) );
  69. seed = para.seed;
  70. stimorder = nan(para.numtrials,length(contgr));
  71. % getting the order and location of each contrast from the psudo random sequence
  72. for ii = 1 : para.numtrials
  73. [stimorder(ii,:),seed,~] = fisher_Yates_shuffle_order(seed,contgr);
  74. end
  75. % experiment parameters
  76. refreshrate = 60;
  77. para.binlength = 10 / 1e3; % 10 ms each bin
  78. para.delay = 0; % 0 ms delay
  79. para.activeregion = [0.025, 0.25]; % region used to measure response (50 to 250 ms)
  80. para.refreshrate = refreshrate;
  81. % make a bin vector for stimulus, used for making PSTH
  82. para.stimduration = (para.stimduration / refreshrate); % in sec
  83. stimBin = linspace(0, para.stimduration, (para.stimduration / para.binlength)+1); % +1 here to avoid issues with histc last bin.
  84. % make a bin vector for preframe, used for making PSTH
  85. para.pfrduration = (para.preframes / refreshrate); % in sec
  86. pfrBin = linspace(0, para.pfrduration,(para.pfrduration / para.binlength) + 1);
  87. % seting output
  88. expdata.stimorder = stimorder;
  89. expdata.stimulusbinvector = stimBin;
  90. expdata.preframebinvector = pfrBin;
  91. expdata.stimpara = para;
  92. end
  93. %--------------------------------------------------------------------------------------------------%
  94. function ci_data = ci_analysis(expdata)
  95. %
  96. ft = expdata.frametimes;
  97. spks = expdata.spiketimes;
  98. para = expdata.stimpara;
  99. % for whole experiment generally has 2000 ms preframe and 500 ms stimulus
  100. [~, allpreframespikes] = spikes_per_frametimes(ft, spks, para.stimduration, para.pfrduration,para);
  101. % this is to get rasters and psth for the graybackground before the stimulus
  102. [ci_data.preframe.allpfrRasters, ci_data.preframe.allpfrPSTH] = reorder_contrast_spikes(allpreframespikes,...
  103. expdata.stimorder,expdata.preframebinvector,para);
  104. %%% for 500 ms before and after stimulus
  105. [stimulusspikes, preframespikes] = spikes_per_frametimes(ft,spks,para.stimduration,para.stimduration,para);
  106. % rasters and pstrh for the 500 ms stimulus
  107. [ci_data.stimulusRasters, ci_data.stimulusPSTH] = reorder_contrast_spikes(stimulusspikes,...
  108. expdata.stimorder,expdata.stimulusbinvector,para);
  109. % rasters and pstrh for the 500 ms gray background before the onset of the stimulus
  110. [ci_data.preframe.preframeRasters, ci_data.preframe.preframePSTH] = reorder_contrast_spikes(preframespikes,...
  111. expdata.stimorder,expdata.stimulusbinvector,para);
  112. ci_data.preframePSTH = ci_data.preframe.preframePSTH;
  113. end
  114. %--------------------------------------------------------------------------------------------------%
  115. function varargout = fisher_Yates_shuffle_order(seed,inputVec,varargin)
  116. %
  117. %%% fisher_Yates_shuffle_order %%%
  118. %
  119. %
  120. % This function generate psudorandom permution similar to randperm in MATLAB
  121. % but works with psudorandom number generator ran1. It also gives back the
  122. % most recent seed value to continue the permuation in case of repeated trials.
  123. % note that the direction of permutation is along x-axix or for columns of
  124. % MATALB not for the rows.
  125. %
  126. %
  127. % ===============================Inputs====================================
  128. %
  129. % seed : seed value for random number generation.
  130. % inputVec : input vector used for permutation.
  131. %
  132. %================================Output====================================
  133. %
  134. % testOrder : vector of permuted indices for the inputVec.
  135. % newSeed : the recent seed that used in the ran1 function.
  136. % outputVec : the permuted input vector along x-axis
  137. %
  138. % Note that the permution algorithem is based on Fisher-Yates shuffle
  139. % algorithem identical to what is used in the stimulus program.
  140. % for more info check :
  141. % https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
  142. %
  143. % written by Mohammad, 01.02.2016
  144. newSeed = seed;
  145. testOrder = zeros(1,size(inputVec,2));
  146. testOrder(1) = 1;
  147. for i = 2:length(inputVec)-1
  148. [randVal,newSeed] = ran1(newSeed);
  149. j = ceil(i*randVal); % based on Fischer-Yates algorithem
  150. testOrder(i) = testOrder(j);
  151. testOrder(j) = i;
  152. end
  153. testOrder = [testOrder(end),testOrder(1:end-1)]+1; % to match MATLAB indexing
  154. varargout{1} = testOrder;
  155. varargout{2} = newSeed;
  156. for j = 1:size(inputVec,1)
  157. varargout{3}(j,:) = inputVec(j,testOrder);
  158. end
  159. end
  160. %--------------------------------------------------------------------------------------------------%
  161. function [stimSpk, pfrSpk] = spikes_per_frametimes(ft,spk,stimdur,pfrdur,para,varargin)
  162. %
  163. delay = para.delay / 1e3; % change delay time to second to match ft and spk
  164. [stimSpk,pfrSpk] = deal( cell( ceil( numel( ft )/ 2), 1));
  165. iter = 1;
  166. for ii = 2:2:numel(ft)
  167. stimSpk{iter} = spk(and(spk > ft(ii)+delay,spk <= ft(ii)+(stimdur+delay))) - (ft(ii)+delay);
  168. % before stimulus onset to get preframe
  169. pfrSpk{iter} = spk(and(spk > ft(ii)-(pfrdur+delay),spk <= ft(ii))) - (ft(ii)-(pfrdur+delay));
  170. if isempty(stimSpk{iter}), stimSpk{iter} = NaN; end
  171. if isempty(pfrSpk{iter}), pfrSpk{iter} = NaN; end
  172. iter = iter+1;
  173. end
  174. end
  175. %--------------------------------------------------------------------------------------------------%
  176. function varargout = reorder_contrast_spikes(spk,order,binVec,para,varargin)
  177. %
  178. neworder = reshape(order',1,[]); % reshape stimulus order
  179. spk = spk(1:length(neworder))'; % cutting the last unfinished stimulus series
  180. % pre-allocating memory
  181. spkorder = cell(size(order));
  182. contspk = cell(1,max(neworder));
  183. psth = nan(length(binVec),max(neworder));
  184. for ii = 1 : max(neworder)
  185. thisorder = neworder == ii; % indexing the orders from 1 to end
  186. spkorder(:,ii) = spk( thisorder ); % sorting spikes based on previous order
  187. contspk{ii} = CelltoMatUE( spkorder(:,ii));
  188. if size(contspk{ii},2) < 2
  189. psth(:,ii) = (histc(contspk{ii}',binVec)/size(contspk{ii},1))*(1/para.binlength); %#ok to fix situation with 1 spike
  190. else
  191. psth(:,ii) = mean(histc(contspk{ii}',binVec),2)*(1/para.binlength); %#ok must transpose contspk for histc to work properly.
  192. end
  193. end
  194. psth = psth(1:length(binVec)-1,:); % delete last zero bin.
  195. % Here we reorder the psth and spikes in a way that the opposing stimuli
  196. % face each other. the indices 22:-1:11 are set to 1:11, these are green on
  197. % to uv off. The indices 1:1:11 are set to 12 to 22, these are green off uv
  198. % on. for the rest of the function no index correction is made.
  199. % the idices for -20:2:20 are as follows
  200. % for first 11 or Green-ON, UV-OFF
  201. % Green => 20 18 16 14 12 10 8 6 4 2 0
  202. % UV => 0 -2 -4 -6 -8 -10 -12 -14 -16 -18 -20
  203. % for second 11 or UV-ON, Green-OFF
  204. % Green => -20 -18 -16 -14 -12 -10 -8 -6 -4 -2 0
  205. % UV => 0 2 4 6 8 10 12 14 16 18 20
  206. contidx = [length(para.contrastgreen) : -1:1 + length(para.contrastgreen)/2, 1: length(para.contrastuv)/2];
  207. varargout{1} = contspk(contidx);
  208. varargout{2} = psth(:,contidx);
  209. varargout{3} = spkorder(:,contidx);
  210. end
  211. %--------------------------------------------------------------------------------------------------%
  212. function [ci, ciang] = chromatic_integration_curves(stimpsth, pfrpsth, para, varargin)
  213. %
  214. contnum = size(stimpsth, 2) / 2;
  215. timevec = linspace(0, para.stimduration, para.stimduration / para.binlength);
  216. activtime = timevec >= para.activeregion(1) & timevec <= para.activeregion(2);
  217. % only between 50-250 ms is counted!
  218. spkdiff = stimpsth(activtime,:) - pfrpsth(1+end-sum(activtime):end,:);
  219. respdiff = mean(spkdiff,1); % difference in response
  220. respsem = nan_sem(spkdiff); % standard error of mean
  221. grONuvOFF = respdiff(1 : contnum);
  222. grOFFuvON = respdiff(1 + contnum : contnum*2);
  223. grONuvOFFsem = respsem(1 : contnum);
  224. grOFFuvONsem = respsem(1 + contnum : contnum*2);
  225. gNuFspk = spkdiff(:, 1:contnum);
  226. gFuNspk = spkdiff(:, 1+contnum : contnum*2);
  227. ci.grONuvOFF = grONuvOFF;
  228. ci.grONuvOFFsem = grONuvOFFsem;
  229. ci.grONuvOFFspkdiff = gNuFspk;
  230. ci.grOFFuvON = grOFFuvON;
  231. ci.grOFFuvONsem = grOFFuvONsem;
  232. ci.grOFFuvONspkdiff = gFuNspk;
  233. if nargout > 1
  234. gNuFangles = [para.contrastgreen(1:contnum);para.contrastuv(1:contnum)];
  235. gFuNangles = [para.contrastgreen(1 + contnum:contnum*2); para.contrastuv(1 + contnum:contnum*2)];
  236. ciang.gNuFangles = gNuFangles;
  237. ciang.gFuNangles = gFuNangles;
  238. end
  239. end
  240. %--------------------------------------------------------------------------------------------------%
  241. function [dp, stimulusnumber, cellnumber, clusternumber] = select_example_cell(varargin)
  242. % first parse the user inputs
  243. p = inputParser(); % check the user options.
  244. p.addParameter('datapath', [], @(x) ischar(x) || isstring(x));
  245. p.addParameter('stimulusnumber', [], @isnumeric);
  246. p.addParameter('cellnumber', [], @isnumeric);
  247. p.addParameter('clusternumber', [], @isnumeric);
  248. p.addParameter('help', false, @(x) islogical(x) || (isnumeric(x) && ismember(x,[0,1])));
  249. p.parse(varargin{:});
  250. % defualt parameters
  251. defpara = p.Results;
  252. if isempty(defpara.datapath)
  253. dp = uigetdir('Select an experiment folder:');
  254. else
  255. dp = defpara.datapath;
  256. end
  257. if isempty(defpara.cellnumber) || isempty(defpara.clusternumber)
  258. listofgoodcells = load([dp,filesep,'list_of_good_cells.txt'],'-ascii');
  259. cellabels = sprintfc('cell %g, cluster %g',listofgoodcells);
  260. if numel(cellabels) > 1
  261. idx = listdlg('PromptString',{'Select an example ganglion cell:'},...
  262. 'SelectionMode','single','ListString',cellabels,'listsize',[250 250]);
  263. else
  264. idx = 1;
  265. end
  266. cellnumber = listofgoodcells(idx,1);
  267. clusternumber = listofgoodcells(idx,2);
  268. else
  269. cellnumber = defpara.cellnumber;
  270. clusternumber = defpara.clusternumber;
  271. end
  272. if isempty(defpara.stimulusnumber)
  273. stimnames = importdata([dp,filesep,'stimuli_names.txt']);
  274. cinames = stimnames(~(cellfun('isempty',(strfind(stimnames,'_Chromatic_Integration_')))));
  275. cinames = cinames((cellfun('isempty',(strfind(cinames,'Local')))));
  276. cinames = cinames((cellfun('isempty',(strfind(cinames,'Grating')))));
  277. if numel(cinames) > 1
  278. idx = listdlg('PromptString',{'Select an example dataset:'},...
  279. 'SelectionMode','single','ListString',cinames,'listsize',[450 100]);
  280. cinames = cinames{idx};
  281. end
  282. stimulusnumber = str2double(extractBefore(cinames,'_Chromatic'));
  283. else
  284. stimulusnumber = defpara.stimulusnumber;
  285. end
  286. end
  287. %--------------------------------------------------------------------------------------------------%
  288. function expdata = load_data_for_selected_cell(dp, stimnum, cellnum, clusternum)
  289. %
  290. ftfolder = [dp,filesep,'frametimes'];
  291. if ~exist(ftfolder,'dir'), ftfolder = dp; end
  292. rasfolder = [dp,filesep,'spiketimes'];
  293. if ~exist(rasfolder,'dir'), rasfolder = dp; end
  294. paramfolder = [dp,filesep,'stimulusparameters'];
  295. if ~exist(paramfolder,'dir'), paramfolder = dp; end
  296. ftnames = dir([ftfolder, filesep, num2str(stimnum,'%02g'),'*_frametimings.txt']);
  297. rastername = [rasfolder, filesep, sprintf('%d_SP_C%d%02d.txt', stimnum, cellnum, clusternum)];
  298. warning('off','all'); % this is supress the warning about long names, hopefully nothing happen in between these lines!
  299. expdata.spiketimes = load(rastername, '-ascii');
  300. expdata.frametimes = load([ftfolder,filesep,ftnames.name], '-ascii');
  301. stimpath = dir([paramfolder,filesep,num2str(stimnum,'%02g'),'*_parameters.txt']);
  302. % now reading the txt file with parameters
  303. fid = fopen([paramfolder,filesep,stimpath.name]);
  304. tline = fgetl(fid);
  305. while ischar(tline)
  306. tline = fgetl(fid);
  307. if tline == -1, break; end
  308. fn = extractBefore(tline,' = ');
  309. if isempty(fn), continue; end
  310. val = extractAfter(tline,' = ');
  311. % to convert to double
  312. if ~isnan(str2double(val))
  313. val = str2double(val);
  314. end
  315. % to convert to logical
  316. if strcmp(val,'true'), val = true; end
  317. if strcmp(val,'false'), val = false; end
  318. % store the values in a structure
  319. stimpara.(fn) = val;
  320. end
  321. fclose(fid);
  322. expdata.stimpara = stimpara;
  323. expdata.name = extractBefore(ftnames.name,'_frametimings.txt');
  324. expdata.paraname = extractBefore(stimpath.name,'_parameters.txt');
  325. if strcmp(expdata.name , expdata.paraname)
  326. expdata = rmfield(expdata,'paraname');
  327. end
  328. warning('on','all');
  329. end
  330. %--------------------------------------------------------------------------------------------------%
  331. function y = nan_sem(x,dim)
  332. %
  333. if isempty(x), y = NaN; return; end
  334. if nargin < 2, dim = find(size(x)~=1, 1 ); if isempty(dim), dim = 1; end; end
  335. % Find NaNs in x and nanmean(x)
  336. nans = isnan(x);
  337. count = size(x,dim) - sum(nans,dim);
  338. % Protect against a all NaNs in one dimension
  339. i = find(count==0);
  340. count(i) = 1;
  341. y = nanstd(x,[],dim)./sqrt(count);
  342. y(i) = i + NaN;
  343. end
  344. %--------------------------------------------------------------------------------------------------%
  345. function OutputMat = CelltoMatUE (input_cell)
  346. %
  347. inptsize = size(input_cell);
  348. maxLength = max(cellfun('length',input_cell));
  349. if inptsize(2) > inptsize(1)
  350. OutputMat = cell2mat(cellfun(@(x)cat(1,x,nan(maxLength-length(x),1)),input_cell,'un',0));
  351. else
  352. OutputMat = cell2mat(cellfun(@(x)cat(2,x',nan(1,maxLength-length(x))),input_cell,'un',0));
  353. end
  354. end
  355. %--------------------------------------------------------------------------------------------------%
  356. %---------- plotting modules ----------%
  357. %--------------------------------------------------------------------------------------------------%
  358. function plot_chromatic_integration(ci_data, cellnum, clusternum, para)
  359. %
  360. figure('pos',[400 150 1100 750],'color','w');
  361. % first plot chromatic integration curve
  362. subplot(1,2,1)
  363. plot_chromatic_integration_curves(ci_data, para);
  364. % then plot the psths
  365. sp = subplot(1,2,2);
  366. plot_chromatic_integration_psths(ci_data, para);
  367. % massive title for both subplots
  368. annotation('textbox', [0.1, 0.95, 0.8, 0], 'string', ['Chromatic integration responses for cell ',...
  369. num2str(cellnum),', cluster ',num2str(clusternum)],'FontSize',16,'EdgeColor','none',...
  370. 'HorizontalAlignment','center','VerticalAlignment','middle');
  371. % stretch the second plot a bit.
  372. sp.Position = [sp.Position(1)-0.05, sp.Position(2)-0.07 sp.Position(3:4)+0.05];
  373. %annotation('textbox', [0.5, 0.06, 0, 0.8], 'string',sprintf('%2g\t\n\n',1:11),'VerticalAlignment','cap');
  374. end
  375. %--------------------------------------------------------------------------------------------------%
  376. function [ci, datplt]= plot_chromatic_integration_curves(ci_data, para)
  377. %
  378. stimpsth = ci_data.stimulusPSTH;
  379. pfrpsth = ci_data.preframePSTH;
  380. ptch = @(x,y,ysem,col)(patch([x,fliplr(x)],[y-ysem,fliplr(y+ysem)],1,...
  381. 'facecolor',col,'edgecolor','none','facealpha',0.3));
  382. pltline = @(x,y,col)plot(x, y,'-o','color',col,'markerfacecolor',col,...
  383. 'markeredgecolor',col,'markersize',7,'linewidth',2);
  384. gNuFcol = [1 0.0781 0.5742]; % green on, uv off color
  385. gFuNcol = [0 0.7461 1]; % green off, uv on color
  386. noaxis = false;
  387. contnum = size(stimpsth,2)/2;
  388. ci = chromatic_integration_curves(stimpsth, pfrpsth, para);
  389. % plotting...plotting...plotting
  390. hold on;
  391. ptch(1 : contnum, ci.grONuvOFF, ci.grONuvOFFsem, gNuFcol);
  392. ptch(1 : contnum, ci.grOFFuvON, ci.grOFFuvONsem, gFuNcol);
  393. respdiff = [ci.grONuvOFF+ci.grONuvOFFsem, ci.grONuvOFF-ci.grONuvOFFsem, ...
  394. ci.grOFFuvON+ci.grOFFuvONsem, ci.grOFFuvON-ci.grOFFuvONsem];
  395. datplt.plot1 = pltline(1:contnum, ci.grONuvOFF,gNuFcol);
  396. datplt.plot2 = pltline(1:contnum, ci.grOFFuvON,gFuNcol);
  397. datplt.zeroline = plot(linspace(0.5,contnum + 0.5,contnum),zeros(1,contnum),'--k','linewidth',0.5);
  398. yAx = round(linspace(floor(min(respdiff)/2)*2,ceil(max(respdiff)/2)*2, 4));
  399. if all(yAx == 0), yAx = [0,10]; end
  400. if all(yAx > 0), yAx = [0,yAx]; end
  401. if any(yAx <= 0), yAx = unique(sort([0,yAx])); end
  402. axis([0, 1+contnum, yAx(1)-1, yAx(end)+1]); axis square; box off;
  403. yAxneg = yAx(yAx<0); yAxpos = yAx(yAx>0);
  404. if not(isempty(yAxneg))
  405. yAxneg = [ceil(yAxneg(1)/5)*5, floor(yAxneg(2:end)/5)*5];
  406. if all(yAxneg == 0), yAxneg = yAx(1); end
  407. end
  408. yAxpos = floor(yAxpos/5)*5; yAxpos = yAxpos(yAxpos~=0);
  409. ylab = unique([yAxneg,0,yAxpos]);
  410. xticks(1 : 1 : contnum); yticks(ylab);
  411. ax = gca; ax.TickDir = 'out';
  412. if noaxis
  413. yrn = (yAx(end)+1)- (yAx(1)-1); %#ok
  414. yrn = ceil((yrn/10)/5)*5;
  415. if not(ishold), hold on; end
  416. plot([1,1],[ylab(end)/4, yrn + ylab(end)/4],'k','linewidth',1);
  417. text(1.2, ylab(end)/4 + yrn/2, [num2str(yrn),' Hz'], 'fontsize', 8);
  418. box off; ax.YTick = []; ax.YColor = 'w';
  419. end
  420. xlabel('Stimulus index');
  421. ylabel('Rate (Hz)');
  422. title('Chromatic Integration Curve');
  423. legend([datplt.plot1, datplt.plot2],'Green-On-UV-Off','Green-Off-UV-On','location','southoutside');
  424. legend boxoff;
  425. end
  426. %--------------------------------------------------------------------------------------------------%
  427. function plot_chromatic_integration_psths(ci_data, para)
  428. %
  429. stimpsth = ci_data.stimulusPSTH;
  430. pfrpsth = ci_data.preframePSTH;
  431. gNuFcol = [1 0.0781 0.5742]; % green on, uv off color
  432. gFuNcol = [0 0.7461 1]; % green off, uv on color
  433. contnum = size(stimpsth,2)/2;
  434. ygap = 1.15;
  435. xgap = 0.2;
  436. pfrcol = 0.75*[1 1 1]; % silver gray color for preframes
  437. % re-arrange input data
  438. cipsth = ([pfrpsth(:,1:contnum);stimpsth(:,1:contnum);...
  439. pfrpsth(:,1+contnum:2*contnum);stimpsth(:,1+contnum:2*contnum)]);
  440. para.pfrduration = para.stimduration;
  441. pfrnumbin = para.pfrduration / para.binlength;
  442. stimnumbin = para.stimduration / para.binlength;
  443. timevec = [linspace(0,para.pfrduration,pfrnumbin);...
  444. linspace(para.pfrduration,para.pfrduration+para.stimduration,stimnumbin);...
  445. linspace(para.pfrduration+para.stimduration,2*para.pfrduration+para.stimduration,pfrnumbin);...
  446. linspace(2*para.pfrduration+para.stimduration,2*(para.pfrduration+para.stimduration),stimnumbin)];
  447. tIdx = reshape(1:length(timevec(:)),[],4)';
  448. ygap = ceil((max(cipsth(:))* ygap)/10)*10;
  449. if (ygap == 0), ygap = 10; end
  450. axis([-xgap, (xgap+para.stimduration+para.pfrduration)*2 ,-ygap/4-15, ygap*contnum]);
  451. hold on; box off;
  452. % add shades
  453. shadefun(xgap);
  454. % draw bars first
  455. for ii = 1 : contnum % start from below
  456. % plotting bars
  457. barfun(timevec(1,:), cipsth(tIdx(1,:),ii), ((ii-1)* ygap), pfrcol,0.5/2);
  458. barfun(timevec(2,:), cipsth(tIdx(2,:),ii), ((ii-1)* ygap), gNuFcol,0.5);
  459. barfun(xgap + timevec(3,:), cipsth(tIdx(3,:),ii), ((ii-1)* ygap),pfrcol,0.5/2);
  460. barfun(xgap + timevec(4,:), cipsth(tIdx(4,:),ii), ((ii-1)* ygap),gFuNcol,0.5);
  461. scalebarfun(xgap, ygap, para, ii);
  462. end
  463. stairsfun([timevec(1,:),timevec(2,:)], cipsth(1:size(cipsth,1)/2,:), ygap, contnum, 0, 0.5);
  464. stairsfun([xgap + timevec(3,:),xgap + timevec(4,:)],cipsth(1+size(cipsth,1)/2:size(cipsth,1),:),...
  465. ygap,contnum, 0, 0.5);
  466. datascalefun(xgap+0.05, ygap, para);
  467. % getting rid of labels
  468. set(gca,'xtick', [],'ytick', [],'xcolor', 'w','ycolor', 'w');
  469. pbaspect([1 1.6 1]);
  470. end
  471. %--------------------------------------------------------------------------------------------------%
  472. function stairsfun(x,y,pltYgap,contnum,drawbaseline,lw)
  473. %
  474. gaps = 0 : pltYgap : pltYgap * (contnum - 1);
  475. yAx = y + repmat(gaps, size(y,1),1);
  476. yAx = [gaps ; yAx ; yAx(end,:); gaps]; % this is to close the ending of stairs function
  477. xAx = [x(1), x , x(end), x(end)]; % this is to make x axis match the ending
  478. stairs( xAx, yAx, 'k', 'linewidth', lw);
  479. if drawbaseline
  480. if ~ishold, hold on; end
  481. line([xAx(1), xAx(end)],[yAx(1,:) ; yAx(1,:)], 'color', 'k', 'linewidth', lw);
  482. end
  483. end
  484. %--------------------------------------------------------------------------------------------------%
  485. function b = barfun(x,y,g,col,transP,varargin)
  486. %
  487. if iscolumn(y), y = y'; end
  488. x = [x;x];
  489. y = [y;y];
  490. xplt = x([2:end end]);
  491. yplt = y(1:end);
  492. b = patch([xplt, fliplr(xplt)],[yplt + g, fliplr( repmat(g, size(yplt)))], 0 ,...
  493. 'FaceColor', col, 'EdgeColor', 'none', 'FaceAlpha', transP);
  494. end
  495. %--------------------------------------------------------------------------------------------------%
  496. function shadefun(xgap, shaderegion, col)
  497. %
  498. if nargin <2, shaderegion = [0.55,0.75,1.55,1.75]; end
  499. if nargin <3, col = [0.9792 0.9792 0.8203]; end
  500. patch([shaderegion(1), shaderegion(1), shaderegion(2), shaderegion(2)],...
  501. [get(gca,'YLim'), fliplr(get(gca,'YLim'))], col, 'edgecolor','none');
  502. patch(xgap + [shaderegion(3), shaderegion(3), shaderegion(4),shaderegion(4)],...
  503. [get(gca,'YLim'), fliplr(get(gca,'YLim'))], col, 'edgecolor','none');
  504. end
  505. %--------------------------------------------------------------------------------------------------%
  506. function scalebarfun(xgap, ygap, para, iter )
  507. %
  508. gr = para.contrastgreen;
  509. uv = para.contrastuv;
  510. grcol = [0 0.8008 0];
  511. uvcol = [0.5391 0.1680 0.8828];
  512. tranp = 0.5;
  513. sc = (ygap*0.3)/max(gr); % scaling ratio for scale bars
  514. tsc = (sc*-8)/2; % text distance from border of each scale bar
  515. barfun([-0.03,0.015], [sc*gr(iter), sc*gr(iter)], ((iter-1)* ygap) + ygap/2, grcol, tranp);
  516. barfun([-0.03,0.015], [sc*uv(iter), sc*uv(iter)], (((iter-1)* ygap) + ygap/2), uvcol, tranp);
  517. text(-0.06,tsc+((((iter-1)* ygap)+ygap/2) + sc*gr(iter)),num2str(gr(iter)),'horiz','right','fontsize',6);
  518. text(-0.06,((((iter-1)* ygap)+ygap/2) + sc*uv(iter))-tsc,num2str(uv(iter)),'horiz','right','fontsize',6);
  519. barfun([xgap+(1.1-0.03),xgap+(1.115)],[-sc*gr(iter),-sc*gr(iter)],((iter-1)* ygap)+ygap/2,grcol,tranp);
  520. barfun([xgap+(1.1-0.03),xgap+(1.115)],[-sc*uv(iter),-sc*uv(iter)],(((iter-1)* ygap)+ygap/2),uvcol,tranp);
  521. text(xgap+(1.1-0.06),((((iter-1)* ygap)+ygap/2) - sc*gr(iter))-tsc,num2str(-gr(iter)),'horiz','right','fontsize',6);
  522. text(xgap+(1.1-0.06),tsc+((((iter-1)* ygap)+ygap/2) - sc*uv(iter)),num2str(-uv(iter)),'horiz','right','fontsize',6);
  523. end
  524. %--------------------------------------------------------------------------------------------------%
  525. function datascalefun(xgap, ygap, para)
  526. % adding scale bar for firing rate
  527. scalloc = -ygap/3.5;
  528. tvec = (para.stimduration + para.pfrduration) * 2;
  529. scalresp = ceil(ygap/50)*10;
  530. plot(xgap + [(0.1/tvec)+tvec tvec tvec],[scalloc scalloc (scalloc + scalresp)],'k-','linewidth',0.5);
  531. text(xgap + tvec + (0.05/tvec),scalloc-5,'100 ms','horiz','center','vert','top','fontsize',7);
  532. text(xgap + tvec - 0.02,(scalloc + scalloc/2)/3,[num2str(scalresp),' spikes'],...
  533. 'horiz','right','vert','middle','fontsize',7);
  534. end
  535. %--------------------------------------------------------------------------------------------------%