Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
SpectraViewer-ML/spectraViewer.m
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
537 lines (496 sloc)
33.9 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function spectraViewer | |
% Viewer for excitation and emission spectra of fluorescent dyes | |
% | |
% Data organization inside spectra files: | |
% All files are expected to be comma separated lists without header. | |
% Data are expected to be (in this order): | |
% Wavelength (abs), Absorption coef, Wavelength (em), Emission coef, | |
% Wavelength (2p), 2p cross section, Wavelength (3p), 3p cross section. | |
% If there are no 2p and/or 3p cross section data the files should only | |
% contain 4 or 6 columns. | |
% | |
% Convert Excel files to csv files: | |
% - Make sure the excel sheet uses '.' as a decimal separator. If not, go | |
% to File -> Options -> Advanced -> Editing options -> Use system separator | |
% and change accordingly. Then, go to File -> Export -> Change File Type -> | |
% CSV -> Save as and save file. The region settings of your OS might result | |
% in semicolon-separated lists (instead of comma-separated lists). If so, | |
% go to Windows Start menu - Region and language - Additional settings - | |
% List separator and set it to comma. | |
% | |
% Use https://apps.automeris.io/wpd/ to digitize plots in figures. Save as | |
% csv file, and import in excel to combine various properties (Excel: Data | |
% - Get External Data: From text. Make sure "Data format" is "Text" for all columns!) | |
% | |
% Spectra of light sources and filters: | |
% Spectra of light sources and/or filter can be selected and displayed from | |
% the Light sources and filter menu. These are assumed to be csv files with | |
% two columns (wavelength, transmission/intensity). In order to avoid | |
% problems with the fluroescence spectra (which assume at least four columns) | |
% these spectra are saved in a subfolder "LightSources+Filter". Data sources | |
% are documented in the same Excel file but in a different sheet (sheets | |
% are named accordingly). These spectra are for now only displayed in the | |
% 1D viewer. | |
% | |
% Resources for spectra: | |
% Chroma: https://www.chroma.com/spectra-viewer (very broad spectra, not searchable) | |
% Semrock: https://searchlight.semrock.com/ (searchable, also light sources and filter) | |
% Thermofisher: http://www.thermofisher.com/de/de/home/life-science/cell-analysis/labeling-chemistry/fluorescence-spectraviewer.html | |
% U of Arizona: http://spectra.arizona.edu/ | |
% | |
% !!! When you add or update spectra please note the source of the data in the excel | |
% sheet Sources.xlsx!!! | |
% Please indicate in the filename whether the file contains 2P and 3P cross | |
% sections (_2P and/or _3P after the file name). This is useful for | |
% filtering the list of fluorophores. | |
% | |
% Stephan Junek, 2018 | |
% To Do!!! | |
% Wavelengths in RGB values from 351 nm to 750 nm | |
% Values calculated using https://de.mathworks.com/matlabcentral/fileexchange/7021-spectral-and-xyz-color-functions | |
% whos information are based on published color matching functions | |
wavelengthScale = 351:750; | |
wavelength2RGB = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 2 2 3 4 5 7 9 11 15 19 24 30 38 47 56 66 77 88 100 113 126 139 153 167 182 196 212 227 242 257 273 287 302 315 328 341 353 364 375 385 395 403 411 417 423 427 431 434 436 437 438 439 439 439 437 435 431 427 421 414 406 396 385 373 359 343 325 305 281 252 216 171 110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 71 259 356 430 492 547 596 642 684 723 761 796 830 862 894 925 954 983 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 988 969 950 931 912 893 874 855 836 817 798 779 760 741 723 704 686 668 650 633 616 599 582 566 550 534 518 503 488 473 459 445 431 417 403 390 378 365 353 341 330 319 308 297 286 276 267 257 248 239 230 221 213 205 197 190 182 175 168 161 155 148 142 136 130 125 119 114 109 104 99 94 90 85 81 77 73 69 65 62 58 55 51 48 45 42 39 36 34 31 29 27 25 24 22 20 19 18 16 15 14 13 12 12 11 10 9 9 8; 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 87 177 231 273 309 342 374 404 434 464 493 520 544 567 588 607 625 641 658 674 689 705 721 737 752 767 780 793 805 816 825 836 848 861 874 887 900 913 925 937 948 960 971 982 993 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 987 974 961 947 932 918 902 887 871 855 839 823 807 790 773 755 737 719 700 681 661 641 621 600 578 557 534 511 488 463 438 413 386 358 329 298 264 227 184 130 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 2 3 4 5 7 9 13 17 23 31 41 52 65 79 94 110 128 147 167 189 212 237 263 291 321 352 384 418 453 489 526 565 604 645 685 727 768 810 850 891 930 969 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 986 958 930 902 874 846 819 792 766 740 715 690 665 639 614 588 562 536 511 487 463 438 412 384 355 323 289 252 209 157 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]/1000; | |
% Variables | |
strIdxDye = []; | |
currData = []; | |
currFilt = []; | |
% Find folder containing the data files | |
pn = which('spectraViewer.m'); | |
[pn, ~,~] = fileparts(pn); | |
%Find files | |
fn = dir([pn filesep '*.csv']); | |
% Add by name instead | |
[sortFn, sortIdx] = sort(lower({fn(:).name})); | |
fn = fn(sortIdx); | |
% Add names to structure | |
nDyes = numel(fn); | |
for f = 1:nDyes | |
spectra(f).name = fn(f).name; | |
end | |
% Make selection GUI | |
guiSz = [100 274 650 626]; | |
gui = figure('Position',guiSz, 'Toolbar','none', 'CloseRequestFcn', @closeGui , 'Resize','off', 'Name','SpectraViewer (GUI)', 'NumberTitle','off'); | |
% Dye-selection panel | |
dyeSelSz = [20 guiSz(4)/2+10 guiSz(3)-40 guiSz(4)/2-20]; | |
dyeSel = uipanel(gui,'Title','Fluorophores','FontSize',12,... | |
'BackgroundColor','white','units','pixel',... | |
'Position',dyeSelSz, 'BackgroundColor', get(gui, 'COlor')); | |
nRowsDye = 0; | |
plusIconDye = uicontrol('Parent',dyeSel,'units','pixel','Style', 'pushbutton', 'Position', [10 dyeSelSz(4)-70 17 17], 'String', '+', 'Tooltip', 'Add dye entry', ... | |
'Callback', {@makeNewRow,nRowsDye}); | |
dyeHandles = []; | |
uicontrol('Parent',dyeSel,'Style','Text', 'Position',[ 5, dyeSelSz(4)-50, 30, 17],'String', 'Add', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',dyeSel,'Style','Text', 'Position',[35, dyeSelSz(4)-50, 30, 17],'String', 'Del', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',dyeSel,'Style','Text', 'Position',[63, dyeSelSz(4)-50, 35, 17],'String', 'Show', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',dyeSel,'Style','Text', 'Position',[95, dyeSelSz(4)-50, 90, 17],'String', 'Selection filter', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',dyeSel,'Style','Text', 'Position',[265, dyeSelSz(4)-50, 100, 17],'String', 'Fluorophore', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',dyeSel,'Style','Text', 'Position',[395, dyeSelSz(4)-50, 40, 17],'String', '1pExc', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',dyeSel,'Style','Text', 'Position',[435, dyeSelSz(4)-50, 40, 17],'String', '2pExc', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',dyeSel,'Style','Text', 'Position',[475, dyeSelSz(4)-50, 40, 17],'String', '3pExc', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',dyeSel,'Style','Text', 'Position',[515, dyeSelSz(4)-50, 40, 17],'String', 'Em', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',dyeSel,'Style','Text', 'Position',[555, dyeSelSz(4)-50, 40, 17],'String', 'Line', 'HorizontalAlignment','center','FontWeight','bold') | |
% Filters and lasers | |
filterSelSz = [20 20 guiSz(3)-40 guiSz(4)/2-20]; | |
filterSel = uipanel(gui,'Title','Filters and light sources','FontSize',12,... | |
'BackgroundColor','white','units','pixel',... | |
'Position',filterSelSz, 'BackgroundColor', get(gui, 'COlor')); | |
nRowsFilt = 0; | |
plusIconFilt = uicontrol('Parent',filterSel,'units','pixel','Style', 'pushbutton', 'Position', [10 filterSelSz(4)-70 17 17], 'String', '+', 'Tooltip', 'Add filter entry', ... | |
'Callback', {@makeNewFiltRow,nRowsFilt}); | |
filtTypeStr = {'Excitation filter';'Emission filter';'Laser'; 'Spectrum'}; % Maybe to add: light source - selection of spectrum from file | |
filtHandles = []; | |
uicontrol('Parent',filterSel,'Style','Text', 'Position',[ 5, filterSelSz(4)-50, 30, 17],'String', 'Add', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',filterSel,'Style','Text', 'Position',[35, filterSelSz(4)-50, 30, 17],'String', 'Del', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',filterSel,'Style','Text', 'Position',[63, filterSelSz(4)-50, 35, 17],'String', 'Show', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',filterSel,'Style','Text', 'Position',[115, filterSelSz(4)-50, 90, 17],'String', 'Type', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',filterSel,'Style','Text', 'Position',[245, filterSelSz(4)-50, 100, 17],'String', 'Wavelength [nm]', 'HorizontalAlignment','center','FontWeight','bold') | |
uicontrol('Parent',filterSel,'Style','Text', 'Position',[365, filterSelSz(4)-50, 100, 17],'String', 'Bandwidth [nm]', 'HorizontalAlignment','center','FontWeight','bold') | |
makeNewFiltRow(0,0,1); | |
% 1D figure | |
fig1D = figure('Position',[780 274 850 600], 'CloseRequestFcn', @closeGui, 'Name','SpectraViewer (1D Viewer)', 'NumberTitle','off'); | |
set(gcf,'Color','k') | |
axLambda = subplot(10,1,1); | |
imagesc( wavelengthScale([1 end]),[0 1], permute(wavelength2RGB,[3 2 1])); | |
axis off | |
set(axLambda, 'XTickLabel', '', 'YTickLabel','') | |
title('Spectra viewer 1D', 'Color','w') | |
ax1D = subplot(10,1,3:10); | |
linkaxes([axLambda, ax1D], 'x') | |
xlabel('wavelength [nm]') | |
ylabel('normalized abs / em') | |
box on | |
set(ax1D, 'XLim', [350 800], 'YLim',[ -0.1 1.05], 'XColor','w','YColor','w','Color',0.3*[1 1 1]) | |
hold on | |
% 2D figure | |
fig2D = figure('Position',[1650 274 850 600], 'CloseRequestFcn', @closeGui, 'Name','SpectraViewer (2D Viewer)', 'NumberTitle','off'); | |
set(gcf,'Color','k') | |
axLambdaX = subplot(10,10,1:9); | |
imagesc( wavelengthScale([1 end]),[0 1], permute(wavelength2RGB,[3 2 1])); | |
axis off | |
set(axLambdaX, 'XTickLabel', '', 'YTickLabel','') | |
title('Spectra viewer 2D', 'Color','w') | |
axLambdaY = subplot(10,10,20:10:100); | |
imagesc([0 1],wavelengthScale([end 1]), flip(permute(wavelength2RGB,[2 3 1]),1)); | |
axis off | |
set(axLambdaY, 'XTickLabel', '', 'YTickLabel','', 'YDir','normal') | |
axIdx = reshape(1:100,[10 10]); | |
axIdx(10,:) = []; | |
axIdx(:,1) = []; | |
ax2D = subplot(10,10,axIdx(:)); | |
set(ax2D, 'XLim', [350 800], 'YLim',[350 800], 'XColor','w','YColor','w','Color',0.3*[1 1 1]) | |
xlabel('exc wavelength [nm]') | |
ylabel('em wavelength [nm]') | |
box on | |
linkaxes([axLambdaX, ax2D], 'x') | |
linkaxes([axLambdaY, ax2D], 'y') | |
hold on | |
makeNewDyeRow(0,0,1); | |
function makeNewDyeRow(~,~,idx) | |
% Delete button | |
dyeHandles(1,idx) = uicontrol('Parent',dyeSel,'Style', 'pushbutton', 'Position', [40 dyeSelSz(4)-(idx+2.5)*20 17 17], 'String', '-', 'Tooltip', 'Remove dye entry', 'Callback', {@deleteRow,idx,'dye'}); | |
% Show / hide checkbox | |
dyeHandles(2,idx) = uicontrol('Parent',dyeSel,'Style', 'checkbox', 'Position', [70 dyeSelSz(4)-(idx+2.5)*20 17 17], 'Value',1, 'Tooltip', 'Hide dye entry', 'callback', @updatePlots); | |
% Filter field | |
dyeHandles(3,idx) = uicontrol('Parent',dyeSel,'Style', 'edit', 'Position', [100 dyeSelSz(4)-(idx+2.5)*20 80 17], 'String','', 'Tooltip', 'Filter by string', 'Callback', {@filterDyeList,idx}); | |
% Dye popup list | |
dyeHandles(4,idx) = uicontrol('Parent',dyeSel,'Style', 'popup ', 'Position', [200 dyeSelSz(4)-(idx+2.5)*20 200 17], 'String',{spectra(:).name}, 'Tooltip', 'List of available fluorophors', 'Callback', {@updateDyeSelection,idx}); | |
% 1p exc | |
dyeHandles(5,idx) = uicontrol('Parent',dyeSel,'Style', 'checkbox', 'Position', [410 dyeSelSz(4)-(idx+2.5)*20 17 17], 'Value',1, 'Tooltip', 'Show 1p exc' , 'callback', @updatePlots); | |
% Emission | |
dyeHandles(6,idx) = uicontrol('Parent',dyeSel,'Style', 'checkbox', 'Position', [530 dyeSelSz(4)-(idx+2.5)*20 25 17], 'Value',1, 'Tooltip', 'Show emission', 'callback', @updatePlots ); | |
% 2p exc | |
dyeHandles(7,idx) = uicontrol('Parent',dyeSel,'Style', 'checkbox', 'Position', [450 dyeSelSz(4)-(idx+2.5)*20 17 17], 'Value',0, 'Tooltip', 'Show 2p exc' , 'callback', @updatePlots); | |
% 3p exc | |
dyeHandles(8,idx) = uicontrol('Parent',dyeSel,'Style', 'checkbox', 'Position', [490 dyeSelSz(4)-(idx+2.5)*20 17 17], 'Value',0, 'Tooltip', 'Show 3p exc', 'callback', @updatePlots ); | |
% Linestyle | |
dyeHandles(9,idx) = uicontrol('Parent',dyeSel,'Style', 'text', 'Position', [555 dyeSelSz(4)-(idx+2.5)*20 40 17], 'String','', 'BackgroundColor',0.3*[1 1 1],'HorizontalAlignment','center', 'Tooltip', 'Plot line style' ); | |
nRowsDye = nRowsDye + 1; | |
strIdxDye{idx} = 1:nDyes; | |
set(plusIconDye,'Callback', {@makeNewDyeRow,nRowsDye+1}); | |
if nRowsDye == 1 | |
set(dyeHandles(1,1), 'Enable','off'); | |
else | |
set(dyeHandles(1,1), 'Enable','on'); | |
end | |
updateDyeSelection(dyeHandles(4,idx),0,idx); | |
end | |
function makeNewFiltRow(~,~,idx) | |
% Delete button | |
filtHandles(1,idx) = uicontrol('Parent',filterSel,'Style', 'pushbutton', 'Position', [40 filterSelSz(4)-(idx+2.5)*20 17 17], 'String', '-', 'Tooltip', 'Remove filter entry', 'Callback', {@deleteRow,idx,'filt'}); | |
% Show / hide checkbox | |
filtHandles(2,idx) = uicontrol('Parent',filterSel,'Style', 'checkbox', 'Position', [70 filterSelSz(4)-(idx+2.5)*20 17 17], 'Value',1, 'Tooltip', 'Hide filter entry', 'callback', @updateFiltObj); | |
% Filter type selection | |
filtHandles(3,idx) = uicontrol('Parent',filterSel,'Style', 'popup ', 'Position', [100 filterSelSz(4)-(idx+2.5)*20 120 17], 'String',filtTypeStr, 'Tooltip', 'Select type', 'Callback', {@setFiltType,idx}); | |
% Wavelength | |
filtHandles(4,idx) = uicontrol('Parent',filterSel,'Style', 'edit', 'Position', [245 dyeSelSz(4)-(idx+2.5)*20 100 17], 'String','', 'Tooltip', 'Enter wavelength', 'Callback', {@updateFilterSelection,idx}, 'Visible','off'); | |
% Wavelength | |
filtHandles(5,idx) = uicontrol('Parent',filterSel,'Style', 'edit', 'Position', [365 dyeSelSz(4)-(idx+2.5)*20 100 17], 'String','', 'Tooltip', 'Enter bandwidth', 'Callback', {@updateFilterSelection,idx}, 'Visible','off'); | |
% Spectrum file selection | |
filtHandles(6,idx) = uicontrol('Parent',filterSel,'Style', 'pushbutton', 'Position', [245 dyeSelSz(4)-(idx+2.5)*20 100 17], 'String','Select file', 'Tooltip', 'Select file containing spectral information', 'Callback', {@loadSpectrum,idx}, 'Visible','off'); | |
% Spectrum file name | |
filtHandles(7,idx) = uicontrol('Parent',filterSel,'Style', 'text', 'Position', [365 dyeSelSz(4)-(idx+2.5)*20 200 17], 'String','---', 'Tooltip', 'File name', 'Visible','off','HorizontalAlignment','left'); | |
currFilt(idx).handle1D = []; | |
currFilt(idx).handle2D = []; | |
nRowsFilt = nRowsFilt + 1; | |
set(plusIconFilt,'Callback', {@makeNewFiltRow,nRowsFilt+1}); | |
if nRowsFilt == 1 | |
set(filtHandles(1,1), 'Enable','off'); | |
else | |
set(filtHandles(1,1), 'Enable','on'); | |
end | |
end | |
% Remove fluorophore row | |
function deleteRow(~,~,idx,dyeFilt) | |
switch dyeFilt | |
case 'dye' | |
hdls = dyeHandles; | |
nEl = nRowsDye; | |
data = currData; | |
case 'filt' | |
hdls = filtHandles; | |
nEl = nRowsFilt; | |
data = currFilt; | |
end | |
% Remove data entry from currData structure | |
keepIdx = 1:nEl; | |
keepIdx(idx) = []; | |
data = data(keepIdx); | |
% Remove uicontrol elements | |
delete(hdls(:,idx)); | |
% Move and update remaining uicontrol elements | |
if idx < nEl | |
for ii = idx+1:nEl | |
for h = 1:size(hdls,1) | |
set(hdls(h,ii), 'Position', get(hdls(h,ii), 'Position') + [0 20 0 0]); | |
cb = get(hdls(h,ii), 'Callback'); | |
if numel(cb)>1 | |
if h == 1 | |
set(hdls(h,ii), 'Callback', {cb{1},ii-1,dyeFilt}); | |
else | |
set(hdls(h,ii), 'Callback', {cb{1},ii-1}); | |
end | |
end | |
end | |
end | |
end | |
% Remove handles of uicontrol | |
hdls(:,idx) = []; | |
nEl = nEl - 1; | |
% Disable "Delete" button if only one row remains | |
if nEl == 1 | |
set(hdls(1,1), 'Enable','off'); | |
else | |
set(hdls(1,1), 'Enable','on'); | |
end | |
switch dyeFilt | |
case 'dye' | |
dyeHandles= hdls; | |
nRowsDye = nEl; | |
currData = data; | |
set(plusIconDye,'Callback', {@makeNewDyeRow,nEl+1}); | |
case 'filt' | |
filtHandles = hdls; | |
nRowsFilt = nEl; | |
currFilt = data; | |
set(plusIconFilt,'Callback', {@makeNewFiltRow,nEl+1}); | |
end | |
updatePlots; | |
end | |
% Apply string-filter to dyes shown in popup menu | |
function filterDyeList(~,~,idx) | |
str = get(dyeHandles(3,idx), 'String'); | |
strIdxDye{idx} = find(contains({spectra(:).name},str,'IgnoreCase',true)); | |
if isempty(strIdxDye{idx}) | |
set(dyeHandles(4,idx),'Value',1, 'String', 'No dye found', 'Enable','off'); | |
else | |
set(dyeHandles(4,idx),'Value',1, 'String', {spectra(strIdxDye{idx}).name}, 'Enable','on'); | |
end | |
end | |
% Switch between different selection in "Filters and lasers" GUI | |
function setFiltType(~,~,idx) | |
v = get(filtHandles(3,idx),'Value'); | |
switch v | |
case 1 | |
set(filtHandles([4 5],idx),'Visible','on'); | |
set(filtHandles([6 7],idx),'Visible','off'); | |
currFilt(idx).type = 'excitation'; | |
case 2 | |
set(filtHandles([4 5],idx),'Visible','on'); | |
set(filtHandles([6 7],idx),'Visible','off'); | |
currFilt(idx).type = 'emission'; | |
case 3 | |
set(filtHandles(4,idx),'Visible','on'); | |
set(filtHandles(5,idx),'Visible','off'); | |
currFilt(idx).type = 'laser'; | |
case 4 | |
set(filtHandles([6 7], idx), 'Visible', 'on'); | |
currFilt(idx).type = 'spectrum'; | |
end | |
end | |
% Update gui elements after dye selection | |
function updateDyeSelection(co,~,idx) | |
popupIdx = get(co, 'Value'); | |
readData(idx, strIdxDye{idx}(popupIdx)); | |
set(dyeHandles([7 8],idx), 'Value',0,'Enable', 'on'); | |
if size(currData(idx).data,2) == 4 | |
set(dyeHandles([7 8],idx), 'Value',0,'Enable', 'off'); | |
elseif size(currData(idx).data,2) == 6 | |
set(dyeHandles(8,idx), 'Value',0,'Enable', 'off'); | |
end | |
updatePlots; | |
end | |
% Read spectral data from csv files and add to data structure | |
function readData(rowIdx, dyeIdx) | |
currData(rowIdx).filename = spectra(dyeIdx).name; | |
currData(rowIdx).data = csvread([pn filesep spectra(dyeIdx).name]); | |
% Exclude data that are zero (set to NaN) | |
for jj = 1:(size(currData(rowIdx).data,2)/2) | |
zeroIdx = min(currData(rowIdx).data(:,[jj*2-1 jj*2]),[],2) == 0; | |
currData(rowIdx).data(zeroIdx,jj*2-1:jj*2) = NaN; | |
end | |
currData(rowIdx).dyeIdx = dyeIdx; | |
currData(rowIdx).lambdaMax1pExc = currData(rowIdx).data(find(currData(rowIdx).data(:,2)==max(currData(rowIdx).data(:,2)),1),1); | |
currData(rowIdx).lambdaMaxEm = currData(rowIdx).data(find(currData(rowIdx).data(:,4)==max(currData(rowIdx).data(:,4)),1),3); | |
basenameIdx = strfind(currData(rowIdx).filename,'_'); | |
if isempty(basenameIdx) | |
currData(rowIdx).dyeName = currData(rowIdx).filename(1:end-4); | |
else | |
currData(rowIdx).dyeName = currData(rowIdx).filename(1:basenameIdx(1)-1); | |
end | |
% Use darker version of emission color for display of 1p excitation | |
% spectrum (for more consistent display) | |
currData(rowIdx).plotColor = wavelength2RGB(:,find(wavelengthScale>currData(rowIdx).lambdaMaxEm,1))/2; | |
% currData(rowIdx).plotColor = wavelength2RGB(:,find(wavelengthScale>currData(rowIdx).lambdaMax1pExc,1)); | |
if isempty(currData(rowIdx).plotColor) | |
currData(rowIdx).plotColor = 0.7*[1 1 1]; | |
end | |
currData(rowIdx).plotColor(:,2) = wavelength2RGB(:,find(wavelengthScale>currData(rowIdx).lambdaMaxEm,1)); | |
if ~sum(currData(rowIdx).plotColor(:,2)) | |
currData(rowIdx).plotColor(:,2) = 0.7*[1 1 1]; | |
end | |
if size(currData(rowIdx).data,2) > 4 | |
currData(rowIdx).lambdaMax2pExc = currData(rowIdx).data(find(currData(rowIdx).data(:,6)==max(currData(rowIdx).data(:,6)),1),5); | |
currData(rowIdx).max2pXSectionVal = max(currData(rowIdx).data(:,6)); | |
currData(rowIdx).plotColor(:,3) = mean([[1 1 1];0.5*currData(rowIdx).plotColor(:,2)'],1); | |
end | |
if size(currData(rowIdx).data,2) > 6 | |
currData(rowIdx).lambdaMax3pExc = currData(rowIdx).data(find(currData(rowIdx).data(:,8)==max(currData(rowIdx).data(:,8)),1),7); | |
currData(rowIdx).max3pXSectionVal = max(currData(rowIdx).data(:,8)); | |
currData(rowIdx).plotColor(:,4) = mean([[1 1 1];[1 1 1];0.5*currData(rowIdx).plotColor(:,2)'],1); | |
end | |
end | |
% Get filter information from edit-boxes | |
function updateFilterSelection(~,~,idx) | |
currFilt(idx).wavelength = str2double(get(filtHandles(4,idx),'String')); | |
currFilt(idx).band = str2double(get(filtHandles(5,idx),'String')); | |
updateFiltObj; | |
end | |
% Load spectral information for light sources or filters | |
function loadSpectrum(~,~,idx) | |
[currFilt(idx).spectrumFilename, currFilt(idx).spectrumPathname] = uigetfile([pn filesep 'LightSources+Filter' filesep '*.csv']); | |
currFilt(idx).spectrum = csvread([currFilt(idx).spectrumPathname currFilt(idx).spectrumFilename]); | |
set(filtHandles(7,idx), 'String',currFilt(idx).spectrumFilename); | |
maxPos = find(currFilt(idx).spectrum(:,2) == max(currFilt(idx).spectrum(:,2)),1); | |
currFilt(idx).wavelength = currFilt(idx).spectrum(maxPos,1); | |
updateFiltObj; | |
end | |
% Update filter/light source settings | |
function updateFiltObj(varargin) | |
for ii = 1:numel(currFilt) | |
if ~isempty(currFilt(ii).handle1D) | |
delete(currFilt(ii).handle1D); | |
currFilt(ii).handle1D = []; | |
end | |
if ~isempty(currFilt(ii).handle2D) | |
delete(currFilt(ii).handle2D); | |
currFilt(ii).handle2D = []; | |
end | |
if get(filtHandles(2,ii), 'Value') | |
yl1D = get(ax1D,'YLim'); | |
yl1D(1) = yl1D(1)+0.1; | |
yl2D = get(ax2D,'YLim'); | |
xl2D = get(ax2D,'XLim'); | |
if ~isempty(currFilt(ii).wavelength) | |
filtCol = wavelength2RGB(:,find(wavelengthScale>currFilt(ii).wavelength,1))'; | |
if isempty(filtCol) || currFilt(ii).wavelength<400 || currFilt(ii).wavelength>700 | |
filtCol = 0.8*[1 1 1]; | |
end | |
switch currFilt(ii).type | |
case 'excitation' | |
currFilt(ii).handle1D = fill([currFilt(ii).wavelength-currFilt(ii).band/2 sum(currFilt(ii).wavelength+currFilt(ii).band/2)*[1 1] currFilt(ii).wavelength-currFilt(ii).band/2], [yl1D(1) yl1D(1) yl1D(2) yl1D(2)],filtCol ,'Parent',ax1D, 'Facealpha',0.2, 'EdgeColor','none'); | |
currFilt(ii).handle1D(2) = text(currFilt(ii).wavelength,-0.075,['Exc' num2str(currFilt(ii).wavelength) '/' num2str(currFilt(ii).band)], 'Color',filtCol,'Parent',ax1D, 'HorizontalAlignment','center', 'VerticalAlignment','bottom'); | |
currFilt(ii).handle2D = fill([currFilt(ii).wavelength-currFilt(ii).band/2 sum(currFilt(ii).wavelength+currFilt(ii).band/2)*[1 1] currFilt(ii).wavelength-currFilt(ii).band/2], [yl2D(1) yl2D(1) yl2D(2) yl2D(2)],wavelength2RGB(:,find(wavelengthScale>currFilt(ii).wavelength,1))' ,'Parent',ax2D, 'Facealpha',0.2, 'EdgeColor','none'); | |
% currFilt(ii).handle2D = fill([currFilt(ii).wavelength-currFilt(ii).band/2 sum(currFilt(ii).wavelength+currFilt(ii).band/2)*[1 1] currFilt(ii).wavelength-currFilt(ii).band/2], [,[1 1 1] ,'Parent',ax2D); | |
case 'emission' | |
currFilt(ii).handle1D = fill([currFilt(ii).wavelength-currFilt(ii).band/2 sum(currFilt(ii).wavelength+currFilt(ii).band/2)*[1 1] currFilt(ii).wavelength-currFilt(ii).band/2], [yl1D(1) yl1D(1) yl1D(2) yl1D(2)],filtCol,'Parent',ax1D, 'Facealpha',0.2, 'EdgeColor','none'); | |
currFilt(ii).handle1D(2) = text(currFilt(ii).wavelength,-0.075,['Em' num2str(currFilt(ii).wavelength) '/' num2str(currFilt(ii).band)], 'Color',filtCol,'Parent',ax1D, 'HorizontalAlignment','center', 'VerticalAlignment','bottom'); | |
currFilt(ii).handle2D = fill([xl2D(1) xl2D(1) xl2D(2) xl2D(2)], [currFilt(ii).wavelength-currFilt(ii).band/2 sum(currFilt(ii).wavelength+currFilt(ii).band/2)*[1 1] currFilt(ii).wavelength-currFilt(ii).band/2],filtCol,'Parent',ax2D, 'Facealpha',0.2, 'EdgeColor','none'); | |
case 'laser' | |
currFilt(ii).handle1D = line(currFilt(ii).wavelength*[1 1], yl1D,'Color',filtCol ,'Parent',ax1D, 'LineWidth',1.5); | |
currFilt(ii).handle2D = line(currFilt(ii).wavelength*[1 1], yl2D,'Color',filtCol ,'Parent',ax2D, 'LineWidth',1.5); | |
currFilt(ii).handle1D(2) = text(currFilt(ii).wavelength,-0.075,num2str(currFilt(ii).wavelength), 'Color',filtCol,'Parent',ax1D, 'HorizontalAlignment','center', 'VerticalAlignment','bottom'); | |
case 'spectrum' | |
currFilt(ii).handle1D = plot(currFilt(ii).spectrum(:,1),currFilt(ii).spectrum(:,2)/max(currFilt(ii).spectrum(:,2)), 'Color',filtCol ,'Parent',ax1D, 'LineWidth',1.5); | |
end | |
end | |
end | |
end | |
uistack([currFilt(:).handle1D],'bottom'); | |
% uistack([currFilt(:).handle2D],'bottom'); | |
end | |
% Update plots | |
function updatePlots(varargin) | |
cla(ax1D); | |
cla(ax2D); | |
plot(ax2D, 1:1600,1:1600,'LineStyle','--','Color',0.5*[1 1 1]); | |
for ii = 1:numel(currData) | |
if get(dyeHandles(2,ii), 'Value') | |
ph = []; | |
for jj = 1:4 | |
if get(dyeHandles(jj+4,ii), 'Value') | |
% Plot Normalized values (mostly normalized already, but | |
% sometimes in range [0,1], sometimes [0,100] | |
ph(end+1) = plot(ax1D, currData(ii).data(:,jj*2-1),currData(ii).data(:,jj*2)/max(currData(ii).data(:,jj*2)), 'Color',currData(ii).plotColor(:,jj), 'LineWidth', 1.5); | |
switch jj | |
case 2 | |
text(ax1D,currData(ii).lambdaMaxEm,1.055+mod(ii-1,4)*0.04,currData(ii).dyeName, 'Color',currData(ii).plotColor(:,jj),'Fontsize',9, 'HorizontalAlignment', 'center', 'VerticalAlignment','bottom', 'Rotation', 0, 'Interpreter','none') | |
case 3 | |
text(ax1D,currData(ii).lambdaMax2pExc,1.055+mod(ii-1,4)*0.04,[currData(ii).dyeName '-2P' ' (' num2str(currData(ii).max2pXSectionVal, '%6.1f') ' GM)'], 'Color',currData(ii).plotColor(:,jj),'Fontsize',9, 'HorizontalAlignment', 'center', 'VerticalAlignment','bottom', 'Rotation', 0, 'Interpreter','none') | |
case 4 | |
text(ax1D,currData(ii).lambdaMax3pExc,1.055+mod(ii-1,4)*0.04,[currData(ii).dyeName '-3P' ' (' num2str(currData(ii).max3pXSectionVal, '%6.1f') ')'], 'Color',currData(ii).plotColor(:,jj),'Fontsize',9, 'HorizontalAlignment', 'center', 'VerticalAlignment','bottom', 'Rotation', 0, 'Interpreter','none') | |
end | |
% if jj == 3 | |
% ph(end+1) = plot(ax1D, currData(ii).data(plotIdx,jj*2-1)/2,currData(ii).data(plotIdx,jj*2)/max(currData(ii).data(:,jj*2)), 'Color',plotColor, 'LineWidth', 1.5); | |
% end | |
end | |
end | |
% 2D plot | |
contHandle = []; | |
for jj = [1 3 4] | |
if get(dyeHandles(jj+4,ii), 'Value') | |
wEx = linspace(min(currData(ii).data(:,jj*2-1)),max(currData(ii).data(:,jj*2-1)),100); | |
wEm = linspace(min(currData(ii).data(:,3)),max(currData(ii).data(:,3)),100); | |
% Make sure no wavelength value appears twice and remove NaN values | |
[c, idx] = unique(currData(ii).data(:,2*jj-1)); | |
notNaN = ~isnan(currData(ii).data(idx,2*jj-1)) & ~isnan(currData(ii).data(idx,2)); | |
interEx = interp1(currData(ii).data(idx(notNaN),2*jj-1),currData(ii).data(idx(notNaN),2*jj),wEx); | |
[c, idx] = unique(currData(ii).data(:,3)); | |
notNaN = ~isnan(currData(ii).data(idx,3)) & ~isnan(currData(ii).data(idx,4)); | |
interEm = interp1(currData(ii).data(idx(notNaN),3),currData(ii).data(idx(notNaN),4),wEm); | |
% Calculate and plot combined spectrum | |
m = repmat(interEx/max(interEx),[100 1]).*repmat(interEm'/max(interEm),[1 100]); | |
[c, contHandle(end+1)] = contour(ax2D, wEx,wEm,m,[0.99 0.8 0.6 0.4 0.2],'Color',currData(ii).plotColor(:,jj),'LineWidth',1.5); | |
switch jj | |
case 1 | |
text(ax2D,currData(ii).lambdaMax1pExc,max(c(2,:))+1 ,currData(ii).dyeName, 'Color',currData(ii).plotColor(:,jj),'Fontsize',9, 'HorizontalAlignment', 'center', 'VerticalAlignment','bottom', 'Rotation', 0, 'Interpreter','none') | |
case 3 | |
text(ax2D,currData(ii).lambdaMax2pExc,max(c(2,:))+1 ,[currData(ii).dyeName '-2P'], 'Color',currData(ii).plotColor(:,jj),'Fontsize',9, 'HorizontalAlignment', 'center', 'VerticalAlignment','bottom', 'Rotation', 0, 'Interpreter','none') | |
case 4 | |
text(ax2D,currData(ii).lambdaMax3pExc,max(c(2,:))+1 ,[currData(ii).dyeName '-3P'], 'Color',currData(ii).plotColor(:,jj),'Fontsize',9, 'HorizontalAlignment', 'center', 'VerticalAlignment','bottom', 'Rotation', 0, 'Interpreter','none') | |
end | |
end | |
end | |
switch mod(ii,4) | |
case 1 | |
set([ph contHandle], 'LineStyle', ':'); | |
set(dyeHandles(9,ii), 'String', char(183*[1 1 1 1 1]),'FontWeight','bold','ForegroundColor',currData(ii).plotColor(:,2)); | |
case 2 | |
set([ph contHandle], 'LineStyle', '--'); | |
set(dyeHandles(9,ii), 'String', '- - -','FontWeight','bold','ForegroundColor',currData(ii).plotColor(:,2)); | |
case 3 | |
set([ph contHandle], 'LineStyle', '-.'); | |
set(dyeHandles(9,ii), 'String', ['- ' char(183) ' -'],'FontWeight','bold','ForegroundColor',currData(ii).plotColor(:,2)); | |
case 0 | |
set([ph contHandle], 'LineStyle', '-'); | |
set(dyeHandles(9,ii), 'String', '___','FontWeight','bold','ForegroundColor',currData(ii).plotColor(:,2)); | |
end | |
yl = get(ax2D, 'YLim'); | |
end | |
end | |
set([ax1D ax2D], 'XLim',[350 750]); | |
if nRowsDye == 1 | |
if get(dyeHandles(7,:),'Value') | |
set([ax1D ax2D], 'XLim',[350 1100]); | |
end | |
if get(dyeHandles(8,:),'Value') | |
set([ax1D ax2D], 'XLim',[350 1500]); | |
end | |
else | |
if sum(cell2mat(get(dyeHandles(7,:),'Value'))) | |
set([ax1D ax2D], 'XLim',[350 1200]); | |
end | |
if sum(cell2mat(get(dyeHandles(8,:),'Value'))) | |
set([ax1D ax2D], 'XLim',[350 1500]); | |
end | |
end | |
text(ax2D, mean(get(ax2D, 'XLim')), yl(1)+diff(yl)/20, 'Contour lines at 0.99, 0.8, 0.6, 0.4, 0.2', 'Color', 0.5*[1 1 1], 'HorizontalAlignment','center'); | |
if ~isempty([currFilt(:).handle1D]) | |
updateFiltObj; | |
end | |
end | |
% Close all windows | |
function closeGui(varargin) | |
delete([gui fig1D fig2D]); | |
end | |
end | |