From 328b34fd550993b2e9350cedadf55563450ddb6c Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 13:42:18 +0100
Subject: [PATCH 01/13] Allowed redrawing colorbars without passing arguments

---
 holoviews/plotting/mpl/chart3d.py |  5 ++++-
 holoviews/plotting/mpl/element.py | 14 ++++++++------
 holoviews/plotting/mpl/path.py    |  2 +-
 3 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/holoviews/plotting/mpl/chart3d.py b/holoviews/plotting/mpl/chart3d.py
index 342c78be23..dd7a1b3e19 100644
--- a/holoviews/plotting/mpl/chart3d.py
+++ b/holoviews/plotting/mpl/chart3d.py
@@ -84,7 +84,10 @@ def _finalize_axis(self, key, **kwargs):
         return super(Plot3D, self)._finalize_axis(key, **kwargs)
 
 
-    def _draw_colorbar(self, artist, element, dim=None):
+    def _draw_colorbar(self, dim=None):
+        element = self.hmap.last
+        artist = self.handles.get('artist', None)
+
         fig = self.handles['fig']
         ax = self.handles['axis']
         # Get colorbar label
diff --git a/holoviews/plotting/mpl/element.py b/holoviews/plotting/mpl/element.py
index d899082463..c7e0e7d908 100644
--- a/holoviews/plotting/mpl/element.py
+++ b/holoviews/plotting/mpl/element.py
@@ -553,21 +553,23 @@ def _adjust_cbar(self, cbar, label, dim):
 
 
     def _finalize_artist(self, key):
-        element = self.hmap.last
         artist = self.handles.get('artist', None)
         if artist and self.colorbar:
-            self._draw_colorbar(artist, element)
+            self._draw_colorbar()
 
 
-    def _draw_colorbar(self, artist, element, dim=None):
+    def _draw_colorbar(self, dim=None, redraw=True):
+        element = self.hmap.last
+        artist = self.handles.get('artist', None)
         fig = self.handles['fig']
         axis = self.handles['axis']
         ax_colorbars, position = ColorbarPlot._colorbars.get(id(axis), ([], None))
         specs = [spec[:2] for _, _, spec, _ in ax_colorbars]
         spec = util.get_spec(element)
 
-        if position is None:
-            fig.canvas.draw()
+        if position is None or not redraw:
+            if redraw:
+                fig.canvas.draw()
             bbox = axis.get_position()
             l, b, w, h = bbox.x0, bbox.y0, bbox.width, bbox.height
         else:
@@ -594,7 +596,7 @@ def _draw_colorbar(self, artist, element, dim=None):
             self.handles['bbox_extra_artists'] += [cax, ylabel]
             ax_colorbars.append((artist, cax, spec, label))
 
-        for i, (artist, cax, spec, label) in enumerate(ax_colorbars[:-1]):
+        for i, (artist, cax, spec, label) in enumerate(ax_colorbars):
             scaled_w = w*width
             cax.set_position([l+w+padding+(scaled_w+padding+w*0.15)*i,
                               b, scaled_w, h])
diff --git a/holoviews/plotting/mpl/path.py b/holoviews/plotting/mpl/path.py
index 755bace782..313a8014e8 100644
--- a/holoviews/plotting/mpl/path.py
+++ b/holoviews/plotting/mpl/path.py
@@ -69,7 +69,7 @@ def init_artists(self, ax, plot_args, plot_kwargs):
         collection = PatchCollection(*plot_args, **plot_kwargs)
         ax.add_collection(collection)
         if self.colorbar:
-            self._draw_colorbar(collection, self.current_frame)
+            self._draw_colorbar()
         return {'artist': collection, 'polys': plot_args[0]}
 
 

From 1f9391aca6d908b0453ba33f41ed5a457e3ab0db Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 13:43:16 +0100
Subject: [PATCH 02/13] Added support for fixing aspects of layout plots

---
 holoviews/plotting/mpl/plot.py     |  31 +++++++--
 holoviews/plotting/mpl/renderer.py |  30 +--------
 holoviews/plotting/mpl/util.py     | 102 +++++++++++++++++++++++++++++
 3 files changed, 132 insertions(+), 31 deletions(-)

diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py
index 2d24f82ea5..3019076c80 100644
--- a/holoviews/plotting/mpl/plot.py
+++ b/holoviews/plotting/mpl/plot.py
@@ -1,5 +1,7 @@
 from __future__ import division
 
+from itertools import chain
+
 import numpy as np
 import matplotlib as mpl
 from mpl_toolkits.mplot3d import Axes3D  # noqa (For 3D plots)
@@ -15,7 +17,7 @@
 from ..plot import DimensionedPlot, GenericLayoutPlot, GenericCompositePlot
 from ..util import get_dynamic_mode, initialize_sampled
 from .renderer import MPLRenderer
-from .util import compute_ratios
+from .util import compute_ratios, fix_aspect
 
 
 class MPLPlot(DimensionedPlot):
@@ -617,7 +619,7 @@ def initialize_plot(self, ranges=None):
         self.drawn = True
 
 
-    def adjust_positions(self):
+    def adjust_positions(self, redraw=True):
         """
         Make adjustments to the positions of subplots (if available)
         relative to the main plot axes as required.
@@ -631,7 +633,8 @@ def adjust_positions(self):
         top = all('top' in check for check in checks)
         if not 'main' in self.subplots or not (top or right):
             return
-        self.handles['fig'].canvas.draw()
+        if redraw:
+            self.handles['fig'].canvas.draw()
         main_ax = self.subplots['main'].handles['axis']
         bbox = main_ax.get_position()
         if right:
@@ -695,6 +698,10 @@ class LayoutPlot(GenericLayoutPlot, CompositePlot):
       (left, bottom, right, top), defining the size of the border
       around the subplots.""")
 
+    fix_aspect = param.Boolean(default=False, doc="""Apply a fix to the
+      figure aspect to take into account non-square plots (will be the
+      default in future versions""")
+
     tight = param.Boolean(default=False, doc="""
       Tightly fit the axes in the layout within the fig_bounds
       and tight_padding.""")
@@ -706,7 +713,7 @@ class LayoutPlot(GenericLayoutPlot, CompositePlot):
       Specifies the space between horizontally adjacent elements in the grid.
       Default value is set conservatively to avoid overlap of subplots.""")
 
-    vspace = param.Number(default=0.1, doc="""
+    vspace = param.Number(default=0.3, doc="""
       Specifies the space between vertically adjacent elements in the grid.
       Default value is set conservatively to avoid overlap of subplots.""")
 
@@ -1025,12 +1032,28 @@ def initialize_plot(self):
             subplot.initialize_plot(ranges=ranges)
 
         # Create title handle
+        title = None
         if self.show_title and len(self.coords) > 1:
             title = self._format_title(key)
             title = self.handles['fig'].suptitle(title, **self._fontsize('title'))
             self.handles['title'] = title
             self.handles['bbox_extra_artists'] += [title]
 
+        fig = self.handles['fig']
+        if (not self.traverse(specs=[GridPlot]) and not isinstance(self.fig_inches, tuple)
+            and self.fix_aspect):
+            traverse_fn = lambda x: x.handles.get('bbox_extra_artists', None)
+            extra_artists = list(chain(*[artists for artists in self.traverse(traverse_fn)
+                                         if artists is not None]))
+            aspect = fix_aspect(fig, title, extra_artists, vspace=self.vspace,
+                                hspace=self.hspace)
+            colorbars = self.traverse(specs=[lambda x: hasattr(x, 'colorbar')])
+            for cbar_plot in colorbars:
+                if cbar_plot.colorbar:
+                    cbar_plot._draw_colorbar(redraw=False)
+            adjoined = self.traverse(specs=[AdjointLayoutPlot])
+            for adjoined in adjoined:
+                adjoined.adjust_positions(redraw=False)
         return self._finalize_axis(None)
 
 
diff --git a/holoviews/plotting/mpl/renderer.py b/holoviews/plotting/mpl/renderer.py
index 0eb0f432d6..923ed1277f 100644
--- a/holoviews/plotting/mpl/renderer.py
+++ b/holoviews/plotting/mpl/renderer.py
@@ -19,6 +19,7 @@
 
 from ..renderer import Renderer, MIME_TYPES
 from .widgets import MPLSelectionWidget, MPLScrubberWidget
+from .util import get_tight_bbox
 
 class OutputWarning(param.Parameterized):pass
 outputwarning = OutputWarning(name='Warning')
@@ -233,34 +234,9 @@ def _compute_bbox(self, fig, kw):
             if not fig_id in MPLRenderer.drawn:
                 fig.set_dpi(self.dpi)
                 fig.canvas.draw()
-                renderer = fig._cachedRenderer
-                bbox_inches = fig.get_tightbbox(renderer)
-                bbox_artists = kw.pop("bbox_extra_artists", [])
-                bbox_artists += fig.get_default_bbox_extra_artists()
-                bbox_filtered = []
-                for a in bbox_artists:
-                    bbox = a.get_window_extent(renderer)
-                    if isinstance(bbox, tuple):
-                        continue
-                    if a.get_clip_on():
-                        clip_box = a.get_clip_box()
-                        if clip_box is not None:
-                            bbox = Bbox.intersection(bbox, clip_box)
-                        clip_path = a.get_clip_path()
-                        if clip_path is not None and bbox is not None:
-                            clip_path = clip_path.get_fully_transformed_path()
-                            bbox = Bbox.intersection(bbox,
-                                                     clip_path.get_extents())
-                    if bbox is not None and (bbox.width != 0 or
-                                             bbox.height != 0):
-                        bbox_filtered.append(bbox)
-                if bbox_filtered:
-                    _bbox = Bbox.union(bbox_filtered)
-                    trans = Affine2D().scale(1.0 / self.dpi)
-                    bbox_extra = TransformedBbox(_bbox, trans)
-                    bbox_inches = Bbox.union([bbox_inches, bbox_extra])
+                extra_artists = kw.pop("bbox_extra_artists", [])
                 pad = plt.rcParams['savefig.pad_inches']
-                bbox_inches = bbox_inches.padded(pad)
+                bbox_inches = get_tight_bbox(fig, extra_artists, pad=pad)
                 MPLRenderer.drawn[fig_id] = bbox_inches
                 kw['bbox_inches'] = bbox_inches
             else:
diff --git a/holoviews/plotting/mpl/util.py b/holoviews/plotting/mpl/util.py
index 15dd388912..22d40acc9f 100644
--- a/holoviews/plotting/mpl/util.py
+++ b/holoviews/plotting/mpl/util.py
@@ -4,6 +4,7 @@
 
 import numpy as np
 from matplotlib import ticker
+from matplotlib.transforms import Bbox, TransformedBbox, Affine2D
 
 from ...core.util import basestring
 
@@ -58,3 +59,104 @@ def compute_ratios(ratios, normalized=True):
     with warnings.catch_warnings():
         warnings.filterwarnings('ignore', r'All-NaN (slice|axis) encountered')
         return np.nanmax(np.vstack([v for _, v in sorted_ratios]), axis=0)
+
+
+def axis_overlap(ax1, ax2):
+    """
+    Tests whether two axes overlap vertically
+    """
+    b1, t1 = ax1.get_position().intervaly
+    b2, t2 = ax2.get_position().intervaly
+    return t1 >= b2 and b1 <= t2
+
+
+def resolve_rows(rows):
+    """
+    Recursively iterate over lists of axes merging
+    them by their vertical overlap leaving a list
+    of rows.
+    """
+    merged_rows = []
+    for row in rows:
+        overlap = False
+        for mrow in merged_rows:
+            if any(axis_overlap(ax1, ax2) for ax1 in row
+                   for ax2 in mrow):
+                mrow += row
+                overlap = True
+                break
+        if not overlap:
+            merged_rows.append(row)
+    if rows == merged_rows:
+        return rows
+    else:
+        return resolve_rows(merged_rows)
+
+
+def fix_aspect(fig, title=None, extra_artists=[], vspace=0.2, hspace=0.2):
+    """
+    Calculate heights and widths of axes and adjust
+    the size of the figure to match the aspect.
+    """
+    fig.canvas.draw()
+    w, h = fig.get_size_inches()
+
+    # Compute maximum height and width of each row and columns
+    rows = resolve_rows([[ax] for ax in fig.axes])
+    rs, cs = len(rows), max([len(r) for r in rows])
+    heights = [[] for i in range(cs)]
+    widths = [[] for i in range(rs)]
+    for r, row in enumerate(rows):
+        for c, ax in enumerate(row):
+            bbox = ax.get_tightbbox(fig.canvas.renderer)
+            heights[c].append(bbox.height)
+            widths[r].append(bbox.width)
+    height = (max([sum(c) for c in heights])) + (rs)*vspace
+    width = (max([sum(r) for r in widths])) + (cs)*hspace
+
+    # Compute aspect and set new size (in inches)
+    aspect = height/width
+    offset = 0.2 if title and title.get_text() else 0
+    fig.set_size_inches(w, (w*aspect)+offset)
+
+    # Redraw and adjust title position if defined
+    fig.canvas.draw()
+    if title and title.get_text():
+        bbox = get_tight_bbox(fig, extra_artists)
+        top = bbox.intervaly[1]
+        extra_artists = [a for a in extra_artists if a is not title]
+        if title and title.get_text():
+            title.set_y((top/(w*aspect)))
+
+
+def get_tight_bbox(fig, bbox_extra_artists=[], pad=None):
+    """
+    Compute a tight bounding box around all the artists in the figure.
+    """
+    renderer = fig._cachedRenderer
+    bbox_inches = fig.get_tightbbox(renderer)
+    bbox_artists = bbox_extra_artists[:]
+    bbox_artists += fig.get_default_bbox_extra_artists()
+    bbox_filtered = []
+    for a in bbox_artists:
+        bbox = a.get_window_extent(renderer)
+        if isinstance(bbox, tuple):
+            continue
+        if a.get_clip_on():
+            clip_box = a.get_clip_box()
+            if clip_box is not None:
+                bbox = Bbox.intersection(bbox, clip_box)
+            clip_path = a.get_clip_path()
+            if clip_path is not None and bbox is not None:
+                clip_path = clip_path.get_fully_transformed_path()
+                bbox = Bbox.intersection(bbox,
+                                         clip_path.get_extents())
+        if bbox is not None and (bbox.width != 0 or
+                                 bbox.height != 0):
+            bbox_filtered.append(bbox)
+    if bbox_filtered:
+        _bbox = Bbox.union(bbox_filtered)
+        trans = Affine2D().scale(1.0 / fig.dpi)
+        bbox_extra = TransformedBbox(_bbox, trans)
+        bbox_inches = Bbox.union([bbox_inches, bbox_extra])
+    return bbox_inches.padded(pad) if pad else bbox_inches

From b210329db6bfdef7fa7460b1313b3c48a9240258 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 15:03:15 +0100
Subject: [PATCH 03/13] Fix for padding calculation

---
 holoviews/plotting/mpl/util.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/holoviews/plotting/mpl/util.py b/holoviews/plotting/mpl/util.py
index 22d40acc9f..22901ff5ba 100644
--- a/holoviews/plotting/mpl/util.py
+++ b/holoviews/plotting/mpl/util.py
@@ -111,8 +111,8 @@ def fix_aspect(fig, title=None, extra_artists=[], vspace=0.2, hspace=0.2):
             bbox = ax.get_tightbbox(fig.canvas.renderer)
             heights[c].append(bbox.height)
             widths[r].append(bbox.width)
-    height = (max([sum(c) for c in heights])) + (rs)*vspace
-    width = (max([sum(r) for r in widths])) + (cs)*hspace
+    height = (max([sum(c) for c in heights])) + (rs)*vspace*fig.dpi
+    width = (max([sum(r) for r in widths])) + (cs)*hspace*fig.dpi
 
     # Compute aspect and set new size (in inches)
     aspect = height/width

From b0811254fa6cbc6196fcd738c7280c4130d0d035 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 15:18:06 +0100
Subject: [PATCH 04/13] Made axis_overlap non-inclusive

---
 holoviews/plotting/mpl/util.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/holoviews/plotting/mpl/util.py b/holoviews/plotting/mpl/util.py
index 22901ff5ba..3136fdbf56 100644
--- a/holoviews/plotting/mpl/util.py
+++ b/holoviews/plotting/mpl/util.py
@@ -67,7 +67,7 @@ def axis_overlap(ax1, ax2):
     """
     b1, t1 = ax1.get_position().intervaly
     b2, t2 = ax2.get_position().intervaly
-    return t1 >= b2 and b1 <= t2
+    return t1 > b2 and b1 < t2
 
 
 def resolve_rows(rows):

From 5be177261e05878ef5dda540703c320d3dc8bf95 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 15:33:15 +0100
Subject: [PATCH 05/13] Removed magic number for title height

---
 holoviews/plotting/mpl/util.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/holoviews/plotting/mpl/util.py b/holoviews/plotting/mpl/util.py
index 3136fdbf56..301a32518a 100644
--- a/holoviews/plotting/mpl/util.py
+++ b/holoviews/plotting/mpl/util.py
@@ -116,7 +116,9 @@ def fix_aspect(fig, title=None, extra_artists=[], vspace=0.2, hspace=0.2):
 
     # Compute aspect and set new size (in inches)
     aspect = height/width
-    offset = 0.2 if title and title.get_text() else 0
+    offset = 0
+    if title and title.get_text():
+        offset = title.get_window_extent().height/fig.dpi
     fig.set_size_inches(w, (w*aspect)+offset)
 
     # Redraw and adjust title position if defined

From 584836e16995d95fd1693ba0a3bdc96d8124065d Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 16:15:54 +0100
Subject: [PATCH 06/13] Simplified figure scaling

---
 holoviews/plotting/mpl/plot.py     | 13 +++++++------
 holoviews/plotting/mpl/renderer.py | 10 ++--------
 2 files changed, 9 insertions(+), 14 deletions(-)

diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py
index 3019076c80..d56cf254cf 100644
--- a/holoviews/plotting/mpl/plot.py
+++ b/holoviews/plotting/mpl/plot.py
@@ -56,7 +56,7 @@ class MPLPlot(DimensionedPlot):
     fig_rcparams = param.Dict(default={}, doc="""
         matplotlib rc parameters to apply to the overall figure.""")
 
-    fig_size = param.Integer(default=100, bounds=(1, None), doc="""
+    fig_size = param.Number(default=100., bounds=(1, None), doc="""
         Size relative to the supplied overall fig_inches in percent.""")
 
     initial_hooks = param.HookList(default=[], doc="""
@@ -99,12 +99,12 @@ def __init__(self, fig=None, axis=None, **params):
         self._create_fig = True
         super(MPLPlot, self).__init__(**params)
         # List of handles to matplotlib objects for animation update
-        scale = self.fig_size/100.
+        self.fig_scale = self.fig_size/100.
         if isinstance(self.fig_inches, (tuple, list)):
-            self.fig_inches = [None if i is None else i*scale
+            self.fig_inches = [None if i is None else i*self.fig_scale
                                for i in self.fig_inches]
         else:
-            self.fig_inches *= scale
+            self.fig_inches *= self.fig_scale
         fig, axis = self._init_axis(fig, axis)
         self.handles['fig'] = fig
         self.handles['axis'] = axis
@@ -1045,8 +1045,9 @@ def initialize_plot(self):
             traverse_fn = lambda x: x.handles.get('bbox_extra_artists', None)
             extra_artists = list(chain(*[artists for artists in self.traverse(traverse_fn)
                                          if artists is not None]))
-            aspect = fix_aspect(fig, title, extra_artists, vspace=self.vspace,
-                                hspace=self.hspace)
+            aspect = fix_aspect(fig, title, extra_artists,
+                                vspace=self.vspace*self.fig_scale,
+                                hspace=self.hspace*self.fig_scale)
             colorbars = self.traverse(specs=[lambda x: hasattr(x, 'colorbar')])
             for cbar_plot in colorbars:
                 if cbar_plot.colorbar:
diff --git a/holoviews/plotting/mpl/renderer.py b/holoviews/plotting/mpl/renderer.py
index 923ed1277f..15ed87cc51 100644
--- a/holoviews/plotting/mpl/renderer.py
+++ b/holoviews/plotting/mpl/renderer.py
@@ -122,15 +122,9 @@ def plot_options(cls, obj, percent_size):
         factor = percent_size / 100.0
         obj = obj.last if isinstance(obj, HoloMap) else obj
         options = Store.lookup_options(cls.backend, obj, 'plot').options
-        fig_inches = options.get('fig_inches', MPLPlot.fig_inches)
+        fig_size = options.get('fig_size', MPLPlot.fig_size)*factor
 
-        if isinstance(fig_inches, (list, tuple)):
-            fig_inches =  (None if fig_inches[0] is None else fig_inches[0] * factor,
-                           None if fig_inches[1] is None else fig_inches[1] * factor)
-        else:
-            fig_inches = MPLPlot.fig_inches * factor
-
-        return dict({'fig_inches':fig_inches},
+        return dict({'fig_size':fig_size},
                     **Store.lookup_options(cls.backend, obj, 'plot').options)
 
 

From f618ecc819c7c1cc1315ecc6ec7c14a4f4058814 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 16:16:41 +0100
Subject: [PATCH 07/13] Small fixes for fix_aspect

---
 holoviews/plotting/mpl/plot.py | 2 +-
 holoviews/plotting/mpl/util.py | 7 ++++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py
index d56cf254cf..597391ef15 100644
--- a/holoviews/plotting/mpl/plot.py
+++ b/holoviews/plotting/mpl/plot.py
@@ -713,7 +713,7 @@ class LayoutPlot(GenericLayoutPlot, CompositePlot):
       Specifies the space between horizontally adjacent elements in the grid.
       Default value is set conservatively to avoid overlap of subplots.""")
 
-    vspace = param.Number(default=0.3, doc="""
+    vspace = param.Number(default=0.1, doc="""
       Specifies the space between vertically adjacent elements in the grid.
       Default value is set conservatively to avoid overlap of subplots.""")
 
diff --git a/holoviews/plotting/mpl/util.py b/holoviews/plotting/mpl/util.py
index 301a32518a..4769e7b8e7 100644
--- a/holoviews/plotting/mpl/util.py
+++ b/holoviews/plotting/mpl/util.py
@@ -111,8 +111,8 @@ def fix_aspect(fig, title=None, extra_artists=[], vspace=0.2, hspace=0.2):
             bbox = ax.get_tightbbox(fig.canvas.renderer)
             heights[c].append(bbox.height)
             widths[r].append(bbox.width)
-    height = (max([sum(c) for c in heights])) + (rs)*vspace*fig.dpi
-    width = (max([sum(r) for r in widths])) + (cs)*hspace*fig.dpi
+    height = (max([sum(c) for c in heights])) + (rs-1)*vspace*fig.dpi
+    width = (max([sum(r) for r in widths])) + (cs-1)*hspace*fig.dpi
 
     # Compute aspect and set new size (in inches)
     aspect = height/width
@@ -124,9 +124,10 @@ def fix_aspect(fig, title=None, extra_artists=[], vspace=0.2, hspace=0.2):
     # Redraw and adjust title position if defined
     fig.canvas.draw()
     if title and title.get_text():
+        extra_artists = [a for a in extra_artists
+                         if a is not title]
         bbox = get_tight_bbox(fig, extra_artists)
         top = bbox.intervaly[1]
-        extra_artists = [a for a in extra_artists if a is not title]
         if title and title.get_text():
             title.set_y((top/(w*aspect)))
 

From d585a2e9814312387a44263558d81aa0a7cdfee4 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 16:33:00 +0100
Subject: [PATCH 08/13] Enabled fix_aspect by default

---
 holoviews/plotting/mpl/plot.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py
index 597391ef15..9a96c3d220 100644
--- a/holoviews/plotting/mpl/plot.py
+++ b/holoviews/plotting/mpl/plot.py
@@ -698,7 +698,7 @@ class LayoutPlot(GenericLayoutPlot, CompositePlot):
       (left, bottom, right, top), defining the size of the border
       around the subplots.""")
 
-    fix_aspect = param.Boolean(default=False, doc="""Apply a fix to the
+    fix_aspect = param.Boolean(default=True, doc="""Apply a fix to the
       figure aspect to take into account non-square plots (will be the
       default in future versions""")
 
@@ -713,7 +713,7 @@ class LayoutPlot(GenericLayoutPlot, CompositePlot):
       Specifies the space between horizontally adjacent elements in the grid.
       Default value is set conservatively to avoid overlap of subplots.""")
 
-    vspace = param.Number(default=0.1, doc="""
+    vspace = param.Number(default=0.25, doc="""
       Specifies the space between vertically adjacent elements in the grid.
       Default value is set conservatively to avoid overlap of subplots.""")
 

From 1a4c2cbcf10c396bac3bc598f4ecf7ee63ed78d0 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 18:41:52 +0100
Subject: [PATCH 09/13] Simplified tutorial layout options

---
 doc/Tutorials/Columnar_Data.ipynb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/doc/Tutorials/Columnar_Data.ipynb b/doc/Tutorials/Columnar_Data.ipynb
index 3d9cea3827..77a674c8bc 100644
--- a/doc/Tutorials/Columnar_Data.ipynb
+++ b/doc/Tutorials/Columnar_Data.ipynb
@@ -820,8 +820,8 @@
    },
    "outputs": [],
    "source": [
-    "%opts HeatMap [show_values=False xticks=40 xrotation=90 aspect=1.2 invert_yaxis=True colorbar=True]\n",
-    "%opts Layout [figure_size=120 aspect_weight=0.5 hspace=0.8 vspace=0]"
+    "%opts HeatMap [show_values=False xticks=40 xrotation=90 aspect=1.5 invert_yaxis=True colorbar=True]\n",
+    "%opts Layout [figure_size=150 vspace=0.4]"
    ]
   },
   {

From 303a9d01c9475294c57881a08276d731ec45ccbb Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 20:54:59 +0100
Subject: [PATCH 10/13] Adjusted default vspace

---
 doc/Tutorials/Columnar_Data.ipynb | 4 ++--
 holoviews/plotting/mpl/plot.py    | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/doc/Tutorials/Columnar_Data.ipynb b/doc/Tutorials/Columnar_Data.ipynb
index 77a674c8bc..41fa1c44b4 100644
--- a/doc/Tutorials/Columnar_Data.ipynb
+++ b/doc/Tutorials/Columnar_Data.ipynb
@@ -820,8 +820,8 @@
    },
    "outputs": [],
    "source": [
-    "%opts HeatMap [show_values=False xticks=40 xrotation=90 aspect=1.5 invert_yaxis=True colorbar=True]\n",
-    "%opts Layout [figure_size=150 vspace=0.4]"
+    "%opts HeatMap [show_values=False xticks=40 xrotation=90 aspect=1.2 invert_yaxis=True colorbar=True]\n",
+    "%opts Layout [figure_size=150]"
    ]
   },
   {
diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py
index 9a96c3d220..6f04944e8b 100644
--- a/holoviews/plotting/mpl/plot.py
+++ b/holoviews/plotting/mpl/plot.py
@@ -713,7 +713,7 @@ class LayoutPlot(GenericLayoutPlot, CompositePlot):
       Specifies the space between horizontally adjacent elements in the grid.
       Default value is set conservatively to avoid overlap of subplots.""")
 
-    vspace = param.Number(default=0.25, doc="""
+    vspace = param.Number(default=0.3, doc="""
       Specifies the space between vertically adjacent elements in the grid.
       Default value is set conservatively to avoid overlap of subplots.""")
 

From b89867c6a6207e04fc973301dd2c9bef0e613c24 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Sun, 21 Aug 2016 21:41:49 +0100
Subject: [PATCH 11/13] Small fixes to Layout padding

---
 holoviews/plotting/mpl/plot.py | 15 ++++++++-------
 holoviews/plotting/mpl/util.py |  7 ++++---
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py
index 6f04944e8b..c4ef606158 100644
--- a/holoviews/plotting/mpl/plot.py
+++ b/holoviews/plotting/mpl/plot.py
@@ -1032,12 +1032,12 @@ def initialize_plot(self):
             subplot.initialize_plot(ranges=ranges)
 
         # Create title handle
-        title = None
-        if self.show_title and len(self.coords) > 1:
-            title = self._format_title(key)
-            title = self.handles['fig'].suptitle(title, **self._fontsize('title'))
-            self.handles['title'] = title
-            self.handles['bbox_extra_artists'] += [title]
+        title_obj = None
+        title = self._format_title(key)
+        if self.show_title and len(self.coords) > 1 and title:
+            title_obj = self.handles['fig'].suptitle(title, **self._fontsize('title'))
+            self.handles['title'] = title_obj
+            self.handles['bbox_extra_artists'] += [title_obj]
 
         fig = self.handles['fig']
         if (not self.traverse(specs=[GridPlot]) and not isinstance(self.fig_inches, tuple)
@@ -1045,7 +1045,8 @@ def initialize_plot(self):
             traverse_fn = lambda x: x.handles.get('bbox_extra_artists', None)
             extra_artists = list(chain(*[artists for artists in self.traverse(traverse_fn)
                                          if artists is not None]))
-            aspect = fix_aspect(fig, title, extra_artists,
+            aspect = fix_aspect(fig, self.rows, self.cols,
+                                title_obj, extra_artists,
                                 vspace=self.vspace*self.fig_scale,
                                 hspace=self.hspace*self.fig_scale)
             colorbars = self.traverse(specs=[lambda x: hasattr(x, 'colorbar')])
diff --git a/holoviews/plotting/mpl/util.py b/holoviews/plotting/mpl/util.py
index 4769e7b8e7..3212e0e8fd 100644
--- a/holoviews/plotting/mpl/util.py
+++ b/holoviews/plotting/mpl/util.py
@@ -93,7 +93,8 @@ def resolve_rows(rows):
         return resolve_rows(merged_rows)
 
 
-def fix_aspect(fig, title=None, extra_artists=[], vspace=0.2, hspace=0.2):
+def fix_aspect(fig, nrows, ncols, title=None, extra_artists=[],
+               vspace=0.2, hspace=0.2):
     """
     Calculate heights and widths of axes and adjust
     the size of the figure to match the aspect.
@@ -111,8 +112,8 @@ def fix_aspect(fig, title=None, extra_artists=[], vspace=0.2, hspace=0.2):
             bbox = ax.get_tightbbox(fig.canvas.renderer)
             heights[c].append(bbox.height)
             widths[r].append(bbox.width)
-    height = (max([sum(c) for c in heights])) + (rs-1)*vspace*fig.dpi
-    width = (max([sum(r) for r in widths])) + (cs-1)*hspace*fig.dpi
+    height = (max([sum(c) for c in heights])) + nrows*vspace*fig.dpi
+    width = (max([sum(r) for r in widths])) + ncols*hspace*fig.dpi
 
     # Compute aspect and set new size (in inches)
     aspect = height/width

From 6298a8895e42aba0bed4c3d3becd5ec3d7d86f2e Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Mon, 22 Aug 2016 12:34:24 +0100
Subject: [PATCH 12/13] Added compatibility flag for new layout format

---
 holoviews/plotting/mpl/plot.py | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py
index c4ef606158..884e5973d8 100644
--- a/holoviews/plotting/mpl/plot.py
+++ b/holoviews/plotting/mpl/plot.py
@@ -698,10 +698,6 @@ class LayoutPlot(GenericLayoutPlot, CompositePlot):
       (left, bottom, right, top), defining the size of the border
       around the subplots.""")
 
-    fix_aspect = param.Boolean(default=True, doc="""Apply a fix to the
-      figure aspect to take into account non-square plots (will be the
-      default in future versions""")
-
     tight = param.Boolean(default=False, doc="""
       Tightly fit the axes in the layout within the fig_bounds
       and tight_padding.""")
@@ -713,12 +709,17 @@ class LayoutPlot(GenericLayoutPlot, CompositePlot):
       Specifies the space between horizontally adjacent elements in the grid.
       Default value is set conservatively to avoid overlap of subplots.""")
 
-    vspace = param.Number(default=0.3, doc="""
+    vspace = param.Number(default=0.1, doc="""
       Specifies the space between vertically adjacent elements in the grid.
       Default value is set conservatively to avoid overlap of subplots.""")
 
     fontsize = param.Parameter(default={'title':16}, allow_None=True)
 
+    # Whether to enable fix for non-square figures
+    # Will be enabled by default in v1.7
+    # If enabled default vspace should be increased to 0.3
+    v17_layout_format = False
+
     def __init__(self, layout, **params):
         super(LayoutPlot, self).__init__(layout=layout, **params)
         self.subplots, self.subaxes, self.layout = self._compute_gridspec(layout)
@@ -1041,7 +1042,7 @@ def initialize_plot(self):
 
         fig = self.handles['fig']
         if (not self.traverse(specs=[GridPlot]) and not isinstance(self.fig_inches, tuple)
-            and self.fix_aspect):
+            and self.v17_layout_format):
             traverse_fn = lambda x: x.handles.get('bbox_extra_artists', None)
             extra_artists = list(chain(*[artists for artists in self.traverse(traverse_fn)
                                          if artists is not None]))

From e34c20f32eca0e46cc8cf61bc29a273781488650 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger <P.Rudiger@ed.ac.uk>
Date: Mon, 22 Aug 2016 12:54:23 +0100
Subject: [PATCH 13/13] Reverted tutorial layout fix

---
 doc/Tutorials/Columnar_Data.ipynb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/Tutorials/Columnar_Data.ipynb b/doc/Tutorials/Columnar_Data.ipynb
index 41fa1c44b4..3d9cea3827 100644
--- a/doc/Tutorials/Columnar_Data.ipynb
+++ b/doc/Tutorials/Columnar_Data.ipynb
@@ -821,7 +821,7 @@
    "outputs": [],
    "source": [
     "%opts HeatMap [show_values=False xticks=40 xrotation=90 aspect=1.2 invert_yaxis=True colorbar=True]\n",
-    "%opts Layout [figure_size=150]"
+    "%opts Layout [figure_size=120 aspect_weight=0.5 hspace=0.8 vspace=0]"
    ]
   },
   {