diff --git a/README.md b/README.md
index 1848be5..0d03c90 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,9 @@ The typical workflow to generate a figure with gramm is the following:
- In the next steps, add graphical layers to your figure: raw data layers (directly plot data as points, lines...) or statistical layers (plot fits, histograms, densities, summaries with confidence intervals...). One instruction is enough to add each layer, and all layers offer many customization options.
- In the last step, gramm draws the figure, and takes care of all the annoying parts: no need to loop over colors or subplots, colors and legends are generated automatically, axes limits are taken care of, etc.
-For example, with gramm, 6 lines of code are enough to create the figure below from the carbig
dataset. Here the figure represents the evolution of fuel economy of new cars in time, with number of cylinders indicated by color, and regions of origin separated across subplot columns:
-
+For example, with gramm, 7 lines of code are enough to create the figure below from the carbig
dataset. Here the figure represents the evolution of fuel economy of new cars in time, with number of cylinders indicated by color, and regions of origin separated across subplot columns:
+
+
```matlab
load carbig.mat %Load example dataset about cars
origin_region=num2cell(org,2); %Convert origin data to a cellstr
@@ -29,6 +30,8 @@ g.geom_point()
g.stat_glm()
% Set appropriate names for legends
g.set_names('column','Origin','x','Year of production','y','Fuel economy (MPG)','color','# Cylinders')
+%Set figure title
+g.set_title('Fuel economy of new cars between 1970 and 1982')
% Do the actual drawing
g.draw()
```
@@ -44,7 +47,7 @@ Type doc gramm
to find links to the documentation of each method.
## Features
-- Accepts x and y data as arrays, matrices or cells of arrays
+- Accepts X Y and Z data as arrays, matrices or cells of arrays
- Accepts grouping data as arrays or cellstr.
@@ -70,6 +73,7 @@ Type doc gramm
to find links to the documentation of each method.
- Custom fits with user-provided anonymous function (stat_fit()
)
- Ellipses of confidence (stat_ellipse()
)
+- When Z data is provided in the call to gramm()
, geom_point()
and geom_line()
generate 3D plots
- Subplots are created without too much empty space in between (and resize properly !)
- Polar coordinates (set_polar()
)
- Color data can also be displayed as a continous variable, not as a grouping factor (set_continuous_color()
)
@@ -77,7 +81,7 @@ Type doc gramm
to find links to the documentation of each method.
- Possibility to change ordering of grouping variables between native, sorted, or custom (set_order_options
)
- Confidence intervals as shaded areas, error bars or thin lines
- Results of computations from stat_
plots are returned in the member structure results
-- Global title (set_title()
)
+- Figure title (set_title()
)
- Multiple gramm plots can be combined in the same figure by creating a matrix of gramm objects and calling the draw()
method on the whole matrix. An overarching title can be added by calling set_title()
on the whole matrix.
- Matlabs axes properties are acessible through the method axe_property()
- Custom legend labels with set_names()
@@ -87,41 +91,43 @@ Type doc gramm
to find links to the documentation of each method.
## Examples
+The code for the following figures and numerous others is in examples.m
.
### Custom fits ###
stat_fit()
-
+
### GLM fits (carbig data) ###
stat_glm()
+Note that the fit is made across color groups. this is done by superimposing gramm plots.
-
+
### Multiple gramm objects in a single figure
Also shows histograms, categorical x values
-
+
### Histograms ###
stat_bin()
with different 'geom'
options: 'bar'
, 'stacked_bar'
,'point'
,'line'
, 'overlaid_bar'
,'stairs'
-
+
### Colormap customization ###
set_color_options()
with 'map'
set as 'lch'
with various customization on the first row. 'map'
set to matlab
,
brewer1
, and brewer2
on the second line
-
+
### 2D density visualizations ###
stat_ellipse()
and stat_bin2d()
with 'geom'
set to 'contour'
,'point'
,'image'
-
+
### Continuous colors
-
+
## Acknowledgements
gramm was inspired and/or used code from:
diff --git a/examples.m b/examples.m
index e7eed04..114a015 100644
--- a/examples.m
+++ b/examples.m
@@ -25,13 +25,13 @@
%% Example use
figure
-g1=gramm('x',x,'y',y,'color',fouraltc,'linestyle',twoaltcb)
-g1.facet_grid(twoaltc,twoaltcb,'scale','fixed')
-g1.geom_point()
-g1.stat_smooth('lambda',1000,'geom','area')
+g1=gramm('x',x,'y',y,'color',fouraltc,'linestyle',twoaltcb);
+g1.facet_grid(twoaltc,twoaltcb,'scale','fixed');
+g1.geom_point();
+g1.stat_smooth('lambda',1000,'geom','area');
%It's possible to set native axis properties
-g1.axe_property('XGrid','on','YGrid','on')
-g1.draw()
+g1.axe_property('XGrid','on','YGrid','on');
+g1.draw();
@@ -43,28 +43,28 @@
clear g2
-g2(1,1)=gramm('x',x,'y',y,'color',fouraltc)
-g2(1,1).facet_grid(twoaltc,twoaltcb) %,'scales','independent'
-g2(1,1).stat_smooth('lambda',1000,'geom','area')
-g2(1,1).geom_point()
+g2(1,1)=gramm('x',x,'y',y,'color',fouraltc);
+g2(1,1).facet_grid(twoaltc,twoaltcb); %,'scales','independent'
+g2(1,1).stat_smooth('lambda',1000,'geom','area');
+g2(1,1).geom_point();
%Also works with categorical data
-g2(1,2)=gramm('x',y,'y',x,'color',categorical(twoaltc))
-g2(1,2).geom_point()
+g2(1,2)=gramm('x',y,'y',x,'color',categorical(twoaltc));
+g2(1,2).geom_point();
% X data can be a cellstr, data will be treated as being categorical
-g2(2,1)=gramm('x',fouraltc,'y',y,'color',twoaltcb,'size',4)
-g2(2,1).facet_grid(twoaltc,[],'scale','fixed')
-g2(2,1).geom_jitter('width',0.2,'height',0) %We can jitter the points in the scatter plot to make the density more apparent
+g2(2,1)=gramm('x',fouraltc,'y',y,'color',twoaltcb,'size',4);
+g2(2,1).facet_grid(twoaltc,[],'scale','fixed');
+g2(2,1).geom_jitter('width',0.2,'height',0); %We can jitter the points in the scatter plot to make the density more apparent
-g2(2,2)=gramm('x',y,'color',twoaltc)
-g2(2,2).no_legend() %It's possible to drop the side legends (useful when the grouping is the same across multiple objects)
-g2(2,2).stat_bin('geom','bar') %Using stat_bin we can create histograms
-g2(2,2).set_limit_extra(0.1,0)
+g2(2,2)=gramm('x',y,'color',twoaltc);
+g2(2,2).no_legend(); %It's possible to drop the side legends (useful when the grouping is the same across multiple objects)
+g2(2,2).stat_bin('geom','bar'); %Using stat_bin we can create histograms
+g2(2,2).set_limit_extra(0.1,0);
%And call the draw function on the whole array !
figure
-g2.draw()
+g2.draw();
@@ -293,9 +293,11 @@
load carbig.mat %Load example dataset about cars
origin_region=num2cell(org,2); %Convert origin data to a cellstr
+
+%figure('Position',[100 100 600 400])
% Create a gramm object, provide x (year of production) and y (fuel economy) data,
% color grouping data (number of cylinders) and select a subset of the data
-g=gramm('x',Model_Year,'y',MPG,'color',Cylinders,'subset',Cylinders~=3 & Cylinders~=5)
+g=gramm('x',Model_Year,'y',MPG,'color',Cylinders,'subset',Cylinders~=3 & Cylinders~=5) %& ~strcmp(origin_region,'Europe ')
% Subdivide the data in subplots horizontally by region of origin
g.facet_grid([],origin_region)
% Plot raw data as points
@@ -304,8 +306,8 @@
g.stat_glm()
% Set appropriate names for legends
g.set_names('column','Origin','x','Year of production','y','Fuel economy (MPG)','color','# Cylinders')
-%Set title
-g.set_title('Evolution of fuel economy between 1970 and 1982')
+%Set figure title
+g.set_title('Fuel economy of new cars between 1970 and 1982')
% Do the actual drawing
g.draw()
@@ -434,19 +436,19 @@
figure
-g10=gramm('x',Horsepower,'y',Acceleration,'color',Cylinders,'subset',Cylinders~=3 & Cylinders~=5)
-g10.set_names('color','# Cylinders','x','Horsepower','y','Acceleration')
-g10.stat_glm('geom','area','disp_fit',true) %Linear fit (default for stat_glm
-g10.geom_point()
-g10.draw(false) %To superimpose over a gramm plot, call draw with false as an argument
-
%We add another gramm plot (without color) to have a fit across all
%cylinders. Superimposition only works with gramm plots that have similar
%faceting
g10b=gramm('x',Horsepower,'y',Acceleration,'subset',Cylinders~=3 & Cylinders~=5)
-g10b.stat_glm('geom','line','disp_fit',false)
+g10b.stat_glm('geom','area','disp_fit',false)
g10b.set_color_options('chroma',0,'lightness',30)
-g10b.draw()
+g10b.draw(false)
+g10=gramm('x',Horsepower,'y',Acceleration,'color',Cylinders,'subset',Cylinders~=3 & Cylinders~=5)
+g10.set_names('color','# Cylinders','x','Horsepower','y','Acceleration')
+%g10.stat_glm('geom','area','disp_fit',true) %Linear fit (default for stat_glm
+g10.geom_point()
+g10.draw()
+
%% Colormap customization
@@ -460,21 +462,21 @@
%Default: LCH-based colormap with hue variation
g(1,1)=gramm('x',Origin,'y',Horsepower,'color',Origin)
g(1,1).stat_summary('geom',{'bar'},'dodge',-1)
-g(1,1).set_title('Default LCH colormap (''color'' grouping)')
+g(1,1).set_title('Default LCH colormap (''color'' groups)')
%Possibility to change the hue range as well as lightness and chroma of
%the LCH-based colormap
g(1,2)=gramm('x',Origin,'y',Horsepower,'color',Origin)
g(1,2).stat_summary('geom',{'bar'},'dodge',-1)
g(1,2).set_color_options('hue_range',[-60 60],'chroma',40,'lightness',90)
-g(1,2).set_title('Modified LCH colormap (''color'' grouping)')
+g(1,2).set_title('Modified LCH colormap (''color'' groups)')
%Possibility to change the lightness and chroma range of the LCH-based
%colormap when a 'lightness' variable is given
g(1,3)=gramm('x',Origin,'y',Horsepower,'lightness',Origin)
g(1,3).stat_summary('geom',{'bar'},'dodge',-1)
g(1,3).set_color_options('lightness_range',[0 95],'chroma_range',[0 0])
-g(1,3).set_title('Modified LCH colormap (''lightness'' grouping)')
+g(1,3).set_title('Modified LCH colormap (''lightness'' groups)')
%Go back to Matlab's defauls colormap
g(2,1)=gramm('x',Origin,'y',Horsepower,'color',Origin)
@@ -496,7 +498,7 @@
%Some methods can be called on all objects at the same time !
g.axe_property('YLim',[0 140])
-%g.axe_property('XTickLabelRotation',60) %Should work for recent Matlab
+g.axe_property('XTickLabelRotation',60) %Should work for recent Matlab
%versions
g.set_names('x','Origin','y','Horsepower','color','Origin','lightness','Origin')
g.set_title('Colormap customizations examples')
@@ -575,7 +577,7 @@
%Here we create x as a 1xN array (see example above), and use a MxN matrix
%for y. Color applies to the M rows of y.
g18=gramm('x',900:2:1700,'y',NIR,'color',octane);
-g18.set_names('x','Wavelength (nm)','y','NIR','color','octane')
+g18.set_names('x','Wavelength (nm)','y','NIR','color','Octane')
g18.set_continuous_color('colormap','hot')
g18.geom_line;
g18.draw;
@@ -651,7 +653,6 @@
%The measurements are now binomial (follows a logit curve centered on 5)
y=random('binomial',1,1./(1+exp(5-x)))
-figure
g(1,2)=gramm('x',x,'y',y)
%We plot jittered points to get a better idea of the distribution
%g.geom_jitter('width',0.2,'height',0.1)
@@ -685,7 +686,7 @@
g=gramm('x',x,'y',y,'color',cat)
g.geom_point()
g.stat_fit('fun',fun,'disp_fit',true) %We provide the function for the fit
-g.set_title('stat_fit() with user-provided function')
+g.set_title({'stat_fit() with user-provided function:' '@(alpha,beta,gamma,x)alpha*exp(cos(x-beta)*gamma)'})
g.draw()
%% Changing the order of elements with set_order_options()
diff --git a/gramm.m b/gramm.m
index 7255799..0c5869e 100644
--- a/gramm.m
+++ b/gramm.m
@@ -72,6 +72,13 @@
title
title_options
+ first_redraw %true for first redraw, used to not repeat costly calls in redraw
+ legend_text_handles %Stores handles of text objects for legend
+ facet_text_handles %Stores handles of text objects for facet row and column titles
+ title_text_handle
+ cached_max_legend_ind %Stores index of legend text handle with rightmost ending
+ cached_max_facet_ind %Stores indices of facet text handles with rightmost and topmost ending
+
title_axe_handle %Store the handle of the title axis
extra %Store extra geom-specific info
@@ -201,7 +208,11 @@
obj.order_options.lightness=1;
obj.with_legend=true;
-
+ obj.first_redraw=true;
+ obj.legend_text_handles=[];
+ obj.facet_text_handles=[];
+ obj.cached_max_facet_ind=[];
+ obj.cached_max_legend_ind=[];
end
% function disp(obj)
@@ -567,6 +578,7 @@
% When display is set to true (default is false), the bounding boxes of all
% elements are drawn in a separate figure
+
if nargin<2
@@ -590,6 +602,13 @@
return;
end
+ %Cool way to prevent reentrant callbacks !
+% persistent inCallback
+% if ~isempty(inCallback)
+% return;
+% end
+% inCallback = true;
+
%If x is empty the object probably is so we skip (happens for multiple graphs)
if isempty(obj.aes.x)
return
@@ -631,12 +650,21 @@
legend_inset=get(obj.legend_axe_handle,'TightInset');
legend_outer=get(obj.legend_axe_handle,'OuterPosition');
- %Retrieve text handles
- text_handles=findobj(gcf,'Type','text');
- %Needed for the rest to work
- set(text_handles,'Unit','normalized');
+ %Get legend axis width because units on legend text are not set
+ %to normalized
+ legend_xlim=get(obj.legend_axe_handle,'XLim');
+ legend_axis_width=diff(legend_xlim);
+
+
+ if obj.first_redraw %Takes a long time so we only do it once
+ %Needed for the rest to work
+ set(obj.title_text_handle,'Unit','normalized');
+ set(obj.facet_text_handles,'Unit','normalized');
+ %set(obj.legend_text_handles,'Unit','normalized');
+ end
if display
+ text_handles=findobj(gcf,'Type','text');
figure(100)
hold on
rectangle('Position',[0 0 1 1])
@@ -652,6 +680,9 @@
rectangle('Position',[legend_pos(1)-legend_inset(1) legend_pos(2)-legend_inset(2) legend_pos(3)+legend_inset(3)+legend_inset(1) legend_pos(4)+legend_inset(4)+legend_inset(2)],'EdgeColor','c');
+ %Retrieve text handles
+
+ set(text_handles,'Unit','normalized');
%display text boxes
for t=1:length(text_handles)
parent_axe=ancestor(text_handles(t),'axes');
@@ -665,9 +696,23 @@
%Move legend to the right
%Get rightmost text
- legend_text_pos=get(findobj(obj.legend_axe_handle,'Type','text'),'Extent');
+ if obj.first_redraw
+ legend_text_pos=get(obj.legend_text_handles,'Extent'); %On first redraw we get all extents
+ else
+ %On next redraws we only get extent of the rightmost one
+ legend_text_pos=get(obj.legend_text_handles(obj.cached_max_legend_ind),'Extent');
+ if ~isempty(legend_text_pos) && ~iscell(legend_text_pos)
+ legend_text_pos={legend_text_pos};
+ end
+ end
+
if ~isempty(legend_text_pos)
- max_text_x=max(cellfun(@(p)legend_pos(1)+legend_pos(3)*p(1)+legend_pos(3)*p(3),legend_text_pos));
+ %Here we correct by the width to get the coordinates in
+ %normalized values
+ [max_text_x,max_ind]=max(cellfun(@(p)legend_pos(1)+legend_pos(3)*(p(1)+p(3))/legend_axis_width,legend_text_pos));
+ if obj.first_redraw
+ obj.cached_max_legend_ind=max_ind; %Cache the index of which text object is the rightmost
+ end
%Move accordingly (here the max available x position takes
%in account the multiple plots
set(obj.legend_axe_handle,'Position',legend_pos+[obj.multi.orig(2)+obj.multi.size(2)-spacing_w-max_text_x 0 0 0]);
@@ -675,10 +720,10 @@
%Move title to the top
if ~isempty(obj.title_axe_handle)
- title_text_pos=get(findobj(obj.title_axe_handle,'Type','text'),'Extent');
+ title_text_pos=get(obj.title_text_handle,'Extent');
title_pos=get(obj.title_axe_handle,'Position');
max_text_y=title_pos(2)+title_pos(4)*title_text_pos(2)+title_pos(4)*title_text_pos(4);
- set(obj.title_axe_handle,'Position',title_pos+[0 obj.multi.orig(1)+obj.multi.size(1)-spacing_h/2-max_text_y 0 0]);
+ set(obj.title_axe_handle,'Position',title_pos+[0 obj.multi.orig(1)+obj.multi.size(1)-spacing_h-max_text_y 0 0]);
end
%Move facets to the right
@@ -693,27 +738,50 @@
%Move and rescale facets to fill up the available space
%Get positions of facet text objects (row titles are the
%rightmost things)
- facet_text_pos=get(findobj(obj.facet_axes_handles,'Type','text'),'Extent');
+ if obj.first_redraw
+ facet_text_pos=get(obj.facet_text_handles,'Extent'); %On first call get all positions
+ else
+ % On the next calls we only get the extents of rightmost
+ % and topmost text objects
+ facet_text_pos=get(obj.facet_text_handles(obj.cached_max_facet_ind),'Extent');
+ if ~isempty(facet_text_pos) && ~iscell(facet_text_pos)
+ facet_text_pos={facet_text_pos};
+ end
+ end
if ~isempty(facet_text_pos)
%Get positions of the corresponding parent axes
%axe_text_pos=get(cell2mat(ancestor(findobj(obj.facet_axes_handles,'Type','text'),'axes')),'Position');
%%didn't work with HG2 graphics
- temp_handles=ancestor(findobj(obj.facet_axes_handles,'Type','text'),'axes');
+ if obj.first_redraw
+ temp_handles=ancestor(obj.facet_text_handles,'axes');
+ else
+ temp_handles=ancestor(obj.facet_text_handles(obj.cached_max_facet_ind),'axes');
+ end
%HACK (when there no color and no legend but a single piece of text from the
%glm fits we don't get cells here so the cellfuns get broken
- if ~iscell(temp_handles)
- temp_handles={temp_handles};
- facet_text_pos={facet_text_pos};
+ if ~iscell(temp_handles)
+ temp_handles={temp_handles};
end
axe_text_pos=cellfun(@(a)get(a,'Position'),temp_handles,'UniformOutput',false);
-
+ if ~iscell(axe_text_pos)
+ axe_text_pos={axe_text_pos};
+ end
%Compute rightmost and topmost text
- max_facet_x_text=max(cellfun(@(tp,ap)ap(1)+ap(3)*tp(1)+tp(3)*ap(3),facet_text_pos,axe_text_pos));
- max_facet_y_text=max(cellfun(@(tp,ap)ap(2)+ap(4)*tp(2)+tp(4)*ap(4),facet_text_pos,axe_text_pos));
+ [max_facet_x_text,max_indx]=max(cellfun(@(tp,ap)ap(1)+ap(3)*tp(1)+tp(3)*ap(3),facet_text_pos,axe_text_pos));
+ [max_facet_y_text,max_indy]=max(cellfun(@(tp,ap)ap(2)+ap(4)*tp(2)+tp(4)*ap(4),facet_text_pos,axe_text_pos));
+
+ %Cache rightmost and topmost text object handles for speed
+ if obj.first_redraw
+ if max_indx==max_indy
+ obj.cached_max_facet_ind=max_indx;
+ else
+ obj.cached_max_facet_ind=[max_indx,max_indy];
+ end
+ end
else
max_facet_x_text=0;
max_facet_y_text=0;
@@ -747,7 +815,7 @@
temp_available_y=obj.multi.orig(1)+obj.multi.size(1);
%Place relative to title axis
if ~isempty(obj.title_axe_handle)
- title_text_pos=get(findobj(obj.title_axe_handle,'Type','text'),'Extent');
+ title_text_pos=get(obj.title_text_handle,'Extent');
title_pos=get(obj.title_axe_handle,'Position');
temp_available_y=title_pos(2)+title_pos(4)*title_text_pos(2);
%temp_available_y=tmp(2);
@@ -784,7 +852,7 @@
for r=1:nr
for c=1:nc
if ind<=length(obj.facet_axes_handles)
- axpos=get(obj.facet_axes_handles(ind),'Position');
+ %axpos=get(obj.facet_axes_handles(ind),'Position');
set(obj.facet_axes_handles(ind),'Position',[min_facet_x+(neww+spacing_w)*(c-1) min_facet_y+(newh+spacing_h)*(nr-r) neww newh]);
end
ind=ind+1;
@@ -798,12 +866,65 @@
newh=max((max_available_y-min_facet_y-spacing_h*(nr-1))/nr,spacing_h);
for r=1:nr
for c=1:nc
- axpos=get(obj.facet_axes_handles(r,c),'Position');
+ %axpos=get(obj.facet_axes_handles(r,c),'Position');
set(obj.facet_axes_handles(r,c),'Position',[min_facet_x+(neww+spacing_w)*(c-1) min_facet_y+(newh+spacing_h)*(nr-r) neww newh]);
end
end
end
-
+
+ %Resize legend y axis in order to get constant spacing between
+ %legend entries
+ L=length(obj.legend_text_handles);
+ if L>1
+ legend_ylim=get(obj.legend_axe_handle,'YLim');
+ % legend_text_pos=get(obj.legend_text_handles,'Extent');
+ % legend_text_pos=vertcat(legend_text_pos{:});
+ % legend_height=nanmean(legend_text_pos(:,4));
+ % legend_spacing=nanmean(legend_text_pos(1:end-1,2)-(legend_text_pos(2:end,2)+legend_text_pos(2:end,4)));
+ % legend_top=legend_text_pos(1,2)+legend_text_pos(1,4);
+ % legend_bottom=legend_text_pos(end,2);
+
+ % We use the first and second legends to compute spacing,
+ % first and last for top and bottom
+ legend_text_pos=get(obj.legend_text_handles([1 end]),'Extent');
+ legend_height=legend_text_pos{1}(4);
+ %legend_spacing=legend_text_pos{1}(2)-(legend_text_pos{2}(2)+legend_text_pos{2}(4));
+ legend_top=legend_text_pos{1}(2)+legend_text_pos{1}(4);
+ legend_bottom=legend_text_pos{2}(2);
+
+ %disp(['height: ' num2str(legend_height) ' spacing: ' num2str(legend_spacing) ])
+ %Theoretical resizing
+ %ratio=(legend_top-legend_bottom)/(legend_ylim(2)-legend_ylim(1));
+
+ %Future text heught stays visually constant so scales inversely
+ %relative to limits
+ %height_in_mod=legend_height*ratio;
+
+ %Future spacing gets adjsuted according to relative text
+ %size and number of text labels
+ %spacing_in_mod=((legend_top-legend_bottom)-(height_in_mod*L))/(L-1);
+
+ %disp(['mod height: ' num2str(height_in_mod) ' mod spacing: ' num2str(spacing_in_mod) ])
+ %set(obj.legend_axe_handle,'YLim',[legend_bottom legend_top]);
+
+ %Here we want future text height=future spacing so by
+ %solving for ratio we get
+ ratio=((legend_top-legend_bottom)/(L-1))/(legend_height+legend_height*L/(L-1));
+
+ new_ylim=legend_ylim*ratio*1.4; %Correct the ratio to get text lines closer to each other
+
+ %Center the limits relative to top and bottom
+ new_ylim=new_ylim-(new_ylim(2)-legend_top)+(new_ylim(2)-legend_top+legend_bottom-new_ylim(1))/2;
+
+ %Only adjust if it's not going tocut our legend.
+ if new_ylim(1)<(-L+1) && new_ylim(2)>1
+ set(obj.legend_axe_handle,'YLim',new_ylim);
+ end
+ end
+
+
+ obj.first_redraw=false;
+ %inCallback = [];
end
@@ -824,7 +945,6 @@ function draw(obj,do_redraw)
%Handle call of draw() on array of gramm objects by dividing the figure
%up and launching the individual draw functions of each object
if numel(obj)>1
-
%Take care of the big title
if isempty(obj(1).bigtitle)
maxh=1;
@@ -858,7 +978,6 @@ function draw(obj,do_redraw)
set(gcf,'SizeChangedFcn',@(a,b)redraw(obj,0.04));
end
end
-
return;
end
@@ -867,6 +986,7 @@ function draw(obj,do_redraw)
return
end
+
temp_aes=validate_aes(obj.aes);
% Create replacement row_facet and color_facet if they were
@@ -1045,6 +1165,7 @@ function draw(obj,do_redraw)
cmap=get_colormap(length(uni_color),length(uni_lightness),obj.color_options);
+
%Initialize results structure (n_groups is an overestimate if
%there are redundant groups
n_groups=length(uni_row)*length(uni_column)*length(uni_marker)...
@@ -1266,6 +1387,7 @@ function draw(obj,do_redraw)
end
if ind_row==1
+ obj.facet_text_handles=[obj.facet_text_handles ...
text('Interpreter','none','String',column_string,'Rotation',0,...
'Units','normalized',...
'Position',[0.5 1.05 2],...
@@ -1273,7 +1395,7 @@ function draw(obj,do_redraw)
'HorizontalAlignment','Center',...
'VerticalAlignment','bottom',...
'FontWeight','bold',...
- 'fontSize',12);
+ 'fontSize',12)];
end
end
if length(uni_row)>1
@@ -1284,6 +1406,7 @@ function draw(obj,do_redraw)
end
if ind_column==length(uni_column)
+ obj.facet_text_handles=[obj.facet_text_handles ...
text('Interpreter','none','String',row_string,'Rotation',-90,...
'Units','normalized',...
'Position',[1.05 0.5 2],...
@@ -1291,7 +1414,7 @@ function draw(obj,do_redraw)
'HorizontalAlignment','Center',...
'VerticalAlignment','bottom',...
'FontWeight','bold',...
- 'fontSize',12);
+ 'fontSize',12)];
end
end
@@ -1310,9 +1433,9 @@ function draw(obj,do_redraw)
obj.multi.orig(1)+0.90*obj.multi.size(1) 0.8*obj.multi.size(2) 0.05*obj.multi.size(1)]);
set(obj.title_axe_handle,'Visible','off','XLim',[-1 1],'YLim',[-1 1]);
- tmp=text(0,0,obj.title,'FontWeight','bold','Interpreter','none','fontSize',14,'HorizontalAlignment','center');
+ obj.title_text_handle=text(0,0,obj.title,'FontWeight','bold','Interpreter','none','fontSize',14,'HorizontalAlignment','center');
if ~isempty(obj.title_options)
- set(tmp,obj.title_options{:});
+ set(obj.title_axe_handle,obj.title_options{:});
end
else
obj.title_axe_handle=[];
@@ -1329,6 +1452,7 @@ function draw(obj,do_redraw)
ind_scale=0;
ind_scale_step=1;
+ ind_scale_additional_step=0.5;
if obj.with_legend
%Color legend
@@ -1336,82 +1460,114 @@ function draw(obj,do_redraw)
%Make a colormap with only the colors and no lightness
color_legend_map=get_colormap(length(uni_color),1,obj.color_options);
- text(1,ind_scale,obj.aes_names.color,'FontWeight','bold','Interpreter','none','fontSize',12)
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(1,ind_scale,obj.aes_names.color,'FontWeight','bold','Interpreter','none','fontSize',12)];
ind_scale=ind_scale-ind_scale_step;
for ind_color=1:length(uni_color)
- plot([1 2],[ind_scale ind_scale],'-','Color',color_legend_map(ind_color,:),'lineWidth',2)
- text(2.5,ind_scale,num2str(uni_color{ind_color}),'Interpreter','none')
+ plot([1 2],[ind_scale ind_scale],'-','Color',color_legend_map(ind_color,:),'lineWidth',3)
+ %line(1.5,ind_scale,'lineStyle','none','Marker','s','MarkerSize',12,'MarkerFaceColor',color_legend_map(ind_color,:),'MarkerEdgeColor','none')
+ %rectangle('Position',[1.25 ind_scale-0.25 0.5 0.5],'EdgeColor','none','FaceColor',color_legend_map(ind_color,:));
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(2.5,ind_scale,num2str(uni_color{ind_color}),'Interpreter','none')];
ind_scale=ind_scale-ind_scale_step;
end
end
+
%Lightness legend
if length(uni_lightness)>1
+ ind_scale=ind_scale-ind_scale_additional_step;
lightness_legend_map=pa_LCH2RGB([linspace(obj.color_options.lightness_range(1),obj.color_options.lightness_range(2),length(uni_lightness))' ...
zeros(length(uni_lightness),1)...
zeros(length(uni_lightness),1)]);
-
- text(1,ind_scale,obj.aes_names.lightness,'FontWeight','bold','Interpreter','none','fontSize',12)
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(1,ind_scale,obj.aes_names.lightness,'FontWeight','bold','Interpreter','none','fontSize',12)];
ind_scale=ind_scale-ind_scale_step;
for ind_lightness=1:length(uni_lightness)
- plot([1 2],[ind_scale ind_scale],'-','Color',lightness_legend_map(ind_lightness,:),'lineWidth',2)
- text(2.5,ind_scale,num2str(uni_lightness{ind_lightness}),'Interpreter','none')
+ plot([1 2],[ind_scale ind_scale],'-','Color',lightness_legend_map(ind_lightness,:),'lineWidth',3)
+ %line(1.5,ind_scale,'lineStyle','none','Marker','s','MarkerSize',12,'MarkerFaceColor',lightness_legend_map(ind_lightness,:),'MarkerEdgeColor','none')
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(2.5,ind_scale,num2str(uni_lightness{ind_lightness}),'Interpreter','none')];
ind_scale=ind_scale-ind_scale_step;
end
end
%Continuous color legend
if obj.continuous_color
- text(1,ind_scale,obj.aes_names.color,'FontWeight','bold','Interpreter','none','fontSize',12)
- ind_scale=ind_scale-ind_scale_step;
+ ind_scale=ind_scale-ind_scale_additional_step;
+
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(1,ind_scale,obj.aes_names.color,'FontWeight','bold','Interpreter','none','fontSize',12)];
+
+ ind_scale=ind_scale-ind_scale_step; %HACK here, we have to multiply by 2 ??
% image(ones(1,length(obj.continuous_color_colormap))+0.5,...
% linspace(ind_scale-2,ind_scale,length(obj.continuous_color_colormap)),...
% reshape(obj.continuous_color_colormap,length(obj.continuous_color_colormap),1,3));
tmp_N=100;
- imagesc([1.3 1.7],[ind_scale-2 ind_scale],linspace(min(min(obj.plot_lim.minc)),max(max(obj.plot_lim.maxc)),tmp_N)')
+ imagesc([1 1.5],[ind_scale-ind_scale_step*2 ind_scale],linspace(min(min(obj.plot_lim.minc)),max(max(obj.plot_lim.maxc)),tmp_N)')
+
+ line([1.8 2.2 ; 1.8 2.2 ;1.8 2.2]',[ind_scale ind_scale;ind_scale-ind_scale_step ind_scale-ind_scale_step ;ind_scale-ind_scale_step*2 ind_scale-ind_scale_step*2 ]','Color','k')
colormap(obj.continuous_color_colormap)
caxis([min(min(obj.plot_lim.minc)) max(max(obj.plot_lim.maxc))]);
- text(2.5,ind_scale,num2str(max(max(obj.plot_lim.maxc))));
- text(2.5,ind_scale-2,num2str(min(min(obj.plot_lim.minc))));
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(2.5,ind_scale,num2str(max(max(obj.plot_lim.maxc))))];
+
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(2.5,ind_scale-ind_scale_step,num2str((max(max(obj.plot_lim.maxc))-min(min(obj.plot_lim.minc)))/2))];
+
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(2.5,ind_scale-ind_scale_step*2,num2str(min(min(obj.plot_lim.minc))))];
ind_scale=ind_scale-ind_scale_step*3;
end
%marker legend
if length(uni_marker)>1
- text(1,ind_scale,obj.aes_names.marker,'FontWeight','bold','Interpreter','none','fontSize',12)
+ ind_scale=ind_scale-ind_scale_additional_step;
+
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(1,ind_scale,obj.aes_names.marker,'FontWeight','bold','Interpreter','none','fontSize',12)];
ind_scale=ind_scale-ind_scale_step;
for ind_marker=1:length(uni_marker)
plot(1.5,ind_scale,markers{ind_marker},'MarkerEdgeColor','none','MarkerFaceColor',[0 0 0])
- text(2.5,ind_scale,num2str(uni_marker{ind_marker}),'Interpreter','none')
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(2.5,ind_scale,num2str(uni_marker{ind_marker}),'Interpreter','none')];
ind_scale=ind_scale-ind_scale_step;
end
end
%linestyle legend
if length(uni_linestyle)>1
- text(1,ind_scale,obj.aes_names.linestyle,'FontWeight','bold','Interpreter','none','fontSize',12)
+ ind_scale=ind_scale-ind_scale_additional_step;
+
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(1,ind_scale,obj.aes_names.linestyle,'FontWeight','bold','Interpreter','none','fontSize',12)];
ind_scale=ind_scale-ind_scale_step;
for ind_linestyle=1:length(uni_linestyle)
plot([1 2],[ind_scale ind_scale],line_styles{ind_linestyle},'Color',[0 0 0])
- text(2.5,ind_scale,num2str(uni_linestyle{ind_linestyle}),'Interpreter','none')
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(2.5,ind_scale,num2str(uni_linestyle{ind_linestyle}),'Interpreter','none')];
ind_scale=ind_scale-ind_scale_step;
end
end
%Size legend
if length(uni_size)>1
- text(1,ind_scale,obj.aes_names.size,'FontWeight','bold','Interpreter','none','fontSize',12)
+ ind_scale=ind_scale-ind_scale_additional_step;
+
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(1,ind_scale,obj.aes_names.size,'FontWeight','bold','Interpreter','none','fontSize',12)];
ind_scale=ind_scale-ind_scale_step;
for ind_size=1:length(uni_size)
plot([1 2],[ind_scale ind_scale],'lineWidth',sizes(ind_size)/4,'Color',[0 0 0])
plot(1.5,ind_scale,'o','markerSize',sizes(ind_size),'MarkerEdgeColor','none','MarkerFaceColor',[0 0 0])
- text(2.5,ind_scale,num2str(uni_size{ind_size}),'Interpreter','none')
+ obj.legend_text_handles=[obj.legend_text_handles...
+ text(2.5,ind_scale,num2str(uni_size{ind_size}),'Interpreter','none')];
ind_scale=ind_scale-ind_scale_step;
end
end
@@ -1433,19 +1589,20 @@ function draw(obj,do_redraw)
%Set current axes
ca = obj.facet_axes_handles(ind_row,ind_column);
- axes(ca);
+ %axes(ca);
%Do the datetick
if ~isempty(obj.datetick_params)
for dtk=1:length(obj.datetick_params)
- datetick(obj.datetick_params{dtk}{:});
+ datetick(ca,obj.datetick_params{dtk}{:});
end
end
if obj.continuous_color
%Set color limits the same way on each plot
- caxis([min(min(obj.plot_lim.minc)) max(max(obj.plot_lim.maxc))]);
+ %caxis([min(min(obj.plot_lim.minc)) max(max(obj.plot_lim.maxc))]);
+ set(ca,'CLimMode','manual','CLim',[min(min(obj.plot_lim.minc)) max(max(obj.plot_lim.maxc))]);
end
@@ -1571,7 +1728,8 @@ function draw(obj,do_redraw)
case 'per_plot'
temp_xlim=[obj.plot_lim.minx(ind_row,ind_column) obj.plot_lim.maxx(ind_row,ind_column)];
end
- xlim(temp_xlim+[-diff(temp_xlim)*obj.xlim_extra*0.5 diff(temp_xlim)*obj.xlim_extra*0.5]);
+ set(ca,'XLim',temp_xlim+[-diff(temp_xlim)*obj.xlim_extra*0.5 diff(temp_xlim)*obj.xlim_extra*0.5]);
+ %xlim(temp_xlim+[-diff(temp_xlim)*obj.xlim_extra*0.5 diff(temp_xlim)*obj.xlim_extra*0.5]);
switch temp_yscale
case 'global'
@@ -1581,7 +1739,8 @@ function draw(obj,do_redraw)
case 'per_plot'
temp_ylim=[obj.plot_lim.miny(ind_row,ind_column) obj.plot_lim.maxy(ind_row,ind_column)];
end
- ylim(temp_ylim+[-diff(temp_ylim)*obj.ylim_extra*0.5 diff(temp_ylim)*obj.ylim_extra*0.5]);
+ set(ca,'YLim',temp_ylim+[-diff(temp_ylim)*obj.ylim_extra*0.5 diff(temp_ylim)*obj.ylim_extra*0.5]);
+ %ylim(temp_ylim+[-diff(temp_ylim)*obj.ylim_extra*0.5 diff(temp_ylim)*obj.ylim_extra*0.5]);
if ~isempty(temp_aes.z) %Only do the Z limit stuff if we have z data
@@ -1591,7 +1750,8 @@ function draw(obj,do_redraw)
case 'per_plot'
temp_zlim=[obj.plot_lim.minz(ind_row,ind_column) obj.plot_lim.maxz(ind_row,ind_column)];
end
- zlim(temp_zlim+[-diff(temp_zlim)*obj.zlim_extra*0.5 diff(temp_zlim)*obj.zlim_extra*0.5]);
+ set(ca,'ZLim',temp_zlim+[-diff(temp_zlim)*obj.zlim_extra*0.5 diff(temp_zlim)*obj.zlim_extra*0.5]);
+ %zlim(temp_zlim+[-diff(temp_zlim)*obj.zlim_extra*0.5 diff(temp_zlim)*obj.zlim_extra*0.5]);
%Always have ticks if we have z data
has_xtick=true;
@@ -1623,12 +1783,12 @@ function draw(obj,do_redraw)
%Set appropriate x ticks if labeled
if obj.x_factor
temp_xlim=get(ca,'xlim');
- xlim([temp_xlim(1)-1 temp_xlim(2)+1])
+ set(ca,'XLim',[temp_xlim(1)-0.6 temp_xlim(2)+0.6]);
set(ca,'XTick',1:length(obj.x_ticks))
if has_xtick
set(ca,'XTickLabel',obj.x_ticks)
- try
- set(ca,'TickLabelInterpreter','none')%Just try it (doesn't exist in pre-2014b)
+ if isprop(ca,'TickLabelInterpreter') %Just try it (doesn't exist in pre-2014b)
+ set(ca,'TickLabelInterpreter','none')
end
%set(ca,'XTickLabelRotation',30)
end
@@ -1636,15 +1796,15 @@ function draw(obj,do_redraw)
%Add axes labels on right and botttom graphs only
if ind_column==1 || (obj.wrap_ncols>0 && mod(ind_column,obj.wrap_ncols)==1) || ~isempty(temp_aes.z)
- ylabel(obj.aes_names.y,'Interpreter','none'); %,'Units','normalized','position',[-0.2 0.5 1]
+ ylabel(ca,obj.aes_names.y,'Interpreter','none'); %,'Units','normalized','position',[-0.2 0.5 1]
end
if (ind_row==length(uni_row) && obj.wrap_ncols<=0) || (obj.wrap_ncols>0 && (length(uni_column)-ind_column)