From 90616d96e97fc00763cf3d1772c85e899802be1d Mon Sep 17 00:00:00 2001 From: Laura Nervo <lnervo@esrf.fr> Date: Mon, 3 Dec 2012 10:34:24 +0000 Subject: [PATCH] gtDrawGrainUnitCells : improved the function, moved to zUtil_Drawing, separated sample envelope drawing part into the function gtSampleGeometry Signed-off-by: Laura Nervo <laura.nervo@esrf.fr> git-svn-id: https://svn.code.sf.net/p/dct/code/trunk@939 4c865b51-4357-4376-afb4-474e03ccb993 --- zUtil_Drawing/gtDrawGrainUnitCells.m | 201 ++++++++++++++++++++ zUtil_Drawing/gtSampleGeometry.m | 71 ++++++++ zUtil_Indexter/gtINDEXDrawGrainCubes.m | 243 ------------------------- zUtil_Strain/gtDrawGrainCubes.m | 116 ------------ 4 files changed, 272 insertions(+), 359 deletions(-) create mode 100644 zUtil_Drawing/gtDrawGrainUnitCells.m create mode 100644 zUtil_Drawing/gtSampleGeometry.m delete mode 100644 zUtil_Indexter/gtINDEXDrawGrainCubes.m delete mode 100644 zUtil_Strain/gtDrawGrainCubes.m diff --git a/zUtil_Drawing/gtDrawGrainUnitCells.m b/zUtil_Drawing/gtDrawGrainUnitCells.m new file mode 100644 index 00000000..248deafb --- /dev/null +++ b/zUtil_Drawing/gtDrawGrainUnitCells.m @@ -0,0 +1,201 @@ +function f_handle = gtDrawGrainUnitCells(grains, varargin) +% f_handle = gtDrawGrainUnitCells(grains, varargin) +% ------------------------------------------------- +% +% Draws a figure representing grain size, location and orientation by +% cubes or hexagonal prismes based on the their Rodrigues vectors. +% +% INPUT: +% grains = grain structure from indexing <cell> +% +% OPTIONAL INPUT (varargin): +% hf = figure handle {[]} +% cmap = can be a component of the strain tensor <double> +% {grain.strain.strainT(3,3)} or a colour map (length must be as grains) +% latticepar = lattice parameters <double 1x6> +% labgeo = laboratory geometry as in parameters <struct> +% phaseid = phase number <int> {1} +% type = cell type <string> {'cubic'} / 'hexagonal' +% hlight = grain id-s to be highlighted {[]} +% scale = number to scale grain size; empty to use bbox info in grain <double> {[]} +% view = 3D angular view <double 1x2> {[-37.5 30]} +% showbar = flag to show the colourbar <logical> {false} +% +% OUTPUT: +% f_handle = handle to the figure +% +% Version 004 15-11-2012 by LNervo +% Use gtSampleGeometry(labgeo) to create the graphics with axis and reference +% system +% +% Version 003 23-10-2012 by LNervo +% Add varargin + + +% modify parameters/global settings +app.hf = []; +app.cmap = []; +app.latticepar = []; +app.labgeo = []; +app.phaseid = 1; +app.type = []; +app.hlight = []; +app.scale = []; +app.view = [-37.5 30]; +app.showbar = false; + +app = parse_pv_pairs(app, varargin); + + +if ~exist('grains','var') || isempty(grains) + disp('Loading grain data from 4_grains/phase_##/index.mat ...') + tmp = load(fullfile('4_grains',sprintf('phase_%02d',app.phaseid),'index.mat'),'grain'); + grains = tmp.grain; +end +if isempty(app.cmap) + if isfield(grains{1},'strain') + app.cmap = gtIndexAllGrainValues(grains,'strain','strainT',3,3); + disp('Using strainT(3,3) as colorscale') + app.showbar = true; + else + app.cmap = zeros(length(grains),1); + app.showbar = false; + end +else + app.showbar = false; + if size(app.cmap,1)==1 + app.cmap = repmat(app.cmap,length(grains),1); + end +end +if isempty(app.latticepar) + load('parameters.mat'); + app.latticepar = parameters.cryst(app.phaseid).latticepar; +end +if isempty(app.labgeo) + load('parameters.mat'); + app.labgeo = parameters.labgeo; +end +if isempty(app.type) + load('parameters.mat'); + app.type = parameters.cryst(app.phaseid).crystal_system; + if ~strcmpi(app.type,'cubic') && ~strcmpi(app.type, 'hexagonal') + app.type = 'cubic'; + disp('Default shape for unit cells: cubic') + end +end + +if isempty(app.hf) + hf = gtSampleGeometry(app.labgeo.samenvrad, app.labgeo.samenvbot, app.labgeo.samenvtop); +else + hf = app.hf; +end + +hold on +view(app.view); + +nof_grains = length(grains); + +pixelsize = (app.labgeo.pixelsizeu + app.labgeo.pixelsizev)/2; + +if app.showbar + colormap(jet(1000)) + cb = colorbar('FontSize', 12, 'ytick', -0.01:0.005:0.01); + cbpos = [0.01 0.2 0.025 0.6]; + set(cb,'Position',cbpos); +end + +% vertices and faces calculation +vertices = []; +faces_sides = []; +faces_end = []; +hkl = []; +if strcmpi(app.type, 'cubic') + vertices = [0 0 0; 1 0 0; 1 1 0; 0 1 0; 0 0 1; 1 0 1; 1 1 1; 0 1 1] - ... + 0.5*repmat([1 1 1],8,1); + % Cube face directions + % +yz -yz -xz +xz +xy -xy + % +x -x -y +y +z -z + faces_sides = [2 3 7 6; 4 1 5 8; 1 2 6 5; 3 4 8 7]; + faces_end = [5 6 7 8; 1 2 3 4]; + hkl = [1 0 0; -1 0 0; 0 -1 0; 0 1 0; ... + 0 0 1; 0 0 -1]'; +elseif strcmpi(app.type, 'hexagonal') + a = app.latticepar(1); + c = app.latticepar(3); + maxx = max(a,c); + % normalize the biggest axis to 1 + vertices = gtHexagonalUnitCell(a/maxx,c/maxx,false); + % hexagon face directions + % +yz -yz -xz +xz +xy -xy + % +x -x -y +y +z -z + faces_sides = [1 2 8 7; 4 5 11 10; 5 6 12 11; 2 3 9 8; 3 4 10 9; 6 1 7 12]; + faces_end = [1 2 3 4 5 6; 7 8 9 10 11 12]; + hkl = [1 0 -1 0; -1 0 -1 0; 0 -1 1 0; 0 1 -1 0; -1 1 0 0; 1 -1 0 0; ... + 0 0 0 1; 0 0 0 -1]'; % column vectors +end + +[Bmat, Amat] = gtCrystHKL2CartesianMatrix(app.latticepar); +pl_cryst = gtCrystHKL2Cartesian(hkl, Bmat); % hkl plane normals in cryst reference + + +% draw unit cells +for ii = 1:nof_grains + + if ismember(ii, app.hlight) + hl = true; + else + hl = false; + end + + sfPlotGrainCell(grains{ii}, vertices, faces_sides, hl, pixelsize, app.cmap(ii,:), app.scale); + sfPlotGrainCell(grains{ii}, vertices, faces_end, hl, pixelsize, app.cmap(ii,:), app.scale); + +end + +f_handle = hf; + +end % of main function + + +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUB-FUNCTIONS +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function sfPlotGrainCell(grain, vertices, faces, hl, pixelsize, cmap, scale) + +if isempty(scale) + ga = 0.5*pixelsize*(grain.stat.bbxsmean + grain.stat.bbysmean)/2; +else + ga = 0.5*pixelsize*scale; +end + +if isfield(grain,'centre') + gc = grain.centre; +else + gc = grain.center; +end + +if isfield(grain, 'g') + g_grain = grain.g; +else + g_grain = Rod2g(grain.R_vector); +end +grain_vertices = ga*vertices*g_grain'; +grain_vertices_st = repmat(gc,size(vertices,1),1) + (eye(3)*grain_vertices')'; + +if isfield(grain,'strain') && ~any(isnan(grain.strain.strainT(:))) + grain_vertices_st = grain_vertices_st + (grain.strain.strainT*50*grain_vertices')'; +end + +facecolor = repmat(cmap,size(faces,1),1); + +if hl + patch('Vertices', grain_vertices_st, 'Faces', faces , ... + 'FaceVertexCData', facecolor, 'FaceColor', 'flat','EdgeColor','r','LineWidth',2); +else + patch('Vertices', grain_vertices_st, 'Faces', faces , ... + 'FaceVertexCData', facecolor, 'FaceColor', 'flat'); +end + +end % of sfPlotGrainCell diff --git a/zUtil_Drawing/gtSampleGeometry.m b/zUtil_Drawing/gtSampleGeometry.m new file mode 100644 index 00000000..7cb8657f --- /dev/null +++ b/zUtil_Drawing/gtSampleGeometry.m @@ -0,0 +1,71 @@ +function hf = gtSampleGeometry(rad, bot, top) +% GTSAMPLEGEOMETRY Draws sample envelope and axis +% hf = gtSampleGeometry(rad, bot, top) +% ------------------------------------ +% +% INPUT: +% rad = parameters.labgeo.samenvrad +% bot = parameters.labgeo.samenvbot +% top = parameters.labgeo.samenvtop +% +% OUTPUT: +% hf = figure handle +% +% Version 001 15-11-2012 by LNervo + + +hf = figure(); + +color = [0.501960813999176 0.501960813999176 0.501960813999176]; + +% Create axes +set(gca,'ZMinorTick','on') + set(gca,'ZColor',color); + set(gca,'YMinorTick','on'); + set(gca,'YColor',color); + set(gca,'XMinorTick','on'); + set(gca,'XColor',color); + set(gca,'FontSize',12); + set(gca,'Visible','on'); + set(gca,'Box','on'); + set(gca,'XGrid','on'); + set(gca,'YGrid','on'); + set(gca,'ZGrid','on'); +% Create xlabel +xlabel('X','Color',color); +% Create ylabel +ylabel('Y','Color',color); +% Create zlabel +zlabel('Z','Color',color); + +h = rotate3d(hf); +set(h,'RotateStyle','box','Enable','on'); +view([-37.5 30]); +hold on + +t = 1:360; +circx = cosd(t)*rad; +circy = sind(t)*rad; +circbotz(1:360) = bot; +circtopz(1:360) = top; + +% sample envelope : circles +plot3(circx, circy, circbotz, 'k-', 'Linewidth', 1); +plot3(circx, circy, circtopz, 'k-', 'Linewidth', 1); +% sample envelope : axis +plot3([0 0],[0 0],[bot top],'k.-.','Linewidth',1); +% sample envelope : lateral bars +plot3([-rad -rad],[0 0],[bot top],'k.-','Linewidth',1); +plot3([0 0],[-rad -rad],[bot top],'k.-','Linewidth',1); +plot3([rad rad],[0 0],[bot top],'k.-','Linewidth',1); +plot3([0 0],[rad rad],[bot top],'k.-','Linewidth',1); + +% lab system : x y z vectors +quiver3(-1,0,0,2,0,0,'r'); +quiver3(0,-1,0,0,2,0,'k'); +quiver3(0,0,-1,0,0,2,'b'); +% axis limits +axis([-rad-1, rad+1, -rad-1, rad+1, bot-1, top+1]) +axis vis3d + +end diff --git a/zUtil_Indexter/gtINDEXDrawGrainCubes.m b/zUtil_Indexter/gtINDEXDrawGrainCubes.m deleted file mode 100644 index b8f31da8..00000000 --- a/zUtil_Indexter/gtINDEXDrawGrainCubes.m +++ /dev/null @@ -1,243 +0,0 @@ -function gtINDEXDrawGrainCubes(grains, parameters, numbered, hlight, strainscale) -% function gtINDEXDrawGrainCubes(grains, parameters, numbered, hlight, strainscale) -% -% Draws a figure representing grain size, location and orientation by cubes -% (unit cells in a cubic lattice) based on the their Rodrigues vectors. -% -% USAGE gtDrawGrainCubes -% gtDrawGrainCubes(grains(1:10),[],[],1) -% -% OPTIONAL INPUT -% grains: grain structure from indexing -% parameters: as in parameters file (required field: labgeo) -% numbered: if true, the grain id-s for each grain are shown -% hlight: grain id-s to be highlighted -% strainscale: a multiplicative factor for the strain components -% for better visibility (if strain data available) - - -if ~exist('grains','var') || isempty(grains) - disp('Loading grain data from 4_grains/phase_01/index.mat ...') - tmp = load('4_grains/phase_01/index.mat','grain'); - grains = tmp.grain; -end - -if ~exist('parameters','var') || isempty(parameters) - disp('Loading parameters from parameters.mat ...') - load('parameters.mat'); -end - -if ~exist('hlight','var') || isempty(hlight) - hlight = []; -end - -if ~exist('numbered','var') || isempty(numbered) - numbered = []; -end - -if ~exist('strainscale','var') || isempty(strainscale) - strainscale = 50; -end - -sample.rad = parameters.labgeo.samenvrad; -sample.bot = parameters.labgeo.samenvbot; -sample.top = parameters.labgeo.samenvtop; - -nof_grains = length(grains); - -fs = 20; -samradmargin = 0.05; -samtopmargin = 0.2; -pixelsize = (parameters.labgeo.pixelsizeu + parameters.labgeo.pixelsizev)/2; - - -figure - -colormap(jet(1000)) -caxis([-0.01 0.01]) -cb = colorbar('FontSize', fs, 'ytick', -0.01:0.005:0.01); -%cb=colorbar('ytick',-0.01:0.002:0.01); -%cbpos=get(cb,'Position'); -cbpos = [0.01 0.2 0.025 0.6]; -set(cb,'Position',cbpos); - -set(gca,'xtick',[],'xcolor','w') -set(gca,'ytick',[],'ycolor','w') -set(gca,'ztick',[],'zcolor','w') - -%set(gca,'Position',[0.1 0 0.9 1]) -set(gca,'Position',[0 0 1 1]) - -view(3); - -axhor = 0.01*floor((sample.rad*(1+samradmargin))/0.01); -axver = 0.01*floor((max(abs(sample.top),abs(sample.bot))*(1+samtopmargin))/0.01); -%axis([-axhor axhor -axhor axhor -axver axver]) - -%xlabel('X') -%ylabel('Y') -%zlabel('Z') -hold on - -get(gca,'CameraPosition'); -get(gca,'CameraViewAngle'); - -t = 1:360; -circx = cosd(t)*sample.rad; -circy = sind(t)*sample.rad; -circbotz(1:360) = sample.bot; -circtopz(1:360) = sample.top; - -plot3(circx, circy, circbotz, 'k-', 'Linewidth', 1) -plot3(circx, circy, circtopz, 'k-', 'Linewidth', 1) - -plot3([0 0],[0 0],[sample.bot sample.top],'k.-.','Linewidth',1) - -plot3([-sample.rad -sample.rad],[0 0],[sample.bot sample.top],'k.-','Linewidth',1) -plot3([0 0],[-sample.rad -sample.rad],[sample.bot sample.top],'k.-','Linewidth',1) -plot3([sample.rad sample.rad],[0 0],[sample.bot sample.top],'k.-','Linewidth',1) -plot3([0 0],[sample.rad sample.rad],[sample.bot sample.top],'k.-','Linewidth',1) - - -% beam direction: -%plot3([-170 170],[0 0],[0 0],'r-','Linewidth',2) - - -cube_vertices = [0 0 0; 1 0 0; 1 1 0; 0 1 0; 0 0 1; 1 0 1; 1 1 1; 0 1 1] - ... - 0.5*repmat([1 1 1],8,1); - -% Cubed face directions -% +yz -yz -xz +xz +xy -xy -% +x -x -y +y +z -z -cube_faces = [2 3 7 6; 4 1 5 8; 1 2 6 5; 3 4 8 7; 5 6 7 8; 1 2 3 4]; - -cube_planes = [1 0 0; -1 0 0; 0 -1 0; 0 1 0; 0 0 1; 0 0 -1]; - -for ii = 1:nof_grains - - if ismember(ii,hlight) - hl = true; - else - hl = false; - end - - sfPlotGrainCube(grains{ii}, cube_vertices, cube_faces, ... - hl, strainscale, pixelsize) - - if numbered - text(grains{ii}.center(1), -axhor,grains{ii}.center(3),num2str(ii), 'Color','c') - text(axhor, grains{ii}.center(2), grains{ii}.center(3), num2str(ii), 'Color','c') - end -end - - -axis equal -axis([-axhor axhor -axhor axhor -axver axver]) - - -end % of main function - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% SUB-FUNCTIONS -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -function sfPlotGrainCube(grain, cube_vertices, cube_faces, ... - hl, strainscale, pixelsize) - -ga = 0.5*pixelsize*(grain.stat.bbxsmean + grain.stat.bbysmean)/2; - -if isfield(grain,'centre') - gc = grain.centre; -else - gc = grain.center; -end - - -% if hl -% facecolors=cool(8); -% facecolors=facecolors(end-5:end,:); -% else -% facecolors=copper(8); -% facecolors=facecolors(end-5:end,:); -% end - - -% ELONGATION ALONG LOADING AXIS -% -ggrain = Rod2g(grain.R_vector); -grain_vertices = ga*cube_vertices*ggrain'; - -if isfield(grain,'strain') && ~any(isnan(grain.strain.strainT(:))) - grain_vertices_st = repmat(gc,8,1) + ... - ((eye(3) + grain.strain.strainT*strainscale)*grain_vertices')'; - facecolor = grain.strain.strainT(3,3); -else - grain_vertices_st = repmat(gc,8,1) + (eye(3)*grain_vertices')'; - facecolor = 0; -end - - -facecolor = repmat(facecolor,6,1); -%facecolor=facecolor.*repmat([1.0; 0.98; 0.96; 0.94; 0.92; 0.90],1,3); - - - -% SHEAR STRAIN ON THE CUBE CELL FACES - -% T=Rod2g(grain.R_vector); % T rotation tensor in lab system (align the axes with the crystal axes); -% -% ECRYST=T'*grain.strain.ST*T; % strain tensor in the crystal reference -% -% facecolor=zeros(6,1); -% facecolor([1,2])=sqrt(ECRYST(1,2)^2+ECRYST(1,3)^2); % xy & xz -% facecolor([3,4])=sqrt(ECRYST(2,1)^2+ECRYST(2,3)^2); % yx & yz -% facecolor([5,6])=sqrt(ECRYST(3,1)^2+ECRYST(3,2)^2); % zx & zy -% -% ST=grain.strain.ST; -% -% grain_vertices=ga*cube_vertices*T'; -% grain_vertices_st=repmat(gc,8,1)+((eye(3)+ST*strainscale)*grain_vertices')'; -% - - -if hl - patch('Vertices', grain_vertices_st, 'Faces', cube_faces , ... - 'FaceVertexCData', facecolor, 'FaceColor', 'flat','EdgeColor','r','LineWidth',2); -else - patch('Vertices', grain_vertices_st, 'Faces', cube_faces , ... - 'FaceVertexCData', facecolor, 'FaceColor', 'flat'); -end - -% x0=[1 0 0]; -% y0=[0 1 0]; -% z0=[0 0 1]; -% -% xcr=T*x0'; -% ycr=T*y0'; -% zcr=T*z0'; -% -% -% quiver3(gc(1),gc(2),gc(3),xcr(1),xcr(2),xcr(3),200,'r') -% quiver3(gc(1),gc(2),gc(3),ycr(1),ycr(2),ycr(3),200,'b') -% quiver3(gc(1),gc(2),gc(3),zcr(1),zcr(2),zcr(3),200,'k') -% -% grain_vertices=ga*cube_vertices*T'; -% grain_vertices_st=repmat(gc,8,1)+((eye(3))*grain_vertices')'; -% -% patch('Vertices',grain_vertices_st,'Faces',cube_faces,... -% 'FaceVertexCData',facecolor,'FaceColor','flat'); -% -% -% edgevec=[0 1 0]; -% facepos=[1 0 0]; -% edgevec_g=edgevec*T'; -% facepos_g=gc+ga/2*facepos*T'; -% quiver3(facepos_g(1),facepos_g(2),facepos(3),edgevec_g(1),edgevec_g(2),edgevec_g(3),100,'c'); - - - -end % of sfPlotGrainCube - - diff --git a/zUtil_Strain/gtDrawGrainCubes.m b/zUtil_Strain/gtDrawGrainCubes.m deleted file mode 100644 index 3d7458cc..00000000 --- a/zUtil_Strain/gtDrawGrainCubes.m +++ /dev/null @@ -1,116 +0,0 @@ -% function gtDrawGrainCubes(grains,sample,numbered,hlight,noaxis) -% -% Draws a figure representing grain size and orientation by cubes -% (unit cells in a cubic lattice) based on the Rodrigues vectors in the -% grain variable output from indexing. -% -% USAGE gtDrawGrainCubes(grains,sample) -% gtDrawGrainCubes(grains([1:10]),sample,1,[1,3,5],1) -% -% INPUT grains: grain structure from indexing -% sample: parameters and definition of the sample reference system -% (sample=gtSampleGeoInSampleSystem) -% numbered: if true, grain id-s are shown -% hlight: ID-s of grains to be highlighted -% noaxis: if true, axes and beam direction are not shown -% - - -function gtDrawGrainCubes(grains,sample,numbered,hlight,noaxis) - - -if ~exist('hlight','var') - hlight=[]; -end -if ~exist('numbered','var') - numbered=[]; -end -if ~exist('noaxis','var') - noaxis=false; -end - -nof_grains=length(grains); - -figure -view(3); - -axhor=10*floor((sample.rad+30)/10); -axver=10*floor((max(sample.top,sample.bot)+50)/10); - -axis([-axhor axhor -axhor axhor -axver axver]) -axis equal; -hold on - -if ~noaxis - xlabel('X') - ylabel('Y') - zlabel('Z') - plot3([-axhor axhor],[0 0],[0 0],'r-','Linewidth',2) -end - -if noaxis - set(gca,'xtick',[],'xcolor','w') - set(gca,'ytick',[],'ycolor','w') - set(gca,'ztick',[],'zcolor','w') -end - -t=1:360; -circx=cosd(t)*sample.rad; -circy=sind(t)*sample.rad; -circbotz(1:360)=sample.bot; -circtopz(1:360)=sample.top; -plot3(circx,circy,circbotz,'k-','Linewidth',1) -plot3(circx,circy,circtopz,'k-','Linewidth',1) -plot3([0 0],[0 0],[sample.bot sample.top],'k.-.','Linewidth',1) -plot3([-sample.rad -sample.rad],[0 0],[sample.bot sample.top],'k.-','Linewidth',1) -plot3([0 0],[-sample.rad -sample.rad],[sample.bot sample.top],'k.-','Linewidth',1) -plot3([sample.rad sample.rad],[0 0],[sample.bot sample.top],'k.-','Linewidth',1) -plot3([0 0],[sample.rad sample.rad],[sample.bot sample.top],'k.-','Linewidth',1) - - -cube_vertices=[0 0 0; 1 0 0; 1 1 0; 0 1 0; 0 0 1; 1 0 1; 1 1 1; 0 1 1]-0.5*repmat([1 1 1],8,1); -cube_faces=[2 3 7 6; 4 1 5 8; 1 2 6 5; 3 4 8 7; 5 6 7 8; 1 2 3 4]; -% directions: +yz -yz -xz +xz +xy -xy - - -for i=1:nof_grains - if ismember(i,hlight) - hl=1; - else - hl=false; - end - sfPlotGrainCube(grains{i},cube_vertices,cube_faces,hl) - if numbered - text(grains{i}.center(1),-axhor,grains{i}.center(3),num2str(i),'Color','c') - text(axhor,grains{i}.center(2),grains{i}.center(3),num2str(i),'Color','c') - end -end - -end - - - -function sfPlotGrainCube(grain,cube_vertices,cube_faces,hl) - -ga=grain.stat.bbysmean/2; -gc=grain.center; - -ggrain=Rod2g(grain.R_vector); -grain_vertices=repmat(gc,8,1)+ga*cube_vertices*ggrain'; - -if hl - facecolors=cool(8); - facecolors=facecolors(end-5:end,:); -else - facecolors=copper(12); - facecolors=facecolors(end-1-5:end-1,:); - %facecolors=gray(12); - %facecolors=facecolors(end-1-5:end-1,:); -end - -patch('Vertices',grain_vertices,'Faces',cube_faces,... - 'FaceVertexCData',facecolors,'FaceColor','flat'); - -end - - -- GitLab