-
Nicola Vigano authored
Signed-off-by:
Nicola Vigano <nicola.vigano@esrf.fr>
Nicola Vigano authoredSigned-off-by:
Nicola Vigano <nicola.vigano@esrf.fr>
gtDrawGrainUnitCells.m 22.78 KiB
function [f_handles, p_handle] = gtDrawGrainUnitCells(grains, varargin)
% GTDRAWGRAINUNITCELLS Generalized case for more than one dataset
% [f_handle, p_handle] = gtDrawGrainUnitCells(grains, varargin)
% -------------------------------------------------------------
% Draws grains by their unit cell representing grain size, location and
% orientation by cubes or hexagonal prismes based on their Rodrigues vectors.
%
% INPUT (- cell array -):
% grains = <cell> grain cell-structure from indexing;
% one for each phase or one for each dataset
%
% OPTIONAL INPUT (p/v pairs - cell arrays -): as in gtAppDrawGrainPars
% ids = <int> list of grain IDs of interest; by default all
% the grains are drawn
% cmap = <double> can be a component of the strain tensor
% {grain.strain.strainT(3,3)} or a color map
% hlight = <int> grain id-s to be highlighted {[]}
% linecolor = <double> color for edges (1x3) {[0 0 0],[1 0 0],...}
% strain = <double> scale factor for the strain values, if wanted. {0}
% (zero to use the unstrained unit cell)
% alpha = <double> transparency value for patches {1}
% phaseid = <int> phase number {1}
% patch = <logical> true if drawing the patch of the unit cell <true>
% pxsize = <double> pixel size (mm/px) {0.001}
% ratio = <double> c/a ratio; if empty, taken from parameters.cryst {[]}
% type = <string> unit cell type; if empty, taken from parameters.cryst {''}
% label = <string> name for entry in the legend {''}
%
% OPTIONAL INPUT (p/v pairs): as in gtAppDrawGrainPars
% orig = <double> sample reference (arrows) origin (mm)
% caxis = <logical> flag to draw the c-axis {false}
% pixels = <logical> flag to switch to pixels {false}
% draw = <logical> Draw grain patches {true}
% scale = <logical> Scale grain size {true}
% size = <double> Factor to scale all the grains equally
% {[]}
% translate = <logical> Translate grain centers {true}
%
% section = <logical> Draw a section for each grain {false}
% secpos = <double> Section position {[]}
% plane = <string> Section plane {'xy'}
% paired = <logical> paired grains drawing useful for twins
% One figure for each pair {false}
%
% figure = <logical> Display or not the figure {true}
% figpos = <double> figure position in pixels (1x4) {[100 100 700 500]}
% figcolor = <double> figure background color {[0 0 0]}
% legend = <logical> display or not the legend {false}
% legendpos = <double> location of the legend box {'NorthEast'}
% showaxes = <string> Shows axes or not {'on'}
% sampleaxes = <logical> Draw sample axes {true}
% sampleenv = <logical> Draw sample envelope {true}
% zoom = <double> zoom in of this quantity {[]}
% view = <double> 3D angular view (1x2) {[-45 20]}
% debug = <logical> print comments {false}
%
% hf = <handle> existing figure handle {[]}
% ha = <handle> existing axes handle {[]}
%
% OUTPUT:
% f_handles = <struct> figure handles
% p_handle = <handle> patches handle
%
% Usage:
% For IPF-Z coloring for phase 1:
% load parameters
% load 4_grains/phase_01/index.mat grain
% gtDrawGrainUnitCells({grain},'cmap',{gtIPFCmap(1,[0 0 1])},'pxsize',{parameters.acq.pixelsize})
%
% With random coloring and displaying only some grains:
% gtDrawGrainUnitCells({grain},'ids',{1:10},'cmap',{gtRandCmap(10)},'pxsize',{parameters.acq.pixelsize})
%
% Version 009 30-07-2014 by LNervo
% Created gtAppDrawGrainPars
% Version 008 20-01-2014 by LNervo
% Version 007 17-10-2013 by LNervo
% Added some options
% Version 006 06-06-2013 by LNervo
% Added function to compute unit cell vertices, modified
% gtPlotGrainUnitCell
% Version 005 11-03-2013 by LNervo
% Use cell arrays to plot multiple datasets
% Version 004 15-11-2012 by LNervo
% Use gtDrawSampleGeometry(labgeo) to create the graphics with axis and reference
% system
% Version 003 23-10-2012 by LNervo
% Add varargin
% default app parameters
app = gtAppDrawGrainPars(grains);
app = parse_pv_pairs(app,varargin);
app.linecolor = app.linecolor(1:numel(grains));
parameters = [];
load('parameters.mat');
labgeo = parameters.labgeo;
if isempty(app.pxsize{1})
app.pxsize{1} = (labgeo.pixelsizeu + labgeo.pixelsizev)/2;
end
if isempty(app.hf)
if (app.pixels)
h_tmp = gtDrawSampleGeometry(labgeo,'orig',app.orig,'pxsize',app.pxsize{1},'max',100,'axes',app.sampleaxes,'sample',app.sampleenv);
else
h_tmp = gtDrawSampleGeometry(labgeo,'orig',app.orig,'centered',true,'axes',false,'sample',app.sampleenv);
end
h.fig = h_tmp.fig;
app.hf = h_tmp.fig;
else
h.fig = app.hf;
end
app.rot3d = h_tmp.rot3d;
if isempty(app.ha)
if ~isempty(gca)
app.ha = get(h.fig,'CurrentAxes');
else
app.ha = axes('Parent',h.fig);
end
end
% create GUI
h = sfCreateGUI(h);
set(app.ha,'Parent',h.axis_panel);
% draw unit cells
hold(app.ha,'on');
hp = [];
% loop over datasets
for ii = 1 : numel(grains)
if ii > 1
if numel(app.type)==1, app.type{ii} = app.type{1}; end
if numel(app.ratio)==1, app.ratio{ii} = app.ratio{1}; end
if numel(app.pxsize)==1, app.pxsize{ii} = app.pxsize{1}; end
if numel(app.cmap)==1, app.cmap{ii} = app.cmap{1}; end
if numel(app.hlight)==1, app.hlight{ii} = app.hlight{1}; end
if numel(app.label)==1, app.label{ii} = app.label{1}; end
if numel(app.phaseid)==1, app.phaseid{ii} = app.phaseid{1}; end
if numel(app.strain)==1, app.strain{ii} = app.strain{1}; end
end
cryst = parameters.cryst(app.phaseid{ii});
if isempty(app.type{ii}), app.type{ii} = cryst.crystal_system; end
if isempty(app.ratio{ii}), app.ratio{ii} = cryst.latticepar(3)/cryst.latticepar(1); end
if isempty(app.ids{ii}), app.ids{ii} = 1:length(grains{ii}); end
if isempty(app.cmap{ii}), app.cmap{ii} = repmat([1 0 0],length(grains{ii}),3); end %red color
ids = app.ids{ii};
grain = grains{ii};
cmap = app.cmap{ii};
% if single value (RGB), copy it for all the grains
if size(cmap,1)==1 && size(cmap,2)>1
cmap = repmat(cmap,length(grain),1);
end
% remove bkg color
if all(cmap(1,:) == [0 0 0])
cmap = cmap(2:end,:);
end
% get selected grains for current datasets
grain = grain(ids);
cmap = cmap(ids,:);
% vertices and faces calculation : TO DO LIST : unify in one function
if strcmpi(app.type{ii},'cubic')
data{ii} = gtCubicUnitCell('ratio',app.ratio{ii},'draw',false,'centered',true,'caxis',app.caxis);
elseif strcmpi(app.type{ii},'hexagonal')
data{ii} = gtHexagonalUnitCell('ratio',app.ratio{ii},'draw',false,'centered',true,'caxis',app.caxis);
end
p = [];
for jj = 1:length(grain)
% grain size
if isfield(grain{jj},'stat')
if isfield(grain{jj}.stat,'size_int')
grain{jj}.radius = 0.5*app.pxsize{ii}*grain{jj}.stat.size_int; % mm
elseif isfield(grain{jj}.stat,'bbxsmean') && isfield(grain{jj}.stat,'bbysmean')
grain{jj}.radius = 0.5*app.pxsize{ii}*(grain{jj}.stat.bbxsmean + grain{jj}.stat.bbysmean)/2; % mm
end
else
grain{jj}.radius = 0.5*app.pxsize{ii}*10;
end
if (app.scale == false && ~isempty(app.size) && isnumeric(app.size) && app.size > 0) % it should be a multiplying factor
grain{jj}.radius = 0.5*app.pxsize{ii}*app.size;
end
if (app.pixels)
grain{jj}.radius = grain{jj}.radius/app.pxsize{ii};
grain{jj}.center = grain{jj}.center/app.pxsize{ii};
end
% translate
if (~app.translate)
grain{jj}.center = zeros(1,3);
end
% grain strain
if ~isfield(grain{jj},'strain')
grain{jj}.strain.strainT = NaN(3);
end
% highlight grain?
if ismember(jj,app.hlight{ii}), hl = true; else hl = false; end
% computes vertices and caxis coordinates
vertices{jj} = gtComputeGrainUnitCell(grain{jj}.R_vector,data{ii}.vertices,...
grain{jj}.radius,grain{jj}.center,grain{jj}.strain.strainT,app.strain{ii});
if (app.caxis)
caxis{jj} = gtComputeGrainUnitCell(grain{jj}.R_vector,data{ii}.c_axis,...
grain{jj}.radius,grain{jj}.center,grain{jj}.strain.strainT,app.strain{ii});
else
caxis{jj} = [];
end
% save the settings
grain{jj}.cmap = cmap(jj,:);
grain{jj}.caxis = caxis{jj};
grain{jj}.vertices = vertices{jj};
grain{jj}.strain.scale = app.strain{ii};
grain{jj}.linecolor = app.linecolor{ii};
grain{jj}.faces = data{ii}.faces;
grain{jj}.alpha = app.alpha{ii};
grain{jj}.patch = app.patch{ii};
grain{jj}.dataset = ii;
if ii > 1 && app.patch{ii-1} == false && app.patch{ii} == true
app.linecolor{ii} = [0 0 0];
end
% draw the patch
if (app.draw)
% draws patches for prismatic planes
p{end+1} = gtPlotGrainUnitCell(vertices{jj},caxis{jj},data{ii}.faces,...
cmap(jj,:),hl,app.linecolor{ii},app.alpha{ii},app.patch{ii});
set(p{end},'UserData',[ii ids(jj)]);
set(p{end},'Tag',sprintf('data_%02d_grain_%04d',ii,grain{jj}.id));
set(p{end},'ButtonDownFcn',@(src,evt)displayTag(src,evt,h.phaseselected_edit,h.grainselected_edit))
patchMenu(p{end}, grain{jj})
end
end % end for jj % loop over grains
% update cmap and data
app.cmap{ii} = cmap;
app.data{ii} = data{ii};
% save the settings
plotdata{ii} = grain;
hp{ii} = p;
% set the labels if empty
if isempty(app.label{ii})
app.label{ii} = app.type{ii};
end
end % end for ii % loop over datasets
% update plotted data
app.plotdata = plotdata;
% draw a section if true
if (app.section)
pos=min(mean(data{1}.vertices(data{1}.faces_end(1,:),1)),mean(data{1}.vertices(data{1}.faces_end(2,:),1)));
if ~isempty(app.secpos)
pos = app.secpos;
end
section_plane_vertices=[pos 1 1; pos 1 -1; pos -1 1; pos -1 -1];
if strcmpi(app.plane,'xz')
section_plane_vertices = section_plane_vertices(:,[2 1 3]);
end
if strcmpi(app.plane,'xy')
section_plane_vertices = section_plane_vertices(:,[2 3 1]);
end
app.h.p_section = gtPlotGrainUnitCell(section_plane_vertices,[],[1 2 4 3],...
[0 0 0],[],[0 0 0],0.4,true);
end
% paired grains
if (app.paired) && length(plotdata{1}) == length(plotdata{2})
N = ceil(length(plotdata{1})/3 ) +1;
for jj=1:length(plotdata{1})
hf = gtSubFigure(N,4,jj);
[~, hh] = gtPlotHexagon([plotdata{1}(jj); plotdata{2}(jj)],...
'ids',[1 2],'cmap',[plotdata{1}{jj}.cmap; plotdata{2}{jj}.cmap;],...
'reference',false,'overlap',true,'caxis',true,'alpha',0.4);
set(hh.hf,'Position',get(hf,'Position'));
delete(hf)
end
end
% draw the legend
if (app.legend)
h.obj = [];
for ii=1:numel(grains)
tmp = hp{ii};
h.obj(ii) = tmp{end}(1);
end
[h.axes_leg,~] = legend(h.obj,app.label,...
'Location',app.legendpos,'Box','off','EdgeColor',[1 1 1],'Parent',h.fig);
h.patch_leg = findobj(get(h.axes_leg,'Children'),'Type','patch');
% only for the legend patches
set(h.patch_leg,'FaceColor',[1 1 1]);
end
% general settings axes and figure
if (~app.scale || ~app.translate)
axis('auto')
end
if (app.pixels)
axis('equal')
axis('tight')
end
if ~isempty(app.zoom)
zoom('out');
zoom(app.zoom)
zoom('off')
end
view(app.view);
axis('equal')
set(app.ha,'Visible',app.showaxes);
set(h.fig,'Position',app.figpos)
set(h.fig,'Renderer','zbuffer')
set(h.fig,'ToolBar','none')
set(h.fig,'Color',app.figcolor);
if (~app.figure)
set(h.fig,'Visible','off');
end
% update handles
app.hf = h.fig;
app.hp = hp;
h.patch = hp;
h.axes = app.ha;
% save application data
setappdata(h.fig,'AppData',app);
setappdata(h.fig,'hp',hp)
set(h.sample_show, 'Callback', @(src,evt)toggleSample(src,evt,h_tmp.senv));
set(h.axis_show, 'Callback', @(src,evt)toggleAxes(src,evt,app.ha));
set(h.grain_select_ok,'Callback', @(src,evt)selectGrains(src,evt,h.fig,h.grain_select_edit));
set(h.patch_show, 'Callback', @(src,evt)togglePatches(src,evt,hp));
set(h.rotate_active, 'Callback', @(src,evt)toggleRotate3d(src,evt,app.rot3d));
set(h.zoom_in, 'Callback', @(src,evt)setZoom(src,evt,app.ha,1.1));
set(h.zoom_out, 'Callback', @(src,evt)setZoom(src,evt,app.ha,0.9));
set(h.patch_display, 'Callback', @(src,evt)toggleFaces(src,evt,hp));
toggleRotate3d(h.rotate_active,[],app.rot3d);
setZoom([],[],app.ha,0.7);
if (app.debug)
print_structure(app,'AppData')
end
% output arguments
if nargout > 0
f_handles = h;
if nargout > 1
p_handle = hp;
end
end
end % end of function
%%
% sub-functions
function h = sfCreateGUI(h)
% GUI
h.main_boxes = uiextras.HBox('Parent',h.fig,...
'Tag','h_main_boxes');
h.left_panel = uipanel('Parent',h.main_boxes,...
'Tag','h_left_panel');
h.leftbutton_boxes = uiextras.VButtonBox('Parent',h.left_panel,...
'Tag','h_leftbutton_boxes');
h.phaseselected_label = uicontrol('Parent',h.leftbutton_boxes,...
'String','Selected dataset','Style','text');
h.phaseselected_edit = uicontrol('Parent',h.leftbutton_boxes,...
'String','','Style','text','FontWeight','bold');
h.grainselected_label = uicontrol('Parent',h.leftbutton_boxes,...
'String','Selected grain','Style','text');
h.grainselected_edit = uicontrol('Parent',h.leftbutton_boxes,...
'String','','Style','text','FontWeight','bold');
uicontrol('Parent',h.leftbutton_boxes,'Visible','off','Enable','off');
h.rotate_active = uicontrol('Parent',h.leftbutton_boxes,...
'Style','pushbutton',...
'String','Rotate3d OFF');
h.zoom_in = uicontrol('Parent',h.leftbutton_boxes,...
'Style','pushbutton',...
'String','Zoom IN');
h.zoom_out = uicontrol('Parent',h.leftbutton_boxes,...
'Style','pushbutton',...
'String','Zoom OUT');
h.patch_display = uicontrol('Parent',h.leftbutton_boxes,...
'Style', 'pushbutton',...
'String','Hide faces');
h.patch_show = uicontrol('Parent',h.leftbutton_boxes,...
'Style','pushbutton',...
'String','Hide grains');
h.sample_show = uicontrol('Parent',h.leftbutton_boxes,...
'Style','pushbutton',...
'String','Hide sample',...
'Callback',@(src,evt)toggleSample(src,evt,h_tmp.senv));
h.axis_show = uicontrol('Parent',h.leftbutton_boxes,...
'Style','pushbutton',...
'String','Hide axes',...
'Callback',@(src,evt)toggleAxes(src,evt,app.ha));
uicontrol('Parent',h.leftbutton_boxes,'Enable','off','Visible','off');
h.grain_panel = uicontrol('Parent',h.leftbutton_boxes,'Style','text','String','Toggle grains');
h.grain_select_edit = uicontrol('Parent',h.leftbutton_boxes,'Style','edit','String','');
h.grain_select_ok = uicontrol('Parent',h.leftbutton_boxes,'Style','pushbutton','String','OK');
h.center_panel = uipanel('Parent',h.main_boxes,'BorderType','none',...
'Tag','h_center_panel');
h.center_boxes = uiextras.VBox('Parent',h.center_panel,...
'Tag','h_center_boxes');
h.axis_panel = uipanel('Parent',h.center_boxes,...
'Position',[0.25 0.1 0.85 0.9],...
'BackgroundColor',[1 1 1],...
'BorderType','line','BorderWidth',15,...
'Tag','h_axis_panel');
set(h.main_boxes,'Sizes',[140 -1]);
buttonSize = get(h.leftbutton_boxes,'ButtonSize');
buttonSize(1) = 130;
set(h.leftbutton_boxes,'ButtonSize',buttonSize);
buttonSize(1) = 50;
end
function patchMenu(hObj,app)
hcmenu = uicontextmenu();
uimenu('Parent',hcmenu,...
'Label','Toggle Trasparency',...
'Callback',@(src,evt)switchPatchRender(src,evt,hObj));
uimenu('Parent',hcmenu,...
'Label','Print Grain Info',...
'Callback',@(src,evt)displayInfoGrain(src,evt,app));
uimenu('Parent',hcmenu,...
'Label','Get Grain Info',...
'Callback',@(src,evt)getInfoGrain(src,evt,app));
set(hObj,'UIContextMenu',hcmenu)
end
function displayInfoGrain(~, ~, app, fields)
if ~exist('fields','var') || isempty(fields)
fields = {'phaseid','id','dataset','R_vector','center','radius',...
'cmap','caxis','vertices','linecolor',...
'faces','alpha','patch'};
end
ind = findValueIntoCell(fieldnames(app), fields);
cellStruct = struct2cell(app);
cellStruct = cellStruct(ind(:,1));
grain = cell2struct(cellStruct, fields, 1);
print_structure(grain,'Grain info')
end
function getInfoGrain(~, ~, app)
gID = app.id;
pID = app.phaseid;
disp(['Saved ''' ['grain' num2str(gID)] ''' variable for grain ' num2str(gID) ' phase ' num2str(pID) ' to the base workspace'])
assignin('base',['grain' num2str(gID)],app)
end
function toggleFaces(hObj, ~, hp, h_edit)
if ~exist('h_edit','var')
h_edit = [];
end
[tags, ids] = getTagsIndexes(hObj,[],hp,h_edit);
tags = vertcat(tags{:});
for ii=1:length(tags)
tag_patch = tags{ii};
current_id = getIDfromTagName(tag_patch);
current_ids = ids(ids(:,1)==current_id(1),:);
[hasP, ind] = ismember(current_id(2), current_ids(:,2));
if (hasP)
switchPatchRender([],[],hp{current_id(1)}{ind});
end
end
end
function switchPatchRender(~,~,hObj)
facecolor = get(hObj, 'FaceColor');
edgecolor = get(hObj, 'EdgeColor');
if ~isempty(findStringIntoCell(facecolor,{'none'},1)) || ~isempty(findValueIntoCell(facecolor,{[0 0 0]}))
for ii=1:length(hObj)
changePatchProperty(hObj(ii), 'FaceColor', edgecolor{ii})
changePatchProperty(hObj(ii), 'EdgeColor', [0 0 0])
changePatchProperty(hObj(ii), 'LineWidth', 1)
end
elseif ~isempty(findStringIntoCell(edgecolor,{'none'},1)) || ~isempty(findValueIntoCell(edgecolor,{[0 0 0]}))
for ii=1:length(hObj)
changePatchProperty(hObj(ii), 'EdgeColor', facecolor{ii})
changePatchProperty(hObj(ii), 'FaceColor', 'none')
changePatchProperty(hObj(ii), 'LineWidth', 2)
end
end
end
function changePatchProperty(h, property, value)
if ~exist('property','var') || isempty(property)
property = 'FaceColor';
end
if ~isprop(h, property)
disp('Wrong property for the surrent handle')
return
end
try
set(h,property,value);
catch Mexc
end
end
function toggleRotate3d(hObj, ~, h)
toggleStatus(h, 'Enable');
toggleLabel(hObj,{'OFF','ON'})
end
function setZoom(~, ~, h, value)
axes(h)
zoom(value)
end
function selectGrains(hObj, ~, hf, h_edit)
hp = getappdata(hf,'hp');
set(hObj,'Callback',@(src,evt)togglePatches(src,evt,hp,h_edit));
end
function closeFigure(~,~,h)
if ishandle(h)
delete(h)
end
end
function [tags, ids] = getTagsIndexes(hObj,~,hp,h_edit)
tags = cell(1,0);
ids = [];
for ii=1:length(hp)
p = cellfun(@(x) unique(get(x,'Tag')), hp{ii}, 'UniformOutput', false);
p = vertcat(p{:});
tags{ii} = p;
tmp = cellfun(@(x) getIDfromTagName(x), tags{ii}, 'UniformOutput', false);
ids = [ids; cell2mat(tmp)];
end
if exist('h_edit','var') && ~isempty(h_edit)
ids = str2num(get(h_edit,'String'));
else
% tmp = cellfun(@(x) getIDfromTagName(x), tags, 'UniformOutput', false);
% ids = mat2cell(cell2mat(tmp),size(tags,1),[2 2]);
toggleLabel(hObj,[]);
end
end
function togglePatches(hObj,~,hp,h_edit)
if ~exist('h_edit','var')
h_edit = [];
end
[tags, ids] = getTagsIndexes(hObj,[],hp,h_edit);
tags = vertcat(tags{:});
all_ids = cell2mat(cellfun(@(x) getIDfromTagName(x), tags, 'UniformOutput', false));
if size(ids,2) == 2 && size(ids,1) > 0
ids = ids(:,2);
end
for ii=1:length(ids)
[hasP, ind] = ismember(ids(ii), reshape(all_ids(:,2), 1, []));
if (hasP)
current_id = all_ids(ind, :);
current_ids = all_ids(all_ids(:,1) == current_id(1), :);
if current_id(1) > 1
[~, ind] = ismember(ids(ii), reshape(current_ids(:,2), 1, []));
end
toggleStatus(hp{current_id(1)}{ind});
end
end
end
function toggleSample(hObj,~,h_sample)
toggleStatus(h_sample);
toggleLabel(hObj,[]);
end
function toggleLabel(hObj,labels)
if ~exist('labels','var') || isempty(labels)
labels = {'Hide','Show'};
end
try
curr_label = get(hObj,'String');
curr_prop = 'String';
catch Mexc
curr_label = get(hObj,'Label');
curr_prop = 'Label';
end
if ~isempty(strfind(curr_label,labels{1}))
set(hObj,curr_prop,strrep(curr_label,labels{1},labels{2}))
elseif ~isempty(strfind(curr_label,labels{2}))
set(hObj,curr_prop,strrep(curr_label,labels{2},labels{1}))
end
end
function toggleAxes(hObj,~,h_ax)
toggleStatus(h_ax);
toggleLabel(hObj,[]);
end
function toggleStatus(h, property)
if ~exist('property','var') || isempty(property)
property = 'Visible';
end
if ~isprop(h, property)
disp('Wrong property for the surrent handle')
return
end
curr_status = get(h,property);
if strcmpi(curr_status, 'on')
set(h,property,'off');
elseif strcmpi(curr_status, 'off')
set(h,property,'on');
end
end
function ids = getIDfromTagName(tagname)
ids = [];
if ~isempty(tagname)
ids = str2double( regexp(tagname, '\d*', 'match') );
end
end
function displayTag(hObj, ~, h1, h2)
current_tag = get(hObj, 'Tag');
id = getIDfromTagName(current_tag);
set(h1,'String',num2str(id(1)));
set(h2,'String',num2str(id(2)));
end