From 506e63662dffd046d773df061b12ded0585f3112 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Sun, 1 Sep 2019 18:06:52 -0600 Subject: [PATCH 01/17] Change existing gridspec when adding panels, instead of allotting zero-width spaces --- proplot/axes.py | 174 ++--- proplot/subplots.py | 1594 ++++++++++++++++++++++--------------------- proplot/wrappers.py | 3 + 3 files changed, 899 insertions(+), 872 deletions(-) diff --git a/proplot/axes.py b/proplot/axes.py index f62239496..8ff0837ee 100644 --- a/proplot/axes.py +++ b/proplot/axes.py @@ -220,13 +220,13 @@ def _get_side_axes(self, side): s = side[0] if s not in 'lrbt': raise ValueError(f'Invalid side {side!r}.') - xy = ('x' if s in 'lr' else 'y') + x = ('x' if s in 'lr' else 'y') idx = (0 if s in 'lt' else 1) # which side of range to test - coord = self._range_gridspec(xy, True)[idx] # side for a particular axes - return [ax for ax in self.figure._main_axes - if ax._range_gridspec(xy, True)[idx] == coord] + coord = self._range_gridspec(x)[idx] # side for a particular axes + return [ax for ax in self.figure._axes_main + if ax._range_gridspec(x)[idx] == coord] - def _get_title(self, abc=False, loc=None): + def _get_title_props(self, abc=False, loc=None): """Returns standardized location name, position keyword arguments, and setting keyword arguments for the relevant title or a-b-c label at location `loc`.""" @@ -311,12 +311,13 @@ def _inset_or_panel_loc(self, loc, **kwargs): if loc in ('left','right','top','bottom'): if isinstance(self, PanelAxes): raise ValueError('Axes {self} is already a PanelAxes. Cannot add a panel to a panel.') - axs = getattr(self, loc + 'panel') # axes_grid of panels - if not axs: - axs = self.panel_axes(loc, **kwargs) - else: - if kwargs: - warnings.warn(f'Ignoring panel keyword arg(s) {kwargs}. Panel already exists.') + axs = self.panel_axes(loc, **kwargs) + # axs = getattr(self, loc + 'panel') # axes_grid of panels + # if not axs: + # axs = self.panel_axes(loc, **kwargs) + # else: + # if kwargs: + # warnings.warn(f'Ignoring panel keyword arg(s) {kwargs}. Panel already exists.') try: ax = axs[idx] except IndexError: @@ -338,37 +339,27 @@ def inset_locator(ax, renderer): return bb return inset_locator - def _range_gridspec(self, mode, topmost): + def _range_gridspec(self, x): """Gets the column or row range for the axes. Use `topmost` to get properties for the main gridspec grid.""" subplotspec = self.get_subplotspec() - if topmost: - subplotspec = subplotspec.get_topmost_subplotspec() - if mode == 'x': - _, _, _, _, col1, col2 = subplotspec.get_rows_columns() - return col1//2, col2//2 # corrects for 'space' gridspec locations - elif mode == 'y': - _, _, row1, row2, _, _ = subplotspec.get_rows_columns() - return row1//2, row2//2 # account for spaces + if x == 'x': + _, _, _, _, col1, col2 = subplotspec.get_active_rows_columns() + return col1, col2 else: - raise ValueError(f'Invalid mode {mode!r}.') + _, _, row1, row2, _, _ = subplotspec.get_active_rows_columns() + return row1, row2 - def _range_tightbbox(self, mode): + def _range_tightbbox(self, x): """Gets span of tight bounding box, including twin axes and panels which are not considered real children and so aren't ordinarily included in the tight bounding box calc. `~proplot.axes.Axes.get_tightbbox` caches tight bounding boxes when `~Figure.get_tightbbox` is called.""" # TODO: Better resting for axes visibility - axs = self._iter_twins() - axs = [ax for ax in axs if ax._tight_bbox is not None] - if mode == 'x': - xs = np.array([(ax._tight_bbox.xmin, ax._tight_bbox.xmax) for ax in axs]) - return xs[:,0].min(), xs[:,1].max() - elif mode == 'y': - ys = np.array([(ax._tight_bbox.ymin, ax._tight_bbox.ymax) for ax in axs]) - return ys[:,0].min(), ys[:,1].max() + if x == 'x': + return self._tight_bbox.xmin, self._tight_bbox.xmax else: - raise ValueError(f'Invalid mode {mode!r}.') + return self._tight_bbox.ymin, self._tight_bbox.ymax def _reassign_suplabel(self, side): """Re-assigns the column and row labels to panel axes, if they exist. @@ -417,7 +408,7 @@ def _reassign_title(self): if not obj.get_text() or loc not in ('left','center','right'): continue kw = {} - loc, tobj, _ = tax._get_title(loc=loc) + loc, tobj, _ = tax._get_title_props(loc=loc) for key in ('text', 'color', 'fontproperties'): # TODO: add to this? kw[key] = getattr(obj, 'get_' + key)() tobj.update(kw) @@ -769,7 +760,7 @@ def format(self, *, title=None, top=None, # Apply new settings # Also if a-b-c label was moved, remove previous one and update # text on new one, in case self._abc_text has not changed. - loc, obj, kw = self._get_title(abc=True) + loc, obj, kw = self._get_title_props(abc=True) iloc = self._abc_loc obj = self._update_title(obj, **kw) titles_dict[loc] = obj @@ -793,7 +784,7 @@ def format(self, *, title=None, top=None, # NOTE: _update_title should never return new objects unless called # with *inner* titles... *outer* titles will just refresh, so we # don't need to re-assign the attributes or anything. - loc, obj, kw = self._get_title() + loc, obj, kw = self._get_title_props() if kw: for iloc,iobj in titles_dict.items(): if iloc is self._abc_loc: @@ -803,7 +794,7 @@ def format(self, *, title=None, top=None, for ikey,ititle in kwargs.items(): if not ikey[-5:] == 'title': raise ValueError(f'format() got an unexpected keyword argument {ikey!r}.') - iloc, iobj, ikw = self._get_title(loc=ikey[:-5]) + iloc, iobj, ikw = self._get_title_props(loc=ikey[:-5]) if ititle is not None: ikw['text'] = ititle if ikw: @@ -1141,10 +1132,10 @@ def heatmap(self, *args, **kwargs): obj = self.pcolormesh(*args, **kwargs) xlocator, ylocator = None, None if hasattr(obj, '_coordinates'): # be careful in case private API changes! but this is only way to infer coordinates - xy = obj._coordinates - xy = (xy[1:,...] + xy[:-1,...])/2 - xy = (xy[:,1:,:] + xy[:,:-1,:])/2 - xlocator, ylocator = xy[0,:,0], xy[:,0,1] + coords = obj._coordinates + coords = (coords[1:,...] + coords[:-1,...])/2 + coords = (coords[:,1:,:] + coords[:,:-1,:])/2 + xlocator, ylocator = coords[0,:,0], coords[:,0,1] self.format( xgrid=False, ygrid=False, xtickminor=False, ytickminor=False, xlocator=xlocator, ylocator=ylocator, @@ -1260,7 +1251,7 @@ def indicate_inset_zoom(self, alpha=None, linewidth=None, color=None, edgecolor= self._zoom = (rectpatch, connects) return (rectpatch, connects) - def panel_axes(self, *args, **kwargs): + def panel_axes(self, side, **kwargs): """ Adds a single `~proplot.axes.PanelAxes` or a stack of panels, and returns an `~proplot.subplots.axes_grid` of the panel stack. Also @@ -1284,9 +1275,9 @@ def panel_axes(self, *args, **kwargs): the stack. If float, units are inches. If string, units are interpreted by `~proplot.utils.units`. space, [lrbt]space : float or str or list thereof, optional - If passed, turns off `tightpanels`. Empty space between the main - subplot and the panel. If float, units are inches. If string, - units are interpreted by `~proplot.utils.units`. + Empty space between the main subplot and the panel. If float, + units are inches. If string, units are interpreted by + `~proplot.utils.units`. share, [lrbt]space : bool, optional Whether to enable axis sharing between the *x* and *y* axes of the main subplot and the panel long axes for each panel in the stack. @@ -1294,17 +1285,16 @@ def panel_axes(self, *args, **kwargs): The number of optional "stacked" panels on the left, right, bottom, and top sides, respectively. Defaults to ``1``. sep, [lrbt]sep : float, str, or list thereof, optional - If passed, turns off `tightpanels`. The separation between stacked - panels. If float, units are inches. If string, units are - interpreted by `~proplot.utils.units`. Ignored if the respecitve - `stack` keyword arg is None. + The separation between stacked panels. If float, units are inches. + If string, units are interpreted by `~proplot.utils.units`. Ignored + if the respecitve `stack` keyword arg is None. Returns ------- `~proplot.subplots.axes_grid` of `~proplot.axes.PanelAxes` The panel axes or stack of panel axes. """ - return self.figure._panel_axes(self, *args, **kwargs) + return self.figure._add_axes_panel(side, self, **kwargs) panel = panel_axes """Alias for `~Axes.panel_axes`.""" @@ -1356,36 +1346,13 @@ def _iter_panels(self, sides='lrbt'): axs.append(ax) return axs - def _iter_twins(self): - """Iterates over axes and twin axes.""" - axs = [] - for iax in (self, self._altx_child, self._alty_child): - if not iax or not iax.get_visible(): - continue - axs.append(iax) - return axs - - def _iter_twins_panels(self, sides='lrbt'): - """Axes tight bounding boxes include child axes. This iterates over - the axes and objects not counted as child axes -- namely panels, - twin axes, and panel twin axes.""" - axs = self._iter_twins() - if not ({*sides} <= {*'lrbt'}): - raise ValueError(f'Invalid sides {sides!r}.') - for s in sides: - for ax in getattr(self, s + 'panel'): - if not ax or not ax.get_visible(): - continue - axs.extend(ax._iter_twins()) - return axs - #-----------------------------------------------------------------------------# # Axes subclasses #-----------------------------------------------------------------------------# -def _rcloc_to_stringloc(xy, string): # figures out string location +def _rcloc_to_stringloc(x, string): # figures out string location """Gets *location string* from the *boolean* rc settings, for a given string prefix like ``'axes.spines'`` or ``'xtick'``.""" - if xy == 'x': + if x == 'x': top = rc[f'{string}.top'] bottom = rc[f'{string}.bottom'] if top is None and bottom is None: @@ -1398,7 +1365,7 @@ def _rcloc_to_stringloc(xy, string): # figures out string location return 'bottom' else: return 'neither' - elif xy == 'y': + elif x == 'y': left = rc[f'{string}.left'] right = rc[f'{string}.right'] if left is None and right is None: @@ -1412,7 +1379,7 @@ def _rcloc_to_stringloc(xy, string): # figures out string location else: return 'neither' else: - raise ValueError(f'"xy" must equal "x" or "y".') + raise ValueError(f'"x" must equal "x" or "y".') class CartesianAxes(Axes): """ @@ -1499,6 +1466,8 @@ def _altx_overrides(self): elif self._altx_parent is not None: # this axes is the result of altx self.spines['bottom'].set_visible(False) self.spines['top'].set_visible(True) + self.spines['left'].set_visible(False) + self.spines['right'].set_visible(False) self.xaxis.tick_top() self.xaxis.set_label_position('top') self.yaxis.set_visible(False) @@ -1514,6 +1483,8 @@ def _alty_overrides(self): elif self._alty_parent is not None: self.spines['left'].set_visible(False) self.spines['right'].set_visible(True) + self.spines['top'].set_visible(False) + self.spines['bottom'].set_visible(False) self.yaxis.tick_right() self.yaxis.set_label_position('right') self.xaxis.set_visible(False) @@ -1809,7 +1780,7 @@ def format(self, *, ylabelloc = 'left' # Begin loop - for (xy, axis, + for (x, axis, label, color, ticklen, margin, bounds, tickloc, spineloc, @@ -1847,11 +1818,11 @@ def format(self, *, ('log','logit','inverse','symlog')): formatter = 'simple' scale, args, kw = axistools.Scale(scale, **scale_kw) - getattr(self, f'set_{xy}scale')(scale, *args, **kw) + getattr(self, f'set_{x}scale')(scale, *args, **kw) # Axis limits # NOTE: 3.1+ has axis.set_inverted(), below is from source code if lim is not None: - getattr(self, f'set_{xy}lim')(lim) + getattr(self, f'set_{x}lim')(lim) if reverse: lo, hi = axis.get_view_interval() axis.set_view_interval( @@ -1867,7 +1838,7 @@ def format(self, *, }) if color is not None: kw['color'] = color - sides = ('bottom','top') if xy == 'x' else ('left','right') + sides = ('bottom','top') if x == 'x' else ('left','right') spines = [self.spines[s] for s in sides] for spine,side in zip(spines,sides): # Line properties @@ -1893,7 +1864,7 @@ def format(self, *, try: spine.set_position(spineloc) except ValueError: - raise ValueError(f'Invalid {xy} spine location {spineloc!r}. Options are {", ".join((*sides, "both", "neither"))}.') + raise ValueError(f'Invalid {x} spine location {spineloc!r}. Options are {", ".join((*sides, "both", "neither"))}.') # Apply spine bounds if bounds is not None and spine.get_visible(): spine.set_bounds(*bounds) @@ -1911,7 +1882,7 @@ def format(self, *, } for which,igrid in zip(('major', 'minor'), (grid,gridminor)): # Tick properties - kw_ticks = rc.category(xy + 'tick.' + which) + kw_ticks = rc.category(x + 'tick.' + which) if kw_ticks is None: kw_ticks = {} else: @@ -1974,7 +1945,7 @@ def format(self, *, kw = rc.fill({ 'labelcolor': 'tick.labelcolor', # new props 'labelsize': 'tick.labelsize', - 'color': xy + 'tick.color', + 'color': x + 'tick.color', }) if color: kw['color'] = color @@ -1984,9 +1955,9 @@ def format(self, *, kw['pad'] = 1 # ticklabels should be much closer if ticklabeldir == 'in': # put tick labels inside the plot tickdir = 'in' - pad = (rc.get(xy + 'tick.major.size') - + rc.get(xy + 'tick.major.pad') - + rc.get(xy + 'tick.labelsize')) + pad = (rc.get(x + 'tick.major.size') + + rc.get(x + 'tick.major.pad') + + rc.get(x + 'tick.labelsize')) kw['pad'] = -pad if tickdir is not None: kw['direction'] = tickdir @@ -2000,7 +1971,7 @@ def format(self, *, }) if rotation is not None: kw = {'rotation':rotation} - if xy == 'x': + if x == 'x': self._datex_rotated = True if rotation not in (0,90,-90): kw['ha'] = ('right' if rotation > 0 else 'left') @@ -2008,7 +1979,7 @@ def format(self, *, t.update(kw) # Margins if margin is not None: - self.margins(**{xy: margin}) + self.margins(**{x: margin}) # Axis label updates # NOTE: This has to come after set_label_position, or ha or va @@ -2047,7 +2018,7 @@ def format(self, *, fixedformatfix = False if formatter is not None or tickrange is not None and not ( isinstance(axis.get_major_formatter(), mticker.NullFormatter) - and getattr(self, '_share' + xy)): + and getattr(self, '_share' + x)): # Tick range if tickrange is not None: if formatter not in (None,'auto'): @@ -2059,7 +2030,7 @@ def format(self, *, locator = axis.get_major_locator() formatter_kw.setdefault('locator', locator) if (isinstance(axis.get_major_formatter(), mticker.NullFormatter) - and getattr(self, '_share' + xy)): + and getattr(self, '_share' + x)): pass # this is a shared axis with disabled ticks else: formatter = axistools.Formatter(formatter, date=date, **formatter_kw) @@ -2076,7 +2047,7 @@ def format(self, *, # have tick_values method, so we just call them. if fixticks or fixedformatfix or bounds is not None or axis.get_scale() == 'cutoff': if bounds is None: - bounds = getattr(self, f'get_{xy}lim')() + bounds = getattr(self, f'get_{x}lim')() locator = axistools.Locator([x for x in axis.get_major_locator()() if bounds[0] <= x <= bounds[1]]) axis.set_major_locator(locator) locator = axistools.Locator([x for x in axis.get_minor_locator()() if bounds[0] <= x <= bounds[1]]) @@ -2099,14 +2070,13 @@ def altx(self, *args, **kwargs): raise ValueError('This *is* a twin axes!') with self.figure._unlock(): ax = self._make_twin_axes(sharey=self, projection=self.name) - # Setup - # Some things we enforce every time draw is invoked, others not ax.set_autoscaley_on(self.get_autoscaley_on()) # shared axes must have matching autoscale ax.grid(False) self._altx_child = ax ax._altx_parent = self self._altx_overrides() ax._altx_overrides() + self.add_child_axes(ax) return ax def alty(self): @@ -2126,6 +2096,7 @@ def alty(self): ax._alty_parent = self self._alty_overrides() ax._alty_overrides() + self.add_child_axes(ax) return ax def dualx(self, offset=0, scale=1, xscale='linear', xlabel=None, **kwargs): @@ -2358,9 +2329,6 @@ def legend(self, *args, fill=True, width=None, **kwargs): if not fill: return super().legend(*args, **kwargs) # Hide content - # TODO: Make panel size dependent on legend width; currently - # just use small number and let tight layout allocate space - self.figure._panel_resize(self, width, 'legend') for s in self.spines.values(): s.set_visible(False) self.xaxis.set_visible(False) @@ -2391,7 +2359,13 @@ def legend(self, *args, fill=True, width=None, **kwargs): else: raise ValueError(f'Invalid panel side {loc!r}.') kwargs['loc'] = loc - return wrappers.legend_wrapper(self, *args, **kwargs) + leg = wrappers.legend_wrapper(self, *args, **kwargs) + + # Resize panel and return + # TODO: Make panel size dependent on legend width, and remove + # legwidth from .proplotrc + self.figure._resize_panel(self, width, 'legend') + return leg def colorbar(self, *args, fill=True, width=None, length=None, **kwargs): """" @@ -2423,7 +2397,6 @@ def colorbar(self, *args, fill=True, width=None, length=None, **kwargs): return super().colorbar(*args, width=width, length=length, **kwargs) # Hide content and resize panel # NOTE: Do not run self.clear in case we want title above this - self.figure._panel_resize(self, width, 'colorbar') for s in self.spines.values(): s.set_visible(False) self.xaxis.set_visible(False) @@ -2477,6 +2450,9 @@ def colorbar(self, *args, fill=True, width=None, length=None, **kwargs): ticklocation = kwargs.pop('ticklocation', None) or ticklocation kwargs.update({'orientation':orientation, 'ticklocation':ticklocation}) cbar = wrappers.colorbar_wrapper(ax, *args, **kwargs) + + # Resize panel and return + self.figure._resize_panel(self, width, 'colorbar') return cbar class ProjectionAxes(Axes): @@ -2746,7 +2722,7 @@ def format(self, *args, self.set_theta_direction(thetadir) # Iterate - for (xy, name, axis, + for (x, name, axis, min_, max_, locator, formatter, locator_kw, formatter_kw, @@ -2780,7 +2756,7 @@ def format(self, *args, # Grid and grid label settings # NOTE: Not sure if polar lines inherit tick or grid props kw = rc.fill({ - 'color': xy + 'tick.color', + 'color': x + 'tick.color', 'labelcolor': 'tick.labelcolor', # new props 'labelsize': 'tick.labelsize', 'grid_color': 'grid.color', diff --git a/proplot/subplots.py b/proplot/subplots.py index a2c4ed149..561e40a48 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -43,6 +43,7 @@ import matplotlib.figure as mfigure import matplotlib.transforms as mtransforms import matplotlib.gridspec as mgridspec +import matplotlib.axes as maxes try: import matplotlib.backends.backend_macosx as mbackend except ImportError: @@ -51,8 +52,7 @@ from .utils import _notNone, _counter, units from . import projs, axes __all__ = [ - 'axes_grid', 'close', 'show', 'subplots', 'Figure', - 'FlexibleGridSpec', 'FlexibleGridSpecBase', 'FlexibleGridSpecFromSubplotSpec', + 'axes_grid', 'close', 'show', 'subplots', 'Figure', 'FlexibleGridSpec', ] # Translation @@ -87,14 +87,24 @@ class axes_grid(list): stacked axes panels. The shape of the array is stored in the ``shape`` attribute. See the `~axes_grid.__getattr__` and `~axes_grid.__getitem__` methods for details.""" - def __init__(self, list_, n=1, order='C'): - # Add special attributes that support 2D grids of axes - # NOTE: The input list is always a vector *already unfurled* in row-major - # or column-major order, and 'n' is the fastest-moving dimension size, i.e. - # ncols for order == 'C' and nrows for order == 'F'. - self._n = n # ncols or nrows - self._order = order # order - super().__init__(list_) + def __init__(self, objs, n=1, order='C'): + """ + Parameters + ---------- + objs : list-like + Any 1D iterable of objects. + n : int, optional + The length of the fastest-moving dimension, i.e. the number of + columns when `order` is ``'C'``, and the number of rows when `order` + is ``'F'``. Used to treat lists as pseudo-2D arrays. + order : {'C', 'F'}, optional + Whether 1D indexing returns results in row-major (C-style) or + column-major (Fortran-style) order, respectively. Used to treat + lists as pseudo-2D arrays. + """ + self._n = n + self._order = order + super().__init__(objs) self.shape = (len(self)//n, n)[::(1 if order == 'C' else -1)] def __repr__(self): @@ -232,98 +242,136 @@ def axes_grid_iterator(*args, **kwargs): #-----------------------------------------------------------------------------# # Gridspec classes #-----------------------------------------------------------------------------# -def _positem(n): - """Account for negative indices.""" - if n < 0: - return 2*(n+1) - 1 # want -1 to stay -1, -2 becomes -3, etc. - else: - return n*2 - -def _normalize(key, size): - """Transform gridspec index into standardized form.""" - if isinstance(key, slice): - start, stop, _ = key.indices(size) - if stop > start: - return start, stop - 1 - else: - if key < 0: - key += size - if 0 <= key < size: - return key, key - raise IndexError(f"Invalid index: {key} with size {size}.") +class FlexibleSubplotSpec(mgridspec.SubplotSpec): + """ + Adds two helper methods to `~matplotlib.gridspec.SubplotSpec` that return + the geometry *excluding* rows and columns allocated for spaces. + """ + def get_active_geometry(self): + """Returns the number of rows, number of columns, and 1D subplot + location indices, ignoring rows and columns allocated for spaces.""" + nrows, ncols, row1, row2, col1, col2 = self.get_active_rows_columns() + num1 = row1*ncols + col1 + num2 = row2*ncols + col2 + return nrows, ncols, num1, num2 + + def get_active_rows_columns(self): + """Returns the number of rows, number of columns, first subplot row, + last subplot row, first subplot column, and last subplot column, + ignoring rows and columns allocated for spaces.""" + gridspec = self.get_gridspec() + nrows, ncols = gridspec.get_geometry() + row1, col1 = divmod(self.num1, ncols) + if self.num2 is not None: + row2, col2 = divmod(self.num2, ncols) + else: + row2 = row1 + col2 = col1 + return nrows//2, ncols//2, row1//2, row2//2, col1//2, col2//2 -class FlexibleGridSpecBase(object): +class FlexibleGridSpec(mgridspec.GridSpec): """ - Generalization of builtin `~matplotlib.gridspec.GridSpec` that allows for - grids with **arbitrary spacing** between rows and columns of axes. + `~matplotlib.gridspec.GridSpec` generalization that allows for grids with + *variable spacing* between successive rows and columns of axes. Accomplishes this by actually drawing ``nrows*2 + 1`` and ``ncols*2 + 1`` - `~matplotlib.gridspec.GridSpecBase` rows and columns, setting - `wspace` and `hspace` to ``0``, and masking out every other row/column - of the `~matplotlib.gridspec.GridSpecBase`, so they act as "spaces". - These "spaces" are allowed to vary in width using the builtin - `width_ratios` and `height_ratios` keyword args. + `~matplotlib.gridspec.GridSpec` rows and columns, setting `wspace` + and `hspace` to ``0``, and masking out every other row and column + of the `~matplotlib.gridspec.GridSpec`, so they act as "spaces". + These "spaces" are then allowed to vary in width using the builtin + `width_ratios` and `height_ratios` properties. """ - def __init__(self, nrows, ncols, **kwargs): + def __init__(self, figure, nrows=1, ncols=1, **kwargs): """ Parameters ---------- - nrows, ncols : int - Number of rows, columns on the subplot grid. - wspace, hspace : float or list of float - The horizontal, vertical spacing between columns, rows of - subplots. In `~proplot.subplots.subplots`, ``wspace`` + figure : `Figure` + The figure instance filled by this gridspec. Unlike + `~matplotlib.gridspec.GridSpec`, this argument is required. + nrows, ncols : int, optional + The number of rows and columns on the subplot grid. + hspace, wspace : float or list of float + The vertical and horizontal spacing between rows and columns of + subplots, respectively. In `~proplot.subplots.subplots`, ``wspace`` and ``hspace`` are in physical units. When calling - `FlexibleGridSpecBase` directly, values are scaled relative to - the average subplot width or height. If list, length of - ``wspace``, ``hspace`` must be ``ncols-1``, ``nrows-1``. + `FlexibleGridSpec` directly, values are scaled relative to + the average subplot height or width. + + If float, the spacing is identical between all rows and columns. If + list of float, the length of the lists must equal ``nrows-1`` + and ``ncols-1``, respectively. height_ratios, width_ratios : list of float - Ratios for the width/height of columns/rows of subplots. - For example, ``width_ratios=[1,2]`` specifes 2 columns of subplots, - the second one twice as wide as the first. + Ratios for the relative heights and widths for rows and columns + of subplots, respectively. For example, ``width_ratios=(1,2)`` + scales a 2-column gridspec so that the second column is twice as + wide as the first column. left, right, top, bottom : float or str - Passed to `~matplotlib.gridspec.GridSpec`, denote the margin - positions for the subplot grid in figure-relative coordinates. + Passed to `~matplotlib.gridspec.GridSpec`, denotes the margin + positions in figure-relative coordinates. + **kwargs + Passed to `~matplotlib.gridspec.GridSpec`. """ self._nrows = nrows*2-1 # used with get_geometry self._ncols = ncols*2-1 - self._nrows_visible = nrows - self._ncols_visible = ncols + self._nrows_active = nrows + self._ncols_active = ncols wratios, hratios, kwargs = self._spaces_as_ratios(**kwargs) super().__init__(self._nrows, self._ncols, - hspace=0, wspace=0, # we implement these as invisible rows/columns + hspace=0, wspace=0, # we implement these as inactive rows/columns width_ratios=wratios, height_ratios=hratios, + figure=figure, **kwargs, ) def __getitem__(self, key): - """Magic obfuscation that renders `~matplotlib.gridspec.GridSpecBase` + """Magic obfuscation that renders `~matplotlib.gridspec.GridSpec` rows and columns designated as 'spaces' inaccessible.""" - # Get indices nrows, ncols = self.get_geometry() - nrows_visible, ncols_visible = self.get_visible_geometry() + nrows_active, ncols_active = self.get_active_geometry() if not isinstance(key, tuple): # usage gridspec[1,2] - num1, num2 = _normalize(key, nrows_visible * ncols_visible) + num1, num2 = self._normalize(key, nrows_active * ncols_active) else: if len(key) == 2: k1, k2 = key else: raise ValueError(f'Invalid index {key!r}.') - num1 = _normalize(k1, nrows_visible) - num2 = _normalize(k2, ncols_visible) + num1 = self._normalize(k1, nrows_active) + num2 = self._normalize(k2, ncols_active) num1, num2 = np.ravel_multi_index((num1, num2), (nrows, ncols)) - # Correct for negative nums - num1, num2 = _positem(num1), _positem(num2) - return mgridspec.SubplotSpec(self, num1, num2) + num1 = self._positem(num1) + num2 = self._positem(num2) + return FlexibleSubplotSpec(self, num1, num2) + + @staticmethod + def _positem(size): + """Account for negative indices.""" + if size < 0: + return 2*(size+1) - 1 # want -1 to stay -1, -2 becomes -3, etc. + else: + return size*2 + + @staticmethod + def _normalize(key, size): + """Transform gridspec index into standardized form.""" + if isinstance(key, slice): + start, stop, _ = key.indices(size) + if stop > start: + return start, stop - 1 + else: + if key < 0: + key += size + if 0 <= key < size: + return key, key + raise IndexError(f"Invalid index: {key} with size {size}.") def _spaces_as_ratios(self, hspace=None, wspace=None, # spacing between axes height_ratios=None, width_ratios=None, **kwargs): - """For keyword arg usage, see `FlexibleGridSpecBase`.""" + """For keyword arg usage, see `FlexibleGridSpec`.""" # Parse flexible input - nrows, ncols = self.get_visible_geometry() + nrows, ncols = self.get_active_geometry() hratios = np.atleast_1d(_notNone(height_ratios, 1)) wratios = np.atleast_1d(_notNone(width_ratios, 1)) hspace = np.atleast_1d(_notNone(hspace, np.mean(hratios)*0.10)) # this is relative to axes @@ -349,8 +397,6 @@ def _spaces_as_ratios(self, raise ValueError(f'Require {nrows-1} height spacings for {nrows} rows, got {len(hspace)}.') # Assign spacing as ratios - # Also return extra kwargs, will be passed to superclass initializers - # or superclass update method. nrows, ncols = self.get_geometry() wratios_final = [None]*ncols wratios_final[::2] = [*wratios] @@ -362,44 +408,49 @@ def _spaces_as_ratios(self, hratios_final[1::2] = [*hspace] return wratios_final, hratios_final, kwargs # bring extra kwargs back - def get_wratios(self): - """Alias for `~matplotlib.gridspec.Gridspecbase.get_width_ratios`.""" - return super().get_width_ratios() + def get_margins(self): + """Returns left, bottom, right, top values. Not sure why this method + doesn't already exist on `~matplotlib.gridspec.GridSpec`.""" + return self.left, self.bottom, self.right, self.top - def get_hratios(self): - """Alias for `~matplotlib.gridspec.Gridspecbase.get_height_ratios`.""" - return super().get_height_ratios() + def get_hspace(self): + """Returns row ratios allocated for spaces.""" + return self.get_height_ratios()[1::2] - def get_visible_geometry(self): - """Like `~matplotlib.gridspec.GridspecBase.get_geometry`, but returns - the number of visible rows and columns, i.e. the number of rows and - columns that aren't skipped over by - `~FlexibleGridSpecBase.__getitem__`.""" - return self._nrows_visible, self._ncols_visible + def get_wspace(self): + """Returns column ratios allocated for spaces.""" + return self.get_width_ratios()[1::2] -class FlexibleGridSpec(FlexibleGridSpecBase, mgridspec.GridSpec): - """Mixes `FlexibleGridSpecBase` with `~matplotlib.gridspec.GridSpec`.""" - def __init__(self, figure, **kwargs): - """ - Parameters - ---------- - figure : `Figure` - The figure instance filled by this gridspec. Unlike - `~matplotlib.gridspec.GridSpec`, this argument is required. - **kwargs - Passed to `~matplotlib.gridspec.GridSpec`. - """ - super().__init__(figure=figure, **kwargs) + def get_active_height_ratios(self): + """Returns height ratios exlucding slots allocated for spaces.""" + return self.get_height_ratios()[::2] + + def get_active_width_ratios(self): + """Returns width ratios excluding slots allocated for spaces.""" + return self.get_width_ratios()[::2] + + def get_active_geometry(self): + """Returns the number of active rows and columns, i.e. the rows and + columns that aren't skipped by `~FlexibleGridSpec.__getitem__`.""" + return self._nrows_active, self._ncols_active def update(self, **kwargs): - """Updates the width ratios, height ratios, and spacing for subplot - columns and rows. The default `~matplotlib.gridspec.GridSpec.update` - fails to update axes positions after successive edits to the figure, - evidently because the figure is removed from the figure manager(s).""" + """ + Updates the width ratios, height ratios, gridspec margins, and spacing + allocated between subplot rows and columns. + + The default `~matplotlib.gridspec.GridSpec.update` tries to update + positions for axes on all active figures -- but this can fail after + successive figure edits if it has been removed from the figure + manager. So, we explicitly require that the gridspec is dedicated to + a particular `~matplotlib.figure.Figure` instance, and just edit axes + positions for axes on that instance. + """ # Convert spaces to ratios wratios, hratios, kwargs = self._spaces_as_ratios(**kwargs) self.set_width_ratios(wratios) self.set_height_ratios(hratios) + # Validate args kwargs.pop('ncols', None) kwargs.pop('nrows', None) @@ -409,6 +460,7 @@ def update(self, **kwargs): self.top = kwargs.pop('top', None) if kwargs: raise ValueError(f'Unknown keyword arg(s): {kwargs}.') + # Apply to figure and all axes fig = self.figure fig.subplotpars.update(self.left, self.bottom, self.right, self.top) @@ -417,10 +469,6 @@ def update(self, **kwargs): ax.set_position(ax.figbox) fig.stale = True -class FlexibleGridSpecFromSubplotSpec(FlexibleGridSpecBase, mgridspec.GridSpecFromSubplotSpec): - """Mixes `FlexibleGridSpecBase` with `~matplotlib.gridspec.GridSpecFromSubplotSpec`.""" - pass - #-----------------------------------------------------------------------------# # Helper funcs #-----------------------------------------------------------------------------# @@ -442,22 +490,59 @@ def _subplots_geometry(**kwargs): wratios, hratios = kwargs['wratios'], kwargs['hratios'] left, bottom = kwargs['left'], kwargs['bottom'] right, top = kwargs['right'], kwargs['top'] - + # Panel string toggles + wpanels, hpanels = kwargs['wpanels'], kwargs['hpanels'] + + # Checks, important now that we modify gridspec geometry + # TODO: Helper func? Nah, no big deal + if len(hratios) != nrows: + raise ValueError(f'Expected {nrows} width ratios for {nrows} rows, got {len(hratios)}.') + if len(wratios) != ncols: + raise ValueError(f'Expected {ncols} width ratios for {ncols} columns, got {len(wratios)}.') + if len(hspace) != nrows - 1: + raise ValueError(f'Expected {nrows - 1} hspaces for {nrows} rows, got {len(hspace)}.') + if len(wspace) != ncols - 1: + raise ValueError(f'Expected {ncols - 1} wspaces for {ncols} columns, got {len(wspace)}.') + if len(hpanels) != nrows: + raise ValueError(f'Expected {nrows} hpanel toggles for {nrows} rows, got {len(hpanels)}.') + if len(wpanels) != ncols: + raise ValueError(f'Expected {ncols} wpanel toggles for {ncols} columns, got {len(wpanels)}.') + + # Get indices corresponding to main axes or main axes space slots + # TODO: Shouldn't panel space be included in these calculations? + idxs_ratios, idxs_space = [], [] + for panels in (hpanels,wpanels): + # Ratio indices + mask = np.array([bool(s) for s in panels]) + ratio_idxs, = np.where(~mask) + idxs_ratios.append(ratio_idxs) + # Space indices + space_idxs = [] + for idx in ratio_idxs[:-1]: # exclude last axes slot + offset = 1 + while panels[idx + offset] not in 'rbf': # main space is next to this + offset += 1 + space_idxs.append(idx + offset - 1) + idxs_space.append(space_idxs) # Separate the panel and axes ratios - axwspace, axhspace = wspace[3:-1:3], hspace[3:-1:3] # ignores figure panels - axwidths, axheights = wratios[2:-1:3], hratios[2:-1:3] - lpanels, tpanels = wratios[1:-1:3], hratios[1:-1:3] - rpanels, bpanels = wratios[3:-1:3], hratios[3:-1:3] - lpanel, rpanel = wratios[0], wratios[-1] - tpanel, bpanel = hratios[0], hratios[-1] + haxes = [hratios[idx] for idx in idxs_ratios[0]] + waxes = [wratios[idx] for idx in idxs_ratios[1]] + hpanels = [ratio for idx,ratio in enumerate(hratios) if idx not in idxs_ratios[0]] + wpanels = [ratio for idx,ratio in enumerate(wratios) if idx not in idxs_ratios[1]] + haxes_space = [hspace[idx] for idx in idxs_space[0]] + waxes_space = [wspace[idx] for idx in idxs_space[1]] + # Reduced geometry + nrows_ax = len(haxes) + ncols_ax = len(waxes) # Get reference properties, account for panel slots in space and ratios - dx, dy = xref[1] - xref[0] + 1, yref[1] - yref[0] + 1 - rwspace = sum(axwspace[xref[0]:xref[1]]) - rhspace = sum(axhspace[yref[0]:yref[1]]) - rwratio = ncols*(sum(axwidths[xref[0]:xref[1]+1])/dx)/sum(axwidths) - rhratio = nrows*(sum(axheights[yref[0]:yref[1]+1])/dy)/sum(axheights) - if np.iterable(aspect): + (x1, x2), (y1, y2) = xref, yref + dx, dy = x2 - x1 + 1, y2 - y1 + 1 + rwspace = sum(waxes_space[x1:x2]) + rhspace = sum(haxes_space[y1:y2]) + rwratio = (ncols_ax*sum(waxes[x1:x2+1]))/(dx*sum(waxes)) + rhratio = (nrows_ax*sum(haxes[y1:y2+1]))/(dy*sum(haxes)) + if np.iterable(aspect): aspect = aspect[0]/aspect[1] # Determine figure and axes dims from input in width or height dimenion. @@ -470,37 +555,31 @@ def _subplots_geometry(**kwargs): axwidth = units(rc['subplots.axwidth']) if axheight is not None: auto_width = True - axheight_all = ((axheight - rhspace)/dy/rhratio)*nrows - height = (axheight_all + top + bottom + sum(hspace) - + sum(bpanels) + sum(tpanels) + bpanel + tpanel) + axheight_all = (nrows_ax*(axheight - rhspace))/(dy*rhratio) + height = axheight_all + top + bottom + sum(hspace) + sum(hpanels) if axwidth is not None: auto_height = True - axwidth_all = ((axwidth - rwspace)/dx/rwratio)*ncols - width = (axwidth_all + left + right + sum(wspace) - + sum(lpanels) + sum(rpanels) + lpanel + rpanel) + axwidth_all = (ncols_ax*(axwidth - rwspace))/(dx*rwratio) + width = axwidth_all + left + right + sum(wspace) + sum(wpanels) if axwidth is not None and axheight is not None: auto_width = auto_height = False else: if height is not None: - axheight_all = (height - top - bottom - sum(hspace) - - sum(bpanels) - sum(tpanels) - bpanel - tpanel) - axheight = (axheight_all*dy/nrows) + rhspace + axheight_all = height - top - bottom - sum(hspace) - sum(hpanels) + axheight = (axheight_all*dy*rhratio)/nrows_ax + rhspace if width is not None: - axwidth_all = (width - left - right - sum(wspace) - - sum(lpanels) - sum(rpanels) - lpanel - rpanel) - axwidth = (axwidth_all*dx/ncols) + rwspace + axwidth_all = width - left - right - sum(wspace) - sum(wpanels) + axwidth = (axwidth_all*dx*rwratio)/ncols_ax + rwspace # Automatically figure dim that was not specified above - if auto_width: - axwidth = axheight*aspect - axwidth_all = ((axwidth - rwspace)/dx/rwratio)*ncols - width = (axwidth_all + left + right + sum(wspace) - + sum(lpanels) + sum(rpanels) + lpanel + rpanel) - elif auto_height: + if auto_height: axheight = axwidth/aspect - axheight_all = ((axheight - rhspace)/dy/rhratio)*nrows - height = (axheight_all + top + bottom + sum(hspace) - + sum(bpanels) + sum(tpanels) + bpanel + tpanel) + axheight_all = (nrows_ax*(axheight - rhspace))/(dy*rhratio) + height = axheight_all + top + bottom + sum(hspace) + sum(hpanels) + elif auto_width: + axwidth = axheight*aspect + axwidth_all = (ncols_ax*(axwidth - rwspace))/(dx*rwratio) + width = axwidth_all + left + right + sum(wspace) + sum(wpanels) if axwidth_all < 0: raise ValueError(f"Not enough room for axes (would have width {axwidth_all}). Try using tight=False, increasing figure width, or decreasing 'left', 'right', or 'wspace' spaces.") if axheight_all < 0: @@ -508,23 +587,22 @@ def _subplots_geometry(**kwargs): # Reconstruct the ratios array with physical units for subplot slots # The panel slots are unchanged because panels have fixed widths - axwidths = axwidth_all*np.array(axwidths)/sum(axwidths) - axheights = axheight_all*np.array(axheights)/sum(axheights) - wratios = np.repeat((None,), 3*ncols + 2) - hratios = np.repeat((None,), 3*nrows + 2) - wratios[2:-1:3], hratios[2:-1:3] = axwidths, axheights - wratios[3:-1:3], hratios[3:-1:3] = rpanels, bpanels - wratios[1:-1:3], hratios[1:-1:3] = lpanels, tpanels - wratios[0], wratios[-1] = lpanel, rpanel - hratios[0], hratios[-1] = tpanel, bpanel + waxes = axwidth_all*np.array(waxes)/sum(waxes) + haxes = axheight_all*np.array(haxes)/sum(haxes) + for idx,ratio in zip(idxs_ratios[0],haxes): + hratios[idx] = ratio + for idx,ratio in zip(idxs_ratios[1],waxes): + wratios[idx] = ratio # Convert margins to figure-relative coordinates - left, right = left/width, 1 - right/width - top, bottom = 1 - top/height, bottom/height + left = left/width + bottom = bottom/height + right = 1 - right/width + top = 1 - top/height # Return gridspec keyword args gridspec_kw = { - 'ncols': ncols*3 + 2, 'nrows': nrows*3 + 2, + 'ncols': ncols, 'nrows': nrows, 'wspace': wspace, 'hspace': hspace, 'width_ratios': wratios, 'height_ratios': hratios, 'left': left, 'bottom': bottom, 'right': right, 'top': top, @@ -532,74 +610,64 @@ def _subplots_geometry(**kwargs): return (width, height), gridspec_kw, kwargs -def _panels_kwargs(sides, kwargs, figure=False): +def _panels_kwargs(side, kwargs, figure=False, mode='panel'): """Converts global keywords like `space` and `width` to side-local keywords like `lspace` and `lwidth`, and applies default settings.""" + # Checks + # NOTE: The 'stack' keyword arg is now invalid, to add stacked panels + # simply call panel axes more than once or add colorbar more than once! + s = side[0] + if s not in 'lrbt': + raise ValueError(f'Invalid panel spec {side!r}.') # Detect *unknown* keyword args, raise error if found! - if not {*sides} <= {*'lrbt'}: - raise ValueError(f'Invalid panel spec {sides!r}. Valid characters are "l", "r", "b", "t".') - unknown = {} - names = ('stack', 'share', 'space', 'width', 'sep') - names = (('array', *names) if figure else names) - regex = re.compile(f'^[tlrb]?({"|".join(names)})$') - for key in (*kwargs.keys(),): - if not regex.match(key): - unknown[key] = kwargs.pop(key) + regex = re.compile(f'^[tlrb]?({"array|" if figure else ""}share|space|width)$') + kwextra = {key:kwargs.pop(key) for key in (*kwargs,) if not regex.match(key)} + deprec1 = {key:value for key,value in kwextra.items() if key in ( + 'sep', 'lsep', 'rsep', 'bsep', 'tsep', + 'stack', 'lstack', 'rstack', 'bstack', 'tstack', + )} + deprec2 = {key:value for key,value in kwextra.items() if key in ( + 'colorbar', 'colorbars', 'legend', 'legends', + 'axcolorbar', 'axcolorbars', 'axlegend', 'axlegends', + 'axcolorbar_kw', 'axcolorbars_kw', 'axlegend_kw', 'axlegends_kw', + )} + unknown = {key:value for key,value in kwextra.items() + if key not in deprec1 and key not in deprec2} + if deprec1: + raise ValueError(f'You used the following deprecated subplots() or panel_axes() keyword arg(s): {deprec1!r}. To draw "stacked" panels with the current API, simply call e.g. Axes.colorbar(loc="r") or Axes.panel("r") more than once, and multiple panels will be drawn on that side.') + if deprec2: + raise ValueError(f'You used the following deprecated subplots() keyword arg(s): {deprec2!r}. With the current API, you just use "panel", "panels", "axpanel", "axpanels", "axpanel_kw", and/or "axpanels_kw". If you "fill" a panel with a colorbar or legend, its width will be adjusted automatically.') if unknown: - raise ValueError(f'Unknown keyword arg(s): {unknown}.') + raise ValueError(f'Unknown subplots() or panel_axes() keyword arg(s): {unknown!r}.') # Detect *ignored* panel keyword args, issue warning - offsides = ''.join({*'lrbt'} - {*sides}) - regex = re.compile(f'^[{offsides}]({"|".join(names)})$') - ikw = {key:value for key,value in kwargs.items() if regex.match(offsides)} + s_off = ''.join({*'lrbt'} - {s}) + regex = re.compile(f'^[{s_off}]({"array|" if figure else ""}share|width|space)$') + ikw = {key:value for key,value in kwargs.items() if regex.match(s_off)} if ikw: warnings.warn(f'Ignoring keyword arg(s): {ikw}. Active sides are: {sides!r}.') - # Get default keyword values, add user input to orig dictionary + # Try to use local first, global if no local found + def get(key): + return _notNone(kwargs.get(s + key, None), kwargs.get(key, None)) kwout, kworig = {}, {} - for side in sides: - # Get arguments, try to use local first, global if no local found - get = lambda key: _notNone(kwargs.get(side + key, None), kwargs.get(key, None)) - width = np.atleast_1d(units(get('width'))) - share = _notNone(get('share'), True) - stack = _notNone(get('stack'), len(width)) - space = units(get('space')) - sep = np.atleast_1d(units(get('sep'))) - - # Validate stackable args, then store originals! - if stack < 1: - raise ValueError(f'{side+stack!r} argument must be integer >=1.') - if len(width) == 1: - width = np.repeat(width, (stack,)) - if len(width) != stack: - raise ValueError(f'For side {side!r}, have {stack} stacked panels, but got {len(width)} widths.') - if len(sep) == 1: - sep = np.repeat(sep, (stack-1,)) - if len(sep) != stack-1: - raise ValueError(f'For side {side!r}, have {stack} stacked panels, but got {len(sep)} separations.') - kworig[side + 'space'] = space - kworig[side + 'width'] = width - kworig[side + 'sep'] = sep - - # Get default arg values - space = _notNone(space, units(rc['subplots.' + ('panel' if share - and not figure - else 'xlab' if side == 'b' else 'ylab' if side == 'l' - else 'inner' if figure else 'panel') + 'space'])) - width = np.array(width) # make a copy - width[width==None] = units(rc['subplots.panelwidth']) - sep = np.array(sep) # make a copy - sep[sep==None] = units(rc['subplots.' + ('inner' if share - else 'ylab' if side in 'lr' else 'xlab') + 'space']) - - # Store in output dictionary - kwout[side + 'share'] = share - kwout[side + 'width'] = width - kwout[side + 'space'] = space - kwout[side + 'sep'] = sep - if figure: - kwout[side + 'array'] = get('array') - + # Store originals, prevents automatic changes if user passed anything + share = _notNone(get('share'), (mode == 'panel')) + width = units(get('width')) + space = units(get('space')) + kworig[s + 'width'] = width + kworig[s + 'space'] = space + # Store values used in initial drawing + width = units(_notNone(width, rc['subplots.' + mode + 'width'])) + space = _notNone(space, units(rc['subplots.' + ('panel' if share + and not figure + else 'xlab' if s == 'b' else 'ylab' if s == 'l' + else 'inner' if figure else 'panel') + 'space'])) + kwout[s + 'share'] = share + kwout[s + 'width'] = width + kwout[s + 'space'] = space + if figure: + kwout[s + 'array'] = get('array') return kwout, kworig #-----------------------------------------------------------------------------# @@ -636,7 +704,8 @@ class Figure(mfigure.Figure): def __init__(self, tight=None, pad=None, axpad=None, panelpad=None, - autoformat=True, ref=1, + autoformat=True, + ref=1, order='C', # documented in subplots but needed here subplots_kw=None, gridspec_kw=None, subplots_orig_kw=None, tight_layout=None, constrained_layout=None, **kwargs): @@ -668,6 +737,8 @@ def __init__(self, Other parameters ---------------- + ref, order + Documented in `subplots`. tight_layout, constrained_layout Ignored, because ProPlot uses its own tight layout algorithm. **kwargs @@ -684,35 +755,205 @@ def __init__(self, self._panelpad = units(_notNone(panelpad, rc['subplots.panelpad'])) self._auto_format = autoformat self._auto_tight_layout = _notNone(tight, rc['tight']) + self._order = order # used for configuring panel axes_grids self._ref_num = ref - self._main_axes = [] + self._axes_main = [] self._subplots_orig_kw = subplots_orig_kw self._subplots_kw = subplots_kw self._leftpanel = axes.EmptyPanel() self._bottompanel = axes.EmptyPanel() self._rightpanel = axes.EmptyPanel() self._toppanel = axes.EmptyPanel() - self._main_gridspec = FlexibleGridSpec(self, **(gridspec_kw or {'nrows':1, 'ncols':1})) + self._gridspec_main = FlexibleGridSpec(self, **(gridspec_kw or {})) self.suptitle('') # add _suptitle attribute - def _align_adjust(self, renderer): - """Aligns row labels, column labels, and super titles, and applies - tight layout automatic spacing.""" - self._adjust_aspect() - self._align_axislabels(False) - self._align_suplabels(renderer) - if self._auto_tight_layout: - self._adjust_tight_layout(renderer) - self._align_axislabels(True) + def _add_figure_panels(self, side, mode='panel', **kwargs): + """Adds figure panels. Also modifies the panel attribute stored + on the figure to include these panels.""" + # Interpret args + # NOTE: Axis sharing not implemented for figure panels, 99% of the + # time this is just used as construct for adding global colorbars and + # legends, really not worth implementing axis sharing + s = side[0] + if s not in 'lrbt': + raise ValueError(f'Invalid side {side!r}.') + side = _side_translate[s] + kwargs, kworig = _panels_kwargs(s, kwargs, mode=mode, figure=True) + array = kwargs[s + 'array'] + width = kwargs[s + 'width'] + space = kwargs[s + 'space'] + width_orig = kworig[s + 'width'] + space_orig = kworig[s + 'space'] + + # Modify existing geometry, and verify validity of the array arg + # using boolean 1D arrays indicating where panels are present for + # the dimension *spanned* by the new panel + subplots_kw = self._subplots_kw + if s in 'lr': + panels = subplots_kw['wpanels'] + nacross, nalong = subplots_kw['ncols'], subplots_kw['nrows'] + else: + panels = subplots_kw['hpanels'] + nacross, nalong = subplots_kw['nrows'], subplots_kw['ncols'] + panels = np.array([bool(s) for s in panels]) + idxs, = np.where(~panels) + if array is None: + array = [1]*len(idxs) + if not np.iterable(array) or len(array) != len(idxs): + raise ValueError(f'Need length-{len(idxs)} list of integers for "{s}array", got {s}array={array!r}.') + iratio = (0 if s in 'lt' else nacross) + gridspec = self._change_gridspec(side, iratio, + width, space, width_orig, space_orig, + figure=True, + ) + + # Pad array so it spans existing panel slots + # NOTE: Does not matter if have e.g. [1, 0, 0, 1, 2, 0, 0, 2], because + # panels are drawn based on minimum and maximum indices + array_new = np.zeros((nalong,)) + array_new[idxs] = array + + # Get keyword args and gridspec args + # Loop through unique numbers + paxs = [] + for num in np.unique(array_new).flat: + # Get subplotspec and insert gridspec + if num == 0: + continue + idx, = np.where(array_new == num) + idx1 = slice(min(idx), max(idx)+1) # ignores axes panel slots + idx2 = -1*(s in 'br') + if s in 'bt': + idx1, idx2 = idx2, idx1 + # Draw axes + with self._unlock(): + ipax = self.add_subplot(gridspec[idx1,idx2], + projection='panel', + side=side, share=False, + ) + paxs += [ipax] + + # Add to panel attributes + pgrid = getattr(self, '_' + side + 'panel') + # Create brand new axes_grid + if not pgrid: + order = self._order + if (s in 'tb' and order == 'C') or (s in 'lr' and order != 'C'): + n = 1 + else: + n = len(paxs) + pgrid = axes_grid(paxs, n=n, order=order) + setattr(self, '_' + side + 'panel', pgrid) + # Modify existing axes_grid + else: + # Possible insertion modes + # The names are relevant for C-major order + order = pgrid._order + top = (s == 't' and order == 'C') or (s == 'l' and order == 'F') + bottom = (s == 'b' and order == 'C') or (s == 'r' and order == 'F') + left = (s == 'l' and order == 'C') or (s == 't' and order == 'F') + right = (s == 'r' and order == 'C') or (s == 'b' and order == 'F') + # Insert into lists + # NOTE: Can get weird grids for weird geometry, e.g. 3 figure + # panels side-by-side on top of one figure panel + n = pgrid._n + for i,ax in enumerate(paxs): + if top: + pgrid.insert(0, ax) + elif bottom: + pgrid.append(ax) + elif left: + pgrid.insert(i*n + i, ax) # plus i offsets after adding stuff + elif right: + pgrid.insert((i + 1)*n - 1 + i, ax) + if left or right: # fastest-moving dim length increased by 1 + n += 1 + pgrid._n = n + + # Return axes_grid of just the axes drawn in this round + return axes_grid(paxs) # no 2D indexing + + @_counter + def _add_axes_panel(self, side, ax, order='C', mode='panel', **kwargs): + """Hidden method that powers `~proplot.axes.panel_axes`.""" + # Interpret args + # NOTE: Axis sharing not implemented for figure panels, 99% of the + # time this is just used as construct for adding global colorbars and + # legends, really not worth implementing axis sharing + s = side[0] + if s not in 'lrbt': + raise ValueError(f'Invalid side {side!r}.') + side = _side_translate[s] + kwargs, kworig = _panels_kwargs(s, kwargs, mode=mode, figure=False) + width = kwargs[s + 'width'] + space = kwargs[s + 'space'] + share = kwargs[s + 'share'] + width_orig = kworig[s + 'width'] + space_orig = kworig[s + 'space'] + + # Modify existing geometry, find incies for new panel by placing it + # to the outside of *existing* panels if possible + # TODO: If panel slot already exists, permit user to change the + # width and space. + subplotspec = ax.get_subplotspec() + nrows, ncols, row1, row2, col1, col2 = subplotspec.get_active_rows_columns() + pgrid = getattr(ax, '_' + side + 'panel') + offset = (len(pgrid)*bool(pgrid)) + 1 + if s in 'lr': + idx1 = slice(row1, row2 + 1) + idx2 = (col1 - offset if s == 'l' else col2 + offset) + iratio = idx2 + else: + idx1 = (row1 - offset if s == 't' else row2 + offset) + idx2 = slice(col1, col2 + 1) + iratio = idx1 + gridspec = self._change_gridspec(side, iratio, + width, space, width_orig, space_orig, + ) + + # Get keyword args and gridspec args + # Loop through unique numbers + with self._unlock(): + pax = self.add_subplot(gridspec[idx1,idx2], + projection='panel', + side=side, share=share, + ) + + # Add to panel attributes + pgrid = getattr(ax, '_' + side + 'panel') + # Create from scratch + if not pgrid: + order = self._order + pgrid = axes_grid([pax], n=1, order=order) + setattr(ax, '_' + side + 'panel', pgrid) + # Modify existing grid + else: + n = pgrid._n + order = pgrid._order + if s in 'lt': + pgrid.insert(0, pax) + else: + pgrid.append(pax) + if (s in 'lr' and order == 'C') or (s in 'tb' and order == 'F'): + n += 1 + pgrid._n = n + + # Set up shared axes + if share: + ax._share_panels_setup() + self._share_axes_setup(ax) + + # Return axes grid of just this axes + return axes_grid([pax]) def _adjust_aspect(self): """Adjust average aspect ratio used for gridspec calculations. This fixes grids with identically fixed aspect ratios, e.g. identically zoomed-in cartopy projections and imshow images.""" # Get aspect ratio - if not self._main_axes: + if not self._axes_main: return - ax = self._main_axes[self._ref_num-1] + ax = self._axes_main[self._ref_num-1] mode = ax.get_aspect() aspect = None if mode == 'equal': @@ -733,22 +974,19 @@ def _adjust_aspect(self): subplots_kw['aspect'] = aspect figsize, gridspec_kw, _ = _subplots_geometry(**subplots_kw) self.set_size_inches(figsize) - self._main_gridspec.update(**gridspec_kw) + self._gridspec_main.update(**gridspec_kw) @_counter - def _adjust_tight_layout(self, renderer=None, - borders=True, subplots=True, panels=True - ): + def _adjust_tight_layout(self, renderer): """Applies tight layout scaling that permits flexible figure dimensions and preserves panel widths and subplot aspect ratios. The `renderer` should be a `~matplotlib.backend_bases.RendererBase` - instance. If ``None``, renderer is inferred from - `~matplotlib.figure.Figure.canvas.get_renderer`.""" + instance.""" # Initial stuff axs = self._iter_axes() obox = self.bbox_inches # original bbox bbox = self.get_tightbbox(renderer) - gridspec = self._main_gridspec + gridspec = self._gridspec_main subplots_kw = self._subplots_kw subplots_orig_kw = self._subplots_orig_kw # tight layout overrides if not axs or not subplots_kw or not subplots_orig_kw: @@ -757,11 +995,16 @@ def _adjust_tight_layout(self, renderer=None, # Tight box *around* figure # Get bounds from old bounding box pad = self._pad - l, b = bbox.xmin, bbox.ymin # left bottom margins - r, t = obox.xmax - bbox.xmax, obox.ymax - bbox.ymax # top right margin *deltas* + left = bbox.xmin + bottom = bbox.ymin + right = obox.xmax - bbox.xmax + top = obox.ymax - bbox.ymax # Apply new bounds, permitting user overrides # TODO: Account for bounding box NaNs? - for key,offset in zip(('left','right','top','bottom'),(l,r,t,b)): + for key,offset in zip( + ('left','right','top','bottom'), + (left,right,top,bottom) + ): previous = subplots_orig_kw[key] current = subplots_kw[key] subplots_kw[key] = _notNone(previous, current - offset + pad) @@ -769,86 +1012,66 @@ def _adjust_tight_layout(self, renderer=None, # Get arrays storing gridspec spacing args axpad = self._axpad panelpad = self._panelpad - nrows, ncols = gridspec.get_visible_geometry() + nrows, ncols = gridspec.get_active_geometry() wspace, hspace = subplots_kw['wspace'], subplots_kw['hspace'] wspace_orig = subplots_orig_kw['wspace'] hspace_orig = subplots_orig_kw['hspace'] - wseps_orig = subplots_orig_kw['wseps'] - hseps_orig = subplots_orig_kw['hseps'] # Get new subplot spacings, axes panel spacing, figure panel spacing - spaces, ratios = [], [] # update these - wratios = np.array(gridspec.get_wratios()[::2]) - hratios = np.array(gridspec.get_hratios()[::2]) - for (wh, xy, ixy, nacross, - iratios, ispace, - ispace_orig, iseps_orig) in zip('wh', 'xy', 'yx', - (nrows,ncols), (wratios,hratios), (wspace,hspace), - (wspace_orig,hspace_orig), (wseps_orig,hseps_orig), + spaces = [] + for (w, x, y, nacross, + ispace, ispace_orig) in zip('wh', 'xy', 'yx', (nrows,ncols), + (wspace,hspace), (wspace_orig,hspace_orig), ): - # Iterate along spaces + # Determine which rows and columns correspond to panels + panels = subplots_kw[w + 'panels'] jspace = [*ispace] - ralong = np.array([ax._range_gridspec(xy, True) for ax in axs]) - racross = np.array([ax._range_gridspec(ixy, True) for ax in axs]) + ralong = np.array([ax._range_gridspec(x) for ax in axs]) + racross = np.array([ax._range_gridspec(y) for ax in axs]) for i,(space,space_orig) in enumerate(zip(ispace,ispace_orig)): - # Special case where (1) current column is zero-width, so want - # zero space next to it, or (2) no more non-zero columns - # to the right of the current column + # Figure out whether this is a normal space, or a + # panel stack space/axes panel space pad = axpad - if i == 0: # left/top figure panel - off1 = 0 - off2 = (1 if iratios[i+1] == 0 else 0) - if iratios[i] == 0: - continue - elif i == (len(ispace) - 1): # bottom/right figure panel - off2 = 0 - off1 = (-1 if iratios[i] == 0 else 0) - if iratios[i+1] == 0: - continue - elif (i % 3 != 0): - pad = panelpad # axes panels - off1, off2 = 0, 0 - if iratios[i] == 0 or iratios[i+1] == 0: # column on either side of interface - continue - else: # may have to jump across multiple columns - off1 = (-1 if iratios[i] == 0 else 0) - off2 = (1 if iratios[i+1] == 0 else 0) + if (panels[i] in ('l','t') and panels[i+1] in ('l','t','') + or panels[i] in ('','r','b') and panels[i+1] in ('r','b') + or panels[i] == 'f' and panels[i+1] == 'f'): + pad = panelpad # Find axes that abutt aginst this space on each row - # TODO: Fix offsets!!! groups = [] - filt1 = ralong[:,1] == i + off1 # i.e. right/bottom edge abutts against this space - filt2 = ralong[:,0] == i + off2 + 1 # i.e. left/top edge abutts against this space + filt1 = ralong[:,1] == i # i.e. right/bottom edge abutts against this space + filt2 = ralong[:,0] == i + 1 # i.e. left/top edge abutts against this space for j in range(nacross): # e.g. each row - filt = ((racross[:,0] <= j) & (j <= racross[:,1])) + # Get indices + filt = (racross[:,0] <= j) & (j <= racross[:,1]) if sum(filt) < 2: # no interface here continue idx1, = np.where(filt & filt1) idx2, = np.where(filt & filt2) - if not idx1.size or not idx2.size: + if idx1.size > 1 or idx2.size > 2: + raise RuntimeError('This should never happen.') + elif not idx1.size or not idx2.size: continue + idx1, idx2 = idx1[0], idx2[0] # Put these axes into unique groups. Store groups as # (left axes, right axes) or (bottom axes, top axes) pairs. - # NOTE: If have stacked panels, there may be more than one - # axes in the same gridspec slot! Account for multiple matches - axs1, axs2 = [axs[k] for k in idx1], [axs[k] for k in idx2] - if xy != 'x': - axs1, axs2 = axs2, axs1 # yrange is top-to-bottom, so make this bottom-to-top + ax1, ax2 = axs[idx1], axs[idx2] + if x != 'x': + ax1, ax2 = ax2, ax1 # yrange is top-to-bottom, so make this bottom-to-top newgroup = True for (group1,group2) in groups: - if (any(ax1 in group1 for ax1 in axs1) or - any(ax2 in group2 for ax2 in axs2)): + if ax1 in group1 or ax2 in group2: newgroup = False - group1.update(axs1) - group2.update(axs2) + group1.add(ax1) + group2.add(ax2) break if newgroup: - groups.append([{*axs1}, {*axs2}]) # form new group + groups.append([{ax1}, {ax2}]) # form new group # Get spaces # Remember layout is lspace, lspaces[0], rspaces[0], wspace, ... # so panels spaces are located where i % 3 is 1 or 2 jspaces = [] for (group1,group2) in groups: - x1 = max(ax._range_tightbbox(xy)[1] for ax in group1) - x2 = min(ax._range_tightbbox(xy)[0] for ax in group2) + x1 = max(ax._range_tightbbox(x)[1] for ax in group1) + x2 = min(ax._range_tightbbox(x)[0] for ax in group2) jspaces.append((x2 - x1)/self.dpi) if jspaces: space = max(0, space - min(jspaces) + pad) # TODO: why max 0? @@ -856,92 +1079,13 @@ def _adjust_tight_layout(self, renderer=None, jspace[i] = space spaces.append(jspace) - # Next handle separation in stacked panel gridspecs - # We are iterating through *ratios* this time - # TODO: This is clunkier than before, maybe go back to iterating - # over axes instead of over the space/ratios arrays? - jratios = [*iratios] - for i,(ratio,sep_orig) in enumerate(zip(iratios,iseps_orig)): - # If this is not a panel column, no panels are activated, sep - # is [] (i.e. no stack), or sep provided manually, continue - panel = (i % 3 != 2) or i in (0,len(iratios)-1) - if not panel or ratio == 0 or all(sep_orig!=None): - continue - # Next - idx, = np.where(ralong[:,0] == i) - iaxs = [axs[k] for k in idx] - if not iaxs: - warnings.warn(f'No panels found in column or row {i}. This is weird.') - continue - # Check for geometry inconsistencies - gridspecs = [iax.get_subplotspec().get_gridspec() for iax in iaxs] - geometry = {tuple(gridspec.get_visible_geometry()) for gridspec in gridspecs} - if len(geometry)>1: - warnings.warn(f'Differing numbers of stacked panels in column or row, skipping tight layout adjustments.') - continue - # Get indices relative to GridSpecFromSubplotSpec - # Also make sure we ahve *active* axes in each column of stack - # for each row, each row of stack for each column - idxs = [iax._range_gridspec(xy, False)[0] for iax in iaxs] - counts = [idxs.count(idx) for idx in {*idxs}] - if any(count!=counts[0] for count in counts): - warnings.warn(f'Differing numbers of stacked panels in column or row, skipping tight layout adjustments.') - continue - # Finally get tight layout, figure out separation between - # each column in stack - pad = panelpad - iadd = [] - idxs_sorted = sorted({*idxs})[:-1] - for idx_i in idxs_sorted: - # Calculate necessary change to seps; ccounts for fact that - # gridspec coords are top-to-bottom, bounding box opposite - if sep_orig[idx_i] is not None: # TODO: check! - iadd.append(0) - else: - off1, off2 = (0, 1) if xy == 'x' else (1, 0) - x1 = [iax._range_tightbbox(xy)[1] for idx,iax in - zip(idxs,iaxs) if idx == idx_i + off1] - x2 = [iax._range_tightbbox(xy)[0] for idx,iax in - zip(idxs,iaxs) if idx == idx_i + off2] - iadd.append(pad - (min(x2) - max(x1))/self.dpi) - # Adjust spacing by adjusting subplot and inner ratios arrays - # NOTE: For inner ratios adjustment, we apply change to mutable - # ratios list, and change is reflected in next gridspec update - jratios[i] += sum(iadd) - for idx,iax in zip(idxs,iaxs): - if idx not in idxs_sorted: # i.e. is on bottom/left - continue - igridspec = iax.get_subplotspec().get_gridspec() - kratios = getattr(igridspec, 'get_' + wh + 'ratios')() - kratios[idx*2+1] += iadd[idx] - ratios.append(jratios) - - # Apply new spaces and widths + # Apply new spaces subplots_kw.update({ 'wspace':spaces[0], 'hspace':spaces[1], - 'wratios':ratios[0], 'hratios':ratios[1] }) figsize, gridspec_kw, _ = _subplots_geometry(**subplots_kw) + self._gridspec_main.update(**gridspec_kw) self.set_size_inches(figsize) - self._main_gridspec.update(**gridspec_kw) - - def _align_helper(self, xy, axs): - """Gets figure coordinates for spanning labels and super title. The - `xy` can be ``'x'`` or ``'y'``.""" - # Get position in figure relative coordinates - ranges = np.array([ax._range_gridspec(xy, True) for ax in axs]) - min_, max_ = ranges[:,0].min(), ranges[:,1].max() - axlo = axs[np.where(ranges[:,0] == min_)[0][0]] - axhi = axs[np.where(ranges[:,1] == max_)[0][0]] - lobox = axlo.get_subplotspec().get_position(self) - hibox = axhi.get_subplotspec().get_position(self) - if xy == 'x': - pos = (lobox.x0 + hibox.x1)/2 - else: - pos = (lobox.y1 + hibox.y0)/2 # 'lo' is actually on top, highest up in gridspec - # Return axis suitable for spanning position - spanax = axs[(np.argmin(ranges[:,0]) + np.argmax(ranges[:,1]))//2] - return pos, spanax def _align_axislabels(self, b=True): """Aligns spanning *x* and *y* axis labels, accounting for figure @@ -950,23 +1094,23 @@ def _align_axislabels(self, b=True): # NOTE: Need to turn off aligned labels before _adjust_tight_layout # call, so cannot put this inside Axes draw tracker = {*()} - for ax in self._main_axes: + for ax in self._axes_main: if not isinstance(ax, axes.CartesianAxes): continue - for xy,axis in zip('xy', (ax.xaxis, ax.yaxis)): + for x,axis in zip('xy', (ax.xaxis, ax.yaxis)): s = axis.get_label_position()[0] # top or bottom, left or right - span = getattr(ax, '_span' + xy) - align = getattr(ax, '_align' + xy) + span = getattr(ax, '_span' + x) + align = getattr(ax, '_align' + x) if s not in 'bl' or axis in tracker: continue axs = ax._get_side_axes(s) for _ in range(2): - axs = [getattr(ax, '_share' + xy) or ax for ax in axs] + axs = [getattr(ax, '_share' + x) or ax for ax in axs] # Align axis label offsets - axises = [getattr(ax, xy + 'axis') for ax in axs] + axises = [getattr(ax, x + 'axis') for ax in axs] tracker.update(axises) if span or align: - grp = getattr(self, '_align_' + xy + 'label_grp', None) + grp = getattr(self, '_align_' + x + 'label_grp', None) if grp is not None: for ax in axs[1:]: grp.join(axs[0], ax) # copied from source code, add to grouper @@ -975,8 +1119,8 @@ def _align_axislabels(self, b=True): if not span: continue # Get spanning label position - pos, spanax = self._align_helper(xy, axs) - spanaxis = getattr(spanax, xy + 'axis') + c, spanax = self._get_align_coord(s, axs) + spanaxis = getattr(spanax, x + 'axis') spanlabel = spanaxis.label if not hasattr(spanlabel, '_orig_transform'): spanlabel._orig_transform = spanlabel.get_transform() @@ -987,12 +1131,12 @@ def _align_axislabels(self, b=True): for axis in axises: axis.label.set_visible(True) else: # toggle on, done after tight layout - if xy == 'x': - position = (pos, 1) + if x == 'x': + position = (c, 1) transform = mtransforms.blended_transform_factory( self.transFigure, mtransforms.IdentityTransform()) else: - position = (1, pos) + position = (1, c) transform = mtransforms.blended_transform_factory( mtransforms.IdentityTransform(), self.transFigure) for axis in axises: @@ -1012,14 +1156,10 @@ def _align_suplabels(self, renderer): suptitle_on = suptitle.get_text().strip() width, height = self.get_size_inches() for s in 'lrbt': - # Geometry - nrows, ncols = self._main_gridspec.get_visible_geometry() - xy = ('x' if s in 'lr' else 'y') - idx = (0 if s in 'lt' else 1) - edge = (2 if s in 'lt' else ncols-3 if s == 'r' else nrows-3) # Get axes and offset the label to relevant panel - axs = [ax._reassign_suplabel(s) for ax in self._main_axes - if ax._range_gridspec(xy, True)[idx] == edge] + x = ('x' if s in 'lr' else 'y') + axs = self._get_align_axes(s) + axs = [ax._reassign_suplabel(s) for ax in axs] labels = [getattr(ax, '_' + s + 'label') for ax in axs] coords = [None]*len(axs) if s == 't' and suptitle_on: @@ -1033,7 +1173,7 @@ def _align_suplabels(self, renderer): # Include twin axes and panels along the same side extra = ('bt' if s in 'lr' else 'lr') icoords = [] - for iax in ax._iter_twins_panels(extra): + for iax in ax._iter_panels(extra): bbox = iax.get_tightbbox(renderer) if s == 'l': jcoords = (bbox.xmin, 0) @@ -1060,199 +1200,212 @@ def _align_suplabels(self, renderer): # Assign coords coords = [i for i in coords if i is not None] if coords: - key = ('x' if s in 'lr' else 'y') if s in 'lb': c = min(coords) else: c = max(coords) for label in labels: - label.update({key: c}) + label.update({x: c}) # Update super title position - if suptitle_on: + # If no axes on the top row are visible, do not try to align! + if suptitle_on and supaxs: ys = [] for ax in supaxs: bbox = ax.get_tightbbox(renderer) _, y = self.transFigure.inverted().transform((0, bbox.ymax)) ys.append(y) - x, _ = self._align_helper('x', axs) + x, _ = self._get_align_coord('t', supaxs) y = max(ys) + (0.3*suptitle.get_fontsize()/72)/height kw = {'x':x, 'y':y, 'ha':'center', 'va':'bottom', 'transform':self.transFigure} suptitle.update(kw) - @_counter - def _panel_axes(self, ax, side, order='C', **kwargs): - """Hidden method that powers `~proplot.axes.panel_axes`. Makes more sense - to define in subplots, because it does a bunch of alignment steps - that rely on the figure instance.""" - # Checks + def _change_gridspec(self, side, idx, + ratio, space, ratio_orig, space_orig, + figure=False): + """Helper function that "overwrites" the main figure gridspec, used + when successively adding panels. The `side` is the panel side, + the `idx` is the slot you want the panel to occupy, and the remaining + args are the panel widths and spacings.""" + # Constants and stuff + # Insert spaces to the left of right panels or to the right of + # left panels. And note that since .insert() pushes everything in + # that column to the right, actually must insert 1 slot farther to + # the right when inserting left panels/spaces s = side[0] - side = _side_translate.get(s, s) if s not in 'lrbt': - raise ValueError(f'Invalid side {side!r}.') - gridspec = self._main_gridspec - if gridspec is None: - raise ValueError(f'Gridspec attribute is empty.') - paxs = getattr(ax, s + 'panel') - if paxs: - warnings.warn(f'{side}panel already exists.') - return paxs # return existing panels! should already be synced - # Parse keyword args + raise ValueError(f'Invalid side {side}.') + idx_space = idx - 1*bool(s in 'br') + idx_offset = 1*bool(s in 'tl')*bool(idx > 0) + if s in 'lr': + x, w, ncols = 'x', 'w', 'ncols' + else: + x, w, ncols = 'y', 'h', 'nrows' + + # Load arrays subplots_kw = self._subplots_kw subplots_orig_kw = self._subplots_orig_kw - kwout, kworig = _panels_kwargs(s, kwargs, figure=False) - width_orig = kworig[s + 'width'] - space_orig = kworig[s + 'space'] - sep_orig = kworig[s + 'sep'] - width = kwout[s + 'width'] - share = kwout[s + 'share'] - space = kwout[s + 'space'] - sep = kwout[s + 'sep'] - # Check that this matches geometry of other panels in row or col - axs = ax._get_side_axes(s) - for iax in axs: - ipaxs = getattr(iax, s + 'panel') - if not ipaxs: - continue - nstack, instack = len(width), len(ipaxs) - if nstack != instack: - raise ValueError(f'An axes in this row/column has {instack} stacked panel(s), but you are trying to add {nstack} stacked panels.') - - # Find location in ratios and space arrays that we need to - # modify to make room for the panel - wh = ('w' if s in 'lr' else 'h') - (x0, x1) = ax._range_gridspec('x', True) - (y0, y1) = ax._range_gridspec('y', True) - if s == 'l': - x, y = x0-1, slice(y0,y1+1) - elif s == 't': - x, y = slice(x0,x1+1), y0-1 - elif s == 'r': - x, y = x1+1, slice(y0,y1+1) - else: - x, y = slice(x0,x1+1), y1+1 - iratio = (x if s in 'lr' else y) # index in ratio array - ispace = (iratio if s in 'lt' else iratio - 1) # index in space array - - # Default to original input space - spaces_orig = subplots_orig_kw[wh + 'space'] - if spaces_orig[ispace] is None: - spaces_orig[ispace] = space_orig - else: # overwrite - space = spaces_orig[ispace] - # Default to original input width(s) - widths_orig = subplots_orig_kw[wh + 'widths'] - iwidths_orig = widths_orig[iratio] - if iwidths_orig is None: - widths_orig[iratio] = width_orig + panels = subplots_kw[w + 'panels'] + ratios = subplots_kw[w + 'ratios'] + spaces = subplots_kw[w + 'space'] + ratios_orig = subplots_orig_kw[w + 'ratios'] + spaces_orig = subplots_orig_kw[w + 'space'] + + # Slot already exists + # NOTE: This is never the case for figure panels, because we always + # add them along entire side all at once + exists = (not figure and idx < len(panels) and panels[idx] == s) + if exists: # already exists! + # Modify existing width, defer to user input + if ratios_orig[idx] is None: + ratios_orig[idx] = units(ratio_orig) + ratios[idx] = _notNone(ratios_orig[idx], ratio) + # Modify existing space + if spaces_orig[idx_space] is None: + spaces_orig[idx_space] = units(space_orig) + spaces[idx_space] = _notNone(spaces_orig[idx_space], space) + # Make room for new panel slot else: - mask = (width_orig != None) - iwidths_orig[mask] = width_orig[mask] - mask = (iwidths_orig != None) - width[mask] = iwidths_orig[mask] # overwrite defaults with orig - # Default to original input sep(s) - seps_orig = subplots_orig_kw[wh + 'seps'] - iseps_orig = seps_orig[iratio] - if iseps_orig is None: - seps_orig[iratio] = sep_orig - else: - mask = (sep_orig != None) - iseps_orig[mask] = sep_orig[mask] - mask = (iseps_orig != None) - sep[mask] = iseps_orig[mask] # overwrite - - # Apply to actual dict and get geometry - subplots_kw[wh + 'ratios'][iratio] = sum(width) + sum(sep) - subplots_kw[wh + 'space'][ispace] = space + # Modify basic geometry + idx += idx_offset + idx_space += idx_offset + subplots_kw[ncols] += 1 + # Ratio array, space array, panel toggles + spaces.insert(idx_space, space) + ratios.insert(idx, ratio) + panels.insert(idx, 'f' if figure else s) + # Original info + ratios_orig.insert(idx, ratio_orig) + spaces_orig.insert(idx_space, space_orig) + # Reference ax location array + ref = subplots_kw[x + 'ref'] + for i,val in enumerate(ref): + if val >= idx: + ref[i] += 1 + + # Update figure figsize, gridspec_kw, _ = _subplots_geometry(**subplots_kw) self.set_size_inches(figsize) - self._main_gridspec.update(**gridspec_kw) + if exists: + gridspec = self._gridspec_main + gridspec.update(**gridspec_kw) + else: + # New gridspec + gridspec = FlexibleGridSpec(self, **gridspec_kw) + self._gridspec_main = gridspec + # Reassign subplotspecs to all axes and update positions + # May seem inefficient but it literally just assigns a hidden, + # attribute, and the creation time for subpltospecs is tiny + axs = [iax for ax in self._iter_axes() for iax in (ax, *ax.child_axes)] + for ax in axs: + # Get old index + # NOTE: Endpoints are inclusive, not exclusive! + if s in 'lr': + inserts = (None, None, idx, idx) + else: + inserts = (idx, idx, None, None) + subplotspec = ax.get_subplotspec() + igridspec = subplotspec.get_gridspec() + topmost = subplotspec.get_topmost_subplotspec() + # Apply new subplotspec! + nrows, ncols, *coords = topmost.get_active_rows_columns() + for i in range(4): + # if inserts[i] is not None and coords[i] >= inserts[i]: + if inserts[i] is not None and coords[i] >= inserts[i]: + coords[i] += 1 + (row1, row2, col1, col2) = coords + subplotspec_new = gridspec[row1:row2+1, col1:col2+1] + if topmost is subplotspec: + ax.set_subplotspec(subplotspec_new) + elif topmost is igridspec._subplot_spec: + igridspec._subplot_spec = subplotspec_new + else: + raise ValueError(f'Unexpected GridSpecFromSubplotSpec nesting.') + # Update parent or child position + ax.update_params() + ax.set_position(ax.figbox) + + return gridspec + + def _get_align_coord(self, side, axs): + """Returns figure coordinate for spanning labels and super title. The + `x` can be ``'x'`` or ``'y'``.""" + # Get position in figure relative coordinates + s = side[0] + x = ('x' if s in 'lr' else 'y') + ranges = np.array([ax._range_gridspec(x) for ax in axs]) + min_, max_ = ranges[:,0].min(), ranges[:,1].max() + axlo = axs[np.where(ranges[:,0] == min_)[0][0]] + axhi = axs[np.where(ranges[:,1] == max_)[0][0]] + lobox = axlo.get_subplotspec().get_position(self) + hibox = axhi.get_subplotspec().get_position(self) + if x == 'x': + pos = (lobox.x0 + hibox.x1)/2 + else: + pos = (lobox.y1 + hibox.y0)/2 # 'lo' is actually on top, highest up in gridspec + # Return axis suitable for spanning position + spanax = axs[(np.argmin(ranges[:,0]) + np.argmax(ranges[:,1]))//2] + return pos, spanax - # Get subplotspec from main gridspec + def _get_align_axes(self, side): + """Returns main axes along the left, right, bottom, or top sides + of the figure.""" + # Initial stuff + s = side[0] + idx = (0 if s in 'lt' else 1) if s in 'lr': - wspace, hspace = sep, [] - wratios, hratios = width, [1] - nrows, ncols = 1, len(width) + x, y = 'x', 'y' else: - wspace, hspace = [], sep - wratios, hratios = [1], width - nrows, ncols = len(width), 1 - subplotspec = gridspec[y,x] - # Put stack gridspec in subplotspec - paxs = [] - igridspec = FlexibleGridSpecFromSubplotSpec( - subplot_spec=subplotspec, - nrows=nrows, ncols=ncols, - wspace=wspace, hspace=hspace, - width_ratios=wratios, height_ratios=hratios) - # Draw panel(s) - for i in range(len(width)): - with self._unlock(): - pax = self.add_subplot(igridspec[i], - projection='panel', - side=side, parent=ax, share=share, - ) - paxs += [pax] - - # Add as axes_grid. Support 2D indexing, even though these are - # always vector stacks, because consistency. See axes_grid docs. - n = (1 if (s in 'tb' and order == 'C') - or (s in 'lr' and order != 'C') else len(width)) - paxs = axes_grid(paxs, n=n, order=order) - setattr(ax, '_' + side + 'panel', paxs) # hidden attribute for property - # Share panel with axes from the main subplot, and update shared settings - if share: - ax._share_panels_setup() - self._share_axes_setup(ax) - return paxs + x, y = 'y', 'x' + # Get edge index + axs = self._axes_main + if not axs: + return [] + ranges = np.array([ax._range_gridspec(x) for ax in axs]) + min_, max_ = ranges[:,0].min(), ranges[:,1].max() + edge = (min_ if s in 'lt' else max_) + # Return axes on edge sorted by order of appearance + axs = [ax for ax in self._axes_main if ax._range_gridspec(x)[idx] == edge] + ord = [ax._range_gridspec(y)[0] for ax in axs] + return [ax for _,ax in sorted(zip(ord, axs)) if ax.get_visible()] - def _panel_resize(self, pax, width, mode): + def _resize_panel(self, pax, width, mode='panel'): """Modifies the panel width in case it is being "filled" by a colorbar or legend. The input is the user input width (may be ``None``) and the mode, one of ``'colorbar'`` or ``'legend'``.""" - # Initial stuff + # Get index + subplotspec = pax.get_subplotspec() + *_, row, _, col, _ = subplotspec.get_active_rows_columns() s = pax._side[0] - wh = ('w' if s in 'lr' else 'h') - xy = ('x' if s in 'lr' else 'y') - ixy = ('y' if s in 'lr' else 'x') - subplots_orig_kw = self._subplots_orig_kw - subplots_kw = self._subplots_kw - istack, _ = pax._range_gridspec(xy, False) # index in panel stack - irange = pax._range_gridspec(xy, True) - jrange = pax._range_gridspec(ixy, True) - iratio = irange[0] + if s in 'lr': + x, y, w, idx = 'x', 'y', 'w', col + else: + x, y, w, idx = 'y', 'x', 'h', row # Set new widths, taking care to obey user input widths! - widths_orig = subplots_orig_kw[wh + 'widths'] - iwidths_orig = widths_orig[iratio] - if iwidths_orig[istack] is None or width is not None: - iwidths_orig[istack] = units(width) - width = units(_notNone(iwidths_orig[istack], rc['subplots.' + mode + 'width'])) - - # Sync all gridspecs on the same side, and update parent gridspec, - # then apply. - gridspecs = {*()} - for ax in self._iter_axes(): - if ax._range_gridspec(xy, True) != irange: - continue - gridspec = ax.get_subplotspec().get_gridspec() - if gridspec in gridspecs: - continue - gridspecs.add(gridspec) - ratios = getattr(gridspec, 'get_' + wh + 'ratios')() - ratios[2*istack] = width - subplots_kw[wh + 'ratios'][iratio] = sum(ratios) + subplots_kw = self._subplots_kw + subplots_orig_kw = self._subplots_orig_kw + ratios = subplots_kw[w + 'ratios'] + ratios_orig = subplots_orig_kw[w + 'ratios'] + if ratios_orig[idx] is None or width is not None: + ratios_orig[idx] = units(width) + width = units(_notNone(ratios_orig[idx], rc['subplots.' + mode + 'width'])) + ratios[idx] = width + + # Apply to panel figsize, gridspec_kw, _ = _subplots_geometry(**subplots_kw) self.set_size_inches(figsize) - self._main_gridspec.update(**gridspec_kw) + self._gridspec_main.update(**gridspec_kw) # Undo action of _sharex_setup and _sharey_setup # NOTE: Largely copied from Figure._remove_ax, except we copy # locators and formatters from parent to child. + irange = pax._range_gridspec(x) + jrange = pax._range_gridspec(y) for ax in self._iter_axes(): - if not (ax._range_gridspec(xy, True) == irange - or ax._range_gridspec(ixy, True) == jrange): + if not (ax._range_gridspec(x) == irange + or ax._range_gridspec(y) == jrange): continue for axis1,axis2,parent,grouper in zip( (pax.xaxis, pax.yaxis), @@ -1278,49 +1431,45 @@ def _update_axislabels(self, axis=None, **kwargs): labels are toggled, keeps the labels synced for all subplots in the same row or column. Label positions will be adjusted at draw-time with _align_axislabels.""" - xy = axis.axis_name - if xy not in 'xy': + x = axis.axis_name + if x not in 'xy': return # Update label on this axes axis.label.update(kwargs) - # Descend up to 2 levels deep -- for example, if on top panel of - # top row, descend to parent axes, then main axes on bottom row + + # Defer to parent (main) axes if possible, then get the axes + # shared by that parent + # TODO: Share panels in successive stacks, but share short axes + # just like sharing long axes ax = axis.axes - for _ in range(2): - sax = getattr(ax, '_share' + xy, None) - if sax is not None and not hasattr(sax, '_side'): - ax = sax # defer to any *main axes* parent + sax = getattr(ax, '_share' + x, None) + while isinstance(ax, axes.PanelAxes) and sax is not None: + ax, sax = sax, getattr(sax, '_share' + x, None) + # Apply to spanning axes and their panels axs = [ax] - if getattr(ax, '_span' + xy): + if getattr(ax, '_span' + x): s = axis.get_label_position()[0] if s in 'lb': axs = ax._get_side_axes(s) for ax in axs: - getattr(ax, xy + 'axis').label.update(kwargs) # apply to main axes - pax = getattr(ax, '_share' + xy, None) + getattr(ax, x + 'axis').label.update(kwargs) # apply to main axes + pax = getattr(ax, '_share' + x, None) if pax is not None: # apply to panel? - getattr(pax, xy + 'axis').label.update(kwargs) + getattr(pax, x + 'axis').label.update(kwargs) def _update_suplabels(self, ax, side, labels, **kwargs): """Assigns side labels, updates label settings.""" s = side[0] if s not in 'lrbt': raise ValueError(f'Invalid label side {side!r}.') - if not self._main_gridspec: - raise ValueError(f'Gridspec missing, cannot update labels.') - # Geometry - nrows, ncols = self._main_gridspec.get_visible_geometry() - xy = ('x' if s in 'lr' else 'y') - ixy = ('y' if s in 'lr' else 'x') - idx = (0 if s in 'lt' else 1) - edge = (2 if s in 'lt' else ncols-3 if s == 'r' else nrows-3) + + # Get main axes on the edge + axs = self._get_align_axes(s) + if not axs: + return # occurs if called while adding axes + # Update label text for axes on the edge - if not self._main_axes: # occurs if this is called while adding axes - axs = [ax] - else: - axs = [ax for ax in self._main_axes if ax._range_gridspec(xy, True)[idx] == edge] - axs = [ax for _,ax in sorted(zip([ax._range_gridspec(ixy, True)[0] for ax in axs], axs))] # order by yrange if labels is None or isinstance(labels, str): # common during testing labels = [labels]*len(axs) if len(labels) != len(axs): @@ -1342,19 +1491,19 @@ def _update_suptitle(self, title, **kwargs): def _share_axes_setup(self, ref=None): """Applies axis sharing to groups of axes that share the same horizontal or vertical extent.""" - axs = [ref] if ref is not None else self._main_axes + axs = [ref] if ref is not None else self._axes_main # Share x axes - ranges = {tuple(ax._range_gridspec('x', True)) for ax in axs} + ranges = {tuple(ax._range_gridspec('x')) for ax in axs} for irange in ranges: - iaxs = [ax for ax in axs if tuple(ax._range_gridspec('x', True)) == irange] - parent = iaxs.pop(np.argmax([iax._range_gridspec('y', True)[1] for iax in iaxs])) + iaxs = [ax for ax in axs if tuple(ax._range_gridspec('x')) == irange] + parent = iaxs.pop(np.argmax([iax._range_gridspec('y')[1] for iax in iaxs])) for child in iaxs: child._sharex_setup(parent, parent._sharex_level) # Share y axes - ranges = {tuple(ax._range_gridspec('y', True)) for ax in axs} + ranges = {tuple(ax._range_gridspec('y')) for ax in axs} for irange in ranges: - iaxs = [ax for ax in axs if tuple(ax._range_gridspec('y', True)) == irange] - parent = iaxs.pop(np.argmin([iax._range_gridspec('x', True)[0] for iax in iaxs])) + iaxs = [ax for ax in axs if tuple(ax._range_gridspec('y')) == irange] + parent = iaxs.pop(np.argmin([iax._range_gridspec('x')[0] for iax in iaxs])) for child in iaxs: child._sharey_setup(parent, parent._sharey_level) @@ -1367,6 +1516,27 @@ def add_subplot(self, *args, **kwargs): ax = super().add_subplot(*args, **kwargs) return ax + def colorbar(self, *args, loc='r', panel_kw=None, **kwargs): + """Draws a colorbar along the left, right, bottom, or top side + of the figure, centered between the leftmost and rightmost (or + topmost and bottommost) main axes.""" + if 'cax' in kwargs: + return super().colorbar(*args, **kwargs) + else: + panel_kw = panel_kw or {} + panel_kw.setdefault('mode', 'colorbar') + paxs = self._add_figure_panels(loc, **panel_kw) + return paxs.colorbar(*args, **kwargs) + + def legend(self, *args, loc='r', panel_kw=None, **kwargs): + """Draws a legend along the left, right, bottom, or top side of the + figure, centered between the leftmost and rightmost (or + topmost and bottommost) main axes.""" + panel_kw = panel_kw or {} + panel_kw.setdefault('mode', 'legend') + paxs = self._add_figure_panels(loc, **panel_kw) + return paxs.legend(*args, **kwargs) + @_counter def draw(self, renderer): """Before drawing the figure, applies "tight layout" and aspect @@ -1390,7 +1560,12 @@ def draw(self, renderer): # and cannot modify PDFPage or SVG renderer props inplace, so idea was # to override get_size_inches. But when get_size_inches is called, the # canvas has no renderer, so cannot apply tight layout yet! - self._align_adjust(renderer) + self._adjust_aspect() + self._align_axislabels(False) + self._align_suplabels(renderer) + if self._auto_tight_layout: + self._adjust_tight_layout(renderer) + self._align_axislabels(True) canvas = getattr(self, 'canvas', None) if hasattr(canvas, 'get_renderer') and (mbackend is None or not isinstance(canvas, mbackend.FigureCanvasMac)): @@ -1416,7 +1591,12 @@ def savefig(self, filename, **kwargs): if hasattr(canvas, 'get_renderer'): renderer = canvas.get_renderer() canvas.renderer = renderer - self._align_adjust(renderer) + self._adjust_aspect() + self._align_axislabels(False) + self._align_suplabels(renderer) + if self._auto_tight_layout: + self._adjust_tight_layout(renderer) + self._align_axislabels(True) else: warnings.warn('Renderer unknown, could not adjust layout before saving.') super().savefig(filename, **kwargs) @@ -1455,7 +1635,7 @@ def _iter_axes(self): """Iterates over all axes and panels in the figure belonging to the `~proplot.axes.Axes` class. Excludes inset and twin axes.""" axs = [] - for ax in (*self._main_axes, *self.leftpanel, *self.rightpanel, + for ax in (*self._axes_main, *self.leftpanel, *self.rightpanel, *self.bottompanel, *self.toppanel): if not ax or not ax.get_visible(): continue @@ -1542,8 +1722,6 @@ def subplots(array=None, ncols=1, nrows=1, ref=1, order='C', aspect=1, figsize=None, width=None, height=None, axwidth=None, axheight=None, journal=None, - wwidth=None, hwidth=None, - axwidths=None, axheights=None, hspace=None, wspace=None, space=None, hratios=None, wratios=None, width_ratios=None, height_ratios=None, @@ -1721,7 +1899,8 @@ def subplots(array=None, ncols=1, nrows=1, an empty space. [lrbt]width, [lrbt]share, [lrbt]stack, [lrbt]sep As in `~proplot.axes.Axes.panel_axes`. Use e.g. `lwidth`, `rwidth`, - `bwidth`, and `twidth` to apply these settings to different panels. + `bwidth`, and `twidth` to apply these settings to panels on different + sides. [lrtb]space : float or str, optional As in `~proplot.axes.Axes.panel_axes`, but controls space between the main subplot grid and the figure panels. @@ -1792,9 +1971,6 @@ def subplots(array=None, ncols=1, nrows=1, ``'agu4'`` full height 2-column ” =========== ==================== ========================================================================================================================================================== """ - #-------------------------------------------------------------------------# - # Array setup - #-------------------------------------------------------------------------# rc._getitem_mode = 0 # ensure still zero; might be non-zero if had error in 'with context' block # Build array if order not in ('C','F'): # better error message @@ -1811,19 +1987,16 @@ def subplots(array=None, ncols=1, nrows=1, raise ValueError array[array == None] = 0 # use zero for placeholder except (TypeError,ValueError): - raise ValueError(f'Invalid subplot array {array}. Must be 1D or 2D array of integers.') + raise ValueError(f'Invalid subplot array {array!r}. Must be 1D or 2D array of integers.') # Get other props nums = np.unique(array[array != 0]) naxs = len(nums) if {*nums.flat} != {*range(1, naxs+1)}: - raise ValueError('Invalid subplot array {array}. Numbers must span integers 1 to naxs (i.e. cannot skip over numbers), with 0 representing empty spaces.') + raise ValueError('Invalid subplot array {array!r}. Numbers must span integers 1 to naxs (i.e. cannot skip over numbers), with 0 representing empty spaces.') if ref not in nums: - raise ValueError(f'Invalid reference number {ref}. For array {array}, must be one of {nums}.') + raise ValueError(f'Invalid reference number {ref!r}. For array {array!r}, must be one of {nums}.') nrows, ncols = array.shape - #-------------------------------------------------------------------------# - # Shared and spanning axes, panel syncing - #-------------------------------------------------------------------------# # Figure out rows and columns "spanned" by each axes in list, for # axis sharing and axis label spanning settings sharex = int(_notNone(sharex, share, rc['share'])) @@ -1846,12 +2019,7 @@ def subplots(array=None, ncols=1, nrows=1, xref = xrange[ref-1,:] # range for reference axes yref = yrange[ref-1,:] - #-------------------------------------------------------------------------# - # Get basemap.Basemap or cartopy.crs.Projection instances for map, and - # override aspect ratio - #-------------------------------------------------------------------------# - # NOTE: Cannot have mutable dict as default arg, because it changes the - # "default" if user calls function more than once! Swap dicts for None. + # Get basemap.Basemap or cartopy.crs.Projection instances for map proj = _notNone(projection, proj, None, names=('projection', 'proj')) proj_kw = _notNone(projection_kw, proj_kw, {}, names=('projection_kw', 'proj_kw')) proj = _axes_dict(naxs, proj, kw=False, default='cartesian') @@ -1859,7 +2027,7 @@ def subplots(array=None, ncols=1, nrows=1, basemap = _axes_dict(naxs, basemap, kw=False, default=False) axes_kw = {num:{} for num in range(1, naxs+1)} # stores add_subplot arguments for num,name in proj.items(): - # The default, my CartesianAxes projection + # The default is CartesianAxes if name is None or name == 'cartesian': axes_kw[num]['projection'] = 'cartesian' # Builtin matplotlib polar axes, just use my overridden version @@ -1875,62 +2043,6 @@ def subplots(array=None, ncols=1, nrows=1, aspect = iaspect axes_kw[num].update({'projection':package, 'map_projection':obj}) - #-------------------------------------------------------------------------# - # Figure and axes panels - #-------------------------------------------------------------------------# - # Input can be string e.g. 'rl' or dictionary e.g. {(1,2,3):'r', 4:'l'} - # Create dictionaries of panel toggles and settings - axpanels = _notNone(axpanel, axpanels, '', names=('axpanel','axpanels')) - axpanels_kw = _notNone(axpanel_kw, axpanels_kw, {}, names=('axpanel_kw', 'axpanels_kw')) - axpanels = _axes_dict(naxs, axpanels, kw=False, default='') - axpanels_kw = _axes_dict(naxs, axpanels_kw, kw=True) - - # Get default figure panel keyword args - # Also fill in empty keyword args - kwout, kworig = {}, {} - panel, panels = _notNone(panel, ''), _notNone(panels, '') - allsides = panel + panels - offsides = ''.join({*'lrbt'} - {*allsides}) - if len(allsides) != len({*allsides}): - raise ValueError('You specified duplicate panel locations, e.g. panel="r" and colorbar="r".') - keys = {*()} - # Sanitize keyword args - names = ('array', 'stack', 'share', 'space', 'width', 'sep') - reg = re.compile(f'^[{offsides}]({"|".join(names)})$') - ikw = {key:value for key,value in kwargs.items() if not reg.match(key)} - ikwout, ikworig = _panels_kwargs(allsides, ikw, figure=True) - keys.update(ikw.keys()) - kwout.update(ikwout) - kworig.update(ikworig) - # Warning - jkw = {key:value for key,value in kwargs.items() if key not in keys} - if jkw: - warnings.warn(f'Ignoring figpanel keyword arg(s): {jkw}. Active sides are: {allsides!r}.') - # Fill keyword args dicts -- unlike axes panels, entires are required - for s in {*'lrbt'} - {*allsides}: - kwout[s + 'array'] = None - kwout[s + 'stack'] = 1 - kwout[s + 'share'] = False - kwout[s + 'width'] = np.array([0]) - kwout[s + 'space'] = 0 - kwout[s + 'sep'] = np.array([]) - kworig[s + 'width'] = None - kworig[s + 'space'] = None - kworig[s + 'sep'] = None - - # Get figure panel arrays, depends on whether use passed 'panel' or 'panels' - # which is not handled by _panels_kwargs! - for ipanel,ispan in zip((panel,panels),(1,0)): - for s in ipanel: - nmax = (ncols if s in 'bt' else nrows) - value = kwout[s + 'array'] - if value is None: - value = ([1]*nmax if ispan else [*range(1,nmax+1)]) - elif not np.iterable(value) or len(value) != nmax: - name = ('columns' if s in 'bt' else 'rows') - raise ValueError(f'Need {nmax}-length list of integers for "{s}array" figure with {nmax} {name}, got {s}array={value!r}.') - kwout[s + 'array'] = value - #-------------------------------------------------------------------------# # Figure architecture #-------------------------------------------------------------------------# @@ -1960,94 +2072,66 @@ def subplots(array=None, ncols=1, nrows=1, for name,value in zip(names,values): if value is not None: warnings.warn(f'You specified both {spec} and {name}={value!r}. Ignoring {name!r}.') - # Standardize input + + # Standardized dimensions width, height = units(width), units(height) axwidth, axheight = units(axwidth), units(axheight) - - # Store and standardize user input spacing args, so can override tight - # layout calculations with them! + # Standardized user input border spaces left, right = units(left), units(right) bottom, top = units(bottom), units(top) - # Space array with Nones allowed - # Also store panel stack separations - axwspace = np.atleast_1d(units(_notNone(wspace, space))) - axhspace = np.atleast_1d(units(_notNone(hspace, space))) - if len(axwspace) == 1: - axwspace = np.repeat(axwspace, (ncols-1,)) - if len(axwspace) != ncols-1: - raise ValueError(f'Require {ncols-1} width spacings for {ncols} columns, got {len(axwspace)}.') - if len(axhspace) == 1: - axhspace = np.repeat(axhspace, (nrows-1,)) - if len(axhspace) != nrows-1: - raise ValueError(f'Require {nrows-1} height spacings for {nrows} rows, got {len(axhspace)}.') - # Pull out originals and put them in arrays that match geometry - # TODO: Why does wspace/hspace have to be an array? - wspace = np.repeat((None,), 3*ncols + 1) - hspace = np.repeat((None,), 3*nrows + 1) - wspace[3:-1:3], hspace[3:-1:3] = axwspace, axhspace # between subplots, and leave panel slots empty so far - wspace[0], wspace[-1] = kworig['lspace'], kworig['rspace'] # figure panels - hspace[0], hspace[-1] = kworig['tspace'], kworig['bspace'] - wseps = np.repeat((None,), 3*ncols + 2) - hseps = np.repeat((None,), 3*nrows + 2) - wseps[0], wseps[-1] = kworig['lsep'], kworig['rsep'] - hseps[0], hseps[-1] = kworig['tsep'], kworig['bsep'] - wwidths = np.repeat((None,), 3*ncols + 2) - hwidths = np.repeat((None,), 3*nrows + 2) - wwidths[0], wwidths[-1] = kworig['lwidth'], kworig['rwidth'] - hwidths[0], hwidths[-1] = kworig['twidth'], kworig['bwidth'] - # Fill the subplots_orig_kw dictionary - # NOTE: We add "seps" and "widths" *only* to subplots_orig_kw, to allow user - # overrides -- info on current widths and seps is stored in ratios lists + # Standardized user input spaces + wspace = np.atleast_1d(units(_notNone(wspace, space))) + hspace = np.atleast_1d(units(_notNone(hspace, space))) + if len(wspace) == 1: + wspace = np.repeat(wspace, (ncols-1,)) + if len(wspace) != ncols-1: + raise ValueError(f'Require {ncols-1} width spacings for {ncols} columns, got {len(wspace)}.') + if len(hspace) == 1: + hspace = np.repeat(hspace, (nrows-1,)) + if len(hspace) != nrows-1: + raise ValueError(f'Require {nrows-1} height spacings for {nrows} rows, got {len(hspace)}.') + # Standardized user input ratios + wratios = np.atleast_1d(_notNone(width_ratios, wratios, 1, + names=('width_ratios', 'wratios'))) + hratios = np.atleast_1d(_notNone(height_ratios, hratios, 1, + names=('height_ratios', 'hratios'))) + if len(wratios) == 1: + wratios = np.repeat(wratios, (ncols,)) + if len(hratios) == 1: + hratios = np.repeat(hratios, (nrows,)) + if len(wratios) != ncols: + raise ValueError(f'Got {ncols} columns, but {len(wratios)} wratios.') + if len(hratios) != nrows: + raise ValueError(f'Got {nrows} rows, but {len(hratios)} hratios.') + + # Fill subplots_orig_kw with user input values + # NOTE: 'Ratios' are only fixed for panel axes, but we store entire array + wspace, hspace = wspace.tolist(), hspace.tolist() + wratios, hratios = wratios.tolist(), hratios.tolist() subplots_orig_kw = { 'left':left, 'right':right, 'top':top, 'bottom':bottom, 'wspace':wspace, 'hspace':hspace, - 'wwidths':wwidths, 'hwidths':hwidths, 'wseps':wseps, 'hseps':hseps, + 'wratios':wratios, 'hratios':hratios, } # Default border spaces - left = _notNone(left, units(rc['subplots.ylabspace'])) - right = _notNone(right, units(rc['subplots.innerspace'])) - top = _notNone(top, units(rc['subplots.titlespace'])) + left = _notNone(left, units(rc['subplots.ylabspace'])) + right = _notNone(right, units(rc['subplots.innerspace'])) + top = _notNone(top, units(rc['subplots.titlespace'])) bottom = _notNone(bottom, units(rc['subplots.xlabspace'])) - # Default spaces including panels - axwspace[axwspace==None] = ( + # Default spaces between axes + wratios, hratios = [*wratios], [*hratios] # copies + wspace, hspace = np.array(wspace), np.array(hspace) # also copies! + wspace[wspace==None] = ( units(rc['subplots.innerspace']) if sharey == 3 else units(rc['subplots.ylabspace']) - units(rc['subplots.titlespace']) if sharey in (1,2) # space for tick labels only else units(rc['subplots.ylabspace'])) - axhspace[axhspace==None] = ( + hspace[hspace==None] = ( units(rc['subplots.titlespace']) + units(rc['subplots.innerspace']) if sharex == 3 else units(rc['subplots.xlabspace']) if sharex in (1,2) # space for tick labels and title else units(rc['subplots.titlespace']) + units(rc['subplots.xlabspace']) ) - wspace = np.repeat((None,), 3*ncols + 1) - hspace = np.repeat((None,), 3*nrows + 1) - wspace[3:-1:3], hspace[3:-1:3] = axwspace, axhspace # between subplots - wspace[2:-1:3], hspace[2:-1:3] = 0, 0 # zero initial space for axes panels - wspace[1:-1:3], hspace[1:-1:3] = 0, 0 - wspace[0], wspace[-1] = kwout['lspace'], kwout['rspace'] # figure panels - hspace[0], hspace[-1] = kwout['tspace'], kwout['bspace'] - # Default ratios including panels - axwidths = np.atleast_1d(_notNone(width_ratios, wratios, 1, - names=('width_ratios', 'wratios'))) - axheights = np.atleast_1d(_notNone(height_ratios, hratios, 1, - names=('height_ratios', 'hratios'))) - if len(axwidths) == 1: - axwidths = np.repeat(axwidths, (ncols,)) - if len(axheights) == 1: - axheights = np.repeat(axheights, (nrows,)) - if len(axwidths) != ncols: - raise ValueError(f'Got {ncols} columns, but {len(axwidths)} wratios.') - if len(axheights) != nrows: - raise ValueError(f'Got {nrows} rows, but {len(axheights)} hratios.') - wratios = np.repeat((None,), 3*ncols + 2) - hratios = np.repeat((None,), 3*nrows + 2) - wratios[2:-1:3], hratios[2:-1:3] = axwidths, axheights # subplots - wratios[3:-1:3], hratios[3:-1:3] = 0, 0 # zero width initial axes panels - wratios[1:-1:3], hratios[1:-1:3] = 0, 0 - wratios[0] = sum(kwout['lwidth']) + sum(kwout['lsep']) - wratios[-1] = sum(kwout['rwidth']) + sum(kwout['lsep']) - hratios[0] = sum(kwout['twidth']) + sum(kwout['tsep']) - hratios[-1] = sum(kwout['bwidth']) + sum(kwout['bsep']) + wspace, hspace = wspace.tolist(), hspace.tolist() # Parse arguments, fix dimensions in light of desired aspect ratio figsize, gridspec_kw, subplots_kw = _subplots_geometry( @@ -2056,12 +2140,13 @@ def subplots(array=None, ncols=1, nrows=1, left=left, right=right, bottom=bottom, top=top, width=width, height=height, axwidth=axwidth, axheight=axheight, wratios=wratios, hratios=hratios, wspace=wspace, hspace=hspace, + wpanels=['']*ncols, hpanels=['']*nrows, ) fig = plt.figure(FigureClass=Figure, tight=tight, figsize=figsize, ref=ref, pad=pad, axpad=axpad, panelpad=panelpad, autoformat=autoformat, subplots_orig_kw=subplots_orig_kw, subplots_kw=subplots_kw, gridspec_kw=gridspec_kw) - gridspec = fig._main_gridspec + gridspec = fig._gridspec_main #-------------------------------------------------------------------------# # Draw on figure @@ -2071,8 +2156,8 @@ def subplots(array=None, ncols=1, nrows=1, for idx in range(naxs): # Get figure gridspec ranges num = idx + 1 - x0, x1 = xrange[idx,0]*3 + 2, xrange[idx,1]*3 + 2 # offsets for panels - y0, y1 = yrange[idx,0]*3 + 2, yrange[idx,1]*3 + 2 + x0, x1 = xrange[idx,0], xrange[idx,1] + y0, y1 = yrange[idx,0], yrange[idx,1] # Draw subplot subplotspec = gridspec[y0:y1+1, x0:x1+1] with fig._unlock(): @@ -2082,13 +2167,28 @@ def subplots(array=None, ncols=1, nrows=1, **axes_kw[num]) # Set up shared axes and assign main axes - fig._main_axes = axs + fig._axes_main = axs fig._share_axes_setup() - # Draw axes panels after all main subplots are drawn + # Draw figure panels + # First get default arrays + panel, panels = _notNone(panel, ''), _notNone(panels, '') + for ipanel,ispan in zip((panel,panels),(1,0)): + for s in ipanel: + nmax = (ncols if s in 'bt' else nrows) + value = ([1]*nmax if ispan else [*range(1,nmax+1)]) + kwargs.setdefault(s + 'array', value) + for s in (panel + panels): + fig._add_figure_panels(s, **kwargs) + + # Draw axes panel + # Input can be string e.g. 'rl' or dictionary e.g. {(1,2,3):'r', 4:'l'} # NOTE: This *must* come after shared axes are set up! Otherwise tight # layout scaling is wrong - names = ('stack', 'share', 'space', 'width', 'sep') + axpanels = _notNone(axpanel, axpanels, '', names=('axpanel','axpanels')) + axpanels_kw = _notNone(axpanel_kw, axpanels_kw, {}, names=('axpanel_kw', 'axpanels_kw')) + axpanels = _axes_dict(naxs, axpanels, kw=False, default='') + axpanels_kw = _axes_dict(naxs, axpanels_kw, kw=True) for idx in range(naxs): ax = axs[idx] num = idx + 1 @@ -2097,67 +2197,15 @@ def subplots(array=None, ncols=1, nrows=1, # Loop through sides for s in sides: offsides = ''.join({*'lrbt'} - {s}) - reg = re.compile(f'^[{offsides}]({"|".join(names)})$') + reg = re.compile(f'^[{offsides}](share|space|width)$') ikw = {key:value for key,value in panels_kw.items() if not reg.match(key)} keys.update(ikw.keys()) - fig._panel_axes(ax, s, order=order, **ikw) + fig._add_axes_panel(s, ax, order=order, **ikw) # Warning message jkw = {key:value for key,value in panels_kw.items() if key not in keys} if jkw: warnings.warn(f'Ignoring axpanels_kw keyword arg(s): {jkw}. Active sides are: axpanels={sides!r}.') - # Draw figure panels - for s in 'blrt': - # Get keyword args and gridspec args - array = kwout[s + 'array'] - width = kwout[s + 'width'] - share = kwout[s + 'share'] - sep = kwout[s + 'sep'] - if array is None: - continue - if s in 'lr': - hspace, hratios, wspace, wratios = [], [1], sep, width - else: - hspace, hratios, wspace, wratios = sep, width, [], [1] - nrows, ncols = len(hratios), len(wratios) - # Loop through unique numbers - paxs = [] - side = _side_translate[s] - for num in np.unique(array).flat: - # Get subplotspec and insert gridspec - if num == 0: - continue - idx, = np.where(array == num) - idx1 = slice(min(idx)*3 + 2, max(idx)*3 + 3) # ignores axes panel slots - idx2 = 0 if s in 'lt' else -1 - if s in 'bt': - idx1, idx2 = idx2, idx1 - subplotspec = gridspec[idx1,idx2] - igridspec = FlexibleGridSpecFromSubplotSpec( - subplot_spec=subplotspec, - nrows=nrows, ncols=ncols, - wspace=wspace, hspace=hspace, - width_ratios=wratios, height_ratios=hratios, - ) - # Draw axes - ipaxs = [] - for i in range(max((nrows,ncols))): - with fig._unlock(): - ipax = fig.add_subplot(igridspec[i], - projection='panel', - side=side, share=share, - ) - ipaxs += [ipax] - paxs += [ipaxs] - # Sort panel axes into row-major or column-major order - if (s in 'bt' and order == 'C') or (s in 'lr' and order != 'C'): - paxs = [*zip(*paxs)] - # Store in axes_grid with support for 2D indexing - n = len(paxs[0]) - paxs = [ax for ipaxs in paxs for ax in ipaxs] # unfurl - paxs = axes_grid(paxs, n=n, order=order) - setattr(fig, '_' + side + 'panel', paxs) - # Return figure and axes n = (ncols if order == 'C' else nrows) return fig, axes_grid(axs, n=n, order=order) diff --git a/proplot/wrappers.py b/proplot/wrappers.py index e0ab555e7..f3056d64e 100644 --- a/proplot/wrappers.py +++ b/proplot/wrappers.py @@ -1583,6 +1583,7 @@ def cycle_wrapper(self, func, *args, # Add colorbar and/or legend if colorbar: # Add handles + panel_kw.setdefault('mode', 'colorbar') ax, loc = self._inset_or_panel_loc(colorbar, **panel_kw) if not isinstance(loc, str): raise ValueError(f'Invalid on-the-fly location {loc!r}. Must be a preset location. See Axes.colorbar') @@ -1598,6 +1599,7 @@ def cycle_wrapper(self, func, *args, ax._auto_colorbar_kw[loc].update(colorbar_kw) if legend: # Add handles + panel_kw.setdefault('mode', 'legend') ax, loc = self._inset_or_panel_loc(legend, **panel_kw) if not isinstance(loc, str): raise ValueError(f'Invalid on-the-fly location {loc!r}. Must be a preset location. See Axes.legend') @@ -2009,6 +2011,7 @@ def cmap_wrapper(self, func, *args, cmap=None, cmap_kw=None, # Add colorbar if colorbar: + panel_kw.setdefault('mode', 'colorbar') ax, loc = self._inset_or_panel_loc(colorbar, **panel_kw) if not isinstance(loc, str): raise ValueError(f'Invalid on-the-fly location {loc!r}. Must be a preset location. See Axes.colorbar.') From ace9794217d1da5150d232618b6bd0edd56be827 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Mon, 2 Sep 2019 13:44:08 -0600 Subject: [PATCH 02/17] Rearrange --- proplot/subplots.py | 155 ++++++++++++++++++++++---------------------- 1 file changed, 76 insertions(+), 79 deletions(-) diff --git a/proplot/subplots.py b/proplot/subplots.py index 561e40a48..9eb1d0dad 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -767,10 +767,84 @@ def __init__(self, self._gridspec_main = FlexibleGridSpec(self, **(gridspec_kw or {})) self.suptitle('') # add _suptitle attribute + @_counter + def _add_axes_panel(self, side, ax, order='C', mode='panel', **kwargs): + """Hidden method that powers `~proplot.axes.panel_axes`.""" + # Interpret args + # NOTE: Axis sharing not implemented for figure panels, 99% of the + # time this is just used as construct for adding global colorbars and + # legends, really not worth implementing axis sharing + s = side[0] + if s not in 'lrbt': + raise ValueError(f'Invalid side {side!r}.') + side = _side_translate[s] + kwargs, kworig = _panels_kwargs(s, kwargs, mode=mode, figure=False) + width = kwargs[s + 'width'] + space = kwargs[s + 'space'] + share = kwargs[s + 'share'] + width_orig = kworig[s + 'width'] + space_orig = kworig[s + 'space'] + + # Modify existing geometry, find incies for new panel by placing it + # to the outside of *existing* panels if possible + # TODO: If panel slot already exists, permit user to change the + # width and space. + subplotspec = ax.get_subplotspec() + nrows, ncols, row1, row2, col1, col2 = subplotspec.get_active_rows_columns() + pgrid = getattr(ax, '_' + side + 'panel') + offset = (len(pgrid)*bool(pgrid)) + 1 + if s in 'lr': + idx1 = slice(row1, row2 + 1) + idx2 = (col1 - offset if s == 'l' else col2 + offset) + iratio = idx2 + else: + idx1 = (row1 - offset if s == 't' else row2 + offset) + idx2 = slice(col1, col2 + 1) + iratio = idx1 + gridspec = self._change_gridspec(side, iratio, + width, space, width_orig, space_orig, + ) + + # Get keyword args and gridspec args + # Loop through unique numbers + with self._unlock(): + pax = self.add_subplot(gridspec[idx1,idx2], + projection='panel', + side=side, share=share, + ) + + # Add to panel attributes + pgrid = getattr(ax, '_' + side + 'panel') + # Create from scratch + if not pgrid: + order = self._order + pgrid = axes_grid([pax], n=1, order=order) + setattr(ax, '_' + side + 'panel', pgrid) + # Modify existing grid + else: + n = pgrid._n + order = pgrid._order + if s in 'lt': + pgrid.insert(0, pax) + else: + pgrid.append(pax) + if (s in 'lr' and order == 'C') or (s in 'tb' and order == 'F'): + n += 1 + pgrid._n = n + + # Set up shared axes + if share: + ax._share_panels_setup() + self._share_axes_setup(ax) + + # Return axes grid of just this axes + return axes_grid([pax]) + def _add_figure_panels(self, side, mode='panel', **kwargs): """Adds figure panels. Also modifies the panel attribute stored on the figure to include these panels.""" # Interpret args + # TODO: Allow successive panels! # NOTE: Axis sharing not implemented for figure panels, 99% of the # time this is just used as construct for adding global colorbars and # legends, really not worth implementing axis sharing @@ -810,14 +884,11 @@ def _add_figure_panels(self, side, mode='panel', **kwargs): # Pad array so it spans existing panel slots # NOTE: Does not matter if have e.g. [1, 0, 0, 1, 2, 0, 0, 2], because # panels are drawn based on minimum and maximum indices + paxs = [] array_new = np.zeros((nalong,)) array_new[idxs] = array - - # Get keyword args and gridspec args - # Loop through unique numbers - paxs = [] + # Get indices and draw axes for num in np.unique(array_new).flat: - # Get subplotspec and insert gridspec if num == 0: continue idx, = np.where(array_new == num) @@ -825,7 +896,6 @@ def _add_figure_panels(self, side, mode='panel', **kwargs): idx2 = -1*(s in 'br') if s in 'bt': idx1, idx2 = idx2, idx1 - # Draw axes with self._unlock(): ipax = self.add_subplot(gridspec[idx1,idx2], projection='panel', @@ -873,79 +943,6 @@ def _add_figure_panels(self, side, mode='panel', **kwargs): # Return axes_grid of just the axes drawn in this round return axes_grid(paxs) # no 2D indexing - @_counter - def _add_axes_panel(self, side, ax, order='C', mode='panel', **kwargs): - """Hidden method that powers `~proplot.axes.panel_axes`.""" - # Interpret args - # NOTE: Axis sharing not implemented for figure panels, 99% of the - # time this is just used as construct for adding global colorbars and - # legends, really not worth implementing axis sharing - s = side[0] - if s not in 'lrbt': - raise ValueError(f'Invalid side {side!r}.') - side = _side_translate[s] - kwargs, kworig = _panels_kwargs(s, kwargs, mode=mode, figure=False) - width = kwargs[s + 'width'] - space = kwargs[s + 'space'] - share = kwargs[s + 'share'] - width_orig = kworig[s + 'width'] - space_orig = kworig[s + 'space'] - - # Modify existing geometry, find incies for new panel by placing it - # to the outside of *existing* panels if possible - # TODO: If panel slot already exists, permit user to change the - # width and space. - subplotspec = ax.get_subplotspec() - nrows, ncols, row1, row2, col1, col2 = subplotspec.get_active_rows_columns() - pgrid = getattr(ax, '_' + side + 'panel') - offset = (len(pgrid)*bool(pgrid)) + 1 - if s in 'lr': - idx1 = slice(row1, row2 + 1) - idx2 = (col1 - offset if s == 'l' else col2 + offset) - iratio = idx2 - else: - idx1 = (row1 - offset if s == 't' else row2 + offset) - idx2 = slice(col1, col2 + 1) - iratio = idx1 - gridspec = self._change_gridspec(side, iratio, - width, space, width_orig, space_orig, - ) - - # Get keyword args and gridspec args - # Loop through unique numbers - with self._unlock(): - pax = self.add_subplot(gridspec[idx1,idx2], - projection='panel', - side=side, share=share, - ) - - # Add to panel attributes - pgrid = getattr(ax, '_' + side + 'panel') - # Create from scratch - if not pgrid: - order = self._order - pgrid = axes_grid([pax], n=1, order=order) - setattr(ax, '_' + side + 'panel', pgrid) - # Modify existing grid - else: - n = pgrid._n - order = pgrid._order - if s in 'lt': - pgrid.insert(0, pax) - else: - pgrid.append(pax) - if (s in 'lr' and order == 'C') or (s in 'tb' and order == 'F'): - n += 1 - pgrid._n = n - - # Set up shared axes - if share: - ax._share_panels_setup() - self._share_axes_setup(ax) - - # Return axes grid of just this axes - return axes_grid([pax]) - def _adjust_aspect(self): """Adjust average aspect ratio used for gridspec calculations. This fixes grids with identically fixed aspect ratios, e.g. identically From 79b988fa01e60de7ad41796636c0bf858d647cf8 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Mon, 2 Sep 2019 16:55:26 -0600 Subject: [PATCH 03/17] Intermediate changes --- proplot/axes.py | 290 +++++++++++++++++++++++++------------------- proplot/subplots.py | 4 + proplot/wrappers.py | 6 +- 3 files changed, 175 insertions(+), 125 deletions(-) diff --git a/proplot/axes.py b/proplot/axes.py index 8ff0837ee..7b8c40050 100644 --- a/proplot/axes.py +++ b/proplot/axes.py @@ -293,7 +293,8 @@ def _get_title_props(self, abc=False, loc=None): obj.set_transform(self.transAxes) return loc, obj, kw - def _inset_or_panel_loc(self, loc, **kwargs): + @staticmethod + def _inset_or_panel_loc(loc, **kwargs): """Translates location string `loc`, returns this axes or a panel axes and the translated location string. If it is a panel axes, returned location is ``'fill'``. If the panel does not exist, it is drawn, @@ -305,25 +306,9 @@ def _inset_or_panel_loc(self, loc, **kwargs): loc, idx = loc # e.g. ('r',2) # Try getting the panel, and translate location - ax = self if isinstance(loc, str): loc = _side_translate.get(loc, loc) - if loc in ('left','right','top','bottom'): - if isinstance(self, PanelAxes): - raise ValueError('Axes {self} is already a PanelAxes. Cannot add a panel to a panel.') - axs = self.panel_axes(loc, **kwargs) - # axs = getattr(self, loc + 'panel') # axes_grid of panels - # if not axs: - # axs = self.panel_axes(loc, **kwargs) - # else: - # if kwargs: - # warnings.warn(f'Ignoring panel keyword arg(s) {kwargs}. Panel already exists.') - try: - ax = axs[idx] - except IndexError: - raise ValueError(f'Stack index {idx} for panel {loc!r} is invalid. You must make room for it with e.g. ax.panels({loc}stack=2).') - loc = 'fill' - elif loc is True: + if loc is True: loc = None elif isinstance(loc, (str, Integral)): loc = _loc_translate.get(loc, loc) # may still be invalid @@ -914,29 +899,26 @@ def cmapline(self, *args, values=None, return hs def colorbar(self, *args, loc=None, pad=None, - length=None, width=None, xspace=None, frame=None, frameon=None, + length=None, width=None, space=None, frame=None, frameon=None, alpha=None, linewidth=None, edgecolor=None, facecolor=None, - panel_kw=None, **kwargs): """ - Adds an *inset* colorbar, or calls the `PanelAxes.colorbar` method for - the panel at location `loc`. See `~proplot.wrappers.colorbar_wrapper` - for details. + Adds colorbar as an *inset* or along the outside edge of the axes. + See `~proplot.wrappers.colorbar_wrapper` for details. Parameters ---------- loc : str, optional - The colorbar location or panel location. The following location keys - are valid. Note that if a panel does not exist, it will be - generated on-the-fly. + The colorbar location. Defaults to ``rc['colorbar.loc']``. The + following location keys are valid. ================== ========================================================== Location Valid keys ================== ========================================================== - left panel ``'l'``, ``'left'`` - right panel ``'r'``, ``'right'`` - bottom panel ``'b'``, ``'bottom'`` - top panel ``'t'``, ``'top'`` + outer left ``'l'``, ``'left'`` + outer right ``'r'``, ``'right'`` + outer bottom ``'b'``, ``'bottom'`` + outer top ``'t'``, ``'top'`` upper right inset ``1``, ``'upper right'``, ``'ur'`` upper left inset ``2``, ``'upper left'``, ``'ul'`` lower left inset ``3``, ``'lower left'``, ``'ll'`` @@ -944,9 +926,10 @@ def colorbar(self, *args, loc=None, pad=None, ================== ========================================================== pad : float or str, optional - Space between the axes edge and the colorbar. - If float, units are inches. If string, units are interpreted by - `~proplot.utils.units`. Defaults to ``rc['colorbar.pad']``. + The space between the axes edge and the colorbar. Ignored for + outer colorbars. If float, units are inches. If string, units + are interpreted by `~proplot.utils.units`. Defaults to + ``rc['colorbar.pad']``. length : float or str, optional The colorbar length. If float, units are inches. If string, units are interpreted by `~proplot.utils.units`. Defaults to @@ -956,10 +939,6 @@ def colorbar(self, *args, loc=None, pad=None, units are interpreted by `~proplot.utils.units`. Defaults to ``rc['colorbar.width']`` for inset colorbars, ``rc['subplots.cbarwidth']`` for panel colorbars. - xspace : float or str, optional - Space allocated for the bottom x-label of the colorbar. - If float, units are inches. If string, units are interpreted - by `~proplot.utils.units`. Defaults to ``rc['colorbar.xspace']``. frame, frameon : bool, optional Whether to draw a frame behind the inset colorbar, just like `~matplotlib.axes.Axes.legend`. Defaults to ``rc['colorbar.frameon']``. @@ -967,93 +946,161 @@ def colorbar(self, *args, loc=None, pad=None, Transparency, edge width, edge color, and face color for the frame. Defaults to ``rc['colorbar.framealpha']``, ``rc['axes.linewidth']``, ``rc['axes.edgecolor']``, and ``rc['axes.facecolor']``. - panel_kw : dict-like, optional - Dictionary of keyword arguments passed to - `~proplot.axes.Axes.panel`, if you are generating an - on-the-fly panel. + width : float or str, optional + The width of the colorbar. If float, units are inches. If string, + units are interpreted by `~proplot.utils.units`. Defaults to + ``rc['colorbar.width']`` for inset colorbars and + ``rc['subplots.colorbarwidth']`` for colorbars outside the axes. + space : float or str, optional + The space between the colorbar and the main axes. Ignored for + non-inset colorbars. If float, units are inches. If string, + units are interpreted by `~proplot.utils.units`. By default, this + is adjusted automatically in the "tight layout" calculation. **kwargs Passed to `~proplot.wrappers.colorbar_wrapper`. """ - loc = _notNone(loc, rc['colorbar.loc']) - panel_kw = panel_kw or {} - if width is not None: - panel_kw.setdefault('width', width) + # TODO: add option to pad inset away from axes edge! kwargs.update({'edgecolor':edgecolor, 'linewidth':linewidth}) - ax, loc = self._inset_or_panel_loc(loc, **panel_kw) + loc = _notNone(loc, rc['colorbar.loc']) + loc = self._inset_or_panel_loc(loc, **panel_kw) if not isinstance(loc, str): # e.g. 2-tuple or ndarray raise ValueError(f'Invalid colorbar location {loc!r}.') - if loc == 'fill': - return ax.colorbar(*args, **kwargs) - # Default props - cbwidth, cblength = width, length - width, height = self.get_size_inches() - extend = units(_notNone(kwargs.get('extendsize',None), rc['colorbar.extendinset'])) - cbwidth = units(_notNone(cbwidth, rc['colorbar.width']))/height - cblength = units(_notNone(cblength, rc['colorbar.length']))/width - pad = units(_notNone(pad, rc['colorbar.axespad'])) - xpad, ypad = pad/width, pad/height - kwargs.setdefault('extendsize', extend) - # Tick location handling - tickloc = kwargs.pop('tickloc', None) - ticklocation = kwargs.pop('ticklocation', None) - if any(loc is not None and loc!='bottom' - for loc in (tickloc,ticklocation)): - warnings.warn(f'Inset colorbars can only have ticks on the bottom.') - kwargs['ticklocation'] = 'bottom' - # Space for labels - if kwargs.get('label', ''): - xspace = 2.4*rc['font.size']/72 + rc['xtick.major.size']/72 - else: - xspace = 1.2*rc['font.size']/72 + rc['xtick.major.size']/72 - xspace /= height - # Get location in axes-relative coordinates - # Bounds are x0, y0, width, height in axes-relative coordinate to start - if loc == 'upper right': - bounds = (1 - xpad - cblength, 1 - ypad - cbwidth) - fbounds = (1 - 2*xpad - cblength, 1 - 2*ypad - cbwidth - xspace) - elif loc == 'upper left': - bounds = (xpad, 1 - ypad - cbwidth) - fbounds = (0, 1 - 2*ypad - cbwidth - xspace) - elif loc == 'lower left': - bounds = (xpad, ypad+xspace) - fbounds = (0, 0) - elif loc == 'lower right': - bounds = (1 - xpad - cblength, ypad+xspace) - fbounds = (1 - 2*xpad - cblength, 0) - else: - raise ValueError(f'Invalid colorbar location {loc!r}.') - bounds = (bounds[0], bounds[1], cblength, cbwidth) - fbounds = (fbounds[0], fbounds[1], 2*xpad + cblength, 2*ypad + cbwidth + xspace) - # Make axes - locator = self._make_inset_locator(bounds, self.transAxes) - bbox = locator(None, None) - ax = maxes.Axes(self.figure, bbox.bounds, zorder=5) - ax.set_axes_locator(locator) - self.add_child_axes(ax) - - # Make colorbar - # WARNING: Inset colorbars are tiny! So use smart default locator - kwargs.setdefault('maxn', 5) - cb = wrappers.colorbar_wrapper(ax, *args, **kwargs) - - # Make frame - # NOTE: We do not allow shadow effects or fancy edges effect. - # Also keep zorder same as with legend. - frameon = _notNone(frame, frameon, rc['colorbar.frameon'], names=('frame','frameon')) - if frameon: - # Make object - xmin, ymin, width, height = fbounds - patch = mpatches.Rectangle((xmin,ymin), width, height, - snap=True, zorder=4.5, transform=self.transAxes) # fontsize defined in if statement - # Properties - alpha = _notNone(alpha, rc['colorbar.framealpha']) - linewidth = _notNone(linewidth, rc['axes.linewidth']) - edgecolor = _notNone(edgecolor, rc['axes.edgecolor']) - facecolor = _notNone(facecolor, rc['axes.facecolor']) - patch.update({'alpha':alpha, 'linewidth':linewidth, 'edgecolor':edgecolor, 'facecolor':facecolor}) - self.add_artist(patch) - return cb + # Filled colorbar + if loc in ('left','right','top','bottom'): + # Generate the panel + ax = self.panel_axes(loc, width=width, space=space) + + # Hide content and resize panel + # NOTE: Do not run self.clear in case we want title above this + for s in ax.spines.values(): + s.set_visible(False) + ax.xaxis.set_visible(False) + ax.yaxis.set_visible(False) + ax.patch.set_alpha(0) + ax._filled = True + + # Draw colorbar with arbitrary length relative to full length of panel + subspec = ax.get_subplotspec() + if length is not None and length != 1: + if length <= 0 or length >= 1: + raise ValueError(f'Panel colorbar length must be between 0 and 1, got length={length!r}.') + if loc in ('bottom','top'): + gridspec = mgridspec.GridSpecFromSubplotSpec( + nrows=1, ncols=3, wspace=0, + subplot_spec=subspec, + width_ratios=((1-length)/2, length, (1-length)/2), + ) + subspec = gridspec[1] + else: + gridspec = mgridspec.GridSpecFromSubplotSpec( + nrows=3, ncols=1, hspace=0, + subplot_spec=subspec, + height_ratios=((1-length)/2, length, (1-length)/2), + ) + subspec = gridspec[1] + + # Get properties + with ax.figure._unlock(): + ax = ax.figure.add_subplot(subspec, projection=None) + if loc in ('bottom','top'): + outside, inside = 'bottom', 'top' + if side == 'top': + outside, inside = inside, outside + ticklocation = outside + orientation = 'horizontal' + else: + outside, inside = 'left', 'right' + if side == 'right': + outside, inside = inside, outside + ticklocation = outside + orientation = 'vertical' + + # For filled axes, call wrapper method directly + ax.add_child_axes(ax) + orient = kwargs.get('orientation', None) + if orient is not None and orient != orientation: + warnings.warn(f'Overriding input orientation={orient!r}.') + ticklocation = kwargs.pop('tickloc', None) or ticklocation + ticklocation = kwargs.pop('ticklocation', None) or ticklocation + kwargs.update({'orientation':orientation, 'ticklocation':ticklocation}) + return wrappers.colorbar_wrapper(ax, *args, **kwargs) + + # Inset colorbar + else: + # Default props + cbwidth, cblength = width, length + width, height = self.get_size_inches() + extend = units(_notNone(kwargs.get('extendsize',None), rc['colorbar.extendinset'])) + cbwidth = units(_notNone(cbwidth, rc['colorbar.width']))/height + cblength = units(_notNone(cblength, rc['colorbar.length']))/width + pad = units(_notNone(pad, rc['colorbar.axespad'])) + xpad, ypad = pad/width, pad/height + kwargs.setdefault('extendsize', extend) + + # Tick location handling + tickloc = kwargs.pop('tickloc', None) + ticklocation = kwargs.pop('ticklocation', None) + if any(loc is not None and loc!='bottom' + for loc in (tickloc,ticklocation)): + warnings.warn(f'Inset colorbars can only have ticks on the bottom.') + kwargs['ticklocation'] = 'bottom' + + # Space for labels + if kwargs.get('label', ''): + xspace = 2.4*rc['font.size']/72 + rc['xtick.major.size']/72 + else: + xspace = 1.2*rc['font.size']/72 + rc['xtick.major.size']/72 + xspace /= height + + # Get location in axes-relative coordinates + # Bounds are x0, y0, width, height in axes-relative coordinate to start + if loc == 'upper right': + bounds = (1 - xpad - cblength, 1 - ypad - cbwidth) + fbounds = (1 - 2*xpad - cblength, 1 - 2*ypad - cbwidth - xspace) + elif loc == 'upper left': + bounds = (xpad, 1 - ypad - cbwidth) + fbounds = (0, 1 - 2*ypad - cbwidth - xspace) + elif loc == 'lower left': + bounds = (xpad, ypad + xspace) + fbounds = (0, 0) + elif loc == 'lower right': + bounds = (1 - xpad - cblength, ypad + xspace) + fbounds = (1 - 2*xpad - cblength, 0) + else: + raise ValueError(f'Invalid colorbar location {loc!r}.') + bounds = (bounds[0], bounds[1], cblength, cbwidth) + fbounds = (fbounds[0], fbounds[1], 2*xpad + cblength, 2*ypad + cbwidth + xspace) + + # Make axes + locator = self._make_inset_locator(bounds, self.transAxes) + bbox = locator(None, None) + ax = maxes.Axes(self.figure, bbox.bounds, zorder=5) + ax.set_axes_locator(locator) + self.add_child_axes(ax) + + # Make colorbar + # WARNING: Inset colorbars are tiny! So use smart default locator + kwargs.setdefault('maxn', 5) + cb = wrappers.colorbar_wrapper(ax, *args, **kwargs) + + # Make frame + # NOTE: We do not allow shadow effects or fancy edges effect. + # Also keep zorder same as with legend. + frameon = _notNone(frame, frameon, rc['colorbar.frameon'], names=('frame','frameon')) + if frameon: + # Make object + xmin, ymin, width, height = fbounds + patch = mpatches.Rectangle((xmin,ymin), width, height, + snap=True, zorder=4.5, transform=self.transAxes) # fontsize defined in if statement + # Properties + alpha = _notNone(alpha, rc['colorbar.framealpha']) + linewidth = _notNone(linewidth, rc['axes.linewidth']) + edgecolor = _notNone(edgecolor, rc['axes.edgecolor']) + facecolor = _notNone(facecolor, rc['axes.facecolor']) + patch.update({'alpha':alpha, 'linewidth':linewidth, 'edgecolor':edgecolor, 'facecolor':facecolor}) + self.add_artist(patch) + return cb def legend(self, *args, loc=None, width=None, panel_kw=None, **kwargs): """ @@ -1098,14 +1145,14 @@ def legend(self, *args, loc=None, width=None, panel_kw=None, **kwargs): panel_kw = panel_kw or {} if width is not None: panel_kw.setdefault('width', width) - ax, loc = self._inset_or_panel_loc(loc, **panel_kw) - if loc == 'fill': - return ax.legend(*args, **kwargs) + loc = self._inset_or_panel_loc(loc, **panel_kw) + # TODO: FIXME + if loc in ('left','right','top','bottom'): + return self.legend(*args, loc=loc, **kwargs) return wrappers.legend_wrapper(ax, *args, loc=loc, **kwargs) def draw(self, renderer=None, *args, **kwargs): """Adds post-processing steps before axes is drawn.""" - self._draw_auto_legends_colorbars() self._reassign_title() super().draw(renderer, *args, **kwargs) @@ -1119,7 +1166,6 @@ def get_size_inches(self): def get_tightbbox(self, renderer, *args, **kwargs): """Adds post-processing steps before tight bounding box is calculated, and stores the bounding box as an attribute.""" - self._draw_auto_legends_colorbars() self._reassign_title() bbox = super().get_tightbbox(renderer, *args, **kwargs) self._tight_bbox = bbox diff --git a/proplot/subplots.py b/proplot/subplots.py index 9eb1d0dad..c16e87b13 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -1557,6 +1557,8 @@ def draw(self, renderer): # and cannot modify PDFPage or SVG renderer props inplace, so idea was # to override get_size_inches. But when get_size_inches is called, the # canvas has no renderer, so cannot apply tight layout yet! + for ax in self._iter_axes(): + ax._draw_auto_legends_colorbars() self._adjust_aspect() self._align_axislabels(False) self._align_suplabels(renderer) @@ -1588,6 +1590,8 @@ def savefig(self, filename, **kwargs): if hasattr(canvas, 'get_renderer'): renderer = canvas.get_renderer() canvas.renderer = renderer + for ax in self._iter_axes(): + ax._draw_auto_legends_colorbars() self._adjust_aspect() self._align_axislabels(False) self._align_suplabels(renderer) diff --git a/proplot/wrappers.py b/proplot/wrappers.py index f3056d64e..e1547d2c0 100644 --- a/proplot/wrappers.py +++ b/proplot/wrappers.py @@ -34,9 +34,9 @@ ] # Xarray and pandas integration -# These are 0.5s in load time! We just want to detect if input arrays already -# belong to these types, which necessarily means user has already imported -# module, so only load objects at that point! +# These are 0.5s in load time! We just want to detect if *input arrays* +# belong to these types, which necessarily means modules have already been +# imported. So, delay these class definitions until user calls plot functions. ndarray = np.ndarray def _load_objects(): global DataArray, DataFrame, Series, Index From bad8024405355a3062ddefadfb2c6fe152f00205 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Mon, 2 Sep 2019 20:50:35 -0600 Subject: [PATCH 04/17] Remove PanelAxes, and remove 'panels' args from subplots()! Colorbar and legend panels can now only be generated on-the-fly. --- proplot/.proplotrc | 14 +- proplot/axes.py | 657 ++++++++++++++------------------------------ proplot/rctools.py | 14 +- proplot/subplots.py | 487 +++++++++----------------------- proplot/wrappers.py | 31 ++- 5 files changed, 372 insertions(+), 831 deletions(-) diff --git a/proplot/.proplotrc b/proplot/.proplotrc index a0723ce05..d42e79aac 100644 --- a/proplot/.proplotrc +++ b/proplot/.proplotrc @@ -164,22 +164,22 @@ innerborders.linewidth: 0.6 rivers.color: k rivers.linewidth: 0.6 # Colorbar settings -colorbar.loc: 'lower right' +colorbar.loc: right colorbar.grid: False colorbar.frameon: True colorbar.framealpha: 0.8 -colorbar.extend: 0.15 -colorbar.extendinset: 1em -colorbar.length: 8em -colorbar.width: 1.2em colorbar.axespad: 0.5em +colorbar.extend: 1.3em +colorbar.extendinset: 1em +colorbar.length: 1 +colorbar.lengthinset: 8em +colorbar.width: 1.5em # 0.17 inches +colorbar.widthinset: 1.2em # Subplot properties # Use font relative units for everything because this is common way user # might want to increase figure resolution subplots.axwidth: 18em # 2 inches subplots.panelwidth: 4em # 0.45 inches -subplots.colorbarwidth: 1.5em # 0.17 inches -subplots.legendwidth: 1.5em # 0.25 inches subplots.pad: 0.5em subplots.axpad: 1em subplots.panelpad: 0.5em diff --git a/proplot/axes.py b/proplot/axes.py index 7b8c40050..6a170a951 100644 --- a/proplot/axes.py +++ b/proplot/axes.py @@ -59,7 +59,6 @@ 'BasemapAxes', 'CartesianAxes', 'CartopyAxes', - 'EmptyPanel', 'PanelAxes', 'PolarAxes', 'ProjectionAxes', ] @@ -72,6 +71,13 @@ } _loc_translate = { None:None, + 'l':'left', + 'r':'right', + 'b':'bottom', + 't':'top', + 'c':'center', + 'i':'best', + 'inset':'best', 0:'best', 1:'upper right', 2:'upper left', @@ -82,7 +88,6 @@ 7:'lower center', 8:'upper center', 9:'center', - 'b':'best', 'ur':'upper right', 'ul':'upper left', 'll':'lower left', @@ -91,7 +96,6 @@ 'cl':'center left', 'uc':'upper center', 'lc':'lower center', - 'c':'center', } # Helper function @@ -154,13 +158,15 @@ def __init__(self, *args, number=None, self._title_pad = rc.get('axes.titlepad') # so we can copy to top panel self._title_above_panel = True # TODO: add rc prop? # Children and related properties - self._bottompanel = EmptyPanel() - self._toppanel = EmptyPanel() - self._leftpanel = EmptyPanel() - self._rightpanel = EmptyPanel() + self._bpanels = [] + self._tpanels = [] + self._lpanels = [] + self._rpanels = [] self._tight_bbox = None # bounding boxes are saved self._zoom = None + self._panel_side = None self._panel_parent = None + self._panel_filled = False # True when panels "filled" with colorbar/legend self._inset_zoom = False self._inset_parent = None self._alty_child = None @@ -171,7 +177,6 @@ def __init__(self, *args, number=None, self._auto_legend = {} self._auto_colorbar_kw = {} # keyword args for auto colorbar() self._auto_legend_kw = {} - self._filled = False # True when panels "filled" with colorbar/legend # Axis sharing, new text attributes, custom formatting self._spanx = spanx # boolean toggles, whether we want to span axes labels self._spany = spany @@ -253,11 +258,12 @@ def _get_title_props(self, abc=False, loc=None): loc = iloc elif iloc is not None and loc != iloc: cache = False - loc = _loc_translate.get(loc, loc) - loc = _side_translate.get(loc, loc) # Above axes - if loc in ('left','right','center'): + loc = _loc_translate.get(loc, loc) + if loc in ('top','bottom'): + raise ValueError(f'Invalid title location {loc!r}.') + elif loc in ('left','right','center'): kw = props(cache) kw.pop('border', None) # no border for titles outside axes kw.pop('linewidth', None) @@ -265,7 +271,6 @@ def _get_title_props(self, abc=False, loc=None): obj = self.title else: obj = getattr(self, '_' + loc + '_title') - # Inside axes elif loc in self._titles_dict: kw = props(cache) @@ -294,25 +299,13 @@ def _get_title_props(self, abc=False, loc=None): return loc, obj, kw @staticmethod - def _inset_or_panel_loc(loc, **kwargs): - """Translates location string `loc`, returns this axes or a panel axes - and the translated location string. If it is a panel axes, returned - location is ``'fill'``. If the panel does not exist, it is drawn, - and extra keyword args are passed to `~Axes.panel`.""" - # Panel index - idx = 0 - if (np.iterable(loc) and not isinstance(loc, str) - and len(loc) == 2 and isinstance(loc[0], str)): - loc, idx = loc # e.g. ('r',2) - - # Try getting the panel, and translate location - if isinstance(loc, str): - loc = _side_translate.get(loc, loc) + def _loc_translate(loc, **kwargs): + """Translates location string `loc` into a standardized form.""" if loc is True: loc = None elif isinstance(loc, (str, Integral)): loc = _loc_translate.get(loc, loc) # may still be invalid - return ax, loc + return loc def _make_inset_locator(self, bounds, trans): """Helper function, copied from private matplotlib version.""" @@ -353,14 +346,16 @@ def _reassign_suplabel(self, side): # this is called on the main axes *or* on the relevant panel itself # TODO: Mixed figure panels with super labels? How does that work? s = side[0] - if isinstance(self, PanelAxes) and s == self._side[0]: - ax = self._parent + side = _side_translate[s] + if s == self._panel_side: + ax = self._panel_parent else: ax = self - idx = (0 if s in 'lt' else -1) - pax = getattr(ax, s + 'panel')[idx] - if not pax: + paxs = getattr(ax, '_' + s + 'panels') + if not paxs: return ax + idx = (0 if s in 'lt' else -1) + pax = paxs[idx] kw = {} obj = getattr(ax, '_' + s + 'label') for key in ('color', 'fontproperties'): # TODO: add to this? @@ -381,13 +376,14 @@ def _reassign_title(self): # called on the main axes *or* on the top panel itself. This is # critical for bounding box calcs; not always clear whether draw() and # get_tightbbox() are called on the main axes or panel first - if isinstance(self, PanelAxes) and self._side == 'top': - ax, tax = self._parent, self + if self._panel_side == 'top': + ax, taxs = self._panel_parent, [self] else: - ax, tax = self, self.toppanel[0] - if not tax or not ax._title_above_panel: + ax, taxs = self, self._tpanels + if not taxs or not ax._title_above_panel: tax = ax else: + tax = taxs[0] tax._title_pad = ax._title_pad for loc,obj in ax._titles_dict.items(): if not obj.get_text() or loc not in ('left','center','right'): @@ -409,7 +405,7 @@ def _reassign_title(self): pos = tax.xaxis.get_ticks_position() labs = tax.xaxis.get_ticklabels() if pos == 'default' or (pos == 'top' and not len(labs)) or ( - pos == 'unknown' and getattr(tax, '_side', None) == 'top' + pos == 'unknown' and tax._panel_side == 'top' and not len(labs) and tax.xaxis.get_visible()): pad = tax.xaxis.get_tick_padding() tax._set_title_offset_trans(self._title_pad + pad) @@ -418,15 +414,15 @@ def _share_short_axis(self, share, side, level): """When sharing main subplots, shares the short axes of their side panels.""" # TODO: Re-calculate share settings at draw time! - if isinstance(self, PanelAxes): + if self._panel_side: # not None return s = side[0] if s not in 'lrbt': raise ValueError(f'Invalid side {side!r}.') - paxs1 = getattr(self, s + 'panel') # calling this means, share properties on this axes with input 'share' axes - paxs2 = getattr(share, s + 'panel') - if not all(pax and not pax._filled for pax in paxs1) or not all( - pax and not pax._filled for pax in paxs2): + paxs1 = getattr(self, '_' + s + 'panels') # calling this means, share properties on this axes with input 'share' axes + paxs2 = getattr(share, '_' + s + 'panels') + if (not all(not pax._panel_filled for pax in paxs1) + or not all(not pax._panel_filled for pax in paxs2)): return if len(paxs1) != len(paxs2): raise AttributeError('Sync error. Different number of stacked panels along axes on like column/row of figure.') @@ -438,13 +434,13 @@ def _share_long_axis(self, share, side, level): """When sharing main subplots, shares the long axes of their side panels, assuming long axis sharing is enabled for that panel.""" # TODO: Re-calculate share settings at draw time! - if isinstance(self, PanelAxes): + if self._panel_side: return s = side[0] if s not in 'lrbt': raise ValueError(f'Invalid side {side!r}.') - paxs = getattr(self, s + 'panel') # calling this means, share properties on this axes with input 'share' axes - if not all(pax and not pax._filled and pax._share for pax in paxs): + paxs = getattr(self, '_' + s + 'panels') # calling this means, share properties on this axes with input 'share' axes + if not all(not pax._panel_filled and pax._panel_share for pax in paxs): return axis = 'x' if s in 'tb' else 'y' for pax in paxs: @@ -506,26 +502,26 @@ def _sharey_setup(self, sharey, level): def _share_panels_setup(self): """Sets up axis sharing between main subplots and panels.""" - share = lambda paxs: (paxs and all(pax._share for pax in paxs)) + share = lambda paxs: (paxs and all(pax._panel_share for pax in paxs)) # Top and bottom bottom = None - if share(self.bottompanel): - bottom = self.bottompanel[-1] - for iax in (self, *self.bottompanel[:-1]): + if share(self._bpanels): + bottom = self._bpanels[-1] + for iax in (self, *self._bpanels[:-1]): iax._sharex_setup(bottom, 3) # parent is *bottom-most* panel - if share(self.toppanel): + if share(self._tpanels): bottom = bottom or self - for iax in self.toppanel: + for iax in self._tpanels: iax._sharex_setup(bottom, 3) # Left and right left = None - if share(self.leftpanel): - left = self.leftpanel[0] - for iax in (*self.leftpanel[1:], self): + if share(self._lpanels): + left = self._lpanels[0] + for iax in (*self._lpanels[1:], self): iax._sharey_setup(left, 3) # parent is *bottom-most* panel - if share(self.rightpanel): + if share(self._rpanels): left = left or self - for iax in self.rightpanel: + for iax in self._rpanels: iax._sharey_setup(left, 3) def _update_title(self, obj, **kwargs): @@ -731,7 +727,7 @@ def format(self, *, title=None, top=None, # A-b-c labels titles_dict = self._titles_dict - if not isinstance(self, PanelAxes): + if not self._panel_side: # Location and text abcformat = rc['abc.format'] # changed, or running format for first time? if abcformat and self.number is not None: @@ -931,79 +927,91 @@ def colorbar(self, *args, loc=None, pad=None, are interpreted by `~proplot.utils.units`. Defaults to ``rc['colorbar.pad']``. length : float or str, optional - The colorbar length. If float, units are inches. If string, - units are interpreted by `~proplot.utils.units`. Defaults to - ``rc['colorbar.length']``. + The colorbar length. For outer colorbars, units are relative to + the axes width or height. For inset colorbars, if float, units are + inches; if string, units are interpreted by `~proplot.utils.units`. + Defaults to ``rc['colorbar.length']``. width : float or str, optional - The colorbar width. If float, units are inches. If string, + The colorbar width. If float, units are inches. If string, units are interpreted by `~proplot.utils.units`. Defaults to ``rc['colorbar.width']`` for inset colorbars, - ``rc['subplots.cbarwidth']`` for panel colorbars. + ``rc['subplots.cbarwidth']`` for outer colorbars. frame, frameon : bool, optional - Whether to draw a frame behind the inset colorbar, just like - `~matplotlib.axes.Axes.legend`. Defaults to ``rc['colorbar.frameon']``. + Whether to draw a frame around inset colorbars, just like + `~matplotlib.axes.Axes.legend`. + Defaults to ``rc['colorbar.frameon']``. alpha, linewidth, edgecolor, facecolor : optional - Transparency, edge width, edge color, and face color for the frame. - Defaults to ``rc['colorbar.framealpha']``, ``rc['axes.linewidth']``, - ``rc['axes.edgecolor']``, and ``rc['axes.facecolor']``. + Transparency, edge width, edge color, and face color for the frame + around the inset colorbar. Defaults to + ``rc['colorbar.framealpha']``, ``rc['axes.linewidth']``, + ``rc['axes.edgecolor']``, and ``rc['axes.facecolor']``, + respectively. width : float or str, optional The width of the colorbar. If float, units are inches. If string, units are interpreted by `~proplot.utils.units`. Defaults to ``rc['colorbar.width']`` for inset colorbars and - ``rc['subplots.colorbarwidth']`` for colorbars outside the axes. + ``rc['subplots.colorbarwidth']`` for outer colorbars. space : float or str, optional - The space between the colorbar and the main axes. Ignored for - non-inset colorbars. If float, units are inches. If string, + The space between the colorbar and the main axes for outer + colorbars. If float, units are inches. If string, units are interpreted by `~proplot.utils.units`. By default, this - is adjusted automatically in the "tight layout" calculation. + is adjusted automatically in the "tight layout" calculation, + or is ``rc['subplots.panelspace']`` if "tight layout" is turned + off. **kwargs Passed to `~proplot.wrappers.colorbar_wrapper`. """ # TODO: add option to pad inset away from axes edge! kwargs.update({'edgecolor':edgecolor, 'linewidth':linewidth}) loc = _notNone(loc, rc['colorbar.loc']) - loc = self._inset_or_panel_loc(loc, **panel_kw) + loc = self._loc_translate(loc) + if loc == 'best': # a white lie + loc = 'lower right' if not isinstance(loc, str): # e.g. 2-tuple or ndarray raise ValueError(f'Invalid colorbar location {loc!r}.') - # Filled colorbar + # Generate panel if loc in ('left','right','top','bottom'): - # Generate the panel - ax = self.panel_axes(loc, width=width, space=space) + ax = self.panel_axes(loc, width=width, space=space, mode='colorbar') + return ax.colorbar(loc='_fill', *args, **kwargs) + # Filled colorbar + if loc == '_fill': # Hide content and resize panel # NOTE: Do not run self.clear in case we want title above this - for s in ax.spines.values(): + for s in self.spines.values(): s.set_visible(False) - ax.xaxis.set_visible(False) - ax.yaxis.set_visible(False) - ax.patch.set_alpha(0) - ax._filled = True - + self.xaxis.set_visible(False) + self.yaxis.set_visible(False) + self.patch.set_alpha(0) + self._panel_filled = True # Draw colorbar with arbitrary length relative to full length of panel - subspec = ax.get_subplotspec() - if length is not None and length != 1: - if length <= 0 or length >= 1: - raise ValueError(f'Panel colorbar length must be between 0 and 1, got length={length!r}.') - if loc in ('bottom','top'): - gridspec = mgridspec.GridSpecFromSubplotSpec( - nrows=1, ncols=3, wspace=0, - subplot_spec=subspec, - width_ratios=((1-length)/2, length, (1-length)/2), - ) - subspec = gridspec[1] - else: - gridspec = mgridspec.GridSpecFromSubplotSpec( - nrows=3, ncols=1, hspace=0, - subplot_spec=subspec, - height_ratios=((1-length)/2, length, (1-length)/2), - ) - subspec = gridspec[1] - - # Get properties - with ax.figure._unlock(): - ax = ax.figure.add_subplot(subspec, projection=None) - if loc in ('bottom','top'): + side = self._panel_side + length = _notNone(length, rc['colorbar.length']) + subplotspec = self.get_subplotspec() + if length <= 0 or length > 1: + raise ValueError(f'Panel colorbar length must satisfy 0 < length <= 1, got length={length!r}.') + if side in ('bottom','top'): + gridspec = mgridspec.GridSpecFromSubplotSpec( + nrows=1, ncols=3, wspace=0, + subplot_spec=subplotspec, + width_ratios=((1-length)/2, length, (1-length)/2), + ) + subplotspec = gridspec[1] + else: + gridspec = mgridspec.GridSpecFromSubplotSpec( + nrows=3, ncols=1, hspace=0, + subplot_spec=subplotspec, + height_ratios=((1-length)/2, length, (1-length)/2), + ) + subplotspec = gridspec[1] + with self.figure._unlock(): + ax = self.figure.add_subplot(subplotspec, projection=None) + if ax is self: + raise ValueError(f'Uh oh.') + self.add_child_axes(ax) + # Location + if side in ('bottom','top'): outside, inside = 'bottom', 'top' if side == 'top': outside, inside = inside, outside @@ -1015,16 +1023,13 @@ def colorbar(self, *args, loc=None, pad=None, outside, inside = inside, outside ticklocation = outside orientation = 'vertical' - - # For filled axes, call wrapper method directly - ax.add_child_axes(ax) + # Keyword args and add as child axes orient = kwargs.get('orientation', None) if orient is not None and orient != orientation: warnings.warn(f'Overriding input orientation={orient!r}.') ticklocation = kwargs.pop('tickloc', None) or ticklocation ticklocation = kwargs.pop('ticklocation', None) or ticklocation kwargs.update({'orientation':orientation, 'ticklocation':ticklocation}) - return wrappers.colorbar_wrapper(ax, *args, **kwargs) # Inset colorbar else: @@ -1032,29 +1037,17 @@ def colorbar(self, *args, loc=None, pad=None, cbwidth, cblength = width, length width, height = self.get_size_inches() extend = units(_notNone(kwargs.get('extendsize',None), rc['colorbar.extendinset'])) - cbwidth = units(_notNone(cbwidth, rc['colorbar.width']))/height - cblength = units(_notNone(cblength, rc['colorbar.length']))/width + cbwidth = units(_notNone(cbwidth, rc['colorbar.widthinset']))/height + cblength = units(_notNone(cblength, rc['colorbar.lengthinset']))/width pad = units(_notNone(pad, rc['colorbar.axespad'])) xpad, ypad = pad/width, pad/height - kwargs.setdefault('extendsize', extend) - - # Tick location handling - tickloc = kwargs.pop('tickloc', None) - ticklocation = kwargs.pop('ticklocation', None) - if any(loc is not None and loc!='bottom' - for loc in (tickloc,ticklocation)): - warnings.warn(f'Inset colorbars can only have ticks on the bottom.') - kwargs['ticklocation'] = 'bottom' - - # Space for labels + # Get location in axes-relative coordinates + # Bounds are x0, y0, width, height in axes-relative coordinate to start if kwargs.get('label', ''): xspace = 2.4*rc['font.size']/72 + rc['xtick.major.size']/72 else: xspace = 1.2*rc['font.size']/72 + rc['xtick.major.size']/72 - xspace /= height - - # Get location in axes-relative coordinates - # Bounds are x0, y0, width, height in axes-relative coordinate to start + xspace /= height # space for labels if loc == 'upper right': bounds = (1 - xpad - cblength, 1 - ypad - cbwidth) fbounds = (1 - 2*xpad - cblength, 1 - 2*ypad - cbwidth - xspace) @@ -1071,42 +1064,49 @@ def colorbar(self, *args, loc=None, pad=None, raise ValueError(f'Invalid colorbar location {loc!r}.') bounds = (bounds[0], bounds[1], cblength, cbwidth) fbounds = (fbounds[0], fbounds[1], 2*xpad + cblength, 2*ypad + cbwidth + xspace) - - # Make axes - locator = self._make_inset_locator(bounds, self.transAxes) - bbox = locator(None, None) - ax = maxes.Axes(self.figure, bbox.bounds, zorder=5) - ax.set_axes_locator(locator) - self.add_child_axes(ax) - - # Make colorbar - # WARNING: Inset colorbars are tiny! So use smart default locator - kwargs.setdefault('maxn', 5) - cb = wrappers.colorbar_wrapper(ax, *args, **kwargs) - # Make frame # NOTE: We do not allow shadow effects or fancy edges effect. # Also keep zorder same as with legend. frameon = _notNone(frame, frameon, rc['colorbar.frameon'], names=('frame','frameon')) if frameon: - # Make object + # Make patch object xmin, ymin, width, height = fbounds patch = mpatches.Rectangle((xmin,ymin), width, height, - snap=True, zorder=4.5, transform=self.transAxes) # fontsize defined in if statement - # Properties + snap=True, zorder=4.5, transform=self.transAxes) + # Update patch props alpha = _notNone(alpha, rc['colorbar.framealpha']) linewidth = _notNone(linewidth, rc['axes.linewidth']) edgecolor = _notNone(edgecolor, rc['axes.edgecolor']) facecolor = _notNone(facecolor, rc['axes.facecolor']) - patch.update({'alpha':alpha, 'linewidth':linewidth, 'edgecolor':edgecolor, 'facecolor':facecolor}) + patch.update({'alpha':alpha, 'linewidth':linewidth, + 'edgecolor':edgecolor, 'facecolor':facecolor}) self.add_artist(patch) - return cb + # Make axes + locator = self._make_inset_locator(bounds, self.transAxes) + bbox = locator(None, None) + ax = maxes.Axes(self.figure, bbox.bounds, zorder=5) + ax.set_axes_locator(locator) + self.add_child_axes(ax) + # Default keyword args + orient = kwargs.pop('orientation', None) + if orient is not None and orient != 'horizontal': + warnings.warn(f'Orientation for inset colorbars must be horizontal, ignoring orient={orient!r}.') + ticklocation = kwargs.pop('tickloc', None) + ticklocation = kwargs.pop('ticklocation', None) or ticklocation + if any(loc is not None and ticklocation != 'bottom'): + warnings.warn(f'Inset colorbars can only have ticks on the bottom.') + kwargs.update({'orientation':'horizontal', 'ticklocation':'bottom'}) + kwargs.setdefault('maxn', 5) + kwargs.setdefault('extendsize', extend) - def legend(self, *args, loc=None, width=None, panel_kw=None, **kwargs): + # Generate colorbar + return wrappers.colorbar_wrapper(ax, *args, **kwargs) + + def legend(self, *args, loc=None, width=None, space=None, **kwargs): """ - Adds an *inset* legend, or calls the `PanelAxes.legend` method - for the panel location at `loc`. See `~matplotlib.axes.Axes.legend` - and `~proplot.wrappers.legend_wrapper` for details. + Adds an *inset* legend or *outer* legend along the edge of the axes. + See `~matplotlib.axes.Axes.legend` and + `~proplot.wrappers.legend_wrapper` for details. Parameters ---------- @@ -1115,14 +1115,14 @@ def legend(self, *args, loc=None, width=None, panel_kw=None, **kwargs): are valid. Note that if a panel does not exist, it will be generated on-the-fly. - ================== =================================== + ================== ======================================= Location Valid keys - ================== =================================== + ================== ======================================= left panel ``'l'``, ``'left'`` right panel ``'r'``, ``'right'`` bottom panel ``'b'``, ``'bottom'`` top panel ``'t'``, ``'top'`` - "best" possible ``0``, ``'best'``, ``'b'`` + "best" possible ``0``, ``'best'``, ``'inset'``, ``'i'`` upper right inset ``1``, ``'upper right'``, ``'ur'`` upper left inset ``2``, ``'upper left'``, ``'ul'`` lower left inset ``3``, ``'lower left'``, ``'ll'`` @@ -1132,24 +1132,59 @@ def legend(self, *args, loc=None, width=None, panel_kw=None, **kwargs): lower center inset ``7``, ``'lower center'``, ``'lc'`` upper center inset ``8``, ``'upper center'``, ``'uc'`` center inset ``9``, ``'center'``, ``'c'`` - ================== =================================== + ================== ======================================= width : float or str, optional - The panel width, if it does not already exist. + The space allocated for the outer legends. This does nothing + if "tight layout" is turned on. If float, units are inches. If + string, units are interpreted by `~proplot.utils.units`. + space : float or str, optional + The space between the axes and the legend for outer legends. If float, units are inches. If string, units are interpreted by - `~proplot.utils.units`. Defaults to ``rc['subplots.legwidth']``. + `~proplot.utils.units`. + Other parameters + ---------------- *args, **kwargs Passed to `~matplotlib.axes.Axes.legend`. """ - panel_kw = panel_kw or {} - if width is not None: - panel_kw.setdefault('width', width) - loc = self._inset_or_panel_loc(loc, **panel_kw) - # TODO: FIXME + loc = self._loc_translate(loc, width=width, space=space) + if isinstance(loc, np.ndarray): + loc = loc.tolist() + + # Generate panel if loc in ('left','right','top','bottom'): - return self.legend(*args, loc=loc, **kwargs) - return wrappers.legend_wrapper(ax, *args, loc=loc, **kwargs) + ax = self.panel_axes(loc, width=width, space=space, mode='legend') + return ax.legend(*args, loc='_fill', **kwargs) + + # Fill + if loc == '_fill': + # Hide content + for s in self.spines.values(): + s.set_visible(False) + self.xaxis.set_visible(False) + self.yaxis.set_visible(False) + self.patch.set_alpha(0) + self._panel_filled = True + # Try to make handles and stuff flush against the axes edge + kwargs.setdefault('borderaxespad', 0) + if not kwargs.get('frameon', rc['legend.frameon']): + kwargs.setdefault('borderpad', 0) + # Apply legend location + side = self._panel_side + if side == 'bottom': + loc = 'upper center' + elif side == 'right': + loc = 'center left' + elif side == 'left': + loc = 'center right' + elif side == 'top': + loc = 'lower center' + else: + raise ValueError(f'Invalid panel side {side!r}.') + + # Draw legend + return wrappers.legend_wrapper(self, *args, loc=loc, **kwargs) def draw(self, renderer=None, *args, **kwargs): """Adds post-processing steps before axes is drawn.""" @@ -1299,46 +1334,29 @@ def indicate_inset_zoom(self, alpha=None, linewidth=None, color=None, edgecolor= def panel_axes(self, side, **kwargs): """ - Adds a single `~proplot.axes.PanelAxes` or a stack of panels, and - returns an `~proplot.subplots.axes_grid` of the panel stack. Also - saves the panel stack as the `~Axes.leftpanel`, - `~Axes.rightpanel`, `~Axes.bottompanel`, or - `~Axes.toppanel` attribute, with respective shorthands - `~Axes.lpanel`, `~Axes.rpanel`, `~Axes.bpanel`, and - `~Axes.tpanel`. - - Note that all `~Axes.panel_axes` keyword args can be side-specific, - e.g. `lwidth` instead of `width`. This is for compatibility with the - `~proplot.subplots.subplots` API. + Returns a panel drawn along the edge of an axes. Parameters ---------- ax : `~proplot.axes.Axes` The axes for which we are drawing a panel. - width, [lrbt]width : float or str or list thereof, optional - The panel width. If float or str, widths are same for all panels - in the stack. If list thereof, specifies widths of each panel in - the stack. If float, units are inches. If string, units are + width : float or str or list thereof, optional + The panel width. If float, units are inches. If string, units are interpreted by `~proplot.utils.units`. - space, [lrbt]space : float or str or list thereof, optional - Empty space between the main subplot and the panel. If float, + space : float or str or list thereof, optional + Empty space between the main subplot and the panel. If float, units are inches. If string, units are interpreted by `~proplot.utils.units`. - share, [lrbt]space : bool, optional + share : bool, optional Whether to enable axis sharing between the *x* and *y* axes of the main subplot and the panel long axes for each panel in the stack. - stack, [lrbt]stack : int, optional - The number of optional "stacked" panels on the left, right, bottom, - and top sides, respectively. Defaults to ``1``. - sep, [lrbt]sep : float, str, or list thereof, optional - The separation between stacked panels. If float, units are inches. - If string, units are interpreted by `~proplot.utils.units`. Ignored - if the respecitve `stack` keyword arg is None. + Sharing between the panel short axis and other panel short axes + is determined by figure-wide `sharex` and `sharey` settings. Returns ------- - `~proplot.subplots.axes_grid` of `~proplot.axes.PanelAxes` - The panel axes or stack of panel axes. + `~proplot.axes.Axes` + The panel axes. """ return self.figure._add_axes_panel(side, self, **kwargs) @@ -1354,39 +1372,13 @@ def number(self): `~proplot.subplots.subplots`.""" return self._number - @property - def leftpanel(self): - """An `~proplot.subplots.axes_grid` of the left panel stack.""" - return self._leftpanel - @property - def rightpanel(self): - """An `~proplot.subplots.axes_grid` of the right panel stack.""" - return self._rightpanel - @property - def toppanel(self): - """An `~proplot.subplots.axes_grid` of the top panel stack.""" - return self._toppanel - @property - def bottompanel(self): - """An `~proplot.subplots.axes_grid` of the bottom panel stack.""" - return self._bottompanel - - lpanel = leftpanel - """Alias for `~Axes.leftpanel`.""" - rpanel = rightpanel - """Alias for `~Axes.rightpanel`.""" - tpanel = toppanel - """Alias for `~Axes.toppanel`.""" - bpanel = bottompanel - """Alias for `~Axes.bottompanel`.""" - def _iter_panels(self, sides='lrbt'): """Iterates over axes and child panel axes.""" axs = [self] if self.get_visible() else [] if not ({*sides} <= {*'lrbt'}): raise ValueError(f'Invalid sides {sides!r}.') for s in sides: - for ax in getattr(self, s + 'panel'): + for ax in getattr(self, '_' + s + 'panels'): if not ax or not ax.get_visible(): continue axs.append(ax) @@ -1434,7 +1426,7 @@ class CartesianAxes(Axes): See also -------- - `~proplot.subplots.subplots`, `Axes`, `PanelAxes` + `~proplot.subplots.subplots`, `Axes` """ name = 'cartesian' """The registered projection name.""" @@ -2262,245 +2254,15 @@ def get_tightbbox(self, renderer, *args, **kwargs): def twinx(self): """Mimics matplotlib's `~matplotlib.axes.Axes.twinx` and intelligently handles axis ticks, gridlines, axis tick labels, axis labels, and axis - sharing. Returns an `CartesianAxes` instance.""" + sharing. Returns a `CartesianAxes` instance.""" return self.alty() def twiny(self): """Mimics matplotlib's `~matplotlib.axes.Axes.twiny` and intelligently handles axis ticks, gridlines, axis tick labels, axis labels, and axis - sharing. Returns an `CartesianAxes` instance.""" + sharing. Returns a `CartesianAxes` instance.""" return self.altx() -class EmptyPanel(object): - """Replaces `PanelAxes` when the axes or figure panel does not exist. - This gives a nicer error message than if we had just ``None``, and - permits indexing to mimick the behavior of a singleton - `~proplot.subplots.axes_grid`.""" - def __bool__(self): - """Returns False. Provides shorthand way to check whether panel - attribute is specifically EmptyPanel.""" - return False # it's empty, so this is 'falsey' - - def __len__(self): - """Returns 1. This allows us to treat `EmptyPanel` like an - `~proplot.subplots.axes_grid` of stacked panels.""" - return 1 - - def __getitem__(self, key): - """Returns itself. This allows us to treat `EmptyPanel` like an - `~proplot.subplots.axes_grid` of stacked panels.""" - # See: https://stackoverflow.com/a/26611639/4970632 - if not isinstance(key, tuple): - key = (key,) - if any(ikey not in (0,-1,slice(None)) for ikey in key): - raise IndexError - return self - - def __getattr__(self, attr, *args): - """Raises AttributeError.""" - # NOTE: `__getattr__` is invoked only when `__getattribute__` fails, i.e. - # when the user requests anything that isn't a builtin method. - raise AttributeError('Panel does not exist.') - -class PanelAxes(CartesianAxes): - """`~proplot.axes.CartesianAxes` subclass, adds `~PanelAxes.legend` and - `~PanelAxes.colorbar` methods that "fill" the entire axes.""" - # Notes: - # See `this post `_ - # and `this example `_. - name = 'panel' - """The registered projection name.""" - def __init__(self, *args, - parent=None, side=None, share=False, gridspec=None, - **kwargs): - """ - Parameters - ---------- - side : {'left', 'right', 'bottom', 'top'} - The side on which the panel is drawn. - parent : `~matplotlib.axes.Axes`, optional - The "parent" of the panel. Not relevant for "outer panel" axes. - share : bool, optional - Whether to share panel *x* and *y* axes with "parent" axes. - Irrelevant for figure panels, and defaults to ``False``. - gridspec : optional - For stacks of panel axes, this is the - `~proplot.subplots.FlexibleGridSpecFromSubplotSpec` used to - specify the panel stack location. - - Other parameters - ---------------- - *args, **kwargs - Passed to the `CartesianAxes` initializer. - - See also - -------- - `~proplot.subplots.subplots`, - `~proplot.subplots.Figure.add_subplot_and_panels` - """ - # Misc props - # WARNING: Need to set flush property before calling super init! - if side not in ('left', 'right', 'bottom', 'top'): - raise ValueError(f'Invalid panel side {side!r}.') - self._share = share - self._side = side - self._parent = parent # used when declaring parent - # Initialize - super().__init__(*args, **kwargs) - axis = (self.yaxis if side in ('left','right') else self.xaxis) - getattr(axis, 'tick_' + side)() # sets tick and tick label positions intelligently - axis.set_label_position(side) - - def legend(self, *args, fill=True, width=None, **kwargs): - """" - Fills the panel with a legend by adding a centered legend and - rendering the axes spines and patch invisible, or draws a normal - inset legend. - - Parameters - ---------- - fill : bool, optional - Whether to fill the panel with a legend or draw a normal - inset legend. Defaults to ``True``. - width : float or str, optional - The width of the panel in physical units. If float, units are - inches. If string, units are interpreted by `~proplot.utils.units`. - Note that the panel width does not matter unless you have turned - off automatic tight layout -- space for the legend is allocated - automatically. - *args, **kwargs - Passed to `~proplot.wrappers.legend_wrapper`. - """ - # Regular inset legend - if not fill: - return super().legend(*args, **kwargs) - # Hide content - for s in self.spines.values(): - s.set_visible(False) - self.xaxis.set_visible(False) - self.yaxis.set_visible(False) - self.patch.set_alpha(0) - self._filled = True - - # Allocate invisible axes for drawing legend; by default try to - # make handles and stuff flush against the axes edge - kwdefault = {'borderaxespad': 0} - if not kwargs.get('frameon', rc['legend.frameon']): - kwdefault['borderpad'] = 0 - kwdefault.update(kwargs) - kwargs = kwdefault - - # Apply legend location - side = self._side - if 'loc' in kwargs: - warnings.warn(f'Overriding input legend location loc={kwargs["loc"]!r}.') - if side == 'bottom': - loc = 'upper center' - elif side == 'right': - loc = 'center left' - elif side == 'left': - loc = 'center right' - elif side == 'top': - loc = 'lower center' - else: - raise ValueError(f'Invalid panel side {loc!r}.') - kwargs['loc'] = loc - leg = wrappers.legend_wrapper(self, *args, **kwargs) - - # Resize panel and return - # TODO: Make panel size dependent on legend width, and remove - # legwidth from .proplotrc - self.figure._resize_panel(self, width, 'legend') - return leg - - def colorbar(self, *args, fill=True, width=None, length=None, **kwargs): - """" - Fills the panel with a colorbar by calling - `~matplotlib.figure.Figure.colorbar` with ``cax=self``, or draws an - inset colorbar. - - Parameters - ---------- - fill : bool, optional - Whether to fill the panel with a colorbar or draw an inset - colorbar. Defaults to ``True``. - length : float, optional - When ``fill=True``, this is the fractional extent of the panel - long axis extended by the colorbar. For example, with - ``length=0.5``, the colorbar extends over the middle 50% - of the panel. When ``fill=False``, this is the inset colorbar - length in physical units, passed to `~BaseAxes.colorbar`. - width : float or str, optional - The width of the panel colorbar or inset colorbar in physical - units. If float, units are inches. If string, units are interpreted - by `~proplot.utils.units`. - *args, **kwargs - Passed to `~proplot.wrappers.colorbar_wrapper` when ``fill=True``, - or `~BaseAxes.colorbar` when ``fill=False``. - """ - # Inset 'legend-style' colorbar - if not fill: - return super().colorbar(*args, width=width, length=length, **kwargs) - # Hide content and resize panel - # NOTE: Do not run self.clear in case we want title above this - for s in self.spines.values(): - s.set_visible(False) - self.xaxis.set_visible(False) - self.yaxis.set_visible(False) - self.patch.set_alpha(0) - self._filled = True - - # Draw colorbar with arbitrary length relative to full length of panel - side = self._side - subspec = self.get_subplotspec() - if length is not None and length != 1: - if length <= 0 or length >= 1: - raise ValueError(f'Panel colorbar length must be between 0 and 1, got length={length!r}.') - if side in ('bottom','top'): - gridspec = mgridspec.GridSpecFromSubplotSpec( - nrows=1, ncols=3, wspace=0, - subplot_spec=subspec, - width_ratios=((1-length)/2, length, (1-length)/2), - ) - subspec = gridspec[1] - elif side in ('left','right'): - gridspec = mgridspec.GridSpecFromSubplotSpec( - nrows=3, ncols=1, hspace=0, - subplot_spec=subspec, - height_ratios=((1-length)/2, length, (1-length)/2), - ) - subspec = gridspec[1] - - # Get properties - with self.figure._unlock(): - ax = self.figure.add_subplot(subspec, projection=None) - if side in ('bottom','top'): - outside, inside = 'bottom', 'top' - if side == 'top': - outside, inside = inside, outside - ticklocation = outside - orientation = 'horizontal' - elif side in ('left','right'): - outside, inside = 'left', 'right' - if side == 'right': - outside, inside = inside, outside - ticklocation = outside - orientation = 'vertical' - - # For filled axes, call wrapper method directly - self.add_child_axes(ax) - orient = kwargs.get('orientation', None) - if orient is not None and orient != orientation: - warnings.warn(f'Overriding input orientation={orient!r}.') - ticklocation = kwargs.pop('tickloc', None) or ticklocation - ticklocation = kwargs.pop('ticklocation', None) or ticklocation - kwargs.update({'orientation':orientation, 'ticklocation':ticklocation}) - cbar = wrappers.colorbar_wrapper(ax, *args, **kwargs) - - # Resize panel and return - self.figure._resize_panel(self, width, 'colorbar') - return cbar - class ProjectionAxes(Axes): """Intermediate class, shared by `CartopyAxes` and `BasemapAxes`. Disables methods that are inappropriate for map @@ -3465,7 +3227,6 @@ def format(self, *, patch_kw=None, **kwargs): # Register the projections mproj.register_projection(Axes) -mproj.register_projection(PanelAxes) mproj.register_projection(PolarAxes) mproj.register_projection(CartesianAxes) mproj.register_projection(BasemapAxes) diff --git a/proplot/rctools.py b/proplot/rctools.py index 60001909a..ff9fa96fc 100644 --- a/proplot/rctools.py +++ b/proplot/rctools.py @@ -161,8 +161,10 @@ ``colorbar.grid`` Boolean, indicates whether to draw borders between each level of the colorbar. ``colorbar.frameon`` Boolean, indicates whether to draw a frame behind inset colorbars. ``colorbar.framealpha`` Opacity for inset colorbar frames. -``colorbar.length`` Length of inset colorbars. -``colorbar.width`` Width of inset colorbars. +``colorbar.length`` Length of outer colorbars. +``colorbar.lengthinset`` Length of inset colorbars. +``colorbar.width`` Width of outer colorbars. +``colorbar.widthinset`` Width of inset colorbars. ``colorbar.axespad`` Padding between axes edge and inset colorbars. ``colorbar.extend`` Length of rectangular or triangular "extensions" for panel colorbars. ``colorbar.extendinset`` Length of rectangular or triangular "extensions" for inset colorbars. @@ -177,8 +179,6 @@ ========================== ================================================================== ``subplots.axwidth`` Default width of each axes. ``subplots.panelwidth`` Width of side panels. -``subplots.colorbarwidth`` Width of "colorbar" panels. -``subplots.legendwidth`` Width of "legend" panels. ``subplots.pad`` Padding around figure edge. ``subplots.axpad`` Padding between adjacent subplots. ``subplots.panelpad`` Padding between subplots and panels, and between stacked panels. @@ -292,9 +292,11 @@ 'geogrid.labels', 'geogrid.alpha', 'geogrid.color', 'geogrid.labelsize', 'geogrid.linewidth', 'geogrid.linestyle', 'geogrid.latmax', 'geogrid.lonstep', 'geogrid.latstep', 'tick.labelweight', 'tick.labelcolor', 'tick.labelsize', 'subplots.pad', 'subplots.axpad', 'subplots.panelpad', - 'subplots.legendwidth', 'subplots.colorbarwidth', 'subplots.ylabspace', 'subplots.xlabspace', 'subplots.innerspace', 'subplots.titlespace', + 'subplots.ylabspace', 'subplots.xlabspace', 'subplots.innerspace', 'subplots.titlespace', 'subplots.axwidth', 'subplots.panelwidth', 'subplots.panelspace', - 'colorbar.grid', 'colorbar.frameon', 'colorbar.framealpha', 'colorbar.length', 'colorbar.width', 'colorbar.loc', 'colorbar.extend', 'colorbar.extendinset', 'colorbar.axespad', + 'colorbar.grid', 'colorbar.frameon', 'colorbar.framealpha', + 'colorbar.loc', 'colorbar.length', 'colorbar.width', 'colorbar.lengthinset', 'colorbar.widthinset', + 'colorbar.extend', 'colorbar.extendinset', 'colorbar.axespad', } # Used by Axes.format, allows user to pass rc settings as keyword args, # way less verbose. For example, landcolor='b' vs. rc_kw={'land.color':'b'}. diff --git a/proplot/subplots.py b/proplot/subplots.py index c16e87b13..30e695665 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -35,7 +35,6 @@ # NOTE: Importing backend causes issues with sphinx, and anyway not sure it's # always included, so make it optional import os -import re import numpy as np import functools import warnings @@ -43,7 +42,6 @@ import matplotlib.figure as mfigure import matplotlib.transforms as mtransforms import matplotlib.gridspec as mgridspec -import matplotlib.axes as maxes try: import matplotlib.backends.backend_macosx as mbackend except ImportError: @@ -212,7 +210,7 @@ def null_iterator(*args, **kwargs): return None return null_iterator # Panels - if all(isinstance(_, (axes_grid, axes.Axes, axes.EmptyPanel)) + if all(isinstance(_, (axes_grid, axes.Axes)) for _ in attrs): return axes_grid(attrs) # Objects @@ -472,6 +470,30 @@ def update(self, **kwargs): #-----------------------------------------------------------------------------# # Helper funcs #-----------------------------------------------------------------------------# +def _panels_kwargs(side, + share=None, width=None, space=None, + mode='panel', figure=False): + """Converts global keywords like `space` and `width` to side-local + keywords like `lspace` and `lwidth`, and applies default settings.""" + # Return values + s = side[0] + if s not in 'lrbt': + raise ValueError(f'Invalid panel spec {side!r}.') + space_orig = units(space) + if mode == 'panel': + default = rc['subplots.panelwidth'] + elif mode == 'colorbar': + default = rc['colorbar.width'] + else: + default = '1em' + share = _notNone(share, (mode == 'panel')) + width = units(_notNone(width, default)) + space = _notNone(space, units(rc['subplots.' + ('panel' if share + and not figure + else 'xlab' if s == 'b' else 'ylab' if s == 'l' + else 'inner' if figure else 'panel') + 'space'])) + return share, width, space, space_orig + def _subplots_geometry(**kwargs): """Saves arguments passed to `subplots`, calculates gridspec settings and figure size necessary for requested geometry, and returns keyword args @@ -610,66 +632,6 @@ def _subplots_geometry(**kwargs): return (width, height), gridspec_kw, kwargs -def _panels_kwargs(side, kwargs, figure=False, mode='panel'): - """Converts global keywords like `space` and `width` to side-local - keywords like `lspace` and `lwidth`, and applies default settings.""" - # Checks - # NOTE: The 'stack' keyword arg is now invalid, to add stacked panels - # simply call panel axes more than once or add colorbar more than once! - s = side[0] - if s not in 'lrbt': - raise ValueError(f'Invalid panel spec {side!r}.') - # Detect *unknown* keyword args, raise error if found! - regex = re.compile(f'^[tlrb]?({"array|" if figure else ""}share|space|width)$') - kwextra = {key:kwargs.pop(key) for key in (*kwargs,) if not regex.match(key)} - deprec1 = {key:value for key,value in kwextra.items() if key in ( - 'sep', 'lsep', 'rsep', 'bsep', 'tsep', - 'stack', 'lstack', 'rstack', 'bstack', 'tstack', - )} - deprec2 = {key:value for key,value in kwextra.items() if key in ( - 'colorbar', 'colorbars', 'legend', 'legends', - 'axcolorbar', 'axcolorbars', 'axlegend', 'axlegends', - 'axcolorbar_kw', 'axcolorbars_kw', 'axlegend_kw', 'axlegends_kw', - )} - unknown = {key:value for key,value in kwextra.items() - if key not in deprec1 and key not in deprec2} - if deprec1: - raise ValueError(f'You used the following deprecated subplots() or panel_axes() keyword arg(s): {deprec1!r}. To draw "stacked" panels with the current API, simply call e.g. Axes.colorbar(loc="r") or Axes.panel("r") more than once, and multiple panels will be drawn on that side.') - if deprec2: - raise ValueError(f'You used the following deprecated subplots() keyword arg(s): {deprec2!r}. With the current API, you just use "panel", "panels", "axpanel", "axpanels", "axpanel_kw", and/or "axpanels_kw". If you "fill" a panel with a colorbar or legend, its width will be adjusted automatically.') - if unknown: - raise ValueError(f'Unknown subplots() or panel_axes() keyword arg(s): {unknown!r}.') - - # Detect *ignored* panel keyword args, issue warning - s_off = ''.join({*'lrbt'} - {s}) - regex = re.compile(f'^[{s_off}]({"array|" if figure else ""}share|width|space)$') - ikw = {key:value for key,value in kwargs.items() if regex.match(s_off)} - if ikw: - warnings.warn(f'Ignoring keyword arg(s): {ikw}. Active sides are: {sides!r}.') - - # Try to use local first, global if no local found - def get(key): - return _notNone(kwargs.get(s + key, None), kwargs.get(key, None)) - kwout, kworig = {}, {} - # Store originals, prevents automatic changes if user passed anything - share = _notNone(get('share'), (mode == 'panel')) - width = units(get('width')) - space = units(get('space')) - kworig[s + 'width'] = width - kworig[s + 'space'] = space - # Store values used in initial drawing - width = units(_notNone(width, rc['subplots.' + mode + 'width'])) - space = _notNone(space, units(rc['subplots.' + ('panel' if share - and not figure - else 'xlab' if s == 'b' else 'ylab' if s == 'l' - else 'inner' if figure else 'panel') + 'space'])) - kwout[s + 'share'] = share - kwout[s + 'width'] = width - kwout[s + 'space'] = space - if figure: - kwout[s + 'array'] = get('array') - return kwout, kworig - #-----------------------------------------------------------------------------# # Figure class and helper classes #-----------------------------------------------------------------------------# @@ -760,16 +722,19 @@ def __init__(self, self._axes_main = [] self._subplots_orig_kw = subplots_orig_kw self._subplots_kw = subplots_kw - self._leftpanel = axes.EmptyPanel() - self._bottompanel = axes.EmptyPanel() - self._rightpanel = axes.EmptyPanel() - self._toppanel = axes.EmptyPanel() + self._bpanels = [] + self._tpanels = [] + self._lpanels = [] + self._rpanels = [] self._gridspec_main = FlexibleGridSpec(self, **(gridspec_kw or {})) self.suptitle('') # add _suptitle attribute @_counter def _add_axes_panel(self, side, ax, order='C', mode='panel', **kwargs): """Hidden method that powers `~proplot.axes.panel_axes`.""" + # Redirect to main axes, e.g. if want to draw outer colorbar or legend + # from data plotted in a panel or inset axes + ax = ax._panel_parent or ax # Interpret args # NOTE: Axis sharing not implemented for figure panels, 99% of the # time this is just used as construct for adding global colorbars and @@ -778,12 +743,8 @@ def _add_axes_panel(self, side, ax, order='C', mode='panel', **kwargs): if s not in 'lrbt': raise ValueError(f'Invalid side {side!r}.') side = _side_translate[s] - kwargs, kworig = _panels_kwargs(s, kwargs, mode=mode, figure=False) - width = kwargs[s + 'width'] - space = kwargs[s + 'space'] - share = kwargs[s + 'share'] - width_orig = kworig[s + 'width'] - space_orig = kworig[s + 'space'] + share, width, space, space_orig = _panels_kwargs(s, + kwargs, mode=mode, figure=False) # Modify existing geometry, find incies for new panel by placing it # to the outside of *existing* panels if possible @@ -791,7 +752,7 @@ def _add_axes_panel(self, side, ax, order='C', mode='panel', **kwargs): # width and space. subplotspec = ax.get_subplotspec() nrows, ncols, row1, row2, col1, col2 = subplotspec.get_active_rows_columns() - pgrid = getattr(ax, '_' + side + 'panel') + pgrid = getattr(ax, '_' + s + 'panels') offset = (len(pgrid)*bool(pgrid)) + 1 if s in 'lr': idx1 = slice(row1, row2 + 1) @@ -801,46 +762,37 @@ def _add_axes_panel(self, side, ax, order='C', mode='panel', **kwargs): idx1 = (row1 - offset if s == 't' else row2 + offset) idx2 = slice(col1, col2 + 1) iratio = idx1 - gridspec = self._change_gridspec(side, iratio, - width, space, width_orig, space_orig, + gridspec = self._insert_row_column(side, iratio, + width, space, space_orig, figure=False, ) # Get keyword args and gridspec args # Loop through unique numbers with self._unlock(): - pax = self.add_subplot(gridspec[idx1,idx2], - projection='panel', - side=side, share=share, - ) + pax = self.add_subplot(gridspec[idx1,idx2], projection='cartesian') - # Add to panel attributes - pgrid = getattr(ax, '_' + side + 'panel') - # Create from scratch - if not pgrid: - order = self._order - pgrid = axes_grid([pax], n=1, order=order) - setattr(ax, '_' + side + 'panel', pgrid) - # Modify existing grid + # Add to panel list + pgrid = getattr(ax, '_' + s + 'panels') + if s in 'lt': + pgrid.insert(0, pax) else: - n = pgrid._n - order = pgrid._order - if s in 'lt': - pgrid.insert(0, pax) - else: - pgrid.append(pax) - if (s in 'lr' and order == 'C') or (s in 'tb' and order == 'F'): - n += 1 - pgrid._n = n + pgrid.append(pax) # Set up shared axes + pax._panel_side = side + pax._panel_share = share + pax._panel_parent = ax if share: ax._share_panels_setup() self._share_axes_setup(ax) + axis = (pax.yaxis if side in ('left','right') else pax.xaxis) + getattr(axis, 'tick_' + side)() # sets tick and tick label positions intelligently + axis.set_label_position(side) # Return axes grid of just this axes - return axes_grid([pax]) + return pax - def _add_figure_panels(self, side, mode='panel', **kwargs): + def _add_figure_panel(self, side, mode='panel', array=None, **kwargs): """Adds figure panels. Also modifies the panel attribute stored on the figure to include these panels.""" # Interpret args @@ -852,12 +804,8 @@ def _add_figure_panels(self, side, mode='panel', **kwargs): if s not in 'lrbt': raise ValueError(f'Invalid side {side!r}.') side = _side_translate[s] - kwargs, kworig = _panels_kwargs(s, kwargs, mode=mode, figure=True) - array = kwargs[s + 'array'] - width = kwargs[s + 'width'] - space = kwargs[s + 'space'] - width_orig = kworig[s + 'width'] - space_orig = kworig[s + 'space'] + _, width, space, space_orig = _panels_kwargs(s, + kwargs, mode=mode, figure=True) # Modify existing geometry, and verify validity of the array arg # using boolean 1D arrays indicating where panels are present for @@ -876,9 +824,8 @@ def _add_figure_panels(self, side, mode='panel', **kwargs): if not np.iterable(array) or len(array) != len(idxs): raise ValueError(f'Need length-{len(idxs)} list of integers for "{s}array", got {s}array={array!r}.') iratio = (0 if s in 'lt' else nacross) - gridspec = self._change_gridspec(side, iratio, - width, space, width_orig, space_orig, - figure=True, + gridspec = self._insert_row_column(side, iratio, + width, space, space_orig, figure=True, ) # Pad array so it spans existing panel slots @@ -897,51 +844,25 @@ def _add_figure_panels(self, side, mode='panel', **kwargs): if s in 'bt': idx1, idx2 = idx2, idx1 with self._unlock(): - ipax = self.add_subplot(gridspec[idx1,idx2], - projection='panel', - side=side, share=False, - ) + ipax = self.add_subplot(gridspec[idx1,idx2], projection='cartesian') + ipax._panel_side = side + ipax._panel_share = False + ipax._panel_parent = None + axis = (ipax.yaxis if side in ('left','right') else ipax.xaxis) + getattr(axis, 'tick_' + side)() # sets tick and tick label positions intelligently + axis.set_label_position(side) paxs += [ipax] # Add to panel attributes - pgrid = getattr(self, '_' + side + 'panel') - # Create brand new axes_grid - if not pgrid: - order = self._order - if (s in 'tb' and order == 'C') or (s in 'lr' and order != 'C'): - n = 1 - else: - n = len(paxs) - pgrid = axes_grid(paxs, n=n, order=order) - setattr(self, '_' + side + 'panel', pgrid) - # Modify existing axes_grid + pgrid = getattr(self, '_' + s + 'panels') + if s in 'lt': + pgrid.insert(0, paxs[0]) else: - # Possible insertion modes - # The names are relevant for C-major order - order = pgrid._order - top = (s == 't' and order == 'C') or (s == 'l' and order == 'F') - bottom = (s == 'b' and order == 'C') or (s == 'r' and order == 'F') - left = (s == 'l' and order == 'C') or (s == 't' and order == 'F') - right = (s == 'r' and order == 'C') or (s == 'b' and order == 'F') - # Insert into lists - # NOTE: Can get weird grids for weird geometry, e.g. 3 figure - # panels side-by-side on top of one figure panel - n = pgrid._n - for i,ax in enumerate(paxs): - if top: - pgrid.insert(0, ax) - elif bottom: - pgrid.append(ax) - elif left: - pgrid.insert(i*n + i, ax) # plus i offsets after adding stuff - elif right: - pgrid.insert((i + 1)*n - 1 + i, ax) - if left or right: # fastest-moving dim length increased by 1 - n += 1 - pgrid._n = n + pgrid.append(paxs[0]) # Return axes_grid of just the axes drawn in this round - return axes_grid(paxs) # no 2D indexing + # TODO: Draw one at a time!!! + return paxs[0] def _adjust_aspect(self): """Adjust average aspect ratio used for gridspec calculations. This @@ -1218,13 +1139,13 @@ def _align_suplabels(self, renderer): 'transform':self.transFigure} suptitle.update(kw) - def _change_gridspec(self, side, idx, - ratio, space, ratio_orig, space_orig, + def _insert_row_column(self, side, idx, + ratio, space, space_orig, figure=False): - """Helper function that "overwrites" the main figure gridspec, used - when successively adding panels. The `side` is the panel side, - the `idx` is the slot you want the panel to occupy, and the remaining - args are the panel widths and spacings.""" + """Helper function that "overwrites" the main figure gridspec to make + room for a panel. The `side` is the panel side, the `idx` is the + slot you want the panel to occupy, and the remaining args are the + panel widths and spacings.""" # Constants and stuff # Insert spaces to the left of right panels or to the right of # left panels. And note that since .insert() pushes everything in @@ -1246,7 +1167,6 @@ def _change_gridspec(self, side, idx, panels = subplots_kw[w + 'panels'] ratios = subplots_kw[w + 'ratios'] spaces = subplots_kw[w + 'space'] - ratios_orig = subplots_orig_kw[w + 'ratios'] spaces_orig = subplots_orig_kw[w + 'space'] # Slot already exists @@ -1254,10 +1174,6 @@ def _change_gridspec(self, side, idx, # add them along entire side all at once exists = (not figure and idx < len(panels) and panels[idx] == s) if exists: # already exists! - # Modify existing width, defer to user input - if ratios_orig[idx] is None: - ratios_orig[idx] = units(ratio_orig) - ratios[idx] = _notNone(ratios_orig[idx], ratio) # Modify existing space if spaces_orig[idx_space] is None: spaces_orig[idx_space] = units(space_orig) @@ -1268,13 +1184,11 @@ def _change_gridspec(self, side, idx, idx += idx_offset idx_space += idx_offset subplots_kw[ncols] += 1 - # Ratio array, space array, panel toggles + # Original space, ratio array, space array, panel toggles + spaces_orig.insert(idx_space, space_orig) spaces.insert(idx_space, space) ratios.insert(idx, ratio) panels.insert(idx, 'f' if figure else s) - # Original info - ratios_orig.insert(idx, ratio_orig) - spaces_orig.insert(idx_space, space_orig) # Reference ax location array ref = subplots_kw[x + 'ref'] for i,val in enumerate(ref): @@ -1367,57 +1281,6 @@ def _get_align_axes(self, side): ord = [ax._range_gridspec(y)[0] for ax in axs] return [ax for _,ax in sorted(zip(ord, axs)) if ax.get_visible()] - def _resize_panel(self, pax, width, mode='panel'): - """Modifies the panel width in case it is being "filled" by a - colorbar or legend. The input is the user input width (may be ``None``) - and the mode, one of ``'colorbar'`` or ``'legend'``.""" - # Get index - subplotspec = pax.get_subplotspec() - *_, row, _, col, _ = subplotspec.get_active_rows_columns() - s = pax._side[0] - if s in 'lr': - x, y, w, idx = 'x', 'y', 'w', col - else: - x, y, w, idx = 'y', 'x', 'h', row - - # Set new widths, taking care to obey user input widths! - subplots_kw = self._subplots_kw - subplots_orig_kw = self._subplots_orig_kw - ratios = subplots_kw[w + 'ratios'] - ratios_orig = subplots_orig_kw[w + 'ratios'] - if ratios_orig[idx] is None or width is not None: - ratios_orig[idx] = units(width) - width = units(_notNone(ratios_orig[idx], rc['subplots.' + mode + 'width'])) - ratios[idx] = width - - # Apply to panel - figsize, gridspec_kw, _ = _subplots_geometry(**subplots_kw) - self.set_size_inches(figsize) - self._gridspec_main.update(**gridspec_kw) - - # Undo action of _sharex_setup and _sharey_setup - # NOTE: Largely copied from Figure._remove_ax, except we copy - # locators and formatters from parent to child. - irange = pax._range_gridspec(x) - jrange = pax._range_gridspec(y) - for ax in self._iter_axes(): - if not (ax._range_gridspec(x) == irange - or ax._range_gridspec(y) == jrange): - continue - for axis1,axis2,parent,grouper in zip( - (pax.xaxis, pax.yaxis), - (ax.xaxis, ax.yaxis), - ('_sharex', '_sharey'), - (ax._shared_x_axes, ax._shared_y_axes)): - if getattr(ax, parent) is not pax: - continue - setattr(ax, parent, None) - grouper.remove(pax) - axis2.label.set_visible(True) - axis2.set_minor_locator(axis1.get_minor_locator()) - axis2.set_major_locator(axis1.get_major_locator()) - axis2.set_major_formatter(axis1.get_major_formatter()) - def _unlock(self): """Prevents warning message when adding subplots one-by-one, used internally.""" @@ -1513,26 +1376,64 @@ def add_subplot(self, *args, **kwargs): ax = super().add_subplot(*args, **kwargs) return ax - def colorbar(self, *args, loc='r', panel_kw=None, **kwargs): - """Draws a colorbar along the left, right, bottom, or top side + def colorbar(self, *args, loc='r', width=None, space=None, **kwargs): + """ + Draws a colorbar along the left, right, bottom, or top side of the figure, centered between the leftmost and rightmost (or - topmost and bottommost) main axes.""" + topmost and bottommost) main axes. + + Parameters + ---------- + panel : str, optional + Adds :ref:`Global figure panels` to the specified side. + String should contain any of the characters ``'l'`` (left), ``'r'`` + (right), ``'b'`` (bottom), or ``'t'`` (top). For example, ``'br'`` + will draw panels on the right and bottom sides of the figure. + panels : str, optional + As with `panel`, but the default behavior is to assign a panel + to *every* row or column of subplots. Individual panels can then + be accessed with e.g. ``fig.leftpanel[0]``, ``fig.leftpanel[1]``. + array : list of int, optional + Defines how figure panels span rows and columns of subplots. + Interpreted like `array` -- the integers specify panels that span + *arbitrary, contiguous* columns or rows of subplots. + + For example, ``plot.suplots(ncols=3, panels='b', barray=[1, 2, 2])`` + draws a panel on the bottom of the first column and spanning the bottom + of the right 2 columns, and ``barray=[0, 2, 2]`` only draws a panel + underneath the right 2 columns -- as with `array`, the ``0`` indicates + an empty space. + width : float or str, optional + As in `~proplot.axes.Axes.panel_axes`. Use e.g. `lwidth`, `rwidth`, + `bwidth`, and `twidth` to apply these settings to panels on different + sides. + space : float or str, optional + As in `~proplot.axes.Axes.panel_axes`, but controls space + between the main subplot grid and the figure panels. + *args, **kwargs + Passed to `~proplot.axes.Axes.colorbar`. + """ if 'cax' in kwargs: return super().colorbar(*args, **kwargs) else: - panel_kw = panel_kw or {} - panel_kw.setdefault('mode', 'colorbar') - paxs = self._add_figure_panels(loc, **panel_kw) - return paxs.colorbar(*args, **kwargs) + ax = self._add_figure_panel(loc, space=space, width=width, array=None, mode='colorbar') + return ax.colorbar(*args, loc='_fill', **kwargs) - def legend(self, *args, loc='r', panel_kw=None, **kwargs): - """Draws a legend along the left, right, bottom, or top side of the + def legend(self, *args, loc='r', width=None, space=None, **kwargs): + """ + Draws a legend along the left, right, bottom, or top side of the figure, centered between the leftmost and rightmost (or - topmost and bottommost) main axes.""" - panel_kw = panel_kw or {} - panel_kw.setdefault('mode', 'legend') - paxs = self._add_figure_panels(loc, **panel_kw) - return paxs.legend(*args, **kwargs) + topmost and bottommost) main axes. + + Parameters + ---------- + loc, width, space + See `~Figure.colorbar`. + *args, **kwargs + Passed to `~proplot.axes.Axes.legend`. + """ + ax = self._add_figure_panel(loc, space=space, width=width, array=None, mode='legend') + return ax.legend(*args, loc='_fill', **kwargs) @_counter def draw(self, renderer): @@ -1606,44 +1507,18 @@ def savefig(self, filename, **kwargs): """Alias for `~Figure.savefig`, because calling ``fig.savefig`` is sort of redundant.""" - @property - def leftpanel(self): - """An `~proplot.subplots.axes_grid` of the left panel stack.""" - return self._leftpanel - @property - def rightpanel(self): - """An `~proplot.subplots.axes_grid` of the right panel stack.""" - return self._rightpanel - @property - def toppanel(self): - """An `~proplot.subplots.axes_grid` of the top panel stack.""" - return self._toppanel - @property - def bottompanel(self): - """An `~proplot.subplots.axes_grid` of the bottom panel stack.""" - return self._bottompanel - - lpanel = leftpanel - """Alias for `~Figure.leftpanel`.""" - rpanel = rightpanel - """Alias for `~Figure.rightpanel`.""" - tpanel = toppanel - """Alias for `~Figure.toppanel`.""" - bpanel = bottompanel - """Alias for `~Figure.bottompanel`.""" - def _iter_axes(self): """Iterates over all axes and panels in the figure belonging to the `~proplot.axes.Axes` class. Excludes inset and twin axes.""" axs = [] - for ax in (*self._axes_main, *self.leftpanel, *self.rightpanel, - *self.bottompanel, *self.toppanel): + for ax in (*self._axes_main, *self._lpanels, *self._rpanels, + *self._bpanels, *self._tpanels): if not ax or not ax.get_visible(): continue axs.append(ax) for ax in axs: for s in 'lrbt': - for iax in getattr(ax, s + 'panel'): + for iax in getattr(ax, '_' + s + 'panels'): if not iax or not iax.get_visible(): continue axs.append(iax) @@ -1732,11 +1607,9 @@ def subplots(array=None, ncols=1, nrows=1, span=None, spanx=None, spany=None, align=None, alignx=None, aligny=None, share=None, sharex=None, sharey=None, - panel=None, panels=None, axpanel=None, axpanels=None, - axpanel_kw=None, axpanels_kw=None, basemap=False, proj=None, projection=None, proj_kw=None, projection_kw=None, autoformat=True, - **kwargs): + ): """ Analogous to `matplotlib.pyplot.subplots`, creates a figure with a single axes or arbitrary grids of axes, any of which can be map projections, @@ -1873,39 +1746,6 @@ def subplots(array=None, ncols=1, nrows=1, If boolean, applies to all subplots. If dictionary, values apply to specific subplots, as with `proj`. - panel : str, optional - Adds :ref:`Global figure panels` to the specified side. - String should contain any of the characters ``'l'`` (left), ``'r'`` - (right), ``'b'`` (bottom), or ``'t'`` (top). For example, ``'br'`` - will draw panels on the right and bottom sides of the figure. - - Figure panels are saved as the `~Figure.leftpanel`, - `~Figure.rightpanel`, `~Figure.bottompanel`, or - `~Figure.toppanel` attributes, with respective shorthands - `~Figure.lpanel`, `~Figure.rpanel`, `~Figure.bpanel`, and - `~Figure.tpanel`. - panels : str, optional - As with `panel`, but the default behavior is to assign a panel - to *every* row or column of subplots. Individual panels can then - be accessed with e.g. ``fig.leftpanel[0]``, ``fig.leftpanel[1]``. - [lrbt]array : list of int, optional - Defines how figure panels span rows and columns of subplots. - Interpreted like `array` -- the integers specify panels that span - *arbitrary, contiguous* columns or rows of subplots. - - For example, ``plot.suplots(ncols=3, panels='b', barray=[1, 2, 2])`` - draws a panel on the bottom of the first column and spanning the bottom - of the right 2 columns, and ``barray=[0, 2, 2]`` only draws a panel - underneath the right 2 columns -- as with `array`, the ``0`` indicates - an empty space. - [lrbt]width, [lrbt]share, [lrbt]stack, [lrbt]sep - As in `~proplot.axes.Axes.panel_axes`. Use e.g. `lwidth`, `rwidth`, - `bwidth`, and `twidth` to apply these settings to panels on different - sides. - [lrtb]space : float or str, optional - As in `~proplot.axes.Axes.panel_axes`, but controls space - between the main subplot grid and the figure panels. - Other parameters ---------------- tight : bool, optional @@ -1918,32 +1758,6 @@ def subplots(array=None, ncols=1, nrows=1, autoformat : bool, optional Whether to automatically format axes when special datasets are passed to plotting commands. See `Figure` for details. - axpanel, axpanels : str or dict-like, optional - Adds :ref:`Bulk axes panels` to subplots with - `~proplot.axes.Axes.panel_axes`. Both `axpanel` and `axpanels` - are acceptable. The argument is interpreted as follows. - - * If string, panels are drawn on the same side for all subplots. - String should contain any of the characters ``'l'`` (left panel), - ``'r'`` (right panel), ``'t'`` (top panel), or ``'b'`` (bottom - panel). For example, ``'rt'`` will draw a right and top panel. - * If dict-like, panels can be drawn on different sides for - different subplots. For example, consider a 3-subplot figure. - With ``axpanels={2:'r', 3:'l'}``, subplot 1 will have no panel, - subplot 2 will have a panel on the right side, and subplot 3 - will have a panel on the left side. - - axpanel_kw, axpanels_kw : dict-like, optional - Keyword args passed to `~proplot.axes.Axes.panel_axes` for panels - listed in `axpanel` and `axpanels`. If dictionary of properties, - applies globally. If *dictionary of dictionary* of properties, applies - to specific subplots, as with `axpanels`. - - For example, consider a 2-subplot figure with ``axpanels='l'``. - With ``axpanel_kw={'lwidth':1}``, both left panels will be 1 inch wide. - With ``axpanel_kw={1:{'lwidth':1}, 2:{'lwidth':0.5}}``, the left - subplot panel will be 1 inch wide and the right subplot panel will be - 0.5 inches wide. Returns ------- @@ -2112,7 +1926,6 @@ def subplots(array=None, ncols=1, nrows=1, subplots_orig_kw = { 'left':left, 'right':right, 'top':top, 'bottom':bottom, 'wspace':wspace, 'hspace':hspace, - 'wratios':wratios, 'hratios':hratios, } # Default border spaces @@ -2171,42 +1984,6 @@ def subplots(array=None, ncols=1, nrows=1, fig._axes_main = axs fig._share_axes_setup() - # Draw figure panels - # First get default arrays - panel, panels = _notNone(panel, ''), _notNone(panels, '') - for ipanel,ispan in zip((panel,panels),(1,0)): - for s in ipanel: - nmax = (ncols if s in 'bt' else nrows) - value = ([1]*nmax if ispan else [*range(1,nmax+1)]) - kwargs.setdefault(s + 'array', value) - for s in (panel + panels): - fig._add_figure_panels(s, **kwargs) - - # Draw axes panel - # Input can be string e.g. 'rl' or dictionary e.g. {(1,2,3):'r', 4:'l'} - # NOTE: This *must* come after shared axes are set up! Otherwise tight - # layout scaling is wrong - axpanels = _notNone(axpanel, axpanels, '', names=('axpanel','axpanels')) - axpanels_kw = _notNone(axpanel_kw, axpanels_kw, {}, names=('axpanel_kw', 'axpanels_kw')) - axpanels = _axes_dict(naxs, axpanels, kw=False, default='') - axpanels_kw = _axes_dict(naxs, axpanels_kw, kw=True) - for idx in range(naxs): - ax = axs[idx] - num = idx + 1 - keys = {*()} - sides, panels_kw = axpanels[num], axpanels_kw[num] - # Loop through sides - for s in sides: - offsides = ''.join({*'lrbt'} - {s}) - reg = re.compile(f'^[{offsides}](share|space|width)$') - ikw = {key:value for key,value in panels_kw.items() if not reg.match(key)} - keys.update(ikw.keys()) - fig._add_axes_panel(s, ax, order=order, **ikw) - # Warning message - jkw = {key:value for key,value in panels_kw.items() if key not in keys} - if jkw: - warnings.warn(f'Ignoring axpanels_kw keyword arg(s): {jkw}. Active sides are: axpanels={sides!r}.') - # Return figure and axes n = (ncols if order == 'C' else nrows) return fig, axes_grid(axs, n=n, order=order) diff --git a/proplot/wrappers.py b/proplot/wrappers.py index 20d7341b2..4f230f29e 100644 --- a/proplot/wrappers.py +++ b/proplot/wrappers.py @@ -615,7 +615,7 @@ def plot_wrapper(self, func, *args, cmap=None, values=None, **kwargs): if len(args) > 3: # e.g. with fmt string raise ValueError(f'Expected 1-3 positional args, got {len(args)}.') if cmap is None: - lines = func(*args, **kwargs) + lines = func(*args, values=values, **kwargs) else: lines = self.cmapline(*args, cmap=cmap, values=values, **kwargs) return lines @@ -1512,6 +1512,7 @@ def cycle_wrapper(self, func, *args, objs = [] ncols = 1 label_leg = None # for colorbar or legend + print('hi!!!', values, labels) labels = _notNone(values, labels, label, None, names=('values', 'labels', 'label')) stacked = kwargs.pop('stacked', False) if name in ('pie','boxplot','violinplot'): @@ -1584,35 +1585,35 @@ def cycle_wrapper(self, func, *args, if colorbar: # Add handles panel_kw.setdefault('mode', 'colorbar') - ax, loc = self._inset_or_panel_loc(colorbar, **panel_kw) + loc = self._loc_translate(colorbar, **panel_kw) if not isinstance(loc, str): raise ValueError(f'Invalid on-the-fly location {loc!r}. Must be a preset location. See Axes.colorbar') - if loc not in ax._auto_colorbar: - ax._auto_colorbar[loc] = [] - ax._auto_colorbar_kw[loc] = {} - ax._auto_colorbar[loc].extend(objs) + if loc not in self._auto_colorbar: + self._auto_colorbar[loc] = [] + self._auto_colorbar_kw[loc] = {} + self._auto_colorbar[loc].extend(objs) # Add keywords if loc != 'fill': colorbar_kw.setdefault('loc', loc) if label_leg: colorbar_kw.setdefault('label', label_leg) - ax._auto_colorbar_kw[loc].update(colorbar_kw) + self._auto_colorbar_kw[loc].update(colorbar_kw) if legend: # Add handles panel_kw.setdefault('mode', 'legend') - ax, loc = self._inset_or_panel_loc(legend, **panel_kw) + loc = self._loc_translate(legend, **panel_kw) if not isinstance(loc, str): raise ValueError(f'Invalid on-the-fly location {loc!r}. Must be a preset location. See Axes.legend') - if loc not in ax._auto_legend: - ax._auto_legend[loc] = [] - ax._auto_legend_kw[loc] = {} - ax._auto_legend[loc].extend(objs) + if loc not in self._auto_legend: + self._auto_legend[loc] = [] + self._auto_legend_kw[loc] = {} + self._auto_legend[loc].extend(objs) # Add keywords if loc != 'fill': legend_kw.setdefault('loc', loc) if label_leg: legend_kw.setdefault('label', label_leg) - ax._auto_legend_kw[loc].update(legend_kw) + self._auto_legend_kw[loc].update(legend_kw) # Return # WARNING: Make sure plot always returns tuple of objects, and bar always @@ -2018,7 +2019,7 @@ def cmap_wrapper(self, func, *args, cmap=None, cmap_kw=None, # Add colorbar if colorbar: panel_kw.setdefault('mode', 'colorbar') - ax, loc = self._inset_or_panel_loc(colorbar, **panel_kw) + loc = self._loc_translate(colorbar, **panel_kw) if not isinstance(loc, str): raise ValueError(f'Invalid on-the-fly location {loc!r}. Must be a preset location. See Axes.colorbar.') if 'label' not in colorbar_kw and self.figure._auto_format: @@ -2029,7 +2030,7 @@ def cmap_wrapper(self, func, *args, cmap=None, cmap_kw=None, colorbar_kw.setdefault('values', values) if loc != 'fill': colorbar_kw.setdefault('loc', loc) - ax.colorbar(obj, **colorbar_kw) + self.colorbar(obj, **colorbar_kw) return obj #------------------------------------------------------------------------------# From aa4cccffa3fa456ff1fd54ade20a16e5e3f546a1 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Mon, 2 Sep 2019 23:41:46 -0600 Subject: [PATCH 05/17] On-the-fly figure colorbars and legends! --- proplot/axes.py | 60 ++++---- proplot/styletools.py | 8 +- proplot/subplots.py | 338 ++++++++++++++++++++++++------------------ proplot/wrappers.py | 4 +- 4 files changed, 227 insertions(+), 183 deletions(-) diff --git a/proplot/axes.py b/proplot/axes.py index 6a170a951..53a6df82d 100644 --- a/proplot/axes.py +++ b/proplot/axes.py @@ -640,19 +640,19 @@ def format(self, *, title=None, top=None, main title. The following locations keys are valid. Defaults to ``rc['abc.loc']`` and ``rc['title.loc']``. - ========================= ============================ - Location Valid keys - ========================= ============================ - center, above axes ``'center'``, ``'c'`` - left, above axes ``'left'``, ``'l'`` - right, above axes ``'right'``, ``'r'`` - lower center, inside axes ``'lower center``', ``'lc'`` - upper center, inside axes ``'upper center'``, ``'uc'`` - upper right, inside axes ``'upper right'``, ``'ur'`` - upper left, inside axes ``'upper left'``, ``'ul'`` - lower left, inside axes ``'lower left'``, ``'ll'`` - lower right, inside axes ``'lower right'``, ``'lr'`` - ========================= ============================ + ======================== ============================ + Location Valid keys + ======================== ============================ + center above axes ``'center'``, ``'c'`` + left above axes ``'left'``, ``'l'`` + right above axes ``'right'``, ``'r'`` + lower center inside axes ``'lower center``', ``'lc'`` + upper center inside axes ``'upper center'``, ``'uc'`` + upper right inside axes ``'upper right'``, ``'ur'`` + upper left inside axes ``'upper left'``, ``'ul'`` + lower left inside axes ``'lower left'``, ``'ll'`` + lower right inside axes ``'lower right'``, ``'lr'`` + ======================== ============================ abcborder, titleborder : bool, optional Whether to draw a white border around titles and a-b-c labels @@ -915,6 +915,7 @@ def colorbar(self, *args, loc=None, pad=None, outer right ``'r'``, ``'right'`` outer bottom ``'b'``, ``'bottom'`` outer top ``'t'``, ``'top'`` + default inset ``0``, ``'i'``, ``'inset'`` upper right inset ``1``, ``'upper right'``, ``'ur'`` upper left inset ``2``, ``'upper left'``, ``'ul'`` lower left inset ``3``, ``'lower left'``, ``'ll'`` @@ -930,12 +931,19 @@ def colorbar(self, *args, loc=None, pad=None, The colorbar length. For outer colorbars, units are relative to the axes width or height. For inset colorbars, if float, units are inches; if string, units are interpreted by `~proplot.utils.units`. - Defaults to ``rc['colorbar.length']``. + Defaults to ``rc['colorbar.length']`` for outer colorbars, + ``rc['colorbar.lengthinset']`` for inset colorbars. width : float or str, optional The colorbar width. If float, units are inches. If string, units are interpreted by `~proplot.utils.units`. Defaults to - ``rc['colorbar.width']`` for inset colorbars, - ``rc['subplots.cbarwidth']`` for outer colorbars. + ``rc['colorbar.width']`` for outer colorbars, + ``rc['colorbar.widthinset']`` for inset colorbars. + space : float or str, optional + The space between the colorbar and the main axes for outer + colorbars. If float, units are inches. If string, + units are interpreted by `~proplot.utils.units`. By default, this + is adjusted automatically in the "tight layout" calculation, or is + ``rc['subplots.panelspace']`` if "tight layout" is turned off. frame, frameon : bool, optional Whether to draw a frame around inset colorbars, just like `~matplotlib.axes.Axes.legend`. @@ -946,18 +954,6 @@ def colorbar(self, *args, loc=None, pad=None, ``rc['colorbar.framealpha']``, ``rc['axes.linewidth']``, ``rc['axes.edgecolor']``, and ``rc['axes.facecolor']``, respectively. - width : float or str, optional - The width of the colorbar. If float, units are inches. If string, - units are interpreted by `~proplot.utils.units`. Defaults to - ``rc['colorbar.width']`` for inset colorbars and - ``rc['subplots.colorbarwidth']`` for outer colorbars. - space : float or str, optional - The space between the colorbar and the main axes for outer - colorbars. If float, units are inches. If string, - units are interpreted by `~proplot.utils.units`. By default, this - is adjusted automatically in the "tight layout" calculation, - or is ``rc['subplots.panelspace']`` if "tight layout" is turned - off. **kwargs Passed to `~proplot.wrappers.colorbar_wrapper`. """ @@ -972,7 +968,7 @@ def colorbar(self, *args, loc=None, pad=None, # Generate panel if loc in ('left','right','top','bottom'): - ax = self.panel_axes(loc, width=width, space=space, mode='colorbar') + ax = self.panel_axes(loc, width=width, space=space, filled=True) return ax.colorbar(loc='_fill', *args, **kwargs) # Filled colorbar @@ -1122,7 +1118,7 @@ def legend(self, *args, loc=None, width=None, space=None, **kwargs): right panel ``'r'``, ``'right'`` bottom panel ``'b'``, ``'bottom'`` top panel ``'t'``, ``'top'`` - "best" possible ``0``, ``'best'``, ``'inset'``, ``'i'`` + "best" inset ``0``, ``'best'``, ``'inset'``, ``'i'`` upper right inset ``1``, ``'upper right'``, ``'ur'`` upper left inset ``2``, ``'upper left'``, ``'ul'`` lower left inset ``3``, ``'lower left'``, ``'ll'`` @@ -1154,7 +1150,7 @@ def legend(self, *args, loc=None, width=None, space=None, **kwargs): # Generate panel if loc in ('left','right','top','bottom'): - ax = self.panel_axes(loc, width=width, space=space, mode='legend') + ax = self.panel_axes(loc, width=width, space=space, filled=True) return ax.legend(*args, loc='_fill', **kwargs) # Fill @@ -1358,7 +1354,7 @@ def panel_axes(self, side, **kwargs): `~proplot.axes.Axes` The panel axes. """ - return self.figure._add_axes_panel(side, self, **kwargs) + return self.figure._add_axes_panel(self, side, **kwargs) panel = panel_axes """Alias for `~Axes.panel_axes`.""" diff --git a/proplot/styletools.py b/proplot/styletools.py index 00719def0..42571da1f 100644 --- a/proplot/styletools.py +++ b/proplot/styletools.py @@ -481,13 +481,9 @@ def _get_channel(color, channel, space='hsl'): # Interpret string or RGB tuple offset = 0 if isinstance(color, str): - regex = '([-+]\S*)$' # user can optionally offset from color; don't filter to just numbers, want to raise our own error if user messes up - match = re.search(regex, color) + match = re.search('([-+][0-9.]+)$', color) if match: - try: - offset = float(match.group(0)) - except ValueError: - raise ValueError(f'Invalid channel identifier "{color}".') + offset = float(match.group(0)) color = color[:match.start()] return offset + to_xyz(to_rgb(color), space)[channel] diff --git a/proplot/subplots.py b/proplot/subplots.py index 30e695665..95ad897ac 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -472,21 +472,21 @@ def update(self, **kwargs): #-----------------------------------------------------------------------------# def _panels_kwargs(side, share=None, width=None, space=None, - mode='panel', figure=False): + filled=False, figure=False): """Converts global keywords like `space` and `width` to side-local keywords like `lspace` and `lwidth`, and applies default settings.""" # Return values + # NOTE: Make default legend width same as default colorbar width, in + # case user draws legend and colorbar panel in same row or column! s = side[0] if s not in 'lrbt': raise ValueError(f'Invalid panel spec {side!r}.') space_orig = units(space) - if mode == 'panel': - default = rc['subplots.panelwidth'] - elif mode == 'colorbar': + if filled: default = rc['colorbar.width'] else: - default = '1em' - share = _notNone(share, (mode == 'panel')) + default = rc['subplots.panelwidth'] + share = _notNone(share, (not filled)) width = units(_notNone(width, default)) space = _notNone(space, units(rc['subplots.' + ('panel' if share and not figure @@ -564,6 +564,8 @@ def _subplots_geometry(**kwargs): rhspace = sum(haxes_space[y1:y2]) rwratio = (ncols_ax*sum(waxes[x1:x2+1]))/(dx*sum(waxes)) rhratio = (nrows_ax*sum(haxes[y1:y2+1]))/(dy*sum(haxes)) + if rwratio == 0 or rhratio == 0: + raise RuntimeError(f'Something went wrong, got wratio={rwratio!r} and hratio={rhratio!r} for reference axes.') if np.iterable(aspect): aspect = aspect[0]/aspect[1] @@ -726,11 +728,17 @@ def __init__(self, self._tpanels = [] self._lpanels = [] self._rpanels = [] - self._gridspec_main = FlexibleGridSpec(self, **(gridspec_kw or {})) + gridspec = FlexibleGridSpec(self, **(gridspec_kw or {})) + nrows, ncols = gridspec.get_active_geometry() + self._barray = np.empty((0, ncols), dtype=bool) + self._tarray = np.empty((0, ncols), dtype=bool) + self._larray = np.empty((0, nrows), dtype=bool) + self._rarray = np.empty((0, nrows), dtype=bool) + self._gridspec_main = gridspec self.suptitle('') # add _suptitle attribute @_counter - def _add_axes_panel(self, side, ax, order='C', mode='panel', **kwargs): + def _add_axes_panel(self, ax, side, filled=False, **kwargs): """Hidden method that powers `~proplot.axes.panel_axes`.""" # Redirect to main axes, e.g. if want to draw outer colorbar or legend # from data plotted in a panel or inset axes @@ -744,12 +752,9 @@ def _add_axes_panel(self, side, ax, order='C', mode='panel', **kwargs): raise ValueError(f'Invalid side {side!r}.') side = _side_translate[s] share, width, space, space_orig = _panels_kwargs(s, - kwargs, mode=mode, figure=False) + kwargs, filled=filled, figure=False) - # Modify existing geometry, find incies for new panel by placing it - # to the outside of *existing* panels if possible - # TODO: If panel slot already exists, permit user to change the - # width and space. + # Get gridspec and subplotspec indices subplotspec = ax.get_subplotspec() nrows, ncols, row1, row2, col1, col2 = subplotspec.get_active_rows_columns() pgrid = getattr(ax, '_' + s + 'panels') @@ -763,106 +768,106 @@ def _add_axes_panel(self, side, ax, order='C', mode='panel', **kwargs): idx2 = slice(col1, col2 + 1) iratio = idx1 gridspec = self._insert_row_column(side, iratio, - width, space, space_orig, figure=False, - ) + width, space, space_orig, figure=False, + ) - # Get keyword args and gridspec args - # Loop through unique numbers + # Draw and setup panel with self._unlock(): pax = self.add_subplot(gridspec[idx1,idx2], projection='cartesian') - - # Add to panel list - pgrid = getattr(ax, '_' + s + 'panels') - if s in 'lt': - pgrid.insert(0, pax) - else: - pgrid.append(pax) - - # Set up shared axes + getattr(ax, '_' + s + 'panels').append(pax) pax._panel_side = side pax._panel_share = share pax._panel_parent = ax - if share: - ax._share_panels_setup() - self._share_axes_setup(ax) - axis = (pax.yaxis if side in ('left','right') else pax.xaxis) - getattr(axis, 'tick_' + side)() # sets tick and tick label positions intelligently - axis.set_label_position(side) - - # Return axes grid of just this axes + + # Axis sharing and axis setup only for non-legend or colorbar axes + if not filled: + if share: + ax._share_panels_setup() + self._share_axes_setup(ax) + axis = (pax.yaxis if side in ('left','right') else pax.xaxis) + getattr(axis, 'tick_' + side)() # sets tick and tick label positions intelligently + axis.set_label_position(side) + return pax - def _add_figure_panel(self, side, mode='panel', array=None, **kwargs): + def _add_figure_panel(self, side, span=None, **kwargs): """Adds figure panels. Also modifies the panel attribute stored on the figure to include these panels.""" - # Interpret args - # TODO: Allow successive panels! - # NOTE: Axis sharing not implemented for figure panels, 99% of the - # time this is just used as construct for adding global colorbars and - # legends, really not worth implementing axis sharing + # Get default panel args s = side[0] if s not in 'lrbt': raise ValueError(f'Invalid side {side!r}.') side = _side_translate[s] - _, width, space, space_orig = _panels_kwargs(s, - kwargs, mode=mode, figure=True) + _, width, space, space_orig = _panels_kwargs(s, kwargs, + filled=True, figure=True) - # Modify existing geometry, and verify validity of the array arg - # using boolean 1D arrays indicating where panels are present for - # the dimension *spanned* by the new panel + # Get props subplots_kw = self._subplots_kw if s in 'lr': - panels = subplots_kw['wpanels'] - nacross, nalong = subplots_kw['ncols'], subplots_kw['nrows'] + panels, nacross = subplots_kw['hpanels'], subplots_kw['ncols'] else: - panels = subplots_kw['hpanels'] - nacross, nalong = subplots_kw['nrows'], subplots_kw['ncols'] - panels = np.array([bool(s) for s in panels]) - idxs, = np.where(~panels) - if array is None: - array = [1]*len(idxs) - if not np.iterable(array) or len(array) != len(idxs): - raise ValueError(f'Need length-{len(idxs)} list of integers for "{s}array", got {s}array={array!r}.') - iratio = (0 if s in 'lt' else nacross) - gridspec = self._insert_row_column(side, iratio, - width, space, space_orig, figure=True, - ) - - # Pad array so it spans existing panel slots - # NOTE: Does not matter if have e.g. [1, 0, 0, 1, 2, 0, 0, 2], because - # panels are drawn based on minimum and maximum indices - paxs = [] - array_new = np.zeros((nalong,)) - array_new[idxs] = array - # Get indices and draw axes - for num in np.unique(array_new).flat: - if num == 0: - continue - idx, = np.where(array_new == num) - idx1 = slice(min(idx), max(idx)+1) # ignores axes panel slots - idx2 = -1*(s in 'br') - if s in 'bt': - idx1, idx2 = idx2, idx1 - with self._unlock(): - ipax = self.add_subplot(gridspec[idx1,idx2], projection='cartesian') - ipax._panel_side = side - ipax._panel_share = False - ipax._panel_parent = None - axis = (ipax.yaxis if side in ('left','right') else ipax.xaxis) - getattr(axis, 'tick_' + side)() # sets tick and tick label positions intelligently - axis.set_label_position(side) - paxs += [ipax] - - # Add to panel attributes - pgrid = getattr(self, '_' + s + 'panels') - if s in 'lt': - pgrid.insert(0, paxs[0]) + panels, nacross = subplots_kw['wpanels'], subplots_kw['nrows'] + array = getattr(self, '_' + s + 'array') + npanels, nalong = array.shape + # Check span array + span = _notNone(span, (1, nalong)) + if not np.iterable(span) or len(span)==1: + span = 2*np.atleast_1d(span).tolist() + if len(span) != 2: + raise ValueError(f'Invalid span {span!r}.') + if span[0] < 1 or span[1] > nalong: + raise ValueError(f'Invalid coordinates in span={span!r}. Coordinates must satisfy 1 <= c <= {nalong}.') + start, stop = span[0] - 1, span[1] # zero-indexed + + # See if there is room for panel on the figure + # The 'array' is an array of boolean values, where each row corresponds + # to another figure panel, moving toward the outside, and + # We use add to panels toward the outside by appending rows to boolean + # arrays indicating which main axes rows and columns are occupied + iratio = (-1 if s in 'lt' else nacross) # default vals + for i in range(npanels): + if not any(array[i,start:stop]): + array[i,start:stop] = True + if s in 'lt': # descending array moves us closer to 0 + # npanels=1, i=0 --> iratio=0 + # npanels=2, i=0 --> iratio=1 + # npanels=2, i=1 --> iratio=0 + iratio = npanels - 1 - i + else: # descending array moves us closer to nacross-1 + # npanels=1, i=0 --> iratio=nacross-1 + # npanels=2, i=0 --> iratio=nacross-2 + # npanels=2, i=1 --> iratio=nacross-1 + iratio = nacross - (npanels - i) + break + # Add to array, since we are adding another panel + if iratio in (-1, nacross): + iarray = np.zeros((1, nalong), dtype=bool) + iarray[0,start:stop] = True + array = np.concatenate((array, iarray), axis=0) + setattr(self, '_' + s + 'array', array) + + # Get gridspec and subplotspec indices + idxs, = np.where(np.array(panels) == '') + if len(idxs) != nalong: + raise RuntimeError('Wut?') + if s in 'lr': + idx1 = slice(idxs[start], idxs[stop-1] + 1) + idx2 = max(iratio, 0) else: - pgrid.append(paxs[0]) + idx1 = max(iratio, 0) + idx2 = slice(idxs[start], idxs[stop-1] + 1) + gridspec = self._insert_row_column(side, iratio, + width, space, space_orig, figure=True, + ) - # Return axes_grid of just the axes drawn in this round - # TODO: Draw one at a time!!! - return paxs[0] + # Draw and setup panel + with self._unlock(): + pax = self.add_subplot(gridspec[idx1,idx2], projection='cartesian') + getattr(self, '_' + s + 'panels').append(pax) + pax._panel_side = side + pax._panel_share = False + pax._panel_parent = None + return pax def _adjust_aspect(self): """Adjust average aspect ratio used for gridspec calculations. This @@ -1140,8 +1145,8 @@ def _align_suplabels(self, renderer): suptitle.update(kw) def _insert_row_column(self, side, idx, - ratio, space, space_orig, - figure=False): + ratio, space, space_orig, figure=False, + ): """Helper function that "overwrites" the main figure gridspec to make room for a panel. The `side` is the panel side, the `idx` is the slot you want the panel to occupy, and the remaining args are the @@ -1155,13 +1160,13 @@ def _insert_row_column(self, side, idx, if s not in 'lrbt': raise ValueError(f'Invalid side {side}.') idx_space = idx - 1*bool(s in 'br') - idx_offset = 1*bool(s in 'tl')*bool(idx > 0) + idx_offset = 1*bool(s in 'tl') if s in 'lr': x, w, ncols = 'x', 'w', 'ncols' else: x, w, ncols = 'y', 'h', 'nrows' - # Load arrays + # Load arrays and test if we need to insert subplots_kw = self._subplots_kw subplots_orig_kw = self._subplots_orig_kw panels = subplots_kw[w + 'panels'] @@ -1170,11 +1175,9 @@ def _insert_row_column(self, side, idx, spaces_orig = subplots_orig_kw[w + 'space'] # Slot already exists - # NOTE: This is never the case for figure panels, because we always - # add them along entire side all at once - exists = (not figure and idx < len(panels) and panels[idx] == s) + entry = ('f' if figure else s) + exists = (idx not in (-1, len(panels)) and panels[idx] == entry) if exists: # already exists! - # Modify existing space if spaces_orig[idx_space] is None: spaces_orig[idx_space] = units(space_orig) spaces[idx_space] = _notNone(spaces_orig[idx_space], space) @@ -1188,12 +1191,12 @@ def _insert_row_column(self, side, idx, spaces_orig.insert(idx_space, space_orig) spaces.insert(idx_space, space) ratios.insert(idx, ratio) - panels.insert(idx, 'f' if figure else s) + panels.insert(idx, entry) # Reference ax location array - ref = subplots_kw[x + 'ref'] - for i,val in enumerate(ref): - if val >= idx: - ref[i] += 1 + # TODO: For now do not need to increment, but need to double + # check algorithm for fixing axes aspect! + # ref = subplots_kw[x + 'ref'] + # ref[:] = [val + 1 if val >= idx else val for val in ref] # Update figure figsize, gridspec_kw, _ = _subplots_geometry(**subplots_kw) @@ -1369,14 +1372,16 @@ def _share_axes_setup(self, ref=None): def add_subplot(self, *args, **kwargs): """Issues warning for new users that try to call - `~matplotlib.figure.Figure.add_subplot` or - `~matplotlib.figure.Figure.colorbar` manually.""" + `~matplotlib.figure.Figure.add_subplot` manually.""" if self._locked: - warnings.warn('Using "fig.add_subplot" or "fig.colorbar" with ProPlot figures may result in unexpected behavior. Please use "proplot.subplots" and "Axes.colorbar" instead.') + warnings.warn('Using "fig.add_subplot()" with ProPlot figures may result in unexpected behavior. Please use "proplot.subplots()" instead.') ax = super().add_subplot(*args, **kwargs) return ax - def colorbar(self, *args, loc='r', width=None, space=None, **kwargs): + def colorbar(self, *args, + loc='r', width=None, space=None, + row=None, col=None, rows=None, cols=None, span=None, + **kwargs): """ Draws a colorbar along the left, right, bottom, or top side of the figure, centered between the leftmost and rightmost (or @@ -1384,42 +1389,57 @@ def colorbar(self, *args, loc='r', width=None, space=None, **kwargs): Parameters ---------- - panel : str, optional - Adds :ref:`Global figure panels` to the specified side. - String should contain any of the characters ``'l'`` (left), ``'r'`` - (right), ``'b'`` (bottom), or ``'t'`` (top). For example, ``'br'`` - will draw panels on the right and bottom sides of the figure. - panels : str, optional - As with `panel`, but the default behavior is to assign a panel - to *every* row or column of subplots. Individual panels can then - be accessed with e.g. ``fig.leftpanel[0]``, ``fig.leftpanel[1]``. - array : list of int, optional - Defines how figure panels span rows and columns of subplots. - Interpreted like `array` -- the integers specify panels that span - *arbitrary, contiguous* columns or rows of subplots. - - For example, ``plot.suplots(ncols=3, panels='b', barray=[1, 2, 2])`` - draws a panel on the bottom of the first column and spanning the bottom - of the right 2 columns, and ``barray=[0, 2, 2]`` only draws a panel - underneath the right 2 columns -- as with `array`, the ``0`` indicates - an empty space. - width : float or str, optional - As in `~proplot.axes.Axes.panel_axes`. Use e.g. `lwidth`, `rwidth`, - `bwidth`, and `twidth` to apply these settings to panels on different - sides. + loc : str, optional + The colorbar location. Valid location keys are as follows. + + =========== ===================== + Location Valid keys + =========== ===================== + left edge ``'l'``, ``'left'`` + right edge ``'r'``, ``'right'`` + bottom edge ``'b'``, ``'bottom'`` + top edge ``'t'``, ``'top'`` + =========== ===================== + + row, col, rows, cols : optional + Aliases for `span`. + span : int or (int, int), optional + Describes how the colorbar spans rows and columns of subplots. + For example, ``fig.colorbar(loc='b', col=1)`` draws a colorbar + beneath the leftmost column of subplots, and + ``fig.colorbar(loc='b', cols=(1,2))`` draws a colorbar beneath the + left two columns of subplots. By default, the colorbar will span + all rows and columns. space : float or str, optional - As in `~proplot.axes.Axes.panel_axes`, but controls space - between the main subplot grid and the figure panels. + The space between the main subplot grid and the colorbar, or the + space between successively stacked colorbars. If float, units + are inches. If string, units are interpreted by + `~proplot.utils.units`. By default, this is adjusted automatically + in the "tight layout" calculation, or is + ``rc['subplots.panelspace']`` if "tight layout" is turned off. + width : float or str, optional + The colorbar width. If float, units are inches. If string, units + are interpreted by `~proplot.utils.units`. Defaults to + ``rc['colorbar.width']``. *args, **kwargs Passed to `~proplot.axes.Axes.colorbar`. """ if 'cax' in kwargs: return super().colorbar(*args, **kwargs) + elif 'ax' in kwargs: + return kwargs.pop('ax').colorbar(*args, + space=space, width=width, **kwargs) else: - ax = self._add_figure_panel(loc, space=space, width=width, array=None, mode='colorbar') + span = _notNone(span, row, col, rows, cols, None, + names=('span', 'row', 'col', 'rows', 'cols')) + ax = self._add_figure_panel(loc, + space=space, width=width, span=span) return ax.colorbar(*args, loc='_fill', **kwargs) - def legend(self, *args, loc='r', width=None, space=None, **kwargs): + def legend(self, *args, + loc='r', width=None, space=None, + row=None, col=None, rows=None, cols=None, span=None, + **kwargs): """ Draws a legend along the left, right, bottom, or top side of the figure, centered between the leftmost and rightmost (or @@ -1427,13 +1447,46 @@ def legend(self, *args, loc='r', width=None, space=None, **kwargs): Parameters ---------- - loc, width, space - See `~Figure.colorbar`. + loc : str, optional + The legend location. Valid location keys are as follows. + + =========== ===================== + Location Valid keys + =========== ===================== + left edge ``'l'``, ``'left'`` + right edge ``'r'``, ``'right'`` + bottom edge ``'b'``, ``'bottom'`` + top edge ``'t'``, ``'top'`` + =========== ===================== + + row, col, rows, cols : optional + Aliases for `span`. + span : int or (int, int), optional + Describes how the legend spans rows and columns of subplots. + For example, ``fig.legend(loc='b', col=1)`` draws a legend + beneath the leftmost column of subplots, and + ``fig.legend(loc='b', cols=(1,2))`` draws a legend beneath the + left two columns of subplots. By default, the legend will span + all rows and columns. + space : float or str, optional + The space between the main subplot grid and the legend, or the + space between successively stacked colorbars. If float, units + are inches. If string, units are interpreted by + `~proplot.utils.units`. By default, this is adjusted automatically + in the "tight layout" calculation, or is + ``rc['subplots.panelspace']`` if "tight layout" is turned off. *args, **kwargs Passed to `~proplot.axes.Axes.legend`. """ - ax = self._add_figure_panel(loc, space=space, width=width, array=None, mode='legend') - return ax.legend(*args, loc='_fill', **kwargs) + if 'ax' in kwargs: + return kwargs.pop('ax').legend(*args, + space=space, width=width, **kwargs) + else: + span = _notNone(span, row, col, rows, cols, None, + names=('span', 'row', 'col', 'rows', 'cols')) + ax = self._add_figure_panel(loc, + space=space, width=width, span=span) + return ax.legend(*args, loc='_fill', **kwargs) @_counter def draw(self, renderer): @@ -1643,8 +1696,7 @@ def subplots(array=None, ncols=1, nrows=1, Tuple specifying the figure `(width, height)`. width, height : float or str, optional The figure width and height. If float, units are inches. If string, - units are interpreted by `~proplot.utils.units`. For example, - ``width="10cm"`` creates a 10cm wide figure. + units are interpreted by `~proplot.utils.units`. journal : str, optional String name corresponding to an academic journal standard that is used to control the figure width (and height, if specified). Valid names diff --git a/proplot/wrappers.py b/proplot/wrappers.py index 4f230f29e..6cdd626bf 100644 --- a/proplot/wrappers.py +++ b/proplot/wrappers.py @@ -2639,9 +2639,9 @@ def colorbar_wrapper(self, maxn_minor = _notNone(maxn_minor, int(length/(0.5*fontsize/72))) # Get locator if tickminor and minorlocator is None: - step = 1 + len(locator)//maxn_minor + step = 1 + len(locator) // max(1, maxn_minor) minorlocator = locator[::step] - step = 1 + len(locator)//maxn + step = 1 + len(locator) // max(1, maxn) locator = locator[::step] # Locator object locator = axistools.Locator(locator, **locator_kw) From c264999ee5c6ed6329d9d39905ee223430cd594b Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Mon, 2 Sep 2019 23:56:38 -0600 Subject: [PATCH 06/17] Return axes_grid of panels, fix _panels_kwargs bug --- proplot/subplots.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/proplot/subplots.py b/proplot/subplots.py index 95ad897ac..d7340ba77 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -209,12 +209,8 @@ def __getattr__(self, attr): def null_iterator(*args, **kwargs): return None return null_iterator - # Panels - if all(isinstance(_, (axes_grid, axes.Axes)) - for _ in attrs): - return axes_grid(attrs) # Objects - elif not any(callable(_) for _ in attrs): + if not any(callable(_) for _ in attrs): if len(self) == 1: return attrs[0] else: @@ -231,6 +227,8 @@ def axes_grid_iterator(*args, **kwargs): return ret[0] elif all(res is None for res in ret): return None + elif all(isinstance(res, axes.Axes) for res in ret): + return axes_grid(ret, n=self._n, order=self._order) else: return ret return axes_grid_iterator @@ -752,7 +750,7 @@ def _add_axes_panel(self, ax, side, filled=False, **kwargs): raise ValueError(f'Invalid side {side!r}.') side = _side_translate[s] share, width, space, space_orig = _panels_kwargs(s, - kwargs, filled=filled, figure=False) + filled=filled, figure=False, **kwargs) # Get gridspec and subplotspec indices subplotspec = ax.get_subplotspec() @@ -798,8 +796,8 @@ def _add_figure_panel(self, side, span=None, **kwargs): if s not in 'lrbt': raise ValueError(f'Invalid side {side!r}.') side = _side_translate[s] - _, width, space, space_orig = _panels_kwargs(s, kwargs, - filled=True, figure=True) + _, width, space, space_orig = _panels_kwargs(s, + filled=True, figure=True, **kwargs) # Get props subplots_kw = self._subplots_kw From 088a5374a3c2ad60b2cc02f67197e2a6c423eb11 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Mon, 2 Sep 2019 23:58:17 -0600 Subject: [PATCH 07/17] Fix axes_grid docstring --- proplot/subplots.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proplot/subplots.py b/proplot/subplots.py index d7340ba77..dc1834ce9 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -198,9 +198,10 @@ def __getattr__(self, attr): ------- >>> import proplot as plot - ... f, axs = plot.subplots(nrows=2, ncols=2, axcolorbars='b') - ... axs.format(xticks=5) # calls "format" on all subplots in the list - ... axs.bpanel.colorbar(m) # calls "colorbar" on all panels in the axes_grid returned by "axs.bpanel" + ... f, axs = plot.subplots(nrows=2, ncols=2) + ... axs.format(...) # calls "format" on all subplots in the list + ... paxs = axs.panel_axes('r') + ... paxs.format(...) # calls "format" on all panels in the axes_grid returned by "axs.panel_axes" """ attrs = (*(getattr(ax, attr) for ax in self),) # may raise error From bf87d3a8e37a1f77666eb3b269cc6692b50c48f0 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Mon, 2 Sep 2019 23:59:17 -0600 Subject: [PATCH 08/17] Minor --- proplot/subplots.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proplot/subplots.py b/proplot/subplots.py index dc1834ce9..15db23e51 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -186,7 +186,8 @@ def __getitem__(self, key): return objs def __getattr__(self, attr): - """If the attribute is *callable*, returns a dummy function that loops + """ + If the attribute is *callable*, returns a dummy function that loops through each identically named method, calls them in succession, and returns a tuple of the results. This lets you call arbitrary methods on multiple axes at once! If the `axes_grid` has length ``1``, From cfe7e66484c093c9019dfcdef1a7cad878b32605 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Tue, 3 Sep 2019 00:00:09 -0600 Subject: [PATCH 09/17] axes_grid docstring --- proplot/subplots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proplot/subplots.py b/proplot/subplots.py index 15db23e51..eadf3615a 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -192,8 +192,8 @@ def __getattr__(self, attr): returns a tuple of the results. This lets you call arbitrary methods on multiple axes at once! If the `axes_grid` has length ``1``, just returns the single result. If the attribute is *not callable*, - returns an `axes_grid` of identically named attributes for every object - in the list. + returns a tuple of identically named attributes for every object in + the list. Example ------- From 3205230583ba5a72946563aae24462247c139f75 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Tue, 3 Sep 2019 00:25:49 -0600 Subject: [PATCH 10/17] Zoom attribute change --- proplot/axes.py | 14 ++++++-------- proplot/subplots.py | 1 - 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/proplot/axes.py b/proplot/axes.py index 53a6df82d..bb3af176e 100644 --- a/proplot/axes.py +++ b/proplot/axes.py @@ -163,12 +163,12 @@ def __init__(self, *args, number=None, self._lpanels = [] self._rpanels = [] self._tight_bbox = None # bounding boxes are saved - self._zoom = None self._panel_side = None self._panel_parent = None self._panel_filled = False # True when panels "filled" with colorbar/legend - self._inset_zoom = False self._inset_parent = None + self._inset_zoom = False + self._inset_zoom_data = None self._alty_child = None self._altx_child = None self._alty_parent = None @@ -1308,24 +1308,22 @@ def indicate_inset_zoom(self, alpha=None, linewidth=None, color=None, edgecolor= kwargs.update({'linewidth':linewidth, 'edgecolor':edgecolor, 'alpha':alpha}) rectpatch, connects = parent.indicate_inset(rect, self, **kwargs) # Adopt properties from old one - if self._zoom: - rectpatch_old, connects_old = self._zoom + if self._inset_zoom_data: + rectpatch_old, connects_old = self._inset_zoom_data rectpatch.update_from(rectpatch_old) rectpatch_old.set_visible(False) for line,line_old in zip(connects,connects_old): - # Actually want to *preserve* whether line is visible! This - # is automatically determined! visible = line.get_visible() line.update_from(line_old) line.set_visible(visible) line_old.set_visible(False) - # By default linewidth is only applied to box + # Format zoom data else: for line in connects: line.set_linewidth(linewidth) line.set_color(edgecolor) line.set_alpha(alpha) - self._zoom = (rectpatch, connects) + self._inset_zoom_data = (rectpatch, connects) return (rectpatch, connects) def panel_axes(self, side, **kwargs): diff --git a/proplot/subplots.py b/proplot/subplots.py index eadf3615a..1d2a772b4 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -899,7 +899,6 @@ def _adjust_aspect(self): self.set_size_inches(figsize) self._gridspec_main.update(**gridspec_kw) - @_counter def _adjust_tight_layout(self, renderer): """Applies tight layout scaling that permits flexible figure dimensions and preserves panel widths and subplot aspect ratios. From 00bd9ffdc3a59f4e6bd77ec5c5e58b4be06b380a Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Tue, 3 Sep 2019 08:52:44 -0600 Subject: [PATCH 11/17] Auto legend cleanup --- proplot/axes.py | 14 +++++--------- proplot/wrappers.py | 14 ++++++-------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/proplot/axes.py b/proplot/axes.py index bb3af176e..1d01bcab4 100644 --- a/proplot/axes.py +++ b/proplot/axes.py @@ -173,10 +173,8 @@ def __init__(self, *args, number=None, self._altx_child = None self._alty_parent = None self._altx_parent = None - self._auto_colorbar = {} # stores plot handles for auto colorbar + self._auto_colorbar = {} # stores handles and kwargs for auto colorbar self._auto_legend = {} - self._auto_colorbar_kw = {} # keyword args for auto colorbar() - self._auto_legend_kw = {} # Axis sharing, new text attributes, custom formatting self._spanx = spanx # boolean toggles, whether we want to span axes labels self._spany = spany @@ -210,14 +208,12 @@ def _draw_auto_legends_colorbars(self): """Generate automatic legends and colorbars. Wrapper funcs let user add handles to location lists with successive calls to make successive calls to plotting commands.""" - for loc,handles in self._auto_colorbar.items(): - self.colorbar(handles, **self._auto_colorbar_kw[loc]) - for loc,handles in self._auto_legend.items(): - self.legend(handles, **self._auto_legend_kw[loc]) + for loc,(handles,kwargs) in self._auto_colorbar.items(): + self.colorbar(handles, **kwargs) + for loc,(handles,kwargs) in self._auto_legend.items(): + self.legend(handles, **kwargs) self._auto_legend = {} self._auto_colorbar = {} - self._auto_legend_kw = {} - self._auto_colorbar_kw = {} def _get_side_axes(self, side): """Returns groups of axes in row or column or the single group in the diff --git a/proplot/wrappers.py b/proplot/wrappers.py index 6cdd626bf..b70ced6f4 100644 --- a/proplot/wrappers.py +++ b/proplot/wrappers.py @@ -1589,15 +1589,14 @@ def cycle_wrapper(self, func, *args, if not isinstance(loc, str): raise ValueError(f'Invalid on-the-fly location {loc!r}. Must be a preset location. See Axes.colorbar') if loc not in self._auto_colorbar: - self._auto_colorbar[loc] = [] - self._auto_colorbar_kw[loc] = {} - self._auto_colorbar[loc].extend(objs) + self._auto_colorbar[loc] = ([], {}) + self._auto_colorbar[loc][0].extend(objs) # Add keywords if loc != 'fill': colorbar_kw.setdefault('loc', loc) if label_leg: colorbar_kw.setdefault('label', label_leg) - self._auto_colorbar_kw[loc].update(colorbar_kw) + self._auto_colorbar[loc][1].update(colorbar_kw) if legend: # Add handles panel_kw.setdefault('mode', 'legend') @@ -1605,15 +1604,14 @@ def cycle_wrapper(self, func, *args, if not isinstance(loc, str): raise ValueError(f'Invalid on-the-fly location {loc!r}. Must be a preset location. See Axes.legend') if loc not in self._auto_legend: - self._auto_legend[loc] = [] - self._auto_legend_kw[loc] = {} - self._auto_legend[loc].extend(objs) + self._auto_legend[loc] = ([], {}) + self._auto_legend[loc][0].extend(objs) # Add keywords if loc != 'fill': legend_kw.setdefault('loc', loc) if label_leg: legend_kw.setdefault('label', label_leg) - self._auto_legend_kw[loc].update(legend_kw) + self._auto_legend[loc][1].update(legend_kw) # Return # WARNING: Make sure plot always returns tuple of objects, and bar always From 25934db4e01d7cca697ef89240f9850aa34fb079 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Tue, 3 Sep 2019 09:00:18 -0600 Subject: [PATCH 12/17] Comments --- proplot/subplots.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/proplot/subplots.py b/proplot/subplots.py index 1d2a772b4..7cec8679f 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -740,9 +740,6 @@ def __init__(self, @_counter def _add_axes_panel(self, ax, side, filled=False, **kwargs): """Hidden method that powers `~proplot.axes.panel_axes`.""" - # Redirect to main axes, e.g. if want to draw outer colorbar or legend - # from data plotted in a panel or inset axes - ax = ax._panel_parent or ax # Interpret args # NOTE: Axis sharing not implemented for figure panels, 99% of the # time this is just used as construct for adding global colorbars and @@ -750,6 +747,7 @@ def _add_axes_panel(self, ax, side, filled=False, **kwargs): s = side[0] if s not in 'lrbt': raise ValueError(f'Invalid side {side!r}.') + ax = ax._panel_parent or ax # redirect to main axes side = _side_translate[s] share, width, space, space_orig = _panels_kwargs(s, filled=filled, figure=False, **kwargs) @@ -793,7 +791,7 @@ def _add_axes_panel(self, ax, side, filled=False, **kwargs): def _add_figure_panel(self, side, span=None, **kwargs): """Adds figure panels. Also modifies the panel attribute stored on the figure to include these panels.""" - # Get default panel args + # Interpret args s = side[0] if s not in 'lrbt': raise ValueError(f'Invalid side {side!r}.') @@ -809,6 +807,7 @@ def _add_figure_panel(self, side, span=None, **kwargs): panels, nacross = subplots_kw['wpanels'], subplots_kw['nrows'] array = getattr(self, '_' + s + 'array') npanels, nalong = array.shape + # Check span array span = _notNone(span, (1, nalong)) if not np.iterable(span) or len(span)==1: @@ -819,11 +818,10 @@ def _add_figure_panel(self, side, span=None, **kwargs): raise ValueError(f'Invalid coordinates in span={span!r}. Coordinates must satisfy 1 <= c <= {nalong}.') start, stop = span[0] - 1, span[1] # zero-indexed - # See if there is room for panel on the figure + # See if there is room for panel in current figure panels # The 'array' is an array of boolean values, where each row corresponds - # to another figure panel, moving toward the outside, and - # We use add to panels toward the outside by appending rows to boolean - # arrays indicating which main axes rows and columns are occupied + # to another figure panel, moving toward the outside, and boolean + # True indicates the slot has been filled iratio = (-1 if s in 'lt' else nacross) # default vals for i in range(npanels): if not any(array[i,start:stop]): @@ -839,8 +837,7 @@ def _add_figure_panel(self, side, span=None, **kwargs): # npanels=2, i=1 --> iratio=nacross-1 iratio = nacross - (npanels - i) break - # Add to array, since we are adding another panel - if iratio in (-1, nacross): + if iratio in (-1, nacross): # add to array iarray = np.zeros((1, nalong), dtype=bool) iarray[0,start:stop] = True array = np.concatenate((array, iarray), axis=0) From 73dd02cfad66ceb38700f64a1e56990a479af603 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Tue, 3 Sep 2019 09:08:14 -0600 Subject: [PATCH 13/17] Better 'span' fig panel aliases --- proplot/subplots.py | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/proplot/subplots.py b/proplot/subplots.py index 7cec8679f..4217eca6d 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -788,16 +788,28 @@ def _add_axes_panel(self, ax, side, filled=False, **kwargs): return pax - def _add_figure_panel(self, side, span=None, **kwargs): + def _add_figure_panel(self, side, + span=None, row=None, col=None, rows=None, cols=None, + **kwargs): """Adds figure panels. Also modifies the panel attribute stored on the figure to include these panels.""" - # Interpret args + # Interpret args and enforce sensible keyword args s = side[0] if s not in 'lrbt': raise ValueError(f'Invalid side {side!r}.') side = _side_translate[s] _, width, space, space_orig = _panels_kwargs(s, filled=True, figure=True, **kwargs) + if s in 'lr': + for key,value in (('col',col),('cols',cols)): + if value is not None: + raise ValueError(f'Invalid keyword arg {key!r} for figure panel on side {side!r}.') + span = _notNone(span, row, rows, None, names=('span', 'row', 'rows')) + else: + for key,value in (('row',row),('rows',rows)): + if value is not None: + raise ValueError(f'Invalid keyword arg {key!r} for figure panel on side {side!r}.') + span = _notNone(span, col, cols, None, names=('span', 'col', 'cols')) # Get props subplots_kw = self._subplots_kw @@ -1397,8 +1409,10 @@ def colorbar(self, *args, top edge ``'t'``, ``'top'`` =========== ===================== - row, col, rows, cols : optional - Aliases for `span`. + row, rows : optional + Aliases for `span` for panels on the left or right side. + col, cols : optional + Aliases for `span` for panels on the top or bottom side. span : int or (int, int), optional Describes how the colorbar spans rows and columns of subplots. For example, ``fig.colorbar(loc='b', col=1)`` draws a colorbar @@ -1426,10 +1440,9 @@ def colorbar(self, *args, return kwargs.pop('ax').colorbar(*args, space=space, width=width, **kwargs) else: - span = _notNone(span, row, col, rows, cols, None, - names=('span', 'row', 'col', 'rows', 'cols')) ax = self._add_figure_panel(loc, - space=space, width=width, span=span) + space=space, width=width, span=span, + row=row, col=col, rows=rows, cols=cols) return ax.colorbar(*args, loc='_fill', **kwargs) def legend(self, *args, @@ -1455,8 +1468,10 @@ def legend(self, *args, top edge ``'t'``, ``'top'`` =========== ===================== - row, col, rows, cols : optional - Aliases for `span`. + row, rows : optional + Aliases for `span` for panels on the left or right side. + col, cols : optional + Aliases for `span` for panels on the top or bottom side. span : int or (int, int), optional Describes how the legend spans rows and columns of subplots. For example, ``fig.legend(loc='b', col=1)`` draws a legend @@ -1478,10 +1493,9 @@ def legend(self, *args, return kwargs.pop('ax').legend(*args, space=space, width=width, **kwargs) else: - span = _notNone(span, row, col, rows, cols, None, - names=('span', 'row', 'col', 'rows', 'cols')) ax = self._add_figure_panel(loc, - space=space, width=width, span=span) + space=space, width=width, span=span, + row=row, col=col, rows=rows, cols=cols) return ax.legend(*args, loc='_fill', **kwargs) @_counter From 7b6e4e0bdab5bcaba43610b1659446df2f2dcdce Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Thu, 5 Sep 2019 11:33:04 -0600 Subject: [PATCH 14/17] Misc bugfixes and cleanup --- proplot/axes.py | 12 ++++------ proplot/styletools.py | 2 +- proplot/subplots.py | 25 ++++++++++++++++---- proplot/wrappers.py | 54 +++++++++++++++++++++++++------------------ 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/proplot/axes.py b/proplot/axes.py index 1d01bcab4..ac1aa4a0b 100644 --- a/proplot/axes.py +++ b/proplot/axes.py @@ -119,8 +119,6 @@ def _abc(i): class Axes(maxes.Axes): """Lowest-level axes subclass. Handles titles and axis sharing. Adds several new methods and overrides existing ones.""" - name = 'base' - """The registered projection name.""" def __init__(self, *args, number=None, sharex=None, sharey=None, sharex_level=0, sharey_level=0, spanx=None, spany=None, alignx=None, aligny=None, @@ -2097,8 +2095,8 @@ def altx(self, *args, **kwargs): if self._altx_parent: raise ValueError('This *is* a twin axes!') with self.figure._unlock(): - ax = self._make_twin_axes(sharey=self, projection=self.name) - ax.set_autoscaley_on(self.get_autoscaley_on()) # shared axes must have matching autoscale + ax = self._make_twin_axes(sharey=self, projection='cartesian') + # ax.set_autoscaley_on(self.get_autoscaley_on()) # shared axes must have matching autoscale ax.grid(False) self._altx_child = ax ax._altx_parent = self @@ -2117,8 +2115,8 @@ def alty(self): if self._alty_parent: raise ValueError('This *is* a twin axes!') with self.figure._unlock(): - ax = self._make_twin_axes(sharex=self, projection=self.name) - ax.set_autoscalex_on(self.get_autoscalex_on()) # shared axes must have matching autoscale + ax = self._make_twin_axes(sharex=self, projection='cartesian') + # ax.set_autoscalex_on(self.get_autoscalex_on()) # shared axes must have matching autoscale ax.grid(False) self._alty_child = ax ax._alty_parent = self @@ -3216,7 +3214,7 @@ def format(self, *, patch_kw=None, **kwargs): BasemapAxes.format.__doc__ = _projection_format_docstring # Register the projections -mproj.register_projection(Axes) +# TODO: Remove BasemapAxes!!! Cartopy will support gridline labels soon. mproj.register_projection(PolarAxes) mproj.register_projection(CartesianAxes) mproj.register_projection(BasemapAxes) diff --git a/proplot/styletools.py b/proplot/styletools.py index 42571da1f..57f69b639 100644 --- a/proplot/styletools.py +++ b/proplot/styletools.py @@ -1600,7 +1600,7 @@ def monochrome_cmap(color, fade, reverse=False, space='hsl', name='no_name', **k #-----------------------------------------------------------------------------# # Return arbitrary normalizer #-----------------------------------------------------------------------------# -def Norm(norm, levels=None, values=None, **kwargs): +def Norm(norm, levels=None, **kwargs): """ Returns an arbitrary `~matplotlib.colors.Normalize` instance, used to interpret the `norm` and `norm_kw` arguments when passed to any plotting diff --git a/proplot/subplots.py b/proplot/subplots.py index 4217eca6d..44ab249db 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -237,6 +237,23 @@ def axes_grid_iterator(*args, **kwargs): # Mixed raise AttributeError(f'Found mixed types for attribute {attr!r}.') + # TODO! No more putting panels, legends, colorbars on the SubplotSpec, + # put them in the margin and increase default space + # def colorbar(self, loc=None): + # """Draws a colorbar that spans axes in the selected range.""" + # for ax in self: + # pass + # + # def legend(self, loc=None): + # """Draws a legend that spans axes in the selected range.""" + # for ax in self: + # pass + # + # def text(self, loc=None): + # """Draws text that spans axes in the selected range.""" + # for ax in self: + # pass + #-----------------------------------------------------------------------------# # Gridspec classes #-----------------------------------------------------------------------------# @@ -1307,15 +1324,15 @@ def _update_axislabels(self, axis=None, **kwargs): return # Update label on this axes axis.label.update(kwargs) + kwargs.pop('color', None) # Defer to parent (main) axes if possible, then get the axes # shared by that parent # TODO: Share panels in successive stacks, but share short axes # just like sharing long axes ax = axis.axes - sax = getattr(ax, '_share' + x, None) - while isinstance(ax, axes.PanelAxes) and sax is not None: - ax, sax = sax, getattr(sax, '_share' + x, None) + ax = ax._panel_parent or ax + ax = getattr(ax, '_share' + x) or ax # Apply to spanning axes and their panels axs = [ax] @@ -1325,7 +1342,7 @@ def _update_axislabels(self, axis=None, **kwargs): axs = ax._get_side_axes(s) for ax in axs: getattr(ax, x + 'axis').label.update(kwargs) # apply to main axes - pax = getattr(ax, '_share' + x, None) + pax = getattr(ax, '_share' + x) if pax is not None: # apply to panel? getattr(pax, x + 'axis').label.update(kwargs) diff --git a/proplot/wrappers.py b/proplot/wrappers.py index b70ced6f4..e643a65ac 100644 --- a/proplot/wrappers.py +++ b/proplot/wrappers.py @@ -90,6 +90,7 @@ def _load_objects(): _map_disabled_methods = ( # These are obvious # TODO: Error bars? Will they work? Also bar and barh can be used w/ polar + 'twinx', 'twiny', 'matshow', 'imshow', 'spy', # don't disable 'bar' or 'barh', can be used in polar plots 'hist', 'hist2d', 'boxplot', 'violinplot', 'step', 'stem', # Look into these @@ -1629,7 +1630,7 @@ def cycle_wrapper(self, func, *args, @_expand_methods_list def cmap_wrapper(self, func, *args, cmap=None, cmap_kw=None, extend='neither', norm=None, norm_kw=None, - N=None, levels=None, values=None, vmin=None, vmax=None, + N=None, levels=None, values=None, centers=None, vmin=None, vmax=None, locator=None, symmetric=False, locator_kw=None, edgefix=None, labels=False, labels_kw=None, fmt=None, precision=2, colorbar=False, colorbar_kw=None, panel_kw=None, @@ -1659,7 +1660,7 @@ def cmap_wrapper(self, func, *args, cmap=None, cmap_kw=None, extend : {'neither', 'min', 'max', 'both'}, optional Where to assign unique colors to out-of-bounds data and draw "extensions" (triangles, by default) on the colorbar. - N, levels : int or list of float, optional + levels, N : int or list of float, optional The number of level edges, or a list of level edges. If the former, `locator` is used to generate this many levels at "nice" intervals. Defaults to ``rc['image.levels']``. @@ -1668,7 +1669,7 @@ def cmap_wrapper(self, func, *args, cmap=None, cmap_kw=None, `~matplotlib.axes.Axes.pcolormesh`, this means they now accept the `levels` keyword arg. You can now discretize your colors in a ``pcolor`` plot just like with ``contourf``. - values : int or list of float, optional + values, centers : int or list of float, optional The number of level centers, or a list of level centers. If provided, levels are inferred using `~proplot.utils.edges`. This will override any `levels` input. @@ -1771,14 +1772,16 @@ def cmap_wrapper(self, func, *args, cmap=None, cmap_kw=None, labels_kw = labels_kw or {} colorbar_kw = colorbar_kw or {} panel_kw = panel_kw or {} - vmin = _notNone(vmin, norm_kw.pop('vmin', None)) - vmax = _notNone(vmax, norm_kw.pop('vmax', None)) # Parse args # Disable edgefix=True for certain keyword combos e.g. if user wants # white lines around their pcolor mesh. name = func.__name__ if not args: return func(*args, **kwargs) + vmin = _notNone(vmin, norm_kw.pop('vmin', None), None, names=('vmin', 'norm_kw={"vmin":value}')) + vmax = _notNone(vmax, norm_kw.pop('vmax', None), None, names=('vmax', 'norm_kw={"vmax":value}')) + levels = _notNone(N, levels, norm_kw.pop('levels', None), rc['image.levels'], names=('N', 'levels', 'norm_kw={"levels":value}')) + values = _notNone(values, centers, None, names=('values', 'centers')) colors = _notNone(color, colors, edgecolor, edgecolors, None, names=('color', 'colors', 'edgecolor', 'edgecolors')) linewidths = _notNone(lw, linewidth, linewidths, None, names=('lw', 'linewidth', 'linewidths')) linestyles = _notNone(ls, linestyle, linestyles, None, names=('ls', 'linestyle', 'linestyles')) @@ -1798,29 +1801,34 @@ def cmap_wrapper(self, func, *args, cmap=None, cmap_kw=None, for key,val in (('levels',levels),('values',values)): if not np.iterable(val): continue + if 'contour' in name and 'contourf' not in name: + continue if len(val) < 2 or any(np.diff(val) <= 0): raise ValueError(f'{key!r} must be monotonically increasing and at least length 2, got {val}.') # Get level edges from level centers - # Make sure values are *averages* of encompassing levels, so that tick - # marks appear in the center of the colorbar level. if values is not None: if isinstance(values, Number): levels = values + 1 elif np.iterable(values): + # Plotting command accepts a 'values' keyword arg if name in ('cmapline',): kwargs['values'] = values + # Try to generate levels such that a LinearSegmentedNorm will + # place values ticks right at the center of each colorbar level if norm is None or norm in ('segments','segmented'): levels = [values[0] - (values[1]-values[0])/2] # reasonable starting point for i,val in enumerate(values): levels.append(2*val - levels[-1]) if any(np.diff(levels) <= 0): # algorithm failed, default to this levels = utils.edges(values) + # Generate levels by finding in-between points in the + # normalized numeric space else: - norm_tmp = styletools.Norm(norm, **norm_kw) - levels = norm_tmp.inverse(utils.edges(norm_tmp(values))) + inorm = styletools.Norm(norm, **norm_kw) + levels = inorm.inverse(utils.edges(inorm(values))) else: - raise ValueError('Unexpected values input {values!r}. Must be integer or list of numbers.') + raise ValueError(f'Unexpected input values={values!r}. Must be integer or list of numbers.') # Data limits used for normalizer Z = ma.masked_invalid(args[-1], copy=False) @@ -1832,7 +1840,7 @@ def cmap_wrapper(self, func, *args, cmap=None, cmap_kw=None, zmin, zmax = 0, 1 # Input colormap, for methods that accept a colormap and normalizer - if not name[-7:] == 'contour': # contour, tricontour, i.e. not a method where cmap is optional + if not ('contour' in name and 'contourf' not in name): # contour, tricontour, i.e. not a method where cmap is optional cmap = _notNone(cmap, rc['image.cmap']) if cmap is not None: # Get colormap object @@ -1845,22 +1853,22 @@ def cmap_wrapper(self, func, *args, cmap=None, cmap_kw=None, # Get default normalizer # Only use LinearSegmentedNorm if necessary, because it is slow - if norm is None and name not in ('hexbin',): - if not np.iterable(levels): - norm = 'linear' - else: - diff = np.diff(levels) - eps = diff.mean()/1e3 - if (np.abs(np.diff(diff)) >= eps).any(): - norm = 'segmented' - else: + if name not in ('hexbin',): + if norm is None: + if not np.iterable(levels) or len(levels) == 1: norm = 'linear' - if norm is not None: - norm = styletools.Norm(norm, levels=levels, **norm_kw) + else: + diff = np.diff(levels) + eps = diff.mean()/1e3 + if (np.abs(np.diff(diff)) >= eps).any(): + norm = 'segmented' + norm_kw.setdefault('levels', levels) + else: + norm = 'linear' + norm = styletools.Norm(norm, **norm_kw) # Get default levels # TODO: Add kernel density plot to hexbin! - levels = _notNone(N, levels, rc['image.levels'], names=('N', 'levels')) if isinstance(levels, Number): # Cannot infer counts a priori, so do nothing if name in ('hexbin',): From b8df9e1f05072cf9549753fb152027ceaaabc705 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Thu, 5 Sep 2019 11:53:56 -0600 Subject: [PATCH 15/17] Panels bugfix --- proplot/axes.py | 2 +- proplot/subplots.py | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/proplot/axes.py b/proplot/axes.py index ac1aa4a0b..0daf52bfe 100644 --- a/proplot/axes.py +++ b/proplot/axes.py @@ -1083,7 +1083,7 @@ def colorbar(self, *args, loc=None, pad=None, warnings.warn(f'Orientation for inset colorbars must be horizontal, ignoring orient={orient!r}.') ticklocation = kwargs.pop('tickloc', None) ticklocation = kwargs.pop('ticklocation', None) or ticklocation - if any(loc is not None and ticklocation != 'bottom'): + if ticklocation is not None and ticklocation != 'bottom': warnings.warn(f'Inset colorbars can only have ticks on the bottom.') kwargs.update({'orientation':'horizontal', 'ticklocation':'bottom'}) kwargs.setdefault('maxn', 5) diff --git a/proplot/subplots.py b/proplot/subplots.py index 44ab249db..c8be29cc1 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -775,13 +775,13 @@ def _add_axes_panel(self, ax, side, filled=False, **kwargs): pgrid = getattr(ax, '_' + s + 'panels') offset = (len(pgrid)*bool(pgrid)) + 1 if s in 'lr': + iratio = (col1 - offset if s == 'l' else col2 + offset) idx1 = slice(row1, row2 + 1) - idx2 = (col1 - offset if s == 'l' else col2 + offset) - iratio = idx2 + idx2 = max(0, iratio) else: - idx1 = (row1 - offset if s == 't' else row2 + offset) + iratio = (row1 - offset if s == 't' else row2 + offset) + idx1 = max(0, iratio) idx2 = slice(col1, col2 + 1) - iratio = idx1 gridspec = self._insert_row_column(side, iratio, width, space, space_orig, figure=False, ) @@ -995,7 +995,9 @@ def _adjust_tight_layout(self, renderer): idx1, = np.where(filt & filt1) idx2, = np.where(filt & filt2) if idx1.size > 1 or idx2.size > 2: - raise RuntimeError('This should never happen.') + warnings.warn('This should never happen.') + continue + # raise RuntimeError('This should never happen.') elif not idx1.size or not idx2.size: continue idx1, idx2 = idx1[0], idx2[0] From c67c3f847231079c8e9ea16670996bc8da5e097e Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Thu, 5 Sep 2019 11:54:59 -0600 Subject: [PATCH 16/17] Remove print statement --- proplot/wrappers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/proplot/wrappers.py b/proplot/wrappers.py index e643a65ac..b6b11fd09 100644 --- a/proplot/wrappers.py +++ b/proplot/wrappers.py @@ -1513,7 +1513,6 @@ def cycle_wrapper(self, func, *args, objs = [] ncols = 1 label_leg = None # for colorbar or legend - print('hi!!!', values, labels) labels = _notNone(values, labels, label, None, names=('values', 'labels', 'label')) stacked = kwargs.pop('stacked', False) if name in ('pie','boxplot','violinplot'): From 5643867879c962d5c3dbc1242438af75919590b1 Mon Sep 17 00:00:00 2001 From: Luke Davis Date: Thu, 5 Sep 2019 14:35:48 -0600 Subject: [PATCH 17/17] Update documentation to reflect new colorbars, legends usage, add inset axes and panel axes demos to Cartesian axes section, misc panel bugfixes --- docs/tutorial/tutorial_100_0.svg | 2852 - docs/tutorial/tutorial_101_0.svg | 5655 + docs/tutorial/tutorial_104_0.svg | 2043 + docs/tutorial/tutorial_105_0.svg | 7221 +- ...{tutorial_110_0.svg => tutorial_106_0.svg} | 0 docs/tutorial/tutorial_108_0.svg | 20558 +- docs/tutorial/tutorial_109_0.svg | 2002 - docs/tutorial/tutorial_112_0.svg | 28153 +- ...{tutorial_116_1.svg => tutorial_112_1.svg} | 0 ...{tutorial_118_0.svg => tutorial_114_0.svg} | 0 ...{tutorial_118_1.svg => tutorial_114_1.svg} | 0 docs/tutorial/tutorial_116_0.svg | 9760 - docs/tutorial/tutorial_117_1.svg | 193147 +++++++++++++ ...{tutorial_123_0.svg => tutorial_119_0.svg} | 0 ...{tutorial_123_1.svg => tutorial_119_1.svg} | 0 ...{tutorial_125_0.svg => tutorial_121_0.svg} | 0 docs/tutorial/tutorial_121_1.svg | 210959 ++------------- docs/tutorial/tutorial_125_1.svg | 19176 -- ...{tutorial_130_0.svg => tutorial_126_0.svg} | 0 docs/tutorial/tutorial_129_0.svg | 2613 + docs/tutorial/tutorial_12_0.svg | 5551 + docs/tutorial/tutorial_12_1.svg | 3574 - ...{tutorial_135_0.svg => tutorial_131_0.svg} | 0 docs/tutorial/tutorial_133_0.svg | 2787 +- docs/tutorial/tutorial_137_0.svg | 1548 - ...{tutorial_142_0.svg => tutorial_138_0.svg} | 0 ...{tutorial_142_1.svg => tutorial_138_1.svg} | 0 ...{tutorial_145_0.svg => tutorial_141_0.svg} | 0 ...{tutorial_148_0.svg => tutorial_144_0.svg} | 0 ...{tutorial_153_0.svg => tutorial_149_0.svg} | 0 docs/tutorial/tutorial_14_0.svg | 4188 + docs/tutorial/tutorial_150_0.svg | 12617 + ...{tutorial_156_0.svg => tutorial_152_0.svg} | 0 docs/tutorial/tutorial_154_0.svg | 16962 +- ...{tutorial_161_0.svg => tutorial_157_0.svg} | 0 ...{tutorial_161_1.svg => tutorial_157_1.svg} | 0 docs/tutorial/tutorial_158_0.svg | 6313 - docs/tutorial/tutorial_15_0.svg | 7190 + ...{tutorial_164_0.svg => tutorial_160_0.svg} | 0 ...{tutorial_167_0.svg => tutorial_163_0.svg} | 0 ...{tutorial_170_0.svg => tutorial_166_0.svg} | 0 ...{tutorial_173_1.svg => tutorial_169_1.svg} | 0 docs/tutorial/tutorial_16_2.svg | 3622 - ...{tutorial_176_1.svg => tutorial_172_1.svg} | 0 ...{tutorial_179_0.svg => tutorial_175_0.svg} | 0 .../{tutorial_88_1.svg => tutorial_17_1.svg} | 24840 +- docs/tutorial/tutorial_18_0.svg | 15698 +- docs/tutorial/tutorial_21_0.svg | 8830 +- docs/tutorial/tutorial_22_0.svg | 6561 +- docs/tutorial/tutorial_26_0.svg | 6927 - docs/tutorial/tutorial_26_2.svg | 8417 +- docs/tutorial/tutorial_28_0.svg | 6331 + docs/tutorial/tutorial_29_0.svg | 9124 - docs/tutorial/tutorial_31_0.svg | 5321 + .../{tutorial_21_1.svg => tutorial_31_1.svg} | 0 docs/tutorial/tutorial_32_0.svg | 5318 +- .../{tutorial_23_0.svg => tutorial_33_0.svg} | 0 docs/tutorial/tutorial_36_0.svg | 9118 +- .../{tutorial_26_1.svg => tutorial_36_1.svg} | 0 docs/tutorial/tutorial_36_2.svg | 6733 + .../{tutorial_26_3.svg => tutorial_36_3.svg} | 0 .../{tutorial_27_0.svg => tutorial_37_0.svg} | 0 .../{tutorial_27_1.svg => tutorial_37_1.svg} | 0 docs/tutorial/tutorial_39_0.svg | 11516 +- docs/tutorial/tutorial_42_0.svg | 2000 + docs/tutorial/tutorial_44_1.svg | 5333 +- docs/tutorial/tutorial_48_0.svg | 4111 + docs/tutorial/tutorial_49_0.svg | 5195 - docs/tutorial/tutorial_51_0.svg | 2714 + .../{tutorial_41_0.svg => tutorial_53_0.svg} | 0 docs/tutorial/tutorial_56_1.svg | 3239 + .../{tutorial_45_0.svg => tutorial_57_0.svg} | 0 .../{tutorial_46_0.svg => tutorial_58_0.svg} | 0 docs/tutorial/tutorial_61_0.svg | 15243 +- docs/tutorial/tutorial_64_0.svg | 62617 ----- .../{tutorial_52_1.svg => tutorial_64_1.svg} | 0 .../{tutorial_52_2.svg => tutorial_64_2.svg} | 0 .../{tutorial_53_1.svg => tutorial_65_1.svg} | 0 docs/tutorial/tutorial_67_0.svg | 10409 +- docs/tutorial/tutorial_68_0.svg | 13254 + docs/tutorial/tutorial_70_0.svg | 3476 + .../{tutorial_58_3.svg => tutorial_75_3.svg} | 0 .../{tutorial_58_4.svg => tutorial_75_4.svg} | 0 docs/tutorial/tutorial_78_0.svg | 10708 + .../{tutorial_61_1.svg => tutorial_78_1.svg} | 0 docs/tutorial/tutorial_81_0.svg | 66922 ++++- docs/tutorial/tutorial_84_0.svg | 10668 +- .../{tutorial_67_1.svg => tutorial_84_1.svg} | 0 docs/tutorial/tutorial_85_0.svg | 10979 - .../{tutorial_70_1.svg => tutorial_87_1.svg} | 0 docs/tutorial/tutorial_89_0.svg | 11768 - .../{tutorial_73_0.svg => tutorial_90_0.svg} | 0 docs/tutorial/tutorial_91_0.svg | 8595 - .../{tutorial_76_0.svg => tutorial_93_0.svg} | 0 docs/tutorial/tutorial_94_0.svg | 3839 - docs/tutorial/tutorial_96_0.svg | 4613 + docs/tutorial/tutorial_97_0.svg | 5338 - docs/tutorial1.rst | 341 +- docs/tutorial2.rst | 108 +- docs/tutorial3.rst | 91 +- docs/tutorial4.rst | 808 +- docs/tutorial5.rst | 832 +- proplot/axes.py | 27 +- proplot/styletools.py | 4 + proplot/subplots.py | 30 +- 105 files changed, 483279 insertions(+), 473208 deletions(-) delete mode 100644 docs/tutorial/tutorial_100_0.svg create mode 100644 docs/tutorial/tutorial_101_0.svg create mode 100644 docs/tutorial/tutorial_104_0.svg rename docs/tutorial/{tutorial_110_0.svg => tutorial_106_0.svg} (100%) delete mode 100644 docs/tutorial/tutorial_109_0.svg rename docs/tutorial/{tutorial_116_1.svg => tutorial_112_1.svg} (100%) rename docs/tutorial/{tutorial_118_0.svg => tutorial_114_0.svg} (100%) rename docs/tutorial/{tutorial_118_1.svg => tutorial_114_1.svg} (100%) delete mode 100644 docs/tutorial/tutorial_116_0.svg create mode 100644 docs/tutorial/tutorial_117_1.svg rename docs/tutorial/{tutorial_123_0.svg => tutorial_119_0.svg} (100%) rename docs/tutorial/{tutorial_123_1.svg => tutorial_119_1.svg} (100%) rename docs/tutorial/{tutorial_125_0.svg => tutorial_121_0.svg} (100%) delete mode 100644 docs/tutorial/tutorial_125_1.svg rename docs/tutorial/{tutorial_130_0.svg => tutorial_126_0.svg} (100%) create mode 100644 docs/tutorial/tutorial_129_0.svg create mode 100644 docs/tutorial/tutorial_12_0.svg delete mode 100644 docs/tutorial/tutorial_12_1.svg rename docs/tutorial/{tutorial_135_0.svg => tutorial_131_0.svg} (100%) delete mode 100644 docs/tutorial/tutorial_137_0.svg rename docs/tutorial/{tutorial_142_0.svg => tutorial_138_0.svg} (100%) rename docs/tutorial/{tutorial_142_1.svg => tutorial_138_1.svg} (100%) rename docs/tutorial/{tutorial_145_0.svg => tutorial_141_0.svg} (100%) rename docs/tutorial/{tutorial_148_0.svg => tutorial_144_0.svg} (100%) rename docs/tutorial/{tutorial_153_0.svg => tutorial_149_0.svg} (100%) create mode 100644 docs/tutorial/tutorial_14_0.svg create mode 100644 docs/tutorial/tutorial_150_0.svg rename docs/tutorial/{tutorial_156_0.svg => tutorial_152_0.svg} (100%) rename docs/tutorial/{tutorial_161_0.svg => tutorial_157_0.svg} (100%) rename docs/tutorial/{tutorial_161_1.svg => tutorial_157_1.svg} (100%) delete mode 100644 docs/tutorial/tutorial_158_0.svg create mode 100644 docs/tutorial/tutorial_15_0.svg rename docs/tutorial/{tutorial_164_0.svg => tutorial_160_0.svg} (100%) rename docs/tutorial/{tutorial_167_0.svg => tutorial_163_0.svg} (100%) rename docs/tutorial/{tutorial_170_0.svg => tutorial_166_0.svg} (100%) rename docs/tutorial/{tutorial_173_1.svg => tutorial_169_1.svg} (100%) delete mode 100644 docs/tutorial/tutorial_16_2.svg rename docs/tutorial/{tutorial_176_1.svg => tutorial_172_1.svg} (100%) rename docs/tutorial/{tutorial_179_0.svg => tutorial_175_0.svg} (100%) rename docs/tutorial/{tutorial_88_1.svg => tutorial_17_1.svg} (58%) delete mode 100644 docs/tutorial/tutorial_26_0.svg create mode 100644 docs/tutorial/tutorial_28_0.svg delete mode 100644 docs/tutorial/tutorial_29_0.svg create mode 100644 docs/tutorial/tutorial_31_0.svg rename docs/tutorial/{tutorial_21_1.svg => tutorial_31_1.svg} (100%) rename docs/tutorial/{tutorial_23_0.svg => tutorial_33_0.svg} (100%) rename docs/tutorial/{tutorial_26_1.svg => tutorial_36_1.svg} (100%) create mode 100644 docs/tutorial/tutorial_36_2.svg rename docs/tutorial/{tutorial_26_3.svg => tutorial_36_3.svg} (100%) rename docs/tutorial/{tutorial_27_0.svg => tutorial_37_0.svg} (100%) rename docs/tutorial/{tutorial_27_1.svg => tutorial_37_1.svg} (100%) create mode 100644 docs/tutorial/tutorial_42_0.svg create mode 100644 docs/tutorial/tutorial_48_0.svg delete mode 100644 docs/tutorial/tutorial_49_0.svg create mode 100644 docs/tutorial/tutorial_51_0.svg rename docs/tutorial/{tutorial_41_0.svg => tutorial_53_0.svg} (100%) create mode 100644 docs/tutorial/tutorial_56_1.svg rename docs/tutorial/{tutorial_45_0.svg => tutorial_57_0.svg} (100%) rename docs/tutorial/{tutorial_46_0.svg => tutorial_58_0.svg} (100%) delete mode 100644 docs/tutorial/tutorial_64_0.svg rename docs/tutorial/{tutorial_52_1.svg => tutorial_64_1.svg} (100%) rename docs/tutorial/{tutorial_52_2.svg => tutorial_64_2.svg} (100%) rename docs/tutorial/{tutorial_53_1.svg => tutorial_65_1.svg} (100%) create mode 100644 docs/tutorial/tutorial_68_0.svg create mode 100644 docs/tutorial/tutorial_70_0.svg rename docs/tutorial/{tutorial_58_3.svg => tutorial_75_3.svg} (100%) rename docs/tutorial/{tutorial_58_4.svg => tutorial_75_4.svg} (100%) create mode 100644 docs/tutorial/tutorial_78_0.svg rename docs/tutorial/{tutorial_61_1.svg => tutorial_78_1.svg} (100%) rename docs/tutorial/{tutorial_67_1.svg => tutorial_84_1.svg} (100%) delete mode 100644 docs/tutorial/tutorial_85_0.svg rename docs/tutorial/{tutorial_70_1.svg => tutorial_87_1.svg} (100%) delete mode 100644 docs/tutorial/tutorial_89_0.svg rename docs/tutorial/{tutorial_73_0.svg => tutorial_90_0.svg} (100%) delete mode 100644 docs/tutorial/tutorial_91_0.svg rename docs/tutorial/{tutorial_76_0.svg => tutorial_93_0.svg} (100%) delete mode 100644 docs/tutorial/tutorial_94_0.svg create mode 100644 docs/tutorial/tutorial_96_0.svg delete mode 100644 docs/tutorial/tutorial_97_0.svg diff --git a/docs/tutorial/tutorial_100_0.svg b/docs/tutorial/tutorial_100_0.svg deleted file mode 100644 index a8db8a46b..000000000 --- a/docs/tutorial/tutorial_100_0.svg +++ /dev/null @@ -1,2852 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_101_0.svg b/docs/tutorial/tutorial_101_0.svg new file mode 100644 index 000000000..2398210ea --- /dev/null +++ b/docs/tutorial/tutorial_101_0.svg @@ -0,0 +1,5655 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_104_0.svg b/docs/tutorial/tutorial_104_0.svg new file mode 100644 index 000000000..66f47be80 --- /dev/null +++ b/docs/tutorial/tutorial_104_0.svg @@ -0,0 +1,2043 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_105_0.svg b/docs/tutorial/tutorial_105_0.svg index 2398210ea..2b579f8e8 100644 --- a/docs/tutorial/tutorial_105_0.svg +++ b/docs/tutorial/tutorial_105_0.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - +" id="HelveticaNeue-52"/> + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - + - - - - - - - - - - + +" id="HelveticaNeue-101"/> - - - - - - - - - - + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" id="HelveticaNeue-108"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - + - - - - - - - - - - + +" id="HelveticaNeue-99"/> - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - + + - - + + - - - - - - - - - - - - - - - - - + + + + + - - + + - + + +" id="HelveticaNeue-Bold-76"/> - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - + + - - - - - - - - - - - + + - - + + - - - + - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - + + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + diff --git a/docs/tutorial/tutorial_110_0.svg b/docs/tutorial/tutorial_106_0.svg similarity index 100% rename from docs/tutorial/tutorial_110_0.svg rename to docs/tutorial/tutorial_106_0.svg diff --git a/docs/tutorial/tutorial_108_0.svg b/docs/tutorial/tutorial_108_0.svg index 66f47be80..744278a5a 100644 --- a/docs/tutorial/tutorial_108_0.svg +++ b/docs/tutorial/tutorial_108_0.svg @@ -2,7 +2,7 @@ - + - - +" style="fill:#ffffff;"/> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - + + + + + - - + + - + - - - - - - - - + + + + + - - + + - + - - - - - - - - - - + + + + + - - + + - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +" id="HelveticaNeue-53"/> - + - - + - - + + + + + + + + + + + + + + + + + + + + + + - + - - + + - +" id="HelveticaNeue-55"/> - - - - + + + - - + + + + + - + - - - - - + + + + + - - - - + + + + + + + + + + + + + + + - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - +" id="Cc_0_3fcb00da59"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + - - - - - - - - - - - - - + + - - - - - - - - - - - - - + + - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + - - - +" id="Ce_0_4cc32e6a5b"/> - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + - + - - - - + + + + + + + + - - + + + + + - + - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + - - + + - + - - - - - - + + + + + - - + + - + - - - - - - + + + + + - - + + - + - - - - - - + + + + + - - + + - + - - - - - - + + + + + + + + + + + + - - - - - - + + + + + - - - - + + - + - - - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - + + + - - + + + + + - + - - - + + + + - - + + + + + - + - - - + + + + - - + + + + + - + - - - + + + + - - + + + + + - + - - - + + + - - - - - - - - - - - - - - - - - + - - + - - + - - + - - + + - + - - - - + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" id="HelveticaNeue-Bold-116"/> + + + + + + + + - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + - - + + + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + + - - + + - - + + + + - + + +" id="HelveticaNeue-Bold-112"/> - - - - - + - + - - - - - - - +" id="HelveticaNeue-Bold-104"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + - - + + - - + + diff --git a/docs/tutorial/tutorial_109_0.svg b/docs/tutorial/tutorial_109_0.svg deleted file mode 100644 index 2b579f8e8..000000000 --- a/docs/tutorial/tutorial_109_0.svg +++ /dev/null @@ -1,2002 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_112_0.svg b/docs/tutorial/tutorial_112_0.svg index 744278a5a..7dbbb3e95 100644 --- a/docs/tutorial/tutorial_112_0.svg +++ b/docs/tutorial/tutorial_112_0.svg @@ -2,7 +2,7 @@ - + - - - +" id="md65e955135" style="stroke:#8f1402;stroke-width:0.4;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - +" id="m5487defcd9" style="stroke:#d62305;stroke-width:0.4;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - +" id="m47f37074cc" style="stroke:#fe645c;stroke-width:0.4;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - + + + + + + + - - + + + + + + + - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - + - + - + + + - + + + + - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" style="fill:#d2c9c5;stroke:#d2c9c5;stroke-width:0.4;"/> + + + + + + + + + + + + + + + + + + + + - - - - - - + + - + - + +" id="HelveticaNeue-114"/> - - - - - - - - - - - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + +M 38.40625 26.703125 +Q 36.796875 25.5 34.1875 24.953125 +Q 31.59375 24.40625 28.75 24.046875 +Q 25.90625 23.703125 23 23.25 +Q 20.09375 22.796875 17.796875 21.796875 +Q 15.5 20.796875 14.046875 18.9375 +Q 12.59375 17.09375 12.59375 13.90625 +Q 12.59375 11.796875 13.4375 10.34375 +Q 14.296875 8.90625 15.640625 8 +Q 17 7.09375 18.796875 6.6875 +Q 20.59375 6.296875 22.59375 6.296875 +Q 26.796875 6.296875 29.796875 7.4375 +Q 32.796875 8.59375 34.6875 10.34375 +Q 36.59375 12.09375 37.5 14.140625 +Q 38.40625 16.203125 38.40625 18 +z +" id="HelveticaNeue-97"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +L 28.5 0 +L 28.5 7.5 +L 24.59375 7.5 +Q 22.59375 7.5 21.34375 7.640625 +Q 20.09375 7.796875 19.390625 8.296875 +Q 18.703125 8.796875 18.453125 9.6875 +Q 18.203125 10.59375 18.203125 12.09375 +L 18.203125 44.203125 +L 28.5 44.203125 +L 28.5 51.703125 +z +" id="HelveticaNeue-116"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + - - + + + + + + + - +" id="md28a8308b0" style="stroke:#bde1ff;stroke-width:0.4;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - + - - + - - + + - +" id="HelveticaNeue-79"/> - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - +" id="HelveticaNeue-Bold-101"/> + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + - - + + diff --git a/docs/tutorial/tutorial_116_1.svg b/docs/tutorial/tutorial_112_1.svg similarity index 100% rename from docs/tutorial/tutorial_116_1.svg rename to docs/tutorial/tutorial_112_1.svg diff --git a/docs/tutorial/tutorial_118_0.svg b/docs/tutorial/tutorial_114_0.svg similarity index 100% rename from docs/tutorial/tutorial_118_0.svg rename to docs/tutorial/tutorial_114_0.svg diff --git a/docs/tutorial/tutorial_118_1.svg b/docs/tutorial/tutorial_114_1.svg similarity index 100% rename from docs/tutorial/tutorial_118_1.svg rename to docs/tutorial/tutorial_114_1.svg diff --git a/docs/tutorial/tutorial_116_0.svg b/docs/tutorial/tutorial_116_0.svg deleted file mode 100644 index 7dbbb3e95..000000000 --- a/docs/tutorial/tutorial_116_0.svg +++ /dev/null @@ -1,9760 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_117_1.svg b/docs/tutorial/tutorial_117_1.svg new file mode 100644 index 000000000..09e6284fd --- /dev/null +++ b/docs/tutorial/tutorial_117_1.svg @@ -0,0 +1,193147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_123_0.svg b/docs/tutorial/tutorial_119_0.svg similarity index 100% rename from docs/tutorial/tutorial_123_0.svg rename to docs/tutorial/tutorial_119_0.svg diff --git a/docs/tutorial/tutorial_123_1.svg b/docs/tutorial/tutorial_119_1.svg similarity index 100% rename from docs/tutorial/tutorial_123_1.svg rename to docs/tutorial/tutorial_119_1.svg diff --git a/docs/tutorial/tutorial_125_0.svg b/docs/tutorial/tutorial_121_0.svg similarity index 100% rename from docs/tutorial/tutorial_125_0.svg rename to docs/tutorial/tutorial_121_0.svg diff --git a/docs/tutorial/tutorial_121_1.svg b/docs/tutorial/tutorial_121_1.svg index 09e6284fd..8b60bc872 100644 --- a/docs/tutorial/tutorial_121_1.svg +++ b/docs/tutorial/tutorial_121_1.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - + + + + + + + + + + + +" style="fill:#ffffff;"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +" id="HelveticaNeue-52"/> + + + + + + + - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - + + - - - - - - - - - - - - + + - - - - - - - - - - - + + - - - - - - - - - - + + + - + - - - + - + - - + - + - +" id="HelveticaNeue-97"/> + + + + + + + + + - - - - - + + +" style="fill:#ffffff;"/> - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + - - + + + + + + + + + + + + + - + - - + + + + + - + - - + + + + + - + - - + + + + + - + - - + + + + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - + - - - - - + + - - - - - - - - + - - - - - + + - - - - - - - - + - - - - - + + - - - - - - - - + - - - - - + + - - - - - - - - + - - - - - + + - + - - + + - + - - + + - + - - + + - + + + - + + + + - + - - - - - + + + + - - + + + + + - + - - - - - + + + + + - - + + + + + - + - - - - - + + + + + - - + + + + + - + - - - - - + + + + + - - + + + + + - + - - - - - + + + + + - - + + + + + - + - - - - - - - - - - - - - + + + + + + + - - + - - + - - + - - + - - + + - + +" id="HelveticaNeue-105"/> - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - + - + - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + - - - - - - - - - - - + + - - - - - - - - - - - + + - - - - - - - + + - - - + + + - + + - + +" id="HelveticaNeue-116"/> + + + + + + + + + + + + + + + + + - - - + + +" style="fill:#ffffff;"/> - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - - - - - + + - - - - - - - - - - - + + - - - - - - - - - + + - - - - - - - - - + + - - - - - - - - - + + - - - - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - + + - - - - - - - + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" id="HelveticaNeue-Bold-110"/> + - - +" id="HelveticaNeue-Bold-114"/> + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + - - + + - - + + - - + + diff --git a/docs/tutorial/tutorial_125_1.svg b/docs/tutorial/tutorial_125_1.svg deleted file mode 100644 index 8b60bc872..000000000 --- a/docs/tutorial/tutorial_125_1.svg +++ /dev/null @@ -1,19176 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_130_0.svg b/docs/tutorial/tutorial_126_0.svg similarity index 100% rename from docs/tutorial/tutorial_130_0.svg rename to docs/tutorial/tutorial_126_0.svg diff --git a/docs/tutorial/tutorial_129_0.svg b/docs/tutorial/tutorial_129_0.svg new file mode 100644 index 000000000..207d9334f --- /dev/null +++ b/docs/tutorial/tutorial_129_0.svg @@ -0,0 +1,2613 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_12_0.svg b/docs/tutorial/tutorial_12_0.svg new file mode 100644 index 000000000..a8580aa2e --- /dev/null +++ b/docs/tutorial/tutorial_12_0.svg @@ -0,0 +1,5551 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_12_1.svg b/docs/tutorial/tutorial_12_1.svg deleted file mode 100644 index 85e1ff443..000000000 --- a/docs/tutorial/tutorial_12_1.svg +++ /dev/null @@ -1,3574 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_135_0.svg b/docs/tutorial/tutorial_131_0.svg similarity index 100% rename from docs/tutorial/tutorial_135_0.svg rename to docs/tutorial/tutorial_131_0.svg diff --git a/docs/tutorial/tutorial_133_0.svg b/docs/tutorial/tutorial_133_0.svg index 207d9334f..dc3701e43 100644 --- a/docs/tutorial/tutorial_133_0.svg +++ b/docs/tutorial/tutorial_133_0.svg @@ -2,7 +2,7 @@ - + - - - +" id="m789d0468c5" style="stroke:#000000;stroke-width:0.6;"/> - + @@ -96,20 +96,20 @@ Q 4.203125 38.703125 4.203125 34.796875 z " id="HelveticaNeue-48"/> - + - - + @@ -199,7 +199,7 @@ L 18.40625 62.203125 z " id="HelveticaNeue-53"/> - + @@ -208,31 +208,31 @@ z - - + - + - - + @@ -255,7 +255,7 @@ Q 45.5 57.09375 50.90625 62.40625 z " id="HelveticaNeue-55"/> - + @@ -264,13 +264,13 @@ z - - + @@ -289,7 +289,7 @@ L 27.09375 0 z " id="HelveticaNeue-49"/> - + @@ -297,1828 +297,985 @@ z - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - - - - + + + + + + - - - + + - + - + - - - - + + + + - - - + + - + - + - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - + + +L 0 2 +" id="m03f684513e" style="stroke:#000000;stroke-width:0.48;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - + + + + + - - - - - + + - + - - - - - + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - + + + - + + + + - + + + + + + + + + + + + + - - - + + - + - + + + + + + + - - - + + - + - + + + + + + + + + - - - + + - + - + + + + + + + - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - + - - + - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + +" id="HelveticaNeue-Bold-108"/> + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + diff --git a/docs/tutorial/tutorial_137_0.svg b/docs/tutorial/tutorial_137_0.svg deleted file mode 100644 index dc3701e43..000000000 --- a/docs/tutorial/tutorial_137_0.svg +++ /dev/null @@ -1,1548 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_142_0.svg b/docs/tutorial/tutorial_138_0.svg similarity index 100% rename from docs/tutorial/tutorial_142_0.svg rename to docs/tutorial/tutorial_138_0.svg diff --git a/docs/tutorial/tutorial_142_1.svg b/docs/tutorial/tutorial_138_1.svg similarity index 100% rename from docs/tutorial/tutorial_142_1.svg rename to docs/tutorial/tutorial_138_1.svg diff --git a/docs/tutorial/tutorial_145_0.svg b/docs/tutorial/tutorial_141_0.svg similarity index 100% rename from docs/tutorial/tutorial_145_0.svg rename to docs/tutorial/tutorial_141_0.svg diff --git a/docs/tutorial/tutorial_148_0.svg b/docs/tutorial/tutorial_144_0.svg similarity index 100% rename from docs/tutorial/tutorial_148_0.svg rename to docs/tutorial/tutorial_144_0.svg diff --git a/docs/tutorial/tutorial_153_0.svg b/docs/tutorial/tutorial_149_0.svg similarity index 100% rename from docs/tutorial/tutorial_153_0.svg rename to docs/tutorial/tutorial_149_0.svg diff --git a/docs/tutorial/tutorial_14_0.svg b/docs/tutorial/tutorial_14_0.svg new file mode 100644 index 000000000..087d20846 --- /dev/null +++ b/docs/tutorial/tutorial_14_0.svg @@ -0,0 +1,4188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_150_0.svg b/docs/tutorial/tutorial_150_0.svg new file mode 100644 index 000000000..10abe70ab --- /dev/null +++ b/docs/tutorial/tutorial_150_0.svg @@ -0,0 +1,12617 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_156_0.svg b/docs/tutorial/tutorial_152_0.svg similarity index 100% rename from docs/tutorial/tutorial_156_0.svg rename to docs/tutorial/tutorial_152_0.svg diff --git a/docs/tutorial/tutorial_154_0.svg b/docs/tutorial/tutorial_154_0.svg index 10abe70ab..f7f18e968 100644 --- a/docs/tutorial/tutorial_154_0.svg +++ b/docs/tutorial/tutorial_154_0.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" id="m487f27f273" style="stroke:#000000;stroke-width:0.6;"/> - + - + + + + - + - + + + + - + - + + + + - + - + +" id="m94b3232e7a" style="stroke:#000000;stroke-width:0.48;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + +" id="m41e5c8b202" style="stroke:#000000;stroke-width:0.6;"/> - + @@ -2645,65 +2098,25 @@ Q 4.203125 38.703125 4.203125 34.796875 z " id="HelveticaNeue-48"/> - + - + + + + - + - + - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - + + + - - - + - + - + - + - - - - - - - - - - - - - - + + + - - - + - + - + - + - - - - - - - - - - - - - - - - - - + + +" id="m45d785b727" style="stroke:#000000;stroke-width:0.48;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - + + - - - + - + - - + - + +" id="HelveticaNeue-100"/> + - - - +" id="HelveticaNeue-111"/> - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" id="HelveticaNeue-118"/> + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - + - + + + + - + - + + + + - + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + + + + + + - + - - + + + + + - + - - + + + + + - + - - + + + + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - + + - - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" style="fill:none;opacity:0;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - + + + + + + + + - - - - - + + + + + - - - - - - + + + + + - - - - - + + + + + - - - - - - + + + + + + + - - - - - + + + + + - - - - - - + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" id="HelveticaNeue-51"/> + + + - - + + - - - + + - - - + + - - - - - - - - - + + + + + + + + + + + + - - - + + + - + - - - - + + + + + + + - - + + - + - - - - - + + + + + + + + + + + + + + + + + + + + - - + + - + - - - + + + - - + + - - + + - + - - - + + + - - + + - - + + - + - - - + + + - - + + - - - + + - - - + + - - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + - - - - + + + + + + + + + + - - + + - + - - - - - + + + + + + + - - + + - + - - - - - - + + + + + + + - - + + - + - - - - - - + + + + + + + - - + + - + - - - - - - + + + + + + + + + + + + + + + + + + + - - - + + - - - + + - - - - - - - - - - - + + + + + + + + + + + + - - - + + + - + - - - - + + + + + + + - - + + - + - - - - - + + + + + + + - - + + - + - - - - - - + + + + + + + - - + + - + - - - - - - + + + + + + + - - + + - + - - - - - - + + + + + + + + + + + + + + + + + + + - - - + + - - + + - - + - + +M 42.296875 6.59375 +L 42.296875 0 +L 55.796875 0 +L 55.796875 71.40625 +L 41.59375 71.40625 +L 41.59375 45.40625 +L 41.40625 45.40625 +Q 39 49.203125 34.84375 51.140625 +Q 30.703125 53.09375 26.09375 53.09375 +Q 20.40625 53.09375 16.09375 50.84375 +Q 11.796875 48.59375 8.9375 44.890625 +Q 6.09375 41.203125 4.640625 36.34375 +Q 3.203125 31.5 3.203125 26.296875 +Q 3.203125 20.90625 4.640625 15.90625 +Q 6.09375 10.90625 8.9375 7.046875 +Q 11.796875 3.203125 16.1875 0.953125 +Q 20.59375 -1.296875 26.40625 -1.296875 +Q 31.5 -1.296875 35.546875 0.546875 +Q 39.59375 2.40625 42.09375 6.59375 +z +" id="HelveticaNeue-Bold-100"/> + - - - - +" id="HelveticaNeue-Bold-110"/> + + - + - - - - - - - +" id="HelveticaNeue-Bold-108"/> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + diff --git a/docs/tutorial/tutorial_161_0.svg b/docs/tutorial/tutorial_157_0.svg similarity index 100% rename from docs/tutorial/tutorial_161_0.svg rename to docs/tutorial/tutorial_157_0.svg diff --git a/docs/tutorial/tutorial_161_1.svg b/docs/tutorial/tutorial_157_1.svg similarity index 100% rename from docs/tutorial/tutorial_161_1.svg rename to docs/tutorial/tutorial_157_1.svg diff --git a/docs/tutorial/tutorial_158_0.svg b/docs/tutorial/tutorial_158_0.svg deleted file mode 100644 index f7f18e968..000000000 --- a/docs/tutorial/tutorial_158_0.svg +++ /dev/null @@ -1,6313 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_15_0.svg b/docs/tutorial/tutorial_15_0.svg new file mode 100644 index 000000000..1e7530d09 --- /dev/null +++ b/docs/tutorial/tutorial_15_0.svg @@ -0,0 +1,7190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_164_0.svg b/docs/tutorial/tutorial_160_0.svg similarity index 100% rename from docs/tutorial/tutorial_164_0.svg rename to docs/tutorial/tutorial_160_0.svg diff --git a/docs/tutorial/tutorial_167_0.svg b/docs/tutorial/tutorial_163_0.svg similarity index 100% rename from docs/tutorial/tutorial_167_0.svg rename to docs/tutorial/tutorial_163_0.svg diff --git a/docs/tutorial/tutorial_170_0.svg b/docs/tutorial/tutorial_166_0.svg similarity index 100% rename from docs/tutorial/tutorial_170_0.svg rename to docs/tutorial/tutorial_166_0.svg diff --git a/docs/tutorial/tutorial_173_1.svg b/docs/tutorial/tutorial_169_1.svg similarity index 100% rename from docs/tutorial/tutorial_173_1.svg rename to docs/tutorial/tutorial_169_1.svg diff --git a/docs/tutorial/tutorial_16_2.svg b/docs/tutorial/tutorial_16_2.svg deleted file mode 100644 index 6d14bd1f4..000000000 --- a/docs/tutorial/tutorial_16_2.svg +++ /dev/null @@ -1,3622 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_176_1.svg b/docs/tutorial/tutorial_172_1.svg similarity index 100% rename from docs/tutorial/tutorial_176_1.svg rename to docs/tutorial/tutorial_172_1.svg diff --git a/docs/tutorial/tutorial_179_0.svg b/docs/tutorial/tutorial_175_0.svg similarity index 100% rename from docs/tutorial/tutorial_179_0.svg rename to docs/tutorial/tutorial_175_0.svg diff --git a/docs/tutorial/tutorial_88_1.svg b/docs/tutorial/tutorial_17_1.svg similarity index 58% rename from docs/tutorial/tutorial_88_1.svg rename to docs/tutorial/tutorial_17_1.svg index f9fe11fb3..31455f0b7 100644 --- a/docs/tutorial/tutorial_88_1.svg +++ b/docs/tutorial/tutorial_17_1.svg @@ -27,2406 +27,2406 @@ z " style="fill:#ffffff;"/> - - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - - + - + - + - - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - + - + - + - - + - + - - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" style="fill:#a4a4a4;stroke:#a4a4a4;stroke-width:0.4;"/> @@ -2434,17 +2434,17 @@ L 111.34375 28.011875 +" id="m55e34583c1" style="stroke:#000000;stroke-width:0.6;"/> - + - + @@ -2453,59 +2453,59 @@ L 0 4 +" id="m64a7bac45d" style="stroke:#000000;stroke-width:0.48;"/> - + - + - + - + - + - + - + - + @@ -2516,10 +2516,10 @@ L 0 2 +" id="mdb5a4fe439" style="stroke:#000000;stroke-width:0.6;"/> - + @@ -2583,7 +2583,7 @@ z - + @@ -2636,7 +2636,7 @@ z - + @@ -2664,7 +2664,7 @@ z - + @@ -2680,115 +2680,115 @@ z +" id="m541e6e116b" style="stroke:#000000;stroke-width:0.48;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2894,2475 +2894,2475 @@ z " style="fill:#ffffff;"/> - - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - - + - + - + - - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - + - + - + - - + - + - - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" style="fill:#a4a4a4;stroke:#a4a4a4;stroke-width:0.4;"/> - + - + - + - + - + - + - + - + - + - + @@ -5371,140 +5371,140 @@ L 209.74375 28.011875 - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -5588,2475 +5588,2475 @@ z " style="fill:#ffffff;"/> - - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - - + - + - + - - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - + - + - + - - + - + - - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" style="fill:#a4a4a4;stroke:#a4a4a4;stroke-width:0.4;"/> - + - + - + - + - + - + - + - + - + - + @@ -8065,140 +8065,140 @@ L 308.14375 28.011875 - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -8265,2483 +8265,2483 @@ z - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" style="fill:#a4a4a4;stroke:#a4a4a4;stroke-width:0.4;"/> - + - + - + - + - + - + - + - + - + - + @@ -10750,12 +10750,12 @@ L 111.34375 135.838437 - + - + @@ -10763,12 +10763,12 @@ L 111.34375 135.838437 - + - + @@ -10776,12 +10776,12 @@ L 111.34375 135.838437 - + - + @@ -10790,7 +10790,7 @@ L 111.34375 135.838437 - + @@ -10804,112 +10804,112 @@ L 111.34375 135.838437 - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -11087,23 +11087,23 @@ z - - - - @@ -11149,7 +11149,7 @@ Q 39.59375 2.40625 42.09375 6.59375 z " id="HelveticaNeue-Bold-100"/> - + @@ -11157,2483 +11157,2483 @@ z - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" style="fill:#a4a4a4;stroke:#a4a4a4;stroke-width:0.4;"/> - + - + - + - + - + - + - + - + - + - + @@ -13642,162 +13642,162 @@ L 209.74375 135.838437 - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - @@ -13836,7 +13836,7 @@ Q 17.40625 15.5 17.09375 22.40625 z " id="HelveticaNeue-Bold-101"/> - + @@ -13844,2483 +13844,2483 @@ z - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - + - + - + - + - + - + - + - + - + - + - + - - - + - - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" style="fill:#a4a4a4;stroke:#a4a4a4;stroke-width:0.4;"/> - + - + - + - + - + - + - + - + - + - + @@ -16329,162 +16329,162 @@ L 308.14375 135.838437 - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - @@ -16512,7 +16512,7 @@ L 0 42.203125 z " id="HelveticaNeue-Bold-102"/> - + @@ -16528,2412 +16528,2412 @@ z " style="fill:#ffffff;"/> - - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - - + - + - + - - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - + - + - + - - + - + - - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" style="fill:#a4a4a4;stroke:#a4a4a4;stroke-width:0.4;"/> - + @@ -18946,7 +18946,7 @@ L 111.34375 243.665 - + @@ -18960,56 +18960,56 @@ L 111.34375 243.665 - + - + - + - + - + - + - + - + @@ -19018,7 +19018,7 @@ L 111.34375 243.665 - + @@ -19031,7 +19031,7 @@ L 111.34375 243.665 - + @@ -19044,7 +19044,7 @@ L 111.34375 243.665 - + @@ -19058,7 +19058,7 @@ L 111.34375 243.665 - + @@ -19072,112 +19072,112 @@ L 111.34375 243.665 - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -19274,2412 +19274,2412 @@ z " style="fill:#ffffff;"/> - - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - - + - + - + - - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - + - + - + - - + - + - - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" style="fill:#a4a4a4;stroke:#a4a4a4;stroke-width:0.4;"/> - + @@ -21692,7 +21692,7 @@ L 209.74375 243.665 - + @@ -21706,56 +21706,56 @@ L 209.74375 243.665 - + - + - + - + - + - + - + - + @@ -21791,140 +21791,140 @@ z - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -21990,2412 +21990,2412 @@ z " style="fill:#ffffff;"/> - - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - - + - + - + - - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - - + - + - + - + - + - + - - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - - + - + - + - + - - + - + - - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + - - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" style="fill:#a4a4a4;stroke:#a4a4a4;stroke-width:0.4;"/> - + @@ -24408,7 +24408,7 @@ L 308.14375 243.665 - + @@ -24422,56 +24422,56 @@ L 308.14375 243.665 - + - + - + - + - + - + - + - + @@ -24480,140 +24480,140 @@ L 308.14375 243.665 - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -24670,101 +24670,101 @@ z - - - - - - - - - - - - - - + - + @@ -24772,7 +24772,7 @@ L 104.86375 369.3225 - + @@ -24795,12 +24795,12 @@ z - + - + @@ -24819,12 +24819,12 @@ z @@ -24833,6 +24833,160 @@ z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - - + - - - - - - - - - - - - - - + + + - + - + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - + - + - + @@ -25183,23 +25337,209 @@ z - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - - + - - - - - - - - - - - + + + + + + + + + + - - + + - + +" id="mc0b0984be2" style="stroke:#000000;stroke-width:0.6;"/> - + - + - + - + - + - + - + @@ -25330,14 +25670,14 @@ L 4 0 - + - + - + - + @@ -25345,12 +25685,12 @@ L 4 0 - + - + - + - + @@ -25413,14 +25753,14 @@ z - + - + - + - + @@ -25428,14 +25768,14 @@ z - + - + - + - + @@ -25443,14 +25783,14 @@ z - + - + - + - + @@ -25458,12 +25798,12 @@ z - + - + - + - + @@ -25491,14 +25831,14 @@ z - + - + - + - + @@ -25506,12 +25846,12 @@ z - + - + - + - + @@ -25568,21 +25908,21 @@ z - + - + - + - + - + - + @@ -25591,445 +25931,105 @@ z - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + @@ -26037,12 +26037,12 @@ L 320.46375 79.34938 - + - + @@ -26050,12 +26050,12 @@ L 320.46375 79.34938 - + - + @@ -26065,12 +26065,12 @@ L 320.46375 79.34938 - + - + @@ -26080,12 +26080,12 @@ L 320.46375 79.34938 - + - + @@ -26095,12 +26095,12 @@ L 320.46375 79.34938 - + - + @@ -26110,12 +26110,12 @@ L 320.46375 79.34938 - + - + @@ -26125,12 +26125,12 @@ L 320.46375 79.34938 - + - + @@ -26140,12 +26140,12 @@ L 320.46375 79.34938 - + - + @@ -26155,12 +26155,12 @@ L 320.46375 79.34938 - + - + @@ -26170,12 +26170,12 @@ L 320.46375 79.34938 - + - + @@ -26185,19 +26185,19 @@ L 320.46375 79.34938 - + - + - + @@ -26208,19 +26208,19 @@ L 320.46375 79.34938 - + - - +" id="HelveticaNeue-Bold-111"/> + - - + @@ -26477,56 +26438,71 @@ z - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + - - + + - - + + - + - + - + - + - + - - + + diff --git a/docs/tutorial/tutorial_18_0.svg b/docs/tutorial/tutorial_18_0.svg index 079181ef2..f77679bf6 100644 --- a/docs/tutorial/tutorial_18_0.svg +++ b/docs/tutorial/tutorial_18_0.svg @@ -2,7 +2,7 @@ - + - - - - - - - - + + + + + + - - + + + - - + + + + + + - - + + - - + + + - - + + - - + + - - + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + - +" id="m4dc793af4d" style="stroke:#000000;stroke-width:0.6;"/> - + - + - - - - - - + + - - + - - - + + + + + + + - - + - - - + + + + + + @@ -1378,114 +3985,72 @@ L 172.51875 23.415469 +" id="m158d0613bd" style="stroke:#000000;stroke-width:0.48;"/> - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + - - - - +" id="HelveticaNeue-98"/> - - - - - - - - - - - - - - + + + + + + + - - + - + +" id="m4d8a213089" style="stroke:#000000;stroke-width:0.6;"/> - + + + + + + + - - + - + - + + + + + + + + + + + - - + - + - + + + + + + + + - - + - + - + + + + + + + + + + + - - + - + - + - - - - - - - - + + + + + - - + + +" id="mc970b2e221" style="stroke:#000000;stroke-width:0.48;"/> - + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - + + +" id="HelveticaNeue-115"/> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - + + + + + + - - - - - - + + + - + - - + + - - - - - - + + - - - - - - + + + - - - - - - + + - - - - - - + + - - - - - - + + - - - - - - + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - + + + + + - - - + + + + + - + + + - + - - - - - - - + + + + + - + - - - - + - - - - - - - + + + + + - + - + + + + + - + - - - - - + + + + + + + + + + - + + + - - + - - - - + + + + - + - - + - - - - - - - + + + + + - + - - + - - - - + + + + + - + - - + - - - - - - - + + + + + - + - - + - - - - + + + + + - + - - - - + - - - - - - - + + + + + - + - - - - + - - - - - - - + + + + + - + - - - - + - - - - + + + + + - + - - - - + - - - - - - - + + + + + - + - - - - + - - - - + + + + + - + - + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + - + - - - - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - + + + - - - + + - + - + - + - + - - + + - - - - - + + - + - - - - - - - + + + + + - - - - - + + - + - - - - + + + + + - - - - - - - - - - - + + + + + - - - - - + + + + + - + + + - + - - - - + + + + + - - - + + + + + + + + + + + + + + + - + - + - - + + - - - + + - + - - - + + - + - + - - - - + + + + + + - - - + + - + - + - - + + - + - - + + - + - - - + + - + - + - - + + - +M 6.5 52.796875 +Q 6.5 48 9.203125 43.953125 +Q 11.90625 39.90625 16.40625 38.09375 +Q 10.40625 36 7.203125 31.34375 +Q 4 26.703125 4 20.40625 +Q 4 15 5.84375 10.953125 +Q 7.703125 6.90625 10.953125 4.203125 +Q 14.203125 1.5 18.59375 0.203125 +Q 23 -1.09375 28.09375 -1.09375 +Q 33 -1.09375 37.296875 0.296875 +Q 41.59375 1.703125 44.75 4.453125 +Q 47.90625 7.203125 49.75 11.203125 +Q 51.59375 15.203125 51.59375 20.40625 +Q 51.59375 27 48.5 31.546875 +Q 45.40625 36.09375 39 38.09375 +Q 43.5 40.09375 46.140625 44.046875 +Q 48.796875 48 48.796875 52.796875 +Q 48.796875 56.203125 47.59375 59.453125 +Q 46.40625 62.703125 43.84375 65.25 +Q 41.296875 67.796875 37.1875 69.34375 +Q 33.09375 70.90625 27.296875 70.90625 +Q 23.203125 70.90625 19.453125 69.703125 +Q 15.703125 68.5 12.796875 66.203125 +Q 9.90625 63.90625 8.203125 60.546875 +Q 6.5 57.203125 6.5 52.796875 +z +M 13 20 +Q 13 23.09375 14.140625 25.59375 +Q 15.296875 28.09375 17.34375 29.890625 +Q 19.40625 31.703125 22.15625 32.640625 +Q 24.90625 33.59375 28 33.59375 +Q 31 33.59375 33.640625 32.546875 +Q 36.296875 31.5 38.296875 29.703125 +Q 40.296875 27.90625 41.4375 25.453125 +Q 42.59375 23 42.59375 20.09375 +Q 42.59375 17.09375 41.546875 14.59375 +Q 40.5 12.09375 38.546875 10.25 +Q 36.59375 8.40625 33.9375 7.34375 +Q 31.296875 6.296875 28.09375 6.296875 +Q 21.5 6.296875 17.25 9.9375 +Q 13 13.59375 13 20 +z +" id="HelveticaNeue-56"/> - - - + + + + - - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - + + + + + - - - - - + + - + - - - - - + + + + + - - - + + + + + - + + + - + - - - - - - - + + + + + - - + + - + - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - - + + + + + - + + + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + - + + + + + + + + + - - + + + + + - + + + + + + + + + - - + + + + + - + + + + + + + + + - - + + + + + - + + + + + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - + - - + - - + - - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - + + - - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + - + - - + + - + - - - - - + + + + - - + + - + - - - - + + + + - - + + - + - - - - - - - - + + + + + - - + + - + - - - - - - - + + + + - - - - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - + + - - - - - - - - - - + + + + + + + + + + + - - - - + + + - + - + - + - - + + - + - - - - - + + + + - - + + - + - - - - + + + + - - + + - + - - - - - + + + + + - - + + - + - - - - + + + + - - - - - + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + + + + + - - - - - + - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +" id="HelveticaNeue-Bold-109"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + - - + + - - + + - - + + diff --git a/docs/tutorial/tutorial_21_0.svg b/docs/tutorial/tutorial_21_0.svg index 99cd2b55a..370434ecd 100644 --- a/docs/tutorial/tutorial_21_0.svg +++ b/docs/tutorial/tutorial_21_0.svg @@ -2,7 +2,7 @@ - + - - - +" id="m5ec9ffb235" style="stroke:#000000;stroke-width:0.6;"/> - + @@ -96,90 +96,267 @@ Q 4.203125 38.703125 4.203125 34.796875 z " id="HelveticaNeue-48"/> - + - - + - + - - +" id="HelveticaNeue-50"/> - - - - + + - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - + + +" id="meaaf3246cf" style="stroke:#000000;stroke-width:0.48;"/> - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - - - - - - - - - - - - - - + @@ -446,4280 +669,4392 @@ z - - + - + +" id="medde04a5b3" style="stroke:#000000;stroke-width:0.6;"/> - + - - - - + + + + - - + - + - + - - + + - +" id="HelveticaNeue-46"/> + - - + + - - + - - + - + - + - - - - - - + + + + - - + - + - + - - - - - - - + + + + - - + - - + - + - + - - - - + + + + + + + - + +" id="m9f025b3472" style="stroke:#000000;stroke-width:0.48;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + - - - - - - - - - - +" style="fill:#ffffff;stroke:#ffffff;stroke-linejoin:miter;stroke-width:0.01;"/> - - - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - +" id="HelveticaNeue-115"/> + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - - - + + - + - + - - - - - - + + + + - - - + + - + - + - - - - + + + + - - - - - + + + - - - + - + + + + + + + - - + + + + + - + + + + + + + - - + + + + + - + + + + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - + + + + + - + + + - + - - - - + + + + + - - - + + + + + - + + + - + - - - - - - - + + + + + - - - + + + + + - + + + - + - - - - - - + + + + + - - - + + + + + - + + + - + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - + - - - - + + + + - + + + + - + + + + + + + - + + + + - + + + + + + + - + + + + - + + + + + + + - + + + + - + + + + + + + - + + + + - + + + + + + + - + + + + - + + + + + + + - + + + + - + + + + + + + - + + + + - + + + + + + + - + + + + - + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - + - - - - - + + - - - - - - - - - - + - - - - - + + - - - - - - - - - + - - - - - + + - - - - - - - - - - + - - - - - + + - - - - - - - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - + + + + + + + + + - - + - - + - - + - - + - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" style="fill:#ffffff;opacity:0.5;stroke:#495057;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 37.59375 25.296875 +Q 36.703125 24.5 35.34375 24.046875 +Q 34 23.59375 32.453125 23.296875 +Q 30.90625 23 29.203125 22.796875 +Q 27.5 22.59375 25.796875 22.296875 +Q 24.203125 22 22.640625 21.5 +Q 21.09375 21 19.9375 20.140625 +Q 18.796875 19.296875 18.09375 18 +Q 17.40625 16.703125 17.40625 14.703125 +Q 17.40625 12.796875 18.09375 11.5 +Q 18.796875 10.203125 20 9.453125 +Q 21.203125 8.703125 22.796875 8.390625 +Q 24.40625 8.09375 26.09375 8.09375 +Q 30.296875 8.09375 32.59375 9.5 +Q 34.90625 10.90625 36 12.84375 +Q 37.09375 14.796875 37.34375 16.796875 +Q 37.59375 18.796875 37.59375 20 +z +" id="HelveticaNeue-Bold-97"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - + + + + - - + + - + - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - + + + + - - - - - + + - + - - - - - - + + + + - - - - - + + - + - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - + + + + + - - + + - + - - - - - + + + + + - - - - - - - + + - + - - - - + + + + + - - - - - + + - + - - - - - - - + + + + + - - - - - + + - + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -4727,255 +5062,350 @@ L 316.765938 264.95125 - - - - - + + - + - + - + - - - - - - - - - + + - + - - - - - + + + + + + + - - + + - + - - - - - + + + + + + - - + + - + - - - - - + + + + + + + - - + + - + - - - - - + + + + - - + + - + - - - - - + + + + + + + - - + + - + - - - - - + + + + + + - - + + - + - - - - - + + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + - - + + - - + + - - - - + +" id="HelveticaNeue-Bold-98"/> + + - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/tutorial/tutorial_22_0.svg b/docs/tutorial/tutorial_22_0.svg index 755a0de3d..fe614b6a2 100644 --- a/docs/tutorial/tutorial_22_0.svg +++ b/docs/tutorial/tutorial_22_0.svg @@ -2,7 +2,7 @@ - + - - - +" id="m749c7043d6" style="stroke:#000000;stroke-width:0.6;"/> - + + + + + + + + + + - - + + + + + + + + + + - - + + + + + + + + + + + - - + + + + + + + + - - - + + + - + - - - - + - + - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - + - + - - - - - - - - - - - - - - - - - - + + + + - - - +" id="HelveticaNeue-108"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - - - - + + - - - + + - + - + - - + + - + - - - - + + - - - - + - - - - - - - + - - - - + + - + - - - - + - - - - - - - - - + + + + + - + - + + + + + - - - - - - - - - - + - + - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + - + - - - - - + @@ -862,1504 +880,1200 @@ z + + + + + + + + + + + + + + + - - - - - - - - - - + + - - + + + + + + + + + + + - - - - - - - +M 15.40625 -6.5 +L 15.40625 51.703125 +L 6.90625 51.703125 +L 6.90625 -5.09375 +Q 6.90625 -9.09375 5.90625 -10.640625 +Q 4.90625 -12.203125 2.09375 -12.203125 +L 0.296875 -12.203125 +Q -0.09375 -12.203125 -0.4375 -12.140625 +Q -0.796875 -12.09375 -1.296875 -12 +L -1.296875 -19.296875 +Q 0.703125 -19.703125 3 -19.703125 +Q 8.703125 -19.703125 12.046875 -16.546875 +Q 15.40625 -13.40625 15.40625 -6.5 +z +" id="HelveticaNeue-106"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - + - + - + + + + + + + - - + - + - + + + + + + + + - - + - + - + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - + + + + + - + + + - + - - - - + + + - + - - - - + + + + + - - - - + + + + + - - - + + + + + - + + + - + - + + + - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + - - - + + - + - + - - - + + + + - - - - + + + + + + + + - + - + - - - - + + + + + + + - - - - + + + + + + + + + + + + - + + + + + + + - - + + - + - + + + - + - - + + - + - + + + - + - - + + - + - + + + - + - - + + - + - + + + - + - - + + - + - + + + - + - - + + - + - + + + - + - - + + - + - + + + - + - - - - - - - - - + + + + + - - - - - - - - + + + + + + - - - - - - - - + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + - - - - - - - - - - - +" style="fill:none;opacity:0;"/> - - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + +" style="fill:none;opacity:0;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + +" id="HelveticaNeue-Bold-101"/> - + + + + + + - + +" id="HelveticaNeue-Bold-109"/> - - - - - - - + - +" id="HelveticaNeue-Bold-105"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + - - + + diff --git a/docs/tutorial/tutorial_26_0.svg b/docs/tutorial/tutorial_26_0.svg deleted file mode 100644 index 17d1e5197..000000000 --- a/docs/tutorial/tutorial_26_0.svg +++ /dev/null @@ -1,6927 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_26_2.svg b/docs/tutorial/tutorial_26_2.svg index 6a7fe6479..6d14bd1f4 100644 --- a/docs/tutorial/tutorial_26_2.svg +++ b/docs/tutorial/tutorial_26_2.svg @@ -2,7 +2,7 @@ - + - - - +" id="m2e7a9fc7ce" style="stroke:#000000;stroke-width:0.6;"/> - + @@ -96,58 +96,31 @@ Q 4.203125 38.703125 4.203125 34.796875 z " id="HelveticaNeue-48"/> - + - - + - + - - - - - - - - - - - - - - - - - - - - +" id="HelveticaNeue-46"/> - - - - - - - - - - - - - + + + + - - - - - + + + - - - + - + - - - - - + + + + + + + + + - - - - - + + + - - - + - + - - - - - + + + + + + + + + - - - - - + + + - - - + - + - - - - - + + + + + + + + + - - - - - + + - - - - + - + - - + + - +" id="HelveticaNeue-49"/> - - - - + + - - - - - + + + + + - - - - - - - + - - - - - + + - - - - - - - - + - - - - - + + - + - - - - - - - - + + + + + - - - - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - - - + +" id="HelveticaNeue-105"/> + + - + +" id="HelveticaNeue-99"/> + + + + + - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + + + + - + - + - + - - - + + - + - + - - - + + + - - - - + + - + - + - - - + + + - - - + + + + + - + - - - - - + + + + + + + - - + + + + + - + - - - - - + + + + - - + + + + + - + + + + + + + + + + - - + + + + + - + + + + + + + - - + + + + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - + + - + - - - - - - + + + + + - - - - - + + - + - - - - + + + + + - - - - - + + - + - - - - - + + + + + - - - - + - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - - + - - + - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - + - - - - + + + + + + + + + + + + + - - + - + - + - - - - + + + + + + + + + + + + - - + - + - + - - - + + + + + + + + + + + + + + - + + + + - + + + + + + + + + + + + + + + + + + + - + + + + - + + + + + + + + + + + + + + + + - + + + + - + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - + + - + - - - - - - + + + + + - - - - - + + - + - - - - + + + + + - - - - - + + - + - - - - - + + + + + - - - - - + + - + - - - - - + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + + + + + + - + + + + + + + + + + - - + + + + + - + + + + + + + + + - - + + + + + - + + + + + + + + + + - - + + + + + - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - - - - + + + + + + + - - - + + - + - + - - - + + + - + + - - - + + - + - + - - - - - + + + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - + + - + - - - - - - + + + + + - - - - - + + - + - - - - + + + + + - - - - - + + - + - - - - - + + + + + - - - - - + + - + - - - - - + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + + + + +" id="HelveticaNeue-Bold-97"/> + + - + + - - + + +" id="HelveticaNeue-Bold-102"/> - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + - - + + - - + + diff --git a/docs/tutorial/tutorial_28_0.svg b/docs/tutorial/tutorial_28_0.svg new file mode 100644 index 000000000..079181ef2 --- /dev/null +++ b/docs/tutorial/tutorial_28_0.svg @@ -0,0 +1,6331 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_29_0.svg b/docs/tutorial/tutorial_29_0.svg deleted file mode 100644 index 417e56a60..000000000 --- a/docs/tutorial/tutorial_29_0.svg +++ /dev/null @@ -1,9124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_31_0.svg b/docs/tutorial/tutorial_31_0.svg new file mode 100644 index 000000000..99cd2b55a --- /dev/null +++ b/docs/tutorial/tutorial_31_0.svg @@ -0,0 +1,5321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_21_1.svg b/docs/tutorial/tutorial_31_1.svg similarity index 100% rename from docs/tutorial/tutorial_21_1.svg rename to docs/tutorial/tutorial_31_1.svg diff --git a/docs/tutorial/tutorial_32_0.svg b/docs/tutorial/tutorial_32_0.svg index f9214c872..755a0de3d 100644 --- a/docs/tutorial/tutorial_32_0.svg +++ b/docs/tutorial/tutorial_32_0.svg @@ -2,7 +2,7 @@ - + - - - +" id="m0049268f75" style="stroke:#000000;stroke-width:0.6;"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -96,24 +285,24 @@ Q 4.203125 38.703125 4.203125 34.796875 z " id="HelveticaNeue-48"/> - + - - - + + - + - + - + - +" id="HelveticaNeue-50"/> - + - + - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -387,17 +398,17 @@ z - - + - + - + - + - + @@ -458,17 +469,17 @@ z - - + - + - + - + - + @@ -537,135 +548,149 @@ z - - + - + - + - + - + + + + - + +" id="m5e3c8ad98a" style="stroke:#000000;stroke-width:0.48;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - + - +M 50.5 16.40625 +L 42.09375 16.40625 +Q 41 11.296875 37.546875 8.796875 +Q 34.09375 6.296875 28.703125 6.296875 +Q 24.5 6.296875 21.390625 7.6875 +Q 18.296875 9.09375 16.296875 11.4375 +Q 14.296875 13.796875 13.390625 16.84375 +Q 12.5 19.90625 12.59375 23.296875 +L 51.296875 23.296875 +Q 51.5 28 50.453125 33.203125 +Q 49.40625 38.40625 46.65625 42.796875 +Q 43.90625 47.203125 39.34375 50.046875 +Q 34.796875 52.90625 27.90625 52.90625 +Q 22.59375 52.90625 18.140625 50.90625 +Q 13.703125 48.90625 10.453125 45.296875 +Q 7.203125 41.703125 5.390625 36.796875 +Q 3.59375 31.90625 3.59375 26 +Q 3.796875 20.09375 5.34375 15.09375 +Q 6.90625 10.09375 10 6.5 +Q 13.09375 2.90625 17.640625 0.90625 +Q 22.203125 -1.09375 28.40625 -1.09375 +Q 37.203125 -1.09375 43 3.296875 +Q 48.796875 7.703125 50.5 16.40625 +z +" id="HelveticaNeue-101"/> - + - - - - - + + + + + - + - + - + - + - - - - + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - + + + + + - - - + + - + - + - - - + + + - + - - - + + - + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - + + + - + - + - - - + + - + - + - - - + + - + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + - - - + + - + - + + + + + + + + + - - - + + - + - + + + + + + + + + - - + + + + + - + + + + + + + + + - - + + + + + - + + + + + + + + + - - + + + + + - + + + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + - + + + - + - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - + - - - + + - + - + - - - - + + + + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + - - - + + - + - + + + + + + + + + - - - + + - + - + - - - - + + + + + - + + + - + - - - - - + + - + - - - + + + + + - + + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + - + - - + + + + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - +" id="HelveticaNeue-Bold-105"/> - - - - + +" id="HelveticaNeue-Bold-104"/> - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + - - + + - - + + diff --git a/docs/tutorial/tutorial_23_0.svg b/docs/tutorial/tutorial_33_0.svg similarity index 100% rename from docs/tutorial/tutorial_23_0.svg rename to docs/tutorial/tutorial_33_0.svg diff --git a/docs/tutorial/tutorial_36_0.svg b/docs/tutorial/tutorial_36_0.svg index cfe28c9e0..17d1e5197 100644 --- a/docs/tutorial/tutorial_36_0.svg +++ b/docs/tutorial/tutorial_36_0.svg @@ -2,7 +2,7 @@ - + - - +" style="fill:#ffffff;"/> - +" id="m4fb5e73385" style="stroke:#000000;stroke-width:0.6;"/> - + @@ -96,245 +96,58 @@ Q 4.203125 38.703125 4.203125 34.796875 z " id="HelveticaNeue-48"/> - + - - + - + - +" id="HelveticaNeue-49"/> - - + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + - - - + + - + - + - - + + - +" id="HelveticaNeue-46"/> - - - - + + + + + - - - - - + + + + + - + - - - - - + + + + - - + + + + + - + + + + + + + + + - - + + + + + - + + + + + + + - - + + + + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - +" style="fill:#ffffff;"/> - - - + + - + - + - + - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + - + - - - - - + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - + + + + + + + + + + + + + + + - - + + + + + + + - + - - - - - + + + + + + - - + + + + + - + - - - - - + + + + - - + + + + + - + - - - - - + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - + + + + - - + + + + + - + - - - - - + + + + + - - + + + + + - + - - - - - + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - + + + + + + + + + + + + + + + - - + + + + + + + - + - - - - - + + + + + + - - + + + + + - + - - - - - + + + + - - + + + + + - + - - - - - + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - - - + + - + - + - - - - - - + + + + + - - - + + - + - - - - - - - + - - - - - + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - + - - - - - + + - - - - - - - - - - + - - - - - + + - - - - - - - - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - + + + - + - + - - - - - - - + + + + + + + - - - - - + + - + - + - - - + + + - - - - - - + + - + - + - + - + - - - + + - + - + - - - - - - - + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - - + + - - + - + - +" id="HelveticaNeue-Bold-105"/> + + + - - - + + + + + + +M 5.40625 51.703125 +L 5.40625 -18.09375 +L 19.59375 -18.09375 +L 19.59375 6.40625 +L 19.796875 6.40625 +Q 22.40625 2.59375 26.453125 0.640625 +Q 30.5 -1.296875 35.296875 -1.296875 +Q 41 -1.296875 45.25 0.890625 +Q 49.5 3.09375 52.34375 6.796875 +Q 55.203125 10.5 56.59375 15.296875 +Q 58 20.09375 58 25.296875 +Q 58 30.796875 56.59375 35.84375 +Q 55.203125 40.90625 52.296875 44.703125 +Q 49.40625 48.5 45 50.796875 +Q 40.59375 53.09375 34.5 53.09375 +Q 29.703125 53.09375 25.703125 51.1875 +Q 21.703125 49.296875 19.09375 45.09375 +L 18.90625 45.09375 +L 18.90625 51.703125 +z +" id="HelveticaNeue-Bold-112"/> + - - - - +" id="HelveticaNeue-Bold-102"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + - - + + - - + + - - + + diff --git a/docs/tutorial/tutorial_26_1.svg b/docs/tutorial/tutorial_36_1.svg similarity index 100% rename from docs/tutorial/tutorial_26_1.svg rename to docs/tutorial/tutorial_36_1.svg diff --git a/docs/tutorial/tutorial_36_2.svg b/docs/tutorial/tutorial_36_2.svg new file mode 100644 index 000000000..6a7fe6479 --- /dev/null +++ b/docs/tutorial/tutorial_36_2.svg @@ -0,0 +1,6733 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_26_3.svg b/docs/tutorial/tutorial_36_3.svg similarity index 100% rename from docs/tutorial/tutorial_26_3.svg rename to docs/tutorial/tutorial_36_3.svg diff --git a/docs/tutorial/tutorial_27_0.svg b/docs/tutorial/tutorial_37_0.svg similarity index 100% rename from docs/tutorial/tutorial_27_0.svg rename to docs/tutorial/tutorial_37_0.svg diff --git a/docs/tutorial/tutorial_27_1.svg b/docs/tutorial/tutorial_37_1.svg similarity index 100% rename from docs/tutorial/tutorial_27_1.svg rename to docs/tutorial/tutorial_37_1.svg diff --git a/docs/tutorial/tutorial_39_0.svg b/docs/tutorial/tutorial_39_0.svg index 428506bb2..417e56a60 100644 --- a/docs/tutorial/tutorial_39_0.svg +++ b/docs/tutorial/tutorial_39_0.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - + - + - - - - - + - - + - + + + + + - + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + - - + - + + + + + - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - +" style="stroke:#ffffff;stroke-linejoin:miter;stroke-width:1.5;"/> + - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + + - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + + - - + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 5.40625 51.703125 +L 5.40625 -18.09375 +L 19.59375 -18.09375 +L 19.59375 6.40625 +L 19.796875 6.40625 +Q 22.40625 2.59375 26.453125 0.640625 +Q 30.5 -1.296875 35.296875 -1.296875 +Q 41 -1.296875 45.25 0.890625 +Q 49.5 3.09375 52.34375 6.796875 +Q 55.203125 10.5 56.59375 15.296875 +Q 58 20.09375 58 25.296875 +Q 58 30.796875 56.59375 35.84375 +Q 55.203125 40.90625 52.296875 44.703125 +Q 49.40625 48.5 45 50.796875 +Q 40.59375 53.09375 34.5 53.09375 +Q 29.703125 53.09375 25.703125 51.1875 +Q 21.703125 49.296875 19.09375 45.09375 +L 18.90625 45.09375 +L 18.90625 51.703125 +z +" id="HelveticaNeue-Bold-112"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_42_0.svg b/docs/tutorial/tutorial_42_0.svg new file mode 100644 index 000000000..f9214c872 --- /dev/null +++ b/docs/tutorial/tutorial_42_0.svg @@ -0,0 +1,2000 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_44_1.svg b/docs/tutorial/tutorial_44_1.svg index 1e2baef4d..85e1ff443 100644 --- a/docs/tutorial/tutorial_44_1.svg +++ b/docs/tutorial/tutorial_44_1.svg @@ -2,7 +2,7 @@ - + - +" style="fill:#dee2e6;"/> - +" style="fill:url(#h49df5de922);"/> - +" id="m5fcc53849d" style="stroke:#000000;stroke-width:1.5;"/> - + - +" id="DejaVuSans-48"/> - - + + - - + - + - - +" id="DejaVuSans-50"/> - - - - + + + - - + - + - +" id="DejaVuSans-52"/> - - - - + + + - - + - + - +" id="DejaVuSans-54"/> - - - - + + + - - + - + - +" id="DejaVuSans-56"/> - - - - + + + - - - - - - - - - - + - - + + - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - + + + + + + + + + + + + + + + + + + - - + - + +" id="m6dd9679cca" style="stroke:#1c7ed6;stroke-width:1.5;"/> + + + + + + + + - + - - - + + + + + + + + + + + + + + - - + + + + + + - + - - - - - - + + + + + + + + + + - - + - - - - - - - - - - - - - - + - - - - - - + + + + + + + + + + - - - + + + + + + + - + - - - - - - + + + + + + + + + + - + - - + - - - - - - - + + + + + + + + + + + + + + + - - + + +" id="m13c538a44e" style="stroke:#1c7ed6;stroke-width:1.2;"/> - + + + + + + + + + + + + + + + + + + + + + - + + + + + + - + - + + + + + + - + - + + + + + + - + - + + + + + + - + - + + + + + + - + - + - + + + + + + - + + + + + + - + - + + + + + + - + - + + + + + + - + - + - + + + + + + - + - + - - - + - + - - + + - + - - - + - + - - + + - - - - - - - - - +" id="DejaVuSans-121"/> - - - - - - - - - - - - - + + + + + + + + - - + + + + + + + + + + + + + + + + + - + - + - + - + - +" style="fill:url(#h49df5de922);"/> - - - + + - + - + - + - - + + - - - + + - + - + - - - - - - + + + + + - - - + + - + - + - - - - - - + + + + + - - - + + - + - + - - - - - - + + + + + - - - + + - + - + - - - - - - + + + + + - - - - - + + - + - - - - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - + - + - - - - - + + + + + + + + + + - - - + + - + - + - - - - - - - - + + + + + - - - + + - + - + - - - - - - - + + + + - - - + + - + - + - - - - - - + + + + - - - + + - + - + - - - - + + + + - - - + + - + - + - - - - - + + + + - - - - - + + - + - - - - - - + + + + + - - - - - + + - + - - - - - - - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - + + + + + + + + + + - + - + + + + + + + + + + + - + - + + + + + + + + - + - + + + + + + + + - + - + + + + + + + + - + - + + + + + + + + + + + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + +M 9.421875 75.984375 +L 18.40625 75.984375 +L 18.40625 64.59375 +L 9.421875 64.59375 +z +" id="DejaVuSans-105"/> - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - + + - - + + - - + + - - + + - - + + - + - + - + +" id="DejaVuSans-Bold-121"/> + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - + + + + + + + + diff --git a/docs/tutorial/tutorial_48_0.svg b/docs/tutorial/tutorial_48_0.svg new file mode 100644 index 000000000..cfe28c9e0 --- /dev/null +++ b/docs/tutorial/tutorial_48_0.svg @@ -0,0 +1,4111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_49_0.svg b/docs/tutorial/tutorial_49_0.svg deleted file mode 100644 index c65724761..000000000 --- a/docs/tutorial/tutorial_49_0.svg +++ /dev/null @@ -1,5195 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_51_0.svg b/docs/tutorial/tutorial_51_0.svg new file mode 100644 index 000000000..428506bb2 --- /dev/null +++ b/docs/tutorial/tutorial_51_0.svg @@ -0,0 +1,2714 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_41_0.svg b/docs/tutorial/tutorial_53_0.svg similarity index 100% rename from docs/tutorial/tutorial_41_0.svg rename to docs/tutorial/tutorial_53_0.svg diff --git a/docs/tutorial/tutorial_56_1.svg b/docs/tutorial/tutorial_56_1.svg new file mode 100644 index 000000000..1e2baef4d --- /dev/null +++ b/docs/tutorial/tutorial_56_1.svg @@ -0,0 +1,3239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_45_0.svg b/docs/tutorial/tutorial_57_0.svg similarity index 100% rename from docs/tutorial/tutorial_45_0.svg rename to docs/tutorial/tutorial_57_0.svg diff --git a/docs/tutorial/tutorial_46_0.svg b/docs/tutorial/tutorial_58_0.svg similarity index 100% rename from docs/tutorial/tutorial_46_0.svg rename to docs/tutorial/tutorial_58_0.svg diff --git a/docs/tutorial/tutorial_61_0.svg b/docs/tutorial/tutorial_61_0.svg index 0a3621dfb..c65724761 100644 --- a/docs/tutorial/tutorial_61_0.svg +++ b/docs/tutorial/tutorial_61_0.svg @@ -2,7 +2,7 @@ - + - +" style="fill:#ffffff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + - - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + +" id="HelveticaNeue-57"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - + + + + + + + + + + + + - - - - - - - - - - - +" style="fill:#e2f4ff;"/> - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - +" id="HelveticaNeue-101"/> + + + + + + + + - - - - - + + + + + + + + + + + + + + - - - - +" id="HelveticaNeue-118"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + - - - - - - - - - - - + + + + + - - - + + +" style="fill:#e2f4ff;"/> - - + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" id="HelveticaNeue-52"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - + + - - - - + + - - - - - +" style="fill:#e2f4ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - +" id="HelveticaNeue-58"/> + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" id="HelveticaNeue-56"/> + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - +" style="fill:#e2f4ff;"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - +" id="HelveticaNeue-105"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - + + + + + + + + + + + + + - - + + - + - - - - - - + + + + + + + + + + + - - + + - + - - - - - - + + + + + + + + + + + + + + - - + + - + - - - - - - + + + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - - - + + + + + + + + - - + + + + + - + - - - - + + + + + + + + - - - - - - - - - + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + - + +" id="HelveticaNeue-Bold-101"/> - + +" id="HelveticaNeue-Bold-80"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/tutorial/tutorial_64_0.svg b/docs/tutorial/tutorial_64_0.svg deleted file mode 100644 index 003033bec..000000000 --- a/docs/tutorial/tutorial_64_0.svg +++ /dev/null @@ -1,62617 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_52_1.svg b/docs/tutorial/tutorial_64_1.svg similarity index 100% rename from docs/tutorial/tutorial_52_1.svg rename to docs/tutorial/tutorial_64_1.svg diff --git a/docs/tutorial/tutorial_52_2.svg b/docs/tutorial/tutorial_64_2.svg similarity index 100% rename from docs/tutorial/tutorial_52_2.svg rename to docs/tutorial/tutorial_64_2.svg diff --git a/docs/tutorial/tutorial_53_1.svg b/docs/tutorial/tutorial_65_1.svg similarity index 100% rename from docs/tutorial/tutorial_53_1.svg rename to docs/tutorial/tutorial_65_1.svg diff --git a/docs/tutorial/tutorial_67_0.svg b/docs/tutorial/tutorial_67_0.svg index 6ef773e76..e2607e8ff 100644 --- a/docs/tutorial/tutorial_67_0.svg +++ b/docs/tutorial/tutorial_67_0.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - +" style="fill:#ffffff;"/> - - - - - - + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - +" id="HelveticaNeue-52"/> + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + - - - - - - + + + + + + + - - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - + + + + + + - - - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - - - - - - + + + + + + - - - - - - - - + + + + + + - - - - - - - - - - - + + + + + + - - - - - - - - - - - + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + - - - + - + - + - - + + - - + + - - + + +" style="fill:#ffffff;"/> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + + + - - - - - - - - - - - - - - - - - - - - - + - - - + + - - - + +" id="HelveticaNeue-Bold-110"/> + + + + + + + + + + + + + - - + + - - + + - - + + +" style="fill:#ffffff;"/> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + +" style="fill:#ffffff;"/> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + +" style="fill:#ffffff;"/> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + +" id="HelveticaNeue-49"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - +" style="fill:#ffffff;"/> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - + - - + - - + - - - - - - - - + + + + - - - - - + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - + + + + + + - - - - - - - - + + + + + + - - - - - - - - + + + + + + - - - - - - - - - + + + + + + - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - + + + + + + + + + - - - - - - - - - - - + + + + + + + + + - - - - - - - - + + + + + + + + + - - - - - - - - + + + + + + + + + - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/docs/tutorial/tutorial_68_0.svg b/docs/tutorial/tutorial_68_0.svg new file mode 100644 index 000000000..916a52828 --- /dev/null +++ b/docs/tutorial/tutorial_68_0.svg @@ -0,0 +1,13254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_70_0.svg b/docs/tutorial/tutorial_70_0.svg new file mode 100644 index 000000000..408c23223 --- /dev/null +++ b/docs/tutorial/tutorial_70_0.svg @@ -0,0 +1,3476 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_58_3.svg b/docs/tutorial/tutorial_75_3.svg similarity index 100% rename from docs/tutorial/tutorial_58_3.svg rename to docs/tutorial/tutorial_75_3.svg diff --git a/docs/tutorial/tutorial_58_4.svg b/docs/tutorial/tutorial_75_4.svg similarity index 100% rename from docs/tutorial/tutorial_58_4.svg rename to docs/tutorial/tutorial_75_4.svg diff --git a/docs/tutorial/tutorial_78_0.svg b/docs/tutorial/tutorial_78_0.svg new file mode 100644 index 000000000..0a3621dfb --- /dev/null +++ b/docs/tutorial/tutorial_78_0.svg @@ -0,0 +1,10708 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_61_1.svg b/docs/tutorial/tutorial_78_1.svg similarity index 100% rename from docs/tutorial/tutorial_61_1.svg rename to docs/tutorial/tutorial_78_1.svg diff --git a/docs/tutorial/tutorial_81_0.svg b/docs/tutorial/tutorial_81_0.svg index f55c08d6b..003033bec 100644 --- a/docs/tutorial/tutorial_81_0.svg +++ b/docs/tutorial/tutorial_81_0.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + - + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - + - + - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" style="fill:#001146;"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + - - + + - - + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + - - + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + - - - + +" id="HelveticaNeue-Bold-114"/> + - - - + + + + + +" id="HelveticaNeue-Bold-109"/> + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + - - + + - - + + diff --git a/docs/tutorial/tutorial_84_0.svg b/docs/tutorial/tutorial_84_0.svg index 7f8943ef6..6ef773e76 100644 --- a/docs/tutorial/tutorial_84_0.svg +++ b/docs/tutorial/tutorial_84_0.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - +" id="HelveticaNeue-87"/> + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - + + + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + + + + + + - - - - - - + + + + + + + + - - - - - - + + + + + + + + + + + - - - - + + + - + + + + + + + + + + + + - + - + + + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" id="HelveticaNeue-120"/> + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +"/> - + - + - + - + - - - - - + + + + - + + + - - + + + - + + + - - - - - - - - - - - - - +"/> - - - + - - - + - - - - + +"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - - + - - - + - - - - + +"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - - + - - - + - - - - + +"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +"/> - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - + + - - - - + +"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - - - + +"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - + - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - + + + + + + + + - - - - - - + + + + + + + + - - - - - - + + + + + + + + - - - - - - + + + + + + + + + - - - - - - + + + + + + + + + - - - - - - + + + + + + + + + - - - - - - + + + + + + + + + - - - - - - + + + + + + + + - - - - - - + + + + + + + + + + + - - + + + + + + + + - - + + + + + + + + - - + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + + + + + + + - - +M -1.796875 -6 +L -1.796875 -17.703125 +Q 0.203125 -18.09375 2.703125 -18.09375 +Q 8.09375 -18.09375 11.59375 -17.140625 +Q 15.09375 -16.203125 17.140625 -14.25 +Q 19.203125 -12.296875 20.046875 -9.390625 +Q 20.90625 -6.5 20.90625 -2.5 +L 20.90625 51.703125 +L 6.703125 51.703125 +L 6.703125 -1.796875 +Q 6.703125 -4.703125 5.046875 -5.546875 +Q 3.40625 -6.40625 1.203125 -6.40625 +Q -0.703125 -6.40625 -1.796875 -6 +z +" id="HelveticaNeue-Bold-106"/> + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + - - + + diff --git a/docs/tutorial/tutorial_67_1.svg b/docs/tutorial/tutorial_84_1.svg similarity index 100% rename from docs/tutorial/tutorial_67_1.svg rename to docs/tutorial/tutorial_84_1.svg diff --git a/docs/tutorial/tutorial_85_0.svg b/docs/tutorial/tutorial_85_0.svg deleted file mode 100644 index 852bc118a..000000000 --- a/docs/tutorial/tutorial_85_0.svg +++ /dev/null @@ -1,10979 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_70_1.svg b/docs/tutorial/tutorial_87_1.svg similarity index 100% rename from docs/tutorial/tutorial_70_1.svg rename to docs/tutorial/tutorial_87_1.svg diff --git a/docs/tutorial/tutorial_89_0.svg b/docs/tutorial/tutorial_89_0.svg deleted file mode 100644 index c6c9ec360..000000000 --- a/docs/tutorial/tutorial_89_0.svg +++ /dev/null @@ -1,11768 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_73_0.svg b/docs/tutorial/tutorial_90_0.svg similarity index 100% rename from docs/tutorial/tutorial_73_0.svg rename to docs/tutorial/tutorial_90_0.svg diff --git a/docs/tutorial/tutorial_91_0.svg b/docs/tutorial/tutorial_91_0.svg deleted file mode 100644 index 0a1ddda68..000000000 --- a/docs/tutorial/tutorial_91_0.svg +++ /dev/null @@ -1,8595 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_76_0.svg b/docs/tutorial/tutorial_93_0.svg similarity index 100% rename from docs/tutorial/tutorial_76_0.svg rename to docs/tutorial/tutorial_93_0.svg diff --git a/docs/tutorial/tutorial_94_0.svg b/docs/tutorial/tutorial_94_0.svg deleted file mode 100644 index af9bc66d1..000000000 --- a/docs/tutorial/tutorial_94_0.svg +++ /dev/null @@ -1,3839 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial/tutorial_96_0.svg b/docs/tutorial/tutorial_96_0.svg new file mode 100644 index 000000000..f55c08d6b --- /dev/null +++ b/docs/tutorial/tutorial_96_0.svg @@ -0,0 +1,4613 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tutorial/tutorial_97_0.svg b/docs/tutorial/tutorial_97_0.svg deleted file mode 100644 index 9f04d194c..000000000 --- a/docs/tutorial/tutorial_97_0.svg +++ /dev/null @@ -1,5338 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/tutorial1.rst b/docs/tutorial1.rst index 2d50984bf..16c2fbad9 100644 --- a/docs/tutorial1.rst +++ b/docs/tutorial1.rst @@ -183,60 +183,237 @@ is demonstrated in the below example. .. image:: tutorial/tutorial_10_0.svg -The rc object -------------- +Plotting commands +----------------- + +In ProPlot, axes plotting commands like +`~matplotlib.axes.Axes.contourf`, +`~matplotlib.axes.Axes.pcolormesh`, `~matplotlib.axes.Axes.plot` +work just like they do in matplotlib, but with several new features. +There are also a few new plotting commands, like +`~proplot.axes.Axes.heatmap`, `~proplot.axes.Axes.area`, and +`~proplot.axes.Axes.areax`. For details on these features, see +:ref:`Plotting wrappers` and :ref:`Color usage`. -A special object named `~proplot.rctools.rc`, belonging to the -`~proplot.rctools.rc_configurator` class, is created whenever you -import ProPlot. This object gives you advanced control over the look of -your plots – it is your **one-stop shop for changing global settings**. -`~proplot.rctools.rc` can be used to change matplotlib -`rcParams `__ settings, -custom ProPlot :ref:`rcExtraParams` settings, and special -:ref:`rcGlobals` meta-settings. See the `~proplot.rctools` -documentation for more info. +.. code:: ipython3 -To modify a setting for just one subplot, pass it to the -`~proplot.axes.Axes.format` command. To reset everything to the -default state, use `~proplot.rctools.rc_configurator.reset`. To -temporarily modify global settings for a block of code, use -`~proplot.rctools.rc_configurator.context`. + import proplot as plot + f, axs = plot.subplots(axwidth=1.5, ncols=2, nrows=2, share=False) + cycle = plot.Cycle('blues', 5) + data = np.random.rand(10,10) + axs[0].plot(data, cycle=cycle, lw=3) + for i in range(5): + axs[1].scatter(data[:,i], data[:,5+i], s=50, cycle=cycle) + axs[2].pcolormesh(data, cmap='reds', colorbar='b') + axs[3].contourf(data, cmap='reds', colorbar='b') + axs.format(suptitle='Super title', title='Title') + + + +.. image:: tutorial/tutorial_12_0.svg + + +Axes colorbars and legends +-------------------------- + +Drawing colorbars and legends is a much smoother experience with +ProPlot. To draw a colorbar or legend along the outside of an axes, use +the `~proplot.axes.Axes.colorbar` and `~proplot.axes.Axes.legend` +``Axes`` methods with e.g. ``loc='right'``. If you do this multiple +times, the colorbars and legends will be “stacked”. Room for colorbars +and legends is allocated from the space between subplot rows and columns +– it is no longer stolen from the axes. + +To plot data and draw a colorbar or legend in one go, pass e.g. +``colorbar='right'`` to any method wrapped by +`~proplot.wrappers.cmap_wrapper`, or e.g. ``colorbar='right'`` or +``legend='right'`` to any method wrapped by +`~proplot.wrappers.cycle_wrapper`. To draw an *inset* colorbar, use +one of the *inset* locations, e.g. ``colorbar='upper right'`` or +``colorbar='ur'``. Inset colorbars have optional rectangular +backgrounds, just like inset legends. .. code:: ipython3 import proplot as plot import numpy as np - # A bunch of different ways to update settings - plot.rc.reset() - plot.rc.cycle = 'colorblind' - plot.rc.update({'fontname': 'DejaVu Sans'}) - plot.rc['figure.facecolor'] = 'gray3' - plot.rc['axes.facecolor'] = 'gray5' - with plot.rc.context(linewidth=1.5): # above mods are persistent, context mod only applies to figure - f, axs = plot.subplots(ncols=2, aspect=1, width=6, span=False, sharey=2) - # Make plot - N, M = 100, 6 - values = np.arange(1,M+1) - cycle = plot.Cycle('C0', 'C1', M, fade=80) - for i,ax in enumerate(axs): - data = np.cumsum(np.random.rand(N,M)-0.5, axis=0) - lines = ax.plot(data, linewidth=3, cycle=cycle) # see "Changing the color cycle" for details - axs.format(ytickloc='both', ycolor='blue7', - xlabel='x label', ylabel='y label', - yticklabelloc='both', - suptitle='Applying new rc settings', - patch_kw={'hatch':'xxx', 'edgecolor':'w'}) - ay = axs[-1].twinx() - ay.format(ycolor='r', linewidth=1.5, ylabel='secondary axis') - ay.plot((np.random.rand(100)-0.2).cumsum(), color='r', lw=3) + with plot.rc.context(abc=True): + f, axs = plot.subplots(ncols=2, share=0) + # Colorbars + ax = axs[0] + m = ax.heatmap(np.random.rand(10,10), colorbar='t', cmap='dusk') + ax.colorbar(m, loc='r') + ax.colorbar(m, loc='ll', label='colorbar label') + ax.format(title='Axes colorbars', suptitle='Axes colorbars and legends demo') + # Legends + ax = axs[1] + ax.format(title='Axes legends', titlepad='0em') + hs = ax.plot((np.random.rand(10,5)-0.5).cumsum(axis=0), lw=3, legend='t', cycle='sharp', + labels=list('abcde'), legend_kw={'ncols':5, 'frame':False}) + ax.legend(hs, loc='r', ncols=1, frame=False) + ax.legend(hs, loc='ll', label='legend label') + axs.format(xlabel='xlabel', ylabel='ylabel') +.. image:: tutorial/tutorial_14_0.svg + + +.. code:: ipython3 + + import proplot as plot + import numpy as np + f, axs = plot.subplots(nrows=2, share=0, axwidth='4cm', panelpad='1em') + axs.format(suptitle='Stacked colorbars demo') + N = 10 + for j,ax in enumerate(axs): + ax.format(xlabel='data', xlocator=np.linspace(0, 0.8, 5), title=f'Subplot #{j+1}') + for i,(x0,y0,x1,y1,cmap,scale) in enumerate(((0,0.5,1,1,'grays',0.5), (0,0,0.5,0.5,'reds',1), (0.5,0,1,0.5,'blues',2))): + if j == 1 and i == 0: + continue + data = np.random.rand(N,N)*scale + x, y = np.linspace(x0, x1, 11), np.linspace(y0, y1, 11) + m = ax.pcolormesh(x, y, data, cmap=cmap, levels=np.linspace(0,scale,11)) + ax.colorbar(m, loc='l', label=f'dataset #{i+1}') + + + +.. image:: tutorial/tutorial_15_0.svg + + +Figure colorbars and legends +---------------------------- + +To draw a colorbar or legend along the edge of a figure, use the +`~proplot.subplots.Figure.colorbar` or +`~proplot.subplots.Figure.legend` ``Figure`` methods. The colorbar or +legend will be aligned between edges of the subplot grid, instead of the +figure bounds. + +To draw a colorbar or legend beneath particular row(s) and column(s) of +the subplot grid, use the ``row``, ``rows``, ``col``, or ``cols`` +keyword arguments. Pass an integer to draw the colorbar or legend beside +a single row or column, or pass a tuple to draw it beside a range of +rows or columns. + +.. code:: ipython3 + + import proplot as plot + import numpy as np + f, axs = plot.subplots(ncols=3, nrows=3, axwidth=1.2) + m = axs.pcolormesh(np.random.rand(20,20), cmap='grays', levels=np.linspace(0,1,11), extend='both')[0] + axs.format(suptitle='Figure colorbars and legends demo', abc=True, abcloc='l', abcformat='a.', xlabel='xlabel', ylabel='ylabel') + f.colorbar(m, label='label', ticks=0.5, loc='b', col=1) + f.colorbar(m, label='label', ticks=0.2, loc='b', cols=(2,3)) + f.colorbar(m, label='label', ticks=0.1, loc='r', length=0.7) + + + + + + + +.. image:: tutorial/tutorial_17_1.svg + + +.. code:: ipython3 + + import proplot as plot + import numpy as np + f, axs = plot.subplots(ncols=4, axwidth=1.3, share=0, wspace=0.3) + data = (np.random.rand(50,50)-0.1).cumsum(axis=0) + m = axs[:2].contourf(data, cmap='grays', extend='both') + cycle = plot.colors('grays', 5) + hs = [] + for abc,color in zip('ABCDEF',cycle): + h = axs[2:].plot(np.random.rand(10), lw=3, color=color, label=f'line {abc}') + hs.extend(h[0]) + f.colorbar(m[0], length=0.8, label='label', loc='b', cols=(1,2)) + f.legend(hs, ncols=5, label='label', frame=True, loc='b', cols=(3,4)) + axs.format(suptitle='Figure colorbars and legends demo', abc=True, abcloc='ul', abcformat='A') + for ax,title in zip(axs, ['2D dataset #1', '2D dataset #2', 'Line set #1', 'Line set #2']): + ax.format(xlabel='xlabel', title=title) + + + +.. image:: tutorial/tutorial_18_0.svg +New colorbar and legend features +-------------------------------- +The `~proplot.subplots.Figure` and `~proplot.axes.Axes` ``colorbar`` +and ``legend`` methods are wrapped by +`~proplot.wrappers.colorbar_wrapper` and +`~proplot.wrappers.legend_wrapper`, which add several new features. -.. image:: tutorial/tutorial_12_1.svg +`~proplot.wrappers.colorbar_wrapper` can draw colorbars from *lists of +colors* or *lists of artists* by passing a list instead of a “mappable” +object – a colormap is constructed from the corresponding colors +on-the-fly. To change outline, divider, tick location, tick label, and +colorbar label settings, just pass the appropriate keyword arg to +`~proplot.wrappers.colorbar_wrapper`. + +`~proplot.wrappers.legend_wrapper` can draw legends with *centered +legend rows*, either by passing ``center=True`` or by passing *list of +lists* of plot handles. This is accomplished by stacking multiple +single-row, horizontally centered legends, then manually adding an +encompassing legend frame. You can also switch between row-major and +column-major order for legend entries (the new default is row-major), +and modify legend text properties and handle properties. + +.. code:: ipython3 + + import proplot as plot + import numpy as np + f, axs = plot.subplots(share=0, ncols=2) + ax = axs[0] + data = 1 + (np.random.rand(12,10)-0.45).cumsum(axis=0) + cycle = plot.Cycle('algae') + hs = ax.plot(data, lw=4, cycle=cycle, colorbar='lr', colorbar_kw={'length':'14em', 'label':'numeric values'}) + ax.colorbar(hs, loc='t', values=np.linspace(0.5,9.5,10)*2, label='alt numeric values', ticks=2) + ax = axs[1] + m = ax.contourf(data.T, extend='both', cmap='algae') + f.colorbar(m, length=0.6, loc='b', label='flipped tick location', tickloc='top', grid=True) + ax.colorbar(m, loc='ul', length=1, ticks=0.5, tickminor=True, extendrect=True, + label='changing colors', labelcolor='gray7', labelweight='bold', + linewidth=1, edgecolor='gray7', ticklabelcolor='gray7', alpha=0.5) + axs.format(suptitle='Colorbar formatting demo', xlabel='xlabel', ylabel='ylabel') + + + +.. image:: tutorial/tutorial_21_0.svg + + +.. code:: ipython3 + + import proplot as plot + import numpy as np + plot.rc.cycle = 'contrast' + labels = ['a', 'bb', 'ccc', 'dddd', 'eeeee'] + f, axs = plot.subplots(ncols=2, span=False, share=1) + hs1, hs2 = [], [] + # Plot lines and add to legends on-the-fly + for i,label in enumerate(labels): + data = (np.random.rand(20)-0.45).cumsum(axis=0) + h1 = axs[0].plot(data, lw=4, label=label, legend='ul', + legend_kw={'order':'F', 'title':'column major'}) # add to legend in upper left + hs1.extend(h1) + h2 = axs[1].plot(data, lw=4, label=label, legend='r', cycle='floral', + legend_kw={'ncols':1, 'frame':False, 'title':'no frame'}) # add to legend in right panel + hs2.extend(h2) + # Outer legends + ax = axs[0] + ax.legend(hs1, loc='b', ncols=3, linewidth=2, title='row major', order='C', + edgecolor='gray4', facecolor='gray2') + ax = axs[1] + ax.legend(hs2, loc='b', ncols=3, center=True, title='centered legend', + handlelength=1) # also works! + axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Legend formatting demo') + + + +.. image:: tutorial/tutorial_22_0.svg Pandas and xarray integration @@ -301,7 +478,7 @@ and :ref:`On-the-fly axes panels`. -.. image:: tutorial/tutorial_16_2.svg +.. image:: tutorial/tutorial_26_2.svg .. code:: ipython3 @@ -338,7 +515,7 @@ and :ref:`On-the-fly axes panels`. -.. image:: tutorial/tutorial_18_0.svg +.. image:: tutorial/tutorial_28_0.svg Automatic subplot spacing @@ -383,11 +560,11 @@ instead. -.. image:: tutorial/tutorial_21_0.svg +.. image:: tutorial/tutorial_31_0.svg -.. image:: tutorial/tutorial_21_1.svg +.. image:: tutorial/tutorial_31_1.svg .. code:: ipython3 @@ -403,7 +580,7 @@ instead. -.. image:: tutorial/tutorial_22_0.svg +.. image:: tutorial/tutorial_32_0.svg .. code:: ipython3 @@ -421,7 +598,7 @@ instead. -.. image:: tutorial/tutorial_23_0.svg +.. image:: tutorial/tutorial_33_0.svg Axis sharing and spanning @@ -454,19 +631,19 @@ example for details. -.. image:: tutorial/tutorial_26_0.svg +.. image:: tutorial/tutorial_36_0.svg -.. image:: tutorial/tutorial_26_1.svg +.. image:: tutorial/tutorial_36_1.svg -.. image:: tutorial/tutorial_26_2.svg +.. image:: tutorial/tutorial_36_2.svg -.. image:: tutorial/tutorial_26_3.svg +.. image:: tutorial/tutorial_36_3.svg .. code:: ipython3 @@ -484,11 +661,11 @@ example for details. -.. image:: tutorial/tutorial_27_0.svg +.. image:: tutorial/tutorial_37_0.svg -.. image:: tutorial/tutorial_27_1.svg +.. image:: tutorial/tutorial_37_1.svg A-b-c subplot labels @@ -512,7 +689,7 @@ the ``abc.format`` `~proplot.rctools.rc` option. See -.. image:: tutorial/tutorial_29_0.svg +.. image:: tutorial/tutorial_39_0.svg Arbitrary physical units @@ -535,6 +712,62 @@ millimeters, and pixels). -.. image:: tutorial/tutorial_32_0.svg +.. image:: tutorial/tutorial_42_0.svg + + +The rc object +------------- + +A special object named `~proplot.rctools.rc`, belonging to the +`~proplot.rctools.rc_configurator` class, is created whenever you +import ProPlot. This object gives you advanced control over the look of +your plots – it is your **one-stop shop for changing global settings**. +`~proplot.rctools.rc` can be used to change matplotlib +`rcParams `__ settings, +custom ProPlot :ref:`rcExtraParams` settings, and special +:ref:`rcGlobals` meta-settings. See the `~proplot.rctools` +documentation for more info. + +To modify a setting for just one subplot, pass it to the +`~proplot.axes.Axes.format` command. To reset everything to the +default state, use `~proplot.rctools.rc_configurator.reset`. To +temporarily modify global settings for a block of code, use +`~proplot.rctools.rc_configurator.context`. + +.. code:: ipython3 + + import proplot as plot + import numpy as np + # A bunch of different ways to update settings + plot.rc.reset() + plot.rc.cycle = 'colorblind' + plot.rc.update({'fontname': 'DejaVu Sans'}) + plot.rc['figure.facecolor'] = 'gray3' + plot.rc['axes.facecolor'] = 'gray5' + with plot.rc.context(linewidth=1.5): # above mods are persistent, context mod only applies to figure + f, axs = plot.subplots(ncols=2, aspect=1, width=6, span=False, sharey=2) + # Make plot + N, M = 100, 6 + values = np.arange(1,M+1) + cycle = plot.Cycle('C0', 'C1', M, fade=80) + for i,ax in enumerate(axs): + data = np.cumsum(np.random.rand(N,M)-0.5, axis=0) + lines = ax.plot(data, linewidth=3, cycle=cycle) # see "Changing the color cycle" for details + axs.format(ytickloc='both', ycolor='blue7', + xlabel='x label', ylabel='y label', + yticklabelloc='both', + suptitle='Applying new rc settings', + patch_kw={'hatch':'xxx', 'edgecolor':'w'}) + ay = axs[-1].twinx() + ay.format(ycolor='r', linewidth=1.5, ylabel='secondary axis') + ay.plot((np.random.rand(100)-0.2).cumsum(), color='r', lw=3) + + + + + + + +.. image:: tutorial/tutorial_44_1.svg diff --git a/docs/tutorial2.rst b/docs/tutorial2.rst index 225ce838a..32a04c07e 100644 --- a/docs/tutorial2.rst +++ b/docs/tutorial2.rst @@ -43,7 +43,7 @@ usually what you’ll want in this context. See -.. image:: tutorial/tutorial_36_0.svg +.. image:: tutorial/tutorial_48_0.svg Axis tick label formatting @@ -72,7 +72,7 @@ some data range*, as demonstrated below. See -.. image:: tutorial/tutorial_39_0.svg +.. image:: tutorial/tutorial_51_0.svg ProPlot also lets you easily change the axis formatter with @@ -107,7 +107,7 @@ See `~proplot.axes.CartesianAxes.format` and -.. image:: tutorial/tutorial_41_0.svg +.. image:: tutorial/tutorial_53_0.svg New and old axis scales @@ -157,7 +157,7 @@ labeling spectral coordinates (this is more useful with the -.. image:: tutorial/tutorial_44_1.svg +.. image:: tutorial/tutorial_56_1.svg .. code:: ipython3 @@ -187,7 +187,7 @@ labeling spectral coordinates (this is more useful with the -.. image:: tutorial/tutorial_45_0.svg +.. image:: tutorial/tutorial_57_0.svg .. code:: ipython3 @@ -227,7 +227,7 @@ labeling spectral coordinates (this is more useful with the -.. image:: tutorial/tutorial_46_0.svg +.. image:: tutorial/tutorial_58_0.svg Datetime axis formatting @@ -271,7 +271,7 @@ details. -.. image:: tutorial/tutorial_49_0.svg +.. image:: tutorial/tutorial_61_0.svg Dual unit axes @@ -323,11 +323,11 @@ pass the name of any registered “axis scale” to the ``xscale`` or -.. image:: tutorial/tutorial_52_1.svg +.. image:: tutorial/tutorial_64_1.svg -.. image:: tutorial/tutorial_52_2.svg +.. image:: tutorial/tutorial_64_2.svg .. code:: ipython3 @@ -358,6 +358,94 @@ pass the name of any registered “axis scale” to the ``xscale`` or -.. image:: tutorial/tutorial_53_1.svg +.. image:: tutorial/tutorial_65_1.svg + + +Panel axes +---------- + +It is common to need “panels” for plotting secondary 1-dimensional +datasets or summary statistics along the sides of axes. With ProPlot, +you can generate “panels” using the `~proplot.axes.Axes.panel` or +`~proplot.axes.Axes.panel_axes` commands. The axes returned by panels +are instances of `~proplot.axes.CartesianAxes`. + +To generate “stacked” panels, call `~proplot.axes.Axes.panel` or +`~proplot.axes.Axes.panel_axes` more than once. To include panels when +centering spanning axis labels and super titles, pass +``includepanels=True`` to `~proplot.subplots.subplots`. + +.. code:: ipython3 + + import proplot as plot + f, axs = plot.subplots(axwidth=1.2, nrows=2, ncols=2, share=0) + for ax,side in zip(axs,'tlbr'): + ax.panel_axes(side) + axs.format(title='Title', suptitle='Panel axes demo', collabels=['Column 1','Column 2'], + abcloc='ul', titleloc='uc', xlabel='xlabel', ylabel='ylabel', abc=True, top=False) + axs.format(xlim=(0,1), ylim=(0,1), ylocator=plot.arange(0.2,0.8,0.2), xlocator=plot.arange(0.2,0.8,0.2)) + + + +.. image:: tutorial/tutorial_67_0.svg + + +.. code:: ipython3 + + import proplot as plot + import numpy as np + data = (np.random.rand(20,20)-0.1).cumsum(axis=1) + f, axs = plot.subplots(axwidth=1.5, nrows=2, ncols=2, share=0, panelpad=0.1, includepanels=True) + maxs = axs.panel('r', space=0) + saxs = axs.panel('r', space=0, share=False) + axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Panel axes demo') + for i,ax in enumerate(axs): + ax.format(title=f'Dataset {i+1}') + axs.contourf(data, cmap='glacial', levels=plot.arange(-1,11), + colorbar='b', colorbar_kw={'label':'cbar'}, extend='both') + maxs.plot(data.mean(axis=1), np.arange(20), color='gray7') + maxs.format(title='Mean') + saxs.plot(data.std(axis=1), np.arange(20), color='gray7', ls='--') + saxs.format(title='Stdev') + + + +.. image:: tutorial/tutorial_68_0.svg + + +Inset axes +---------- + +`Inset +axes `__ +in ProPlot can be generated with the `~proplot.axes.Axes.inset` or +`~proplot.axes.Axes.inset_axes` command. The generated axes are +instances of `~proplot.axes.CartesianAxes`, and therefore can be +modified with the `~proplot.axes.CartesianAxes.format` command. +Passing ``zoom=True`` to `~proplot.axes.Axes.inset` draws a “zoom +indication” with `~matplotlib.axes.Axes.indicate_inset_zoom`, and the +“zoom indication” lines are *updated* when the axis limits of the parent +axes change. To modify the “zoom indication” line properties, simply use +the ``zoom_kw`` argument. + +.. code:: ipython3 + + import proplot as plot + import numpy as np + N = 20 + f, ax = plot.subplots() + x, y = np.arange(10), np.arange(10) + data = np.random.rand(10,10) + m = ax.pcolormesh(data, cmap='Grays', levels=N) + ax.colorbar(m, loc='b', label='label') + ax.format(xlabel='xlabel', ylabel='ylabel') + axi = ax.inset([5,5,4,4], transform='data', zoom=True, zoom_kw={'color':'red', 'lw':2}) + axi.format(xlim=(2,4), ylim=(2,4), color='red', linewidth=1.5, ticklabelweight='bold') + axi.pcolormesh(data, cmap='Grays', levels=N) + ax.format(suptitle='Inet axes demo') + + + +.. image:: tutorial/tutorial_70_0.svg diff --git a/docs/tutorial3.rst b/docs/tutorial3.rst index a91111358..ecbdd0d0c 100644 --- a/docs/tutorial3.rst +++ b/docs/tutorial3.rst @@ -56,11 +56,11 @@ the `cartopy.crs.Projection` classes, e.g. ``lon_0`` instead of -.. image:: tutorial/tutorial_58_3.svg +.. image:: tutorial/tutorial_75_3.svg -.. image:: tutorial/tutorial_58_4.svg +.. image:: tutorial/tutorial_75_4.svg Plotting geophysical data @@ -111,11 +111,11 @@ These features are powered by the `~proplot.wrappers.cartopy_gridfix`, -.. image:: tutorial/tutorial_61_0.svg +.. image:: tutorial/tutorial_78_0.svg -.. image:: tutorial/tutorial_61_1.svg +.. image:: tutorial/tutorial_78_1.svg Formatting projection axes @@ -152,7 +152,7 @@ subplot titles, a-b-c labels, and figure titles as before. -.. image:: tutorial/tutorial_64_0.svg +.. image:: tutorial/tutorial_81_0.svg Zooming into projections @@ -193,11 +193,11 @@ any of the ``boundinglat``, ``llcrnrlon``, ``llcrnrlat``, ``urcrnrlon``, -.. image:: tutorial/tutorial_67_0.svg +.. image:: tutorial/tutorial_84_0.svg -.. image:: tutorial/tutorial_67_1.svg +.. image:: tutorial/tutorial_84_1.svg Registered cartopy projections @@ -234,7 +234,7 @@ Lambert Azimuthal Equal-Area, and Gnomic projections (i.e. -.. image:: tutorial/tutorial_70_1.svg +.. image:: tutorial/tutorial_87_1.svg Registered basemap projections @@ -264,7 +264,7 @@ args to `~mpl_toolkits.basemap.Basemap` if you fail to specify them. -.. image:: tutorial/tutorial_73_0.svg +.. image:: tutorial/tutorial_90_0.svg Polar projections @@ -300,6 +300,77 @@ axes, just pass e.g. ``proj='polar'`` or ``proj={1:'polar'}`` to -.. image:: tutorial/tutorial_76_0.svg +.. image:: tutorial/tutorial_93_0.svg + + +It is common to need colorbars and legends along the outside edge of +*individual axes* or on the inner edge of the *figure*. It is also +common to need “panels” for plotting secondary 1-dimensional datasets or +summary statistics next to a larger subplot. ProPlot satisfies both of +these needs with the `~proplot.axes.PanelAxes` class, which can be +generated with the `~proplot.subplots.subplots` and +`~proplot.axes.Axes.panel_axes` functions. + +`~proplot.axes.PanelAxes` have special +`~proplot.axes.PanelAxes.colorbar` and +`~proplot.axes.PanelAxes.legend` methods. +`~proplot.axes.PanelAxes.colorbar` *fills* the panel with a colorbar – +that is, the panel is used as the ``cax`` argument in the call to +`~matplotlib.figure.Figure.colorbar`, and its default width is +changed. `~proplot.axes.PanelAxes.legend` *fills* the panel with a +legend – that is, a legend is drawn in the center, and the axes +background and spines are hidden. + +On-the-fly panels are a great way to draw colorbars and legends along +the edges of axes. There are three ways to generate and *fill* an +on-the-fly axes panel. + +1. Pass ``colorbar`` to any method wrapped by + `~proplot.wrappers.cmap_wrapper`, or pass ``colorbar`` or + ``legend`` to any method wrapped by + `~proplot.wrappers.cycle_wrapper`. The argument is the panel + location, e.g. ``colorbar='left'`` or ``colorbar='l'``. To specify + panel settings, use the ``panel_kw`` keyword arg. +2. Pass ``loc`` to the `~proplot.axes.Axes.colorbar` or + `~proplot.axes.Axes.legend` ``Axes`` methods. Again, the argument + is the panel location, e.g. ``loc='left'`` or ``loc='l'``. This is + what approach #1 does internally. To specify panel settings, use the + ``panel_kw`` keyword arg. +3. Directly call the `~proplot.axes.Axes.panel_axes` method, e.g. + ``pax = ax.panel('l', **kwargs)``, and then call the + `~proplot.axes.PanelAxes.colorbar` or + `~proplot.axes.PanelAxes.legend` ``PanelAxes`` methods on ``pax``. + This is what the approach #2 does internally. + +No matter the combination of axes panels in your subplot grid, the +layout will stay aligned. To modify default panel settings, use the +`~proplot.rctools.rc` object or create a custom ``.proplotrc`` file +(see the `~proplot.rctools` documentation for details). + +.. code:: ipython3 + + import proplot as plot + import numpy as np + with plot.rc.context(abc=True): + f, axs = plot.subplots(ncols=2, tight=True, share=0) + ax = axs[0] + m = ax.heatmap(np.random.rand(10,10), colorbar='t', cmap='dusk') + ax.colorbar(m, loc='r') + ax.format(title='On-the-fly colorbars', suptitle='On-the-fly panels demo') + ax = axs[1] + ax.format(title='On-the-fly legends', titlepad='0em') + hs = ax.plot((np.random.rand(10,5)-0.5).cumsum(axis=0), lw=3, legend='t', cycle='sharp', + labels=list('abcde'), legend_kw={'ncols':5, 'frame':False}) + ax.legend(hs, loc='r', ncols=1, frame=False) + # Calling the panel method + for ax in axs: + pax = ax.panel('b', share=True) + pax.plot(np.random.rand(10,4), cycle_kw={'linestyle':('-','--','-.',':')}) + # ax.bpanel.plot(...) # also works! + axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Super title') + + + +.. image:: tutorial/tutorial_96_0.svg diff --git a/docs/tutorial4.rst b/docs/tutorial4.rst index bdc8bbd43..d4701e6c9 100644 --- a/docs/tutorial4.rst +++ b/docs/tutorial4.rst @@ -1,357 +1,647 @@ -Panel axes -========== - -It is common to need colorbars and legends along the outside edge of -*individual axes* or on the inner edge of the *figure*. It is also -common to need “panels” for plotting secondary 1-dimensional datasets or -summary statistics next to a larger subplot. ProPlot satisfies both of -these needs with the `~proplot.axes.PanelAxes` class, which can be -generated with the `~proplot.subplots.subplots` and -`~proplot.axes.Axes.panel_axes` functions. - -`~proplot.axes.PanelAxes` have special -`~proplot.axes.PanelAxes.colorbar` and -`~proplot.axes.PanelAxes.legend` methods. -`~proplot.axes.PanelAxes.colorbar` *fills* the panel with a colorbar – -that is, the panel is used as the ``cax`` argument in the call to -`~matplotlib.figure.Figure.colorbar`, and its default width is -changed. `~proplot.axes.PanelAxes.legend` *fills* the panel with a -legend – that is, a legend is drawn in the center, and the axes -background and spines are hidden. - -On-the-fly panels ------------------ - -On-the-fly panels are a great way to draw colorbars and legends along -the edges of axes. There are three ways to generate and *fill* an -on-the-fly axes panel. - -1. Pass ``colorbar`` to any method wrapped by - `~proplot.wrappers.cmap_wrapper`, or pass ``colorbar`` or - ``legend`` to any method wrapped by - `~proplot.wrappers.cycle_wrapper`. The argument is the panel - location, e.g. ``colorbar='left'`` or ``colorbar='l'``. To specify - panel settings, use the ``panel_kw`` keyword arg. -2. Pass ``loc`` to the `~proplot.axes.Axes.colorbar` or - `~proplot.axes.Axes.legend` ``Axes`` methods. Again, the argument - is the panel location, e.g. ``loc='left'`` or ``loc='l'``. This is - what approach #1 does internally. To specify panel settings, use the - ``panel_kw`` keyword arg. -3. Directly call the `~proplot.axes.Axes.panel_axes` method, e.g. - ``pax = ax.panel('l', **kwargs)``, and then call the - `~proplot.axes.PanelAxes.colorbar` or - `~proplot.axes.PanelAxes.legend` ``PanelAxes`` methods on ``pax``. - This is what the approach #2 does internally. - -No matter the combination of axes panels in your subplot grid, the -layout will stay aligned. To modify default panel settings, use the -`~proplot.rctools.rc` object or create a custom ``.proplotrc`` file -(see the `~proplot.rctools` documentation for details). +Color usage +=========== + +ProPlot isn’t just an alternative to `~matplotlib.pyplot`. It also +adds some neat features to help you use colors effectively in your +figures, and integrates palettes from several online data visualization +tools. + +First things first, ProPlot makes a distinction between *colormaps* and +*color cycles*. + +- A *colormap* is a palette constructed by sampling some *smooth, + linear* function between two end colors. Colormaps are generally used + for 2-D or 3-D plots, where the color serves as an extra “dimension”. + This is implemented in matplotlib with the + `~matplotlib.colors.LinearSegmentedColormap` class, and also with + the special ProPlot + `~proplot.styletools.PerceptuallyUniformColormap` subclass (see + :ref:`Making your own colormaps`). +- A *color cycle* is a palette composed of a *jumbled set* of distinct + colors. Interpolation between these colors does not make sense. Color + cycles are generally used with line plots, bar plots, and other plot + elements. They are conceptually implemented in matplotlib with the + `~matplotlib.colors.ListedColormap` class (although it is often + improperly used). ProPlot uses this class to register color cycles, + and the color cycles are “applied” by globally or axes-locally + modifying the `property + cycler `__. + *Colormaps* can also be cut up and used as color cycles (see + :ref:`Making your own color cycles`). + +This section documents the colormaps and cycles registered after +importing ProPlot, explains how to make custom colormaps and cycles, and +shows how to apply them to your plots using axes methods wrapped by +`~proplot.wrappers.cmap_wrapper` or +`~proplot.wrappers.cycle_wrapper`. + +Registered colormaps +-------------------- + +On import, ProPlot registers a few sample +`~proplot.styletools.PerceptuallyUniformColormap` colormaps (see +:ref:`Perceptually uniform colormaps`) plus a ton of other colormaps +from other online data viz projects. Use +`~proplot.styletools.show_cmaps` to generate a table of registered +maps, as shown below. The figure is broken down into the following +sections: + +- “User” colormaps, i.e. colormaps saved to your ``~/.proplot/cmaps`` + folder. A great way to save colormaps to this folder is using the + `~proplot.styletools.Colormap` function. See + :ref:`Making your own colormaps` for details. +- Matplotlib and seaborn original colormaps. +- ProPlot colormaps belonging to the + `~proplot.styletools.PerceptuallyUniformColormap` class. See the + :ref:`Perceptually uniform colormaps` section. +- Miscellaneous diverging colormaps. +- `cmOcean `__ colormaps, originally + intended for oceanographic visualizations but useful for all + scientific fields. +- `ColorBrewer `__ colormaps, included with + matplotlib by default. +- Colormaps from the + `SciVisColor `__ online + interactive tool. There are so many of these maps because they are + intended to be *merged* with one another – suitable for complex + datasets with complex statistical distributions. + +ProPlot removes some default matplotlib colormaps with erratic color +transitions. Note that colormap and color cycle identification is now +flexible: names are *case-insensitive* (e.g. ``'Viridis'``, +``'viridis'``, and ``'ViRiDiS'`` are equivalent) and can be specified in +their “reversed” form (e.g. ``'BuRd'`` is equivalent to ``'RdBu_r'``). +See `~proplot.styletools.CmapDict` for more info. .. code:: ipython3 import proplot as plot - import numpy as np - with plot.rc.context(abc=True): - f, axs = plot.subplots(ncols=2, tight=True, share=0) - ax = axs[0] - m = ax.heatmap(np.random.rand(10,10), colorbar='t', cmap='dusk') - ax.colorbar(m, loc='r') - ax.format(title='On-the-fly colorbars', suptitle='On-the-fly panels demo') - ax = axs[1] - ax.format(title='On-the-fly legends', titlepad='0em') - hs = ax.plot((np.random.rand(10,5)-0.5).cumsum(axis=0), lw=3, legend='t', cycle='sharp', - labels=list('abcde'), legend_kw={'ncols':5, 'frame':False}) - ax.legend(hs, loc='r', ncols=1, frame=False) - # Calling the panel method - for ax in axs: - pax = ax.panel('b', share=True) - pax.plot(np.random.rand(10,4), cycle_kw={'linestyle':('-','--','-.',':')}) - # ax.bpanel.plot(...) # also works! - axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Super title') + f = plot.show_cmaps() + + + +.. image:: tutorial/tutorial_101_0.svg +Perceptually uniform colormaps +------------------------------ -.. image:: tutorial/tutorial_81_0.svg +ProPlot’s custom colormaps are instances of the new +`~proplot.styletools.PerceptuallyUniformColormap` class (see +:ref:`Registered colormaps` for a table). +`~proplot.styletools.PerceptuallyUniformColormap` objects generate +colors by interpolating between coordinates in any of three possible +“perceptually uniform” colorspaces: + +- **HCL**: A purely perceptually uniform colorspace, where colors are + broken down into “hue” (color, range 0-360), “chroma” (saturation, + range 0-100), and “luminance” (brightness, range 0-100). +- **HPLuv**: Hue and luminance are identical to HCL, but 100 saturation + is scaled to be the *minimum maximum saturation* across all hues for + a given luminance. HPLuv is more appropriate for multi-hue colormaps. +- **HSLuv**: Hue and luminance are identical to HCL, but 100 saturation + is scaled to be the *maximum possible saturation* for a given hue and + luminance. HSLuv is more appropriate for single-hue colormaps – + saturation banding can occur when crossing hue thresholds in this + space. + +HCL is the only “purely” perceptually uniform colorspace. But +interpolating between coordinates in this space can result in +“impossible” colors – colors that, when translated from HCL back into +RGB, result in RGB channels greater than 1. HSLuv and HPLuv help resolve +this issue by respectively *scaling* and *clipping* the +highest-saturation colors across different hues and luminances. See +`this page `__ for more info. + +To plot arbitrary cross-sections of these colorspaces, use +`~proplot.styletools.show_colorspaces`. The blacked out regions +represent “impossible” colors. + +.. code:: ipython3 + + import proplot as plot + f = plot.show_colorspaces(luminance=50) -Bulk axes panels ----------------- -If you want to plot stuff on panels in a large subplot array, generating -each panel on-the-fly would be cumbersome. That’s why -`~proplot.subplots.subplots` allows you to generate axes panels in -*bulk* with the ``axpanel`` or ``axpanels`` keyword args. To modify -panel properties, use the ``axpanel_kw`` or ``axpanels_kw`` dictionary -keyword args. See `~proplot.subplots.subplots` for details. +.. image:: tutorial/tutorial_104_0.svg -The below examples demonstrate a few more panel features. To draw panels -“flush” against the subplot, use the ``bflush``, ``tflush``, ``lflush``, -and ``rflush`` keyword args. If you want to disable “axis sharing” with -the parent subplot (see :ref:`Axis sharing and spanning`), use the -``bshare``, ``tshare``, ``rshare``, and ``lshare`` keyword args. See -`~proplot.axes.Axes.panel` for details. .. code:: ipython3 import proplot as plot - f, axs = plot.subplots(axwidth=1.5, nrows=2, ncols=2, - axpanels={1:'t', 2:'l', 3:'b', 4:'r'}, - tight=True, share=0) - axs.format(title='Title', suptitle='Bulk axes panels demo', collabels=['Column 1','Column 2'], - abcloc='ul', titleloc='uc', xlabel='xlabel', ylabel='ylabel', abc=True, top=False) - axs.format(xlim=(0,1), ylim=(0,1), ylocator=plot.arange(0.2,0.8,0.2), xlocator=plot.arange(0.2,0.8,0.2)) + f = plot.show_colorspaces(saturation=60) -.. image:: tutorial/tutorial_84_0.svg +.. image:: tutorial/tutorial_105_0.svg +.. code:: ipython3 + + import proplot as plot + f = plot.show_colorspaces(hue=0) + + + +.. image:: tutorial/tutorial_106_0.svg + + +To see how any colormap varies with respect to each channel, use the +`~proplot.styletools.cmap_breakdown` function. Below, we do this for +the “magma”, “rocket”, and ProPlot “Fire” colormaps. The first two are +nicely-designed `~matplotlib.colors.LinearSegmentedColormap` maps, and +the last one is a `~proplot.styletools.PerceptuallyUniformColormap`. +They are all roughly linear across the hue and luminance channels, but +not the chroma channel (top row). “Fire” is linear in the HSL scaling of +the chroma channel (bottom left), while other ProPlot colormaps are +linear in the HPL scaling of the chroma channel (bottom right). + +.. code:: ipython3 + + import proplot as plot + f = plot.show_channels('magma', 'rocket', 'fire', axwidth=1.2, minhue=-180, rgb=False) + + + +.. image:: tutorial/tutorial_108_0.svg + + +Making your own colormaps +------------------------- + +You can make new colormaps with ProPlot’s on-the-fly colormap generator +`~proplot.styletools.Colormap`. Every command that accepts a ``cmap`` +argument (see `~proplot.wrappers.cmap_wrapper`) is passed to +`~proplot.styletools.Colormap`, and `~proplot.styletools.Colormap` +keyword args can be specified with ``cmap_kw``. If you want to save your +own colormap into the ``~/.proplot/cmaps`` folder, simply use +``save=True``. Colormaps in this folder are loaded every time you import +ProPlot. See `~proplot.styletools.Colormap` and +`~proplot.wrappers.cmap_wrapper` for details. + +To build monochromatic +`~proplot.styletools.PerceptuallyUniformColormap` maps from arbitrary +colors, just pass a color name, hex string, or RGB tuple to +`~proplot.styletools.Colormap`. The colormap colors will vary from the +specified color to some shade near white (controlled by the ``fade`` +keyword arg). The default is to fade to pure white. The first plot shows +several of these maps merged into one, and the second is just one map. + .. code:: ipython3 import proplot as plot import numpy as np - plot.rc.reset() - f, axs = plot.subplots(axwidth=1.7, nrows=2, ncols=2, share=0, panelpad=0.1, - axpanels='r', axpanels_kw={'share':False, 'space':0}) - axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Bulk axes panels demo') - for i,ax in enumerate(axs): - ax.format(title=f'Dataset {i+1}') - data = (np.random.rand(20,20)-0.1).cumsum(axis=1) - m = axs.contourf(data, cmap='glacial', levels=plot.arange(-1,11), - colorbar='b', colorbar_kw={'label':'cbar'}) - axs.rpanel.plot(data.mean(axis=1), np.arange(20), color='k') - axs.rpanel.format(title='Mean') + f, axs = plot.subplots(ncols=2, axwidth=2, aspect=1, bottom=0.1) + data = np.random.rand(50,50).cumsum(axis=1) + cmap1 = plot.Colormap('brick red_r', 'denim_r', 'warm gray_r', fade=90, name='tricolor') + m = axs[0].contourf(data, cmap=cmap1, levels=12) + m = axs[1].contourf(data, cmap='ocean blue', cmap_kw={'name':'ocean blue'}) + cmap2 = m.cmap + axs.format(xticks='none', yticks='none', suptitle='Monochromatic PerceptuallyUniformColormaps') + for ax,title in zip(axs, ['Three monochromatic colormaps', 'One monochromatic colormap']): + ax.format(title=title) + f = plot.show_channels(cmap1, cmap2, axwidth=1.2, rgb=False) -.. image:: tutorial/tutorial_85_0.svg +.. image:: tutorial/tutorial_112_0.svg -Global figure panels --------------------- -ProPlot also supports “figure” panels. These panels are generally filled -with colorbars and legends as *global* references for content that -appears in more than one subplot. Figure panels are declared with the -``panel``, ``colorbar``, ``legend``, ``panels``, ``colorbars``, and -``legends`` keyword args. They can extend across entire sides of the -figure, or across arbitrary contiguous rows and columns of subplots, -using the ``barray``, ``rarray``, or ``larray`` keyword args. +.. image:: tutorial/tutorial_112_1.svg + -Figure panel axes are stored on the `~proplot.subplots.Figure` -instance as the attributes ``bottompanel``, ``leftpanel``, and -``rightpanel`` and the shorthands ``bpanel``, ``lpanel``, and -``rpanel``. See `~proplot.subplots.subplots` for details. +To generate `~proplot.styletools.PerceptuallyUniformColormap` maps, +you can pass a *dictionary* to `~proplot.styletools.Colormap`, which +calls the `~proplot.styletools.PerceptuallyUniformColormap.from_hsl` +static method, or pass a *list of colors* to +`~proplot.styletools.Colormap`, which calls the +`~proplot.styletools.PerceptuallyUniformColormap.from_list` static +method. + +`~proplot.styletools.PerceptuallyUniformColormap.from_list` +interpolates between the listed colors in a perceptually uniform +colorspace (see :ref:`Perceptually uniform colormaps`). +`~proplot.styletools.PerceptuallyUniformColormap.from_hsl` draws lines +between channel values specified by the keyword args ``'hue'``, +``'saturation'``, and ``'luminance'``. The values can be numbers, color +strings, or lists thereof. Numbers indicate the channel value. For color +strings, the channel value is *inferred* from the specified color. You +can end any color string with ``+N`` or ``-N`` to *offset* the channel +value by the number ``N``, as shown below. .. code:: ipython3 import proplot as plot import numpy as np - f, axs = plot.subplots(ncols=3, nrows=3, axwidth=1.2, panel='br', barray=[1,2,2]) - m = axs.pcolormesh(np.random.rand(20,20), cmap='grays', levels=np.linspace(0,1,11), extend='both')[0] - axs.format(suptitle='Figure panels demo', abc=True, abcloc='l', abcformat='a.', xlabel='xlabel', ylabel='ylabel') - f.bpanel[0].colorbar(m, label='label', ticks=0.5) - f.bpanel[1].colorbar(m, label='label', ticks=0.2) - f.rpanel.colorbar(m, label='label', ticks=0.1, length=0.7) + f, axs = plot.subplots(ncols=3, span=False, axwidth=2, aspect=1.5) + ax = axs[0] + # From dicts + data = np.random.rand(10,15) + cmap1 = plot.Colormap({'hue':['red-90', 'red+90'], 'saturation':[50, 70, 30], 'luminance':[20, 100]}, name='Matter', space='hcl') + m = ax.pcolormesh(data, cmap=cmap1) + ax.format(xlabel='x axis', ylabel='y axis', title='From channel values', + suptitle='Building your own PerceptuallyUniformColormaps') + ax = axs[1] + cmap2 = plot.Colormap({'hue':['red', 'red-720'], 'saturation':[80,20], 'luminance':[20, 100]}, name='cubehelix', space='hpl') + m = ax.pcolormesh(data, cmap=cmap2) + ax.format(xlabel='x axis', ylabel='y axis', title='From channel values') + # From list + ax = axs[2] + m = ax.pcolormesh(data, cmap=('maroon', 'goldenrod'), cmap_kw={'name':'reddish'}) + cmap3 = m.cmap + ax.format(title='From list of colors') + # Breakdowns + f = plot.show_channels(cmap1, cmap2, cmap3, minhue=-180, axwidth=1.2, rgb=False) + +.. image:: tutorial/tutorial_114_0.svg +.. image:: tutorial/tutorial_114_1.svg -.. image:: tutorial/tutorial_88_1.svg +Merging and modifying colormaps +------------------------------- +`~proplot.styletools.Colormap` also lets you merge arbitrary colormaps +and modify existing colormaps. To merge colormaps, simply pass multiple +arguments to the `~proplot.styletools.Colormap` constructor. This +makes it easy to create complex +`SciVisColor `__-style +colormaps, which may be desirable for complex datasets with funky +statistical distributions. The below reconstructs the colormap from +`this +example `__. .. code:: ipython3 import proplot as plot import numpy as np - f, axs = plot.subplots(ncols=4, axwidth=1.3, panel='b', barray=[1,1,2,2], share=0, wspace=0.3) - data = (np.random.rand(50,50)-0.1).cumsum(axis=0) - m = axs[:2].contourf(data, cmap='grays', extend='both') - cycle = plot.colors('grays', 5) - hs = [] - for abc,color in zip('ABCDEF',cycle): - h = axs[2:].plot(np.random.rand(10), lw=3, color=color, label=f'line {abc}') - hs.extend(h[0]) - f.bpanel[0].colorbar(m[0], length=0.8, label='label') - f.bpanel[1].legend(hs, ncols=5, label='label', frame=True) - axs.format(suptitle='Figure panels demo', abc=True, abcloc='ul', abcformat='A') - for ax,title in zip(axs, ['2D dataset #1', '2D dataset #2', 'Line set #1', 'Line set #2']): - ax.format(xlabel='xlabel', title=title) + f, axs = plot.subplots(ncols=2, axwidth=2, panels='b', span=False) + data = np.random.rand(100,100).cumsum(axis=1) + # Make colormap, save as "test1.json" + cmap = plot.Colormap('Green1_r', 'Orange5', 'Blue1_r', 'Blue6', name='test1', save=True) + m = axs[0].contourf(data, cmap=cmap, levels=100) + f.bpanel[0].colorbar(m, locator='none') + # Make colormap, save as "test2.json" + cmap = plot.Colormap('Green1_r', 'Orange5', 'Blue1_r', 'Blue6', ratios=(1,3,5,10), name='test2', save=True) + m = axs[1].contourf(data, cmap=cmap, levels=100) + f.bpanel[1].colorbar(m, locator='none') + axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Merging existing colormaps') + for ax,title in zip(axs, ['Evenly spaced', 'Matching SciVisColor example']): + ax.format(title=title) -.. image:: tutorial/tutorial_89_0.svg +.. image:: tutorial/tutorial_117_1.svg -Stacked panels --------------- -You can draw *stacks* of figure and axes panels by passing the -``lstack``, ``bstack``, ``rstack``, and ``tstack`` keyword args to -`~proplot.axes.Axes.panel`. This is useful when you need multiple -*global* colorbars, when using more than one colormap inside a *single -subplot*, or when you need multiple panels for displaying different -statistics. The spacing between stacked panels is adjusted automatically -to account for axis and tick labels. See `~proplot.subplots.subplots` -and `~proplot.subplots.Figure.add_subplot_and_panels` for details. - -You can access individual panels in a stack by *indexing* the panel -attribute. The default order is row-major, from top-left to -bottom-right. For example, ``ax.lpanel[1]`` gives you a left panel, -second from the left. If you are stacking *figure* panels and have -different panels on each row and column (see -:ref:`Global figure panels`), you can use 2D indexing. For example, -``fig.bpanel[1,0]`` gives you a panel in the first column, second from -the top. +To modify a diverging colormap by cutting out some central colors, pass +the ``cut`` keyword arg to `~proplot.styletools.Colormap`. This is +great when you want to have a sharper cutoff between negative and +positive values. To cut out colors from the left or right of a colormap, +pass the ``left`` and ``right`` keyword args to +`~proplot.styletools.Colormap`. + +To rotate a cyclic colormap, pass the ``shift`` argument to +`~proplot.styletools.Colormap`. Cyclic colormaps are colormaps for +which ``cyclic=True`` was passed to `~proplot.styletools.Colormap` on +construction. ProPlot ensures the colors at the ends of these maps are +distinct, so that levels don’t blur together. .. code:: ipython3 import proplot as plot import numpy as np - f, axs = plot.subplots(nrows=2, axwidth='4cm', share=0) - axs.panel_axes('l', stack=3) # subplots(..., axpanels='l') also works - axs.panel_axes('r', stack=2, space=0, sep=0, width=0.5) # subplots(..., axpanels='r') also works - axs[0].format(title='Stacked panels demo', titleweight='bold') - # Draw stuff in axes - N = 10 - for ax in axs: - # Colormap data - # Specify colorbar location with colorbar=('l', index) where index is the stack index - ax.format(xlabel='data', xlocator=np.linspace(0, 0.8, 5)) - for i,(x0,y0,x1,y1,cmap,scale) in enumerate(((0,0.5,1,1,'grays',0.5), (0,0,0.5,0.5,'reds',1), (0.5,0,1,0.5,'blues',2))): - data = np.random.rand(N,N)*scale - x, y = np.linspace(x0, x1, 11), np.linspace(y0, y1, 11) - ax.pcolormesh(x, y, data, cmap=cmap, levels=np.linspace(0,scale,11), colorbar=('l',i)) - # ax.bpanel[i].colorbar(m) # also works - # Plot data - n = 20 - for i,pax in enumerate(ax.rpanel): - data = (np.random.rand(n,n)-0.5).cumsum(axis=0) - data = (data.mean(axis=1) if i==0 else data.std(axis=1)) - label = ('mean' if i==0 else 'stdev') - pax.plot(data, np.linspace(0,1,n), lw=2, color='k') - pax.format(yticklen=0, xlabel=label, xlocator=0.5) - - - -.. image:: tutorial/tutorial_91_0.svg - - -Inset colorbars ---------------- - -As seen above, `~proplot.axes.Axes` and `~proplot.axes.PanelAxes` -have their own colorbar methods. Calling ``PanelAxes`` -`~proplot.axes.PanelAxes.colorbar` fills the panel with a colorbar, -but calling `~proplot.axes.Axes` ``PanelAxes.colorbar`` draws an -*inset* colorbar. You can also draw inset colorbars on panel axes by -passing ``fill=False`` to ``PanelAxes`` -`~proplot.axes.PanelAxes.colorbar`. + f, axs = plot.subplots([[1,1,2,2,3,3],[0,4,4,5,5,0]], axwidth=1.5) + data = np.random.rand(50,50).cumsum(axis=0) - 50 + # Cutting central colors + for ax,cut in zip(axs[:3],(0, 0.1, 0.2)): + m = ax.contourf(data, cmap='Div', cmap_kw={'cut':cut}, levels=13) + ax.format(xlabel='xlabel', ylabel='ylabel', title=f'cut = {cut}', + suptitle='Slicing existing colormaps') + ax.colorbar(m, loc='b', locator='null') + # Cutting left and right + for ax,cut in zip(axs[3:],(0.2,0.8)): + if cut<0.5: + title, cmap, cmap_kw = f'left={cut}', 'grays', {'left':cut} + else: + title, cmap, cmap_kw = f'right={cut}', 'grays', {'right':cut} + ax.contourf(data, cmap=cmap, cmap_kw=cmap_kw, colorbar='b', colorbar_kw={'locator':'null'}) + ax.format(xlabel='xlabel', ylabel='ylabel', title=title) + # Rotating cyclic + f, axs = plot.subplots(ncols=3, axwidth=1.5) + data = (np.random.rand(50,50)-0.48).cumsum(axis=1).cumsum(axis=0) - 50 + for ax,shift in zip(axs,(0, 90, 180)): + m = ax.contourf(data, cmap='twilight', cmap_kw={'shift':shift}, levels=12) + ax.format(xlabel='x axis', ylabel='y axis', title=f'shift = {shift}', + suptitle='Rotating cyclic colormaps') + ax.colorbar(m, loc='b', locator='null') + + + +.. image:: tutorial/tutorial_119_0.svg + + + +.. image:: tutorial/tutorial_119_1.svg + + +You can also change the “gamma” of any +`~proplot.styletools.PerceptuallyUniformColormap` map on-the-fly. The +“gamma” controls how the luminance and saturation channels vary between +segments of the colormap. A gamma larger than ``1`` emphasizes high +luminance, low saturation colors, and a gamma smaller than ``1`` +emphasizes low luminance, high saturation colors. See +`~proplot.styletools.PerceptuallyUniformColormap` for details. .. code:: ipython3 import proplot as plot import numpy as np - f, ax = plot.subplots() - data = (np.random.rand(20,20)).cumsum(axis=0) - m = ax.contourf(data, extend='both', levels=np.linspace(0,10,11), cmap='marine') - ax.format(xlabel='xlabel', ylabel='ylabel', xlim=(0,19), ylim=(0,19)) - ax.colorbar(m, ticks=2, label='data label', frame=True) - ax.colorbar(m, ticks=2, loc='lower left', frame=False) - ax.colorbar(m, loc='b', label='standard outer colorbar', length=0.9) - ax.format(suptitle='Inset colorbars demo') + name = 'boreal' + # Illustrations + f, axs = plot.subplots(ncols=3, axwidth=1.5, aspect=1) + data = np.random.rand(10,10).cumsum(axis=1) + cmaps = [] + for ax,gamma in zip(axs,(0.8, 1.0, 1.4)): + cmap = plot.Colormap(name, gamma=gamma) + cmap.name = f'gamma={gamma}' + cmaps.append(cmap) + m = ax.pcolormesh(data, cmap=cmap, levels=10, extend='both') + ax.colorbar(m, loc='r', locator='none') + ax.format(title=f'gamma = {gamma}', xlabel='x axis', ylabel='y axis', suptitle='Modifying existing PerceptuallyUniformColormaps') + # Breakdowns + f = plot.show_channels(*cmaps, axwidth=1.2, rgb=False) -.. image:: tutorial/tutorial_94_0.svg +.. image:: tutorial/tutorial_121_0.svg -Misc colorbar features ----------------------- -``PanelAxes`` `~proplot.axes.PanelAxes.colorbar` and ``Axes`` -`~proplot.axes.PanelAxes.colorbar` are both wrapped by -`~proplot.wrappers.colorbar_wrapper`, which adds several new features. +.. image:: tutorial/tutorial_121_1.svg + + +Adding online colormaps +----------------------- + +There are plenty of online interactive tools for generating perceptually +uniform colormaps, including +`HCLWizard `__, +`Chroma.js `__, +`SciVisColor `__, and `HCL +picker `__. + +To add colormaps downloaded from any of these sources, save the colormap +data to a file in your ``~/.proplot/cmaps`` folder, then call +`~proplot.styletools.register_cmaps`. The file should be named +``name.ext``, where ``name`` is the registered colormap name and ``ext`` +is the file extension. See `~proplot.styletools.register_cmaps` for +valid file extensions. + +Registered color cycles +----------------------- + +Use `~proplot.styletools.show_cycles` to generate a table of the color +cycles registered by default and loaded from your ``~/.proplot/cycles`` +folder. You can make your own color cycles using the +`~proplot.styletools.Cycle` constructor function. See +:ref:`Colormaps, cycles, colors, and fonts` for more on the +differences between colormaps and color cycles. + +.. code:: ipython3 + + import proplot as plot + f = plot.show_cycles() + + -`~proplot.wrappers.colorbar_wrapper` can draw colorbars from *lists of -colors* or *lists of artists* by passing a list instead of a “mappable” -object – a colormap is constructed from the corresponding colors -on-the-fly. To change outline, divider, tick location, tick label, and -colorbar label settings, just pass the appropriate keyword arg to -`~proplot.wrappers.colorbar_wrapper`. The below example demos the -various keyword args accepted by this wrapper. +.. image:: tutorial/tutorial_126_0.svg + + +Making your own color cycles +---------------------------- + +You can make new color cycles with ProPlot’s on-the-fly property cycler +generator `~proplot.styletools.Cycle`. ProPlot lets you specify a +property cycle by passing ``cycle`` to plotting commands like +`~matplotlib.axes.Axes.plot` and `~matplotlib.axes.Axes.scatter` +(see `~proplot.wrappers.cycle_wrapper`), which is passed to +`~proplot.styletools.Cycle`, and `~proplot.styletools.Cycle` keyword +args can be specified with ``cycle_kw``. If you want to save your own +color cycle into the ``~/.proplot/cycles`` folder, simply pass +``save=True`` to `~proplot.styletools.Cycle`. Color cycles in this +folder are loaded every time you import ProPlot. If you want to change +the global property cycler, use the ``plot.rc.cycle`` setting (see the +`~proplot.rctools` documentation). .. code:: ipython3 import proplot as plot import numpy as np - f, axs = plot.subplots(share=0, ncols=2, panel='b') + data = (np.random.rand(12,12)-0.45).cumsum(axis=0) + plot.rc.cycle = 'contrast' + lw = 5 + f, axs = plot.subplots(ncols=3, axwidth=1.7) + # Here the default cycle is used ax = axs[0] - data = 1 + (np.random.rand(12,10)-0.45).cumsum(axis=0) - cycle = plot.Cycle('algae') - hs = ax.plot(data, lw=4, cycle=cycle, colorbar='lr', colorbar_kw={'length':'14em', 'label':'numeric values'}) - ax.colorbar(hs, loc='t', values=np.linspace(0.5,9.5,10)*2, label='alt numeric values', ticks=2) + ax.plot(data, lw=lw) + # Note that specifying "cycle" does not reset the color cycle ax = axs[1] - m = ax.contourf(data.T, extend='both', cmap='algae') - f.bpanel.colorbar(m, length=0.6, label='flipped tick location', tickloc='top', grid=True) - ax.colorbar(m, loc='ul', length=1, ticks=0.5, tickminor=True, extendrect=True, - label='changing colors', labelcolor='gray7', labelweight='bold', - linewidth=1, edgecolor='gray7', ticklabelcolor='gray7', alpha=0.5) - axs.format(suptitle='Colorbar formatting demo', xlabel='xlabel', ylabel='ylabel') + ax.plot(data, cycle='qual2', lw=lw) + ax = axs[2] + for i in range(data.shape[1]): + ax.plot(data[:,i], cycle='qual2', lw=lw) + # Format + axs.format(suptitle='Local and global color cycles demo') -.. image:: tutorial/tutorial_97_0.svg +.. image:: tutorial/tutorial_129_0.svg -Misc legend features --------------------- +Colormaps or combinations thereof can be used as sources for making +color cycles. Just pass colormap name(s) to the +`~proplot.styletools.Cycle` constructor, with the last positional +argument indicating the number of samples you want to draw. To exclude +near-white colors on the end of a colormap, pass e.g. ``left=x`` to +`~proplot.styletools.Cycle` (or supply a plotting command with e.g. +``cycle_kw={'left':x}``). See `~proplot.styletools.Colormap` for +details. + +.. code:: ipython3 + + import proplot as plot + import numpy as np + f, axs = plot.subplots(ncols=2, panels='b', share=0, axwidth=2.2, aspect=1.5) + data = (20*np.random.rand(10,21)-10).cumsum(axis=0) + # Example 1 + ax = axs[0] + lines = ax.plot(data[:,:5], cycle='purples', cycle_kw={'left':0.3}, lw=5) + f.bpanel[0].colorbar(lines, values=np.arange(0,len(lines)), label='clabel') + ax.format(title='Simple cycle') + # Example 2 + ax = axs[1] + cycle = plot.Cycle('blues', 'reds', 'oranges', 21, left=[0.1]*3) + lines = ax.plot(data, cycle=cycle, lw=5) + f.bpanel[1].colorbar(lines, values=np.arange(0,len(lines)), locator=2, label='clabel') + ax.format(title='Complex cycle', suptitle='Color cycles from colormaps demo') + + + +.. image:: tutorial/tutorial_131_0.svg -``PanelAxes`` `~proplot.axes.PanelAxes.legend` and ``Axes`` -`~proplot.axes.PanelAxes.legend` are both wrapped by -`~proplot.wrappers.legend_wrapper`, which adds several new features. -`~proplot.wrappers.legend_wrapper` can draw legends with *centered -legend rows*, either by passing ``center=True`` or by passing *list of -lists* of plot handles. This is accomplished by stacking multiple -single-row, horizontally centered legends, then manually adding an -encompassing legend frame. You can also switch between row-major and -column-major order for legend entries (the new default is row-major), -and modify legend text properties and handle properties. See -`~proplot.wrappers.legend_wrapper` for details. +`~proplot.styletools.Cycle` can also generate cyclers that change +properties other than color. Below, a single-color dash style cycler is +constructed and applied to the axes locally. To apply it globally, +simply use ``plot.rc['axes.prop_cycle'] = cycle``. .. code:: ipython3 import proplot as plot import numpy as np - plot.rc.cycle = 'contrast' - labels = ['a', 'bb', 'ccc', 'dddd', 'eeeee'] - f, axs = plot.subplots(ncols=2, span=False, share=1) - hs1, hs2 = [], [] - # Plot lines and add to legends on-the-fly - for i,label in enumerate(labels): - data = (np.random.rand(20)-0.45).cumsum(axis=0) - h1 = axs[0].plot(data, lw=4, label=label, legend='ul', - legend_kw={'order':'F', 'title':'column major'}) # add to legend in upper left - hs1.extend(h1) - h2 = axs[1].plot(data, lw=4, label=label, legend='r', cycle='floral', - legend_kw={'ncols':1, 'frame':False, 'title':'no frame'}) # add to legend in right panel - hs2.extend(h2) - # Outer legends + import pandas as pd + f, ax = plot.subplots(axwidth=3, aspect=1.5) + data = (np.random.rand(20,4)-0.5).cumsum(axis=0) + data = pd.DataFrame(data, columns=pd.Index(['a','b','c','d'], name='label')) + ax.format(suptitle='Plot without color cycle') + cycle = plot.Cycle(dashes=[(1,0.5),(1,1.5),(3,0.5),(3,1.5)]) + obj = ax.plot(data, lw=3, cycle=cycle, legend='ul', legend_kw={'ncols':2, 'handlelength':3}) + + + +.. image:: tutorial/tutorial_133_0.svg + + +Adding online color cycles +-------------------------- + +There are plenty of online interactive tools for generating and testing +color cycles, including `i want +hue `__, +`coolers `__, and `viz +palette `__. + +To add color cycles downloaded from any of these sources, save the cycle +data to a file in your ``~/.proplot/cycles`` folder, then call +`~proplot.styletools.register_cycles`. The file should be named +``name.ext``, where ``name`` is the registered cycle name and ``ext`` is +the file extension. See `~proplot.styletools.register_cmaps` for valid +file extensions. + +Registered color names +---------------------- + +ProPlot defines new color names from the `XKCD “color +survey” `__, +official `Crayola crayon +colors `__, +and from the `“Open color” `__ +Github project. This was inspired by +`seaborn `__. +Use `~proplot.styletools.show_colors` to generate tables of these +colors, as shown below. Note that the native matplotlib `CSS4 named +colors `__ are +still registered, but I encourage using colors from the tables instead. + +To reduce the number of registered color names to a more manageable +size, XKCD and Crayola colors must have *sufficiently distinct +coordinates* in the HCL perceptually uniform colorspace before they are +added to ProPlot. This makes it a bit easier to pick out colors from a +table generated with `~proplot.styletools.show_colors`. Similar names +were also cleaned up – for example, “reddish” and “reddy” are changed to +“red”. + +.. code:: ipython3 + + import proplot as plot + f = plot.show_colors() + + + +.. image:: tutorial/tutorial_138_0.svg + + + +.. image:: tutorial/tutorial_138_1.svg + + +Individual color sampling +------------------------- + +If you want to draw an individual color from a smooth colormap or a +color cycle, use ``color=(cmapname, position)`` or +``color=(cyclename, index)`` with any command that accepts the ``color`` +keyword! The ``position`` should be between 0 and 1, while the ``index`` +is the index on the list of colors in the cycle. This feature is powered +by the `~proplot.styletools.ColorCacheDict` class. + +.. code:: ipython3 + + import proplot as plot + import numpy as np + plot.rc.reset() + f, axs = plot.subplots(nrows=2, aspect=2, axwidth=3, share=0) + # Drawing from colormap ax = axs[0] - ax.legend(hs1, loc='b', ncols=3, linewidth=2, title='row major', order='C', - edgecolor='gray4', facecolor='gray2') + cmap = 'deep' + m = ax.pcolormesh([[0],[1]], cmap=cmap, N=1000) + idxs = plot.arange(0,1,0.2) + np.random.shuffle(idxs) + for idx in idxs: + h = ax.plot((np.random.rand(20)-0.4).cumsum(), lw=5, color=(cmap, idx), + label=f'idx {idx:.1f}', legend='r', legend_kw={'ncols':1}) + ax.colorbar(m, loc='ul', locator=0.2, label='colormap') + ax.format(title='Drawing from the Solar colormap', grid=True) + # Drawing from color cycle ax = axs[1] - ax.legend(hs2, loc='b', ncols=3, center=True, title='centered legend', - handlelength=1) # also works! - axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Legend formatting demo') + idxs = np.arange(6) + np.random.shuffle(idxs) + for idx in idxs: + h = ax.plot((np.random.rand(20)-0.4).cumsum(), lw=5, color=('qual1', idx), + label=f'idx {idx:.0f}', legend='r', legend_kw={'ncols':1}) + ax.format(title='Drawing from the ggplot color cycle') + axs.format(xlocator='null', abc=True, abcloc='ur', abcformat='A.', + suptitle='Getting individual colors from colormaps and cycles') + + + +.. image:: tutorial/tutorial_141_0.svg + + +Font selection +-------------- + +DejaVu Sans is the default matplotlib font, but it’s not very +aesthetically pleasing. ProPlot adds a bunch of sans-serif fonts so that +you have them on every workstation, introduces a +`~proplot.styletools.show_fonts` command to display them (see below), +and makes Helvetica the default, as in MATLAB. Generally speaking, +simple, clean sans-serif fonts are more appropriate for figures than +serif fonts. + +You can register your own fonts by adding ``.ttf`` and ``.otf`` files to +the ``~/.proplot/fonts`` directory and calling +`~proplot.styletools.register_fonts` (which is also called on import). +To change the default font, use the `~proplot.rctools.rc` object or +modify your ``~/.proplotrc``. See the `~proplot.styletools` and +`~proplot.rctools` documentation for more info. + +.. code:: ipython3 + + import proplot as plot + plot.rc.reset() + f = plot.show_fonts() -.. image:: tutorial/tutorial_100_0.svg +.. image:: tutorial/tutorial_144_0.svg diff --git a/docs/tutorial5.rst b/docs/tutorial5.rst index d8c95c1f3..3e0ebd4a5 100644 --- a/docs/tutorial5.rst +++ b/docs/tutorial5.rst @@ -1,647 +1,431 @@ -Color usage -=========== - -ProPlot isn’t just an alternative to `~matplotlib.pyplot`. It also -adds some neat features to help you use colors effectively in your -figures, and integrates palettes from several online data visualization -tools. - -First things first, ProPlot makes a distinction between *colormaps* and -*color cycles*. - -- A *colormap* is a palette constructed by sampling some *smooth, - linear* function between two end colors. Colormaps are generally used - for 2-D or 3-D plots, where the color serves as an extra “dimension”. - This is implemented in matplotlib with the - `~matplotlib.colors.LinearSegmentedColormap` class, and also with - the special ProPlot - `~proplot.styletools.PerceptuallyUniformColormap` subclass (see - :ref:`Making your own colormaps`). -- A *color cycle* is a palette composed of a *jumbled set* of distinct - colors. Interpolation between these colors does not make sense. Color - cycles are generally used with line plots, bar plots, and other plot - elements. They are conceptually implemented in matplotlib with the - `~matplotlib.colors.ListedColormap` class (although it is often - improperly used). ProPlot uses this class to register color cycles, - and the color cycles are “applied” by globally or axes-locally - modifying the `property - cycler `__. - *Colormaps* can also be cut up and used as color cycles (see - :ref:`Making your own color cycles`). - -This section documents the colormaps and cycles registered after -importing ProPlot, explains how to make custom colormaps and cycles, and -shows how to apply them to your plots using axes methods wrapped by -`~proplot.wrappers.cmap_wrapper` or -`~proplot.wrappers.cycle_wrapper`. - -Registered colormaps --------------------- - -On import, ProPlot registers a few sample -`~proplot.styletools.PerceptuallyUniformColormap` colormaps (see -:ref:`Perceptually uniform colormaps`) plus a ton of other colormaps -from other online data viz projects. Use -`~proplot.styletools.show_cmaps` to generate a table of registered -maps, as shown below. The figure is broken down into the following -sections: - -- “User” colormaps, i.e. colormaps saved to your ``~/.proplot/cmaps`` - folder. A great way to save colormaps to this folder is using the - `~proplot.styletools.Colormap` function. See - :ref:`Making your own colormaps` for details. -- Matplotlib and seaborn original colormaps. -- ProPlot colormaps belonging to the - `~proplot.styletools.PerceptuallyUniformColormap` class. See the - :ref:`Perceptually uniform colormaps` section. -- Miscellaneous diverging colormaps. -- `cmOcean `__ colormaps, originally - intended for oceanographic visualizations but useful for all - scientific fields. -- `ColorBrewer `__ colormaps, included with - matplotlib by default. -- Colormaps from the - `SciVisColor `__ online - interactive tool. There are so many of these maps because they are - intended to be *merged* with one another – suitable for complex - datasets with complex statistical distributions. - -ProPlot removes some default matplotlib colormaps with erratic color -transitions. Note that colormap and color cycle identification is now -flexible: names are *case-insensitive* (e.g. ``'Viridis'``, -``'viridis'``, and ``'ViRiDiS'`` are equivalent) and can be specified in -their “reversed” form (e.g. ``'BuRd'`` is equivalent to ``'RdBu_r'``). -See `~proplot.styletools.CmapDict` for more info. - -.. code:: ipython3 - - import proplot as plot - f = plot.show_cmaps() - - - -.. image:: tutorial/tutorial_105_0.svg - - -Perceptually uniform colormaps ------------------------------- - -ProPlot’s custom colormaps are instances of the new -`~proplot.styletools.PerceptuallyUniformColormap` class (see -:ref:`Registered colormaps` for a table). -`~proplot.styletools.PerceptuallyUniformColormap` objects generate -colors by interpolating between coordinates in any of three possible -“perceptually uniform” colorspaces: - -- **HCL**: A purely perceptually uniform colorspace, where colors are - broken down into “hue” (color, range 0-360), “chroma” (saturation, - range 0-100), and “luminance” (brightness, range 0-100). -- **HPLuv**: Hue and luminance are identical to HCL, but 100 saturation - is scaled to be the *minimum maximum saturation* across all hues for - a given luminance. HPLuv is more appropriate for multi-hue colormaps. -- **HSLuv**: Hue and luminance are identical to HCL, but 100 saturation - is scaled to be the *maximum possible saturation* for a given hue and - luminance. HSLuv is more appropriate for single-hue colormaps – - saturation banding can occur when crossing hue thresholds in this - space. - -HCL is the only “purely” perceptually uniform colorspace. But -interpolating between coordinates in this space can result in -“impossible” colors – colors that, when translated from HCL back into -RGB, result in RGB channels greater than 1. HSLuv and HPLuv help resolve -this issue by respectively *scaling* and *clipping* the -highest-saturation colors across different hues and luminances. See -`this page `__ for more info. +Plotting wrappers +================= + +New features have been added to various matplotlib plotting commands +thanks to a set of wrapper functions. These features are a strict +*superset* of the existing matplotlib API – if you want, you can use +plotting commands exactly as you always have. This section documents +these wrapper functions. For details, see the `~proplot.axes` +documentation. + +You should also see :ref:`Making your own colormaps` and +:ref:`Making your own color cycles`, which explain how +`~proplot.wrappers.cmap_wrapper` and +`~proplot.wrappers.cycle_wrapper` can be used to create and apply new +colormaps and property cyclers on-the-fly. + +Normalizers and levels +---------------------- -To plot arbitrary cross-sections of these colorspaces, use -`~proplot.styletools.show_colorspaces`. The blacked out regions -represent “impossible” colors. +`~proplot.wrappers.cmap_wrapper` assigns the +`~proplot.styletools.BinNorm` “meta-normalizer” as the data normalizer +for all plotting commands involving colormaps. This permits discrete +``levels`` even for commands like `~matplotlib.axes.Axes.pcolor` and +`~matplotlib.axes.Axes.pcolormesh`. `~proplot.styletools.BinNorm` +also ensures that colorbar colors span the entire colormap range, +independent of the ``extend`` setting, and that color levels on the ends +of colorbars for “cyclic” colormaps are distinct. .. code:: ipython3 import proplot as plot - f = plot.show_colorspaces(luminance=50) + import numpy as np + f, axs = plot.subplots(ncols=2, axwidth=2) + cmap = 'spectral' + data = (np.random.rand(15,15)-0.5).cumsum(axis=0) + axs.format(suptitle='Pcolor with levels demo') + ax = axs[0] + ax.pcolor(data, cmap=cmap, N=200, symmetric=True, colorbar='l', colorbar_kw={'locator':0.5}) + ax.format(title='Ambiguous values', yformatter='null') + ax = axs[1] + ax.pcolor(data, cmap=cmap, N=8, symmetric=True, colorbar='r') + ax.format(title='Discernible values') -.. image:: tutorial/tutorial_108_0.svg +.. image:: tutorial/tutorial_149_0.svg .. code:: ipython3 import proplot as plot - f = plot.show_colorspaces(saturation=60) + import numpy as np + f, axs = plot.subplots(ncols=5, axwidth=2, ref=1, wratios=(5,3,3,3,3)) + axs.format(suptitle='Demo of colorbar color-range standardization') + levels = plot.arange(0,360,45) + data = (20*(np.random.rand(20,20) - 0.4).cumsum(axis=0).cumsum(axis=1)) % 360 + ax = axs[0] + ax.pcolormesh(data, levels=levels, cmap='phase', extend='neither', colorbar='b') + ax.format(title='Cyclic map with separate ends') + for ax,extend in zip(axs[1:], ('min','max','neither','both')): + ax.pcolormesh(data[:,:10], levels=levels, cmap='oxy', extend=extend, colorbar='b', colorbar_kw={'locator':90}) + ax.format(title=f'Map with extend={extend}') -.. image:: tutorial/tutorial_109_0.svg +.. image:: tutorial/tutorial_150_0.svg +If you pass unevenly spaced ``levels``, the +`~proplot.styletools.LinearSegmentedNorm` normalizer is applied by +default. This results in even color gradations across *indices* of the +level list, no matter their spacing. To use an arbitrary colormap +normalizer, just pass ``norm`` and optionally ``norm_kw`` to a command +wrapped by `~proplot.wrappers.cmap_wrapper`. These arguments are +passed to the `~proplot.styletools.Norm` constructor. + .. code:: ipython3 import proplot as plot - f = plot.show_colorspaces(hue=0) + import numpy as np + f, axs = plot.subplots(ncols=2, axwidth=2.5, aspect=1.5) + data = 10**(2*np.random.rand(20,20).cumsum(axis=0)/7) + ticks = [5, 10, 20, 50, 100, 200, 500, 1000] + for i,(norm,title) in enumerate(zip(('linear','segments'),('Linear normalizer','LinearSegmentedNorm'))): + m = axs[i].contourf(data, levels=ticks, extend='both', cmap='Mako', norm=norm, colorbar='b') + axs[i].format(title=title) + axs.format(suptitle='Level normalizers demo') -.. image:: tutorial/tutorial_110_0.svg +.. image:: tutorial/tutorial_152_0.svg -To see how any colormap varies with respect to each channel, use the -`~proplot.styletools.cmap_breakdown` function. Below, we do this for -the “magma”, “rocket”, and ProPlot “Fire” colormaps. The first two are -nicely-designed `~matplotlib.colors.LinearSegmentedColormap` maps, and -the last one is a `~proplot.styletools.PerceptuallyUniformColormap`. -They are all roughly linear across the hue and luminance channels, but -not the chroma channel (top row). “Fire” is linear in the HSL scaling of -the chroma channel (bottom left), while other ProPlot colormaps are -linear in the HPL scaling of the chroma channel (bottom right). +Finally, there is a new `~proplot.styletools.MidpointNorm` class that +warps your colormap so that its midpoint lies on some central data +value, no matter the minimum and maximum colormap colors. Again, to use +an arbitrary colormap normalizer, just pass ``norm`` and optionally +``norm_kw`` to a command wrapped by `~proplot.wrappers.cmap_wrapper`. +These arguments are passed to the `~proplot.styletools.Norm` +constructor. .. code:: ipython3 import proplot as plot - f = plot.show_channels('magma', 'rocket', 'fire', axwidth=1.2, minhue=-180, rgb=False) - - - -.. image:: tutorial/tutorial_112_0.svg - - -Making your own colormaps -------------------------- - -You can make new colormaps with ProPlot’s on-the-fly colormap generator -`~proplot.styletools.Colormap`. Every command that accepts a ``cmap`` -argument (see `~proplot.wrappers.cmap_wrapper`) is passed to -`~proplot.styletools.Colormap`, and `~proplot.styletools.Colormap` -keyword args can be specified with ``cmap_kw``. If you want to save your -own colormap into the ``~/.proplot/cmaps`` folder, simply use -``save=True``. Colormaps in this folder are loaded every time you import -ProPlot. See `~proplot.styletools.Colormap` and + import numpy as np + data1 = (np.random.rand(20,20) - 0.43).cumsum(axis=0) + data2 = (np.random.rand(20,20) - 0.57).cumsum(axis=0) + f, axs = plot.subplots(ncols=2, axwidth=2.5, aspect=1.5) + cmap = plot.Colormap('Moisture', cut=0.1) + axs.format(suptitle='Midpoint normalizer demo') + axs[0].contourf(data1, norm='midpoint', cmap=cmap, colorbar='b') + axs[0].format(title='Skewed positive data') + axs[1].contourf(data2, norm='midpoint', cmap=cmap, colorbar='b') + axs[1].format(title='Skewed negative data') + + + +.. image:: tutorial/tutorial_154_0.svg + + +Heatmaps and labels +------------------- + +The new `~proplot.axes.Axes.heatmap` command calls +`~matplotlib.axes.Axes.pcolormesh` and applies default formatting that +is suitable for heatmaps: no gridlines, no minor ticks, and major ticks +at the center of each box. Among other things, this is useful for +displaying autocorrelation matrices (see below). + +You can also add labels to `~matplotlib.axes.Axes.pcolor`, +`~matplotlib.axes.Axes.pcolormesh`, `~proplot.axes.Axes.heatmap`, +`~matplotlib.axes.Axes.contour`, and +`~matplotlib.axes.Axes.contourf` plots, thanks to +`~proplot.wrappers.cmap_wrapper`. Just pass the ``labels=True`` +keyword argument, and ProPlot will draw contour labels with +`~matplotlib.axes.Axes.clabel` or grid box labels with +`~matplotlib.axes.Axes.text`. Label colors are automatically chosen +based on the luminance of the underlying box or contour color. The label +text objects can be changed with the ``labels_kw`` dictionary keyword +arg and the ``precision`` keyword arg. See `~proplot.wrappers.cmap_wrapper` for details. -To build monochromatic -`~proplot.styletools.PerceptuallyUniformColormap` maps from arbitrary -colors, just pass a color name, hex string, or RGB tuple to -`~proplot.styletools.Colormap`. The colormap colors will vary from the -specified color to some shade near white (controlled by the ``fade`` -keyword arg). The default is to fade to pure white. The first plot shows -several of these maps merged into one, and the second is just one map. - .. code:: ipython3 import proplot as plot + import pandas as pd import numpy as np - f, axs = plot.subplots(ncols=2, axwidth=2, aspect=1, bottom=0.1) - data = np.random.rand(50,50).cumsum(axis=1) - cmap1 = plot.Colormap('brick red_r', 'denim_r', 'warm gray_r', fade=90, name='tricolor') - m = axs[0].contourf(data, cmap=cmap1, levels=12) - m = axs[1].contourf(data, cmap='ocean blue', cmap_kw={'name':'ocean blue'}) - cmap2 = m.cmap - axs.format(xticks='none', yticks='none', suptitle='Monochromatic PerceptuallyUniformColormaps') - for ax,title in zip(axs, ['Three monochromatic colormaps', 'One monochromatic colormap']): - ax.format(title=title) - f = plot.show_channels(cmap1, cmap2, axwidth=1.2, rgb=False) + # Heatmap with labels + f, axs = plot.subplots(ncols=2, axwidth=2, span=False, share=1) + data = np.random.rand(6,6) + data = pd.DataFrame(data, index=pd.Index(['a','b','c','d','e','f'])) + axs.format(suptitle='Labels demo') + ax = axs[0] + m = ax.heatmap(data, cmap='rocket', labels=True, precision=2, labels_kw={'weight':'bold'}) + ax.format(xlabel='xlabel', ylabel='ylabel', title='Heatmap plot with labels') + # Filled contours with labels + ax = axs[1] + m = ax.contourf(data.cumsum(axis=0), labels=True, cmap='rocket', labels_kw={'weight':'bold'}) + ax.format(xlabel='xlabel', ylabel='ylabel', title='Contourf plot with labels') + # Cross-correlation matrix + f, ax = plot.subplots(axwidth=3) + data = np.random.normal(size=(10,10)).cumsum(axis=0) + data = (data - data.mean(axis=0)) / data.std(axis=0) + data = (data.T @ data) / data.shape[0] + data[np.tril_indices(data.shape[0], -1)] = np.nan # empty boxes + data = pd.DataFrame(data, columns=list('abcdefghij'), index=list('abcdefghij')) + m = ax.heatmap(data, cmap='ColdHot', vmin=-1, vmax=1, N=100, + labels=True, labels_kw={'size':7, 'weight':'bold'}) + ax.format(title='Cross-correlation matrix', alpha=0, linewidth=0, + xloc='top', yloc='right', yreverse=True) -.. image:: tutorial/tutorial_116_0.svg +.. image:: tutorial/tutorial_157_0.svg -.. image:: tutorial/tutorial_116_1.svg +.. image:: tutorial/tutorial_157_1.svg -To generate `~proplot.styletools.PerceptuallyUniformColormap` maps, -you can pass a *dictionary* to `~proplot.styletools.Colormap`, which -calls the `~proplot.styletools.PerceptuallyUniformColormap.from_hsl` -static method, or pass a *list of colors* to -`~proplot.styletools.Colormap`, which calls the -`~proplot.styletools.PerceptuallyUniformColormap.from_list` static -method. +Easy error bars +--------------- -`~proplot.styletools.PerceptuallyUniformColormap.from_list` -interpolates between the listed colors in a perceptually uniform -colorspace (see :ref:`Perceptually uniform colormaps`). -`~proplot.styletools.PerceptuallyUniformColormap.from_hsl` draws lines -between channel values specified by the keyword args ``'hue'``, -``'saturation'``, and ``'luminance'``. The values can be numbers, color -strings, or lists thereof. Numbers indicate the channel value. For color -strings, the channel value is *inferred* from the specified color. You -can end any color string with ``+N`` or ``-N`` to *offset* the channel -value by the number ``N``, as shown below. +Thanks to the `~proplot.wrappers.add_errorbars` wrapper, you can now +add error bars when using the `~matplotlib.axes.Axes.plot`, +`~matplotlib.axes.Axes.scatter`, `~matplotlib.axes.Axes.bar`, +`~matplotlib.axes.Axes.barh`, and `~matplotlib.axes.Axes.violinplot` +methods. If you pass 2D arrays of data to these commands with +``means=True`` or ``medians=True``, the *means or medians* of each +column are drawn as points, lines, or bars, and error bars represent the +*spread* in each column. You can draw both thin “bars” with optional +whiskers, and thick “boxes” overlayed on top of these bars. You can also +pass error bar coordinates manually with the ``bardata`` and ``boxdata`` +keyword args. See `~proplot.wrappers.add_errorbars` for details. .. code:: ipython3 import proplot as plot import numpy as np - f, axs = plot.subplots(ncols=3, span=False, axwidth=2, aspect=1.5) + import pandas as pd + plot.rc['title.loc'] = 'uc' + plot.rc['axes.ymargin'] = plot.rc['axes.xmargin'] = 0.05 + data = np.random.rand(20,8).cumsum(axis=0).cumsum(axis=1)[:,::-1] + 20*np.random.normal(size=(20,8)) + 30 + f, axs = plot.subplots(nrows=3, aspect=1.5, axwidth=3, share=0, hratios=(2,1,1)) + axs.format(suptitle='Error bars with various plotting commands') + # Asking add_errorbars to calculate bars ax = axs[0] - # From dicts - data = np.random.rand(10,15) - cmap1 = plot.Colormap({'hue':['red-90', 'red+90'], 'saturation':[50, 70, 30], 'luminance':[20, 100]}, name='Matter', space='hcl') - m = ax.pcolormesh(data, cmap=cmap1) - ax.format(xlabel='x axis', ylabel='y axis', title='From channel values', - suptitle='Building your own PerceptuallyUniformColormaps') + obj = ax.barh(data, color='red orange', means=True) + ax.format(title='Column statistics') + # Showing a standard deviation range instead of percentile range ax = axs[1] - cmap2 = plot.Colormap({'hue':['red', 'red-720'], 'saturation':[80,20], 'luminance':[20, 100]}, name='cubehelix', space='hpl') - m = ax.pcolormesh(data, cmap=cmap2) - ax.format(xlabel='x axis', ylabel='y axis', title='From channel values') - # From list + ax.scatter(data, color='k', marker='x', markersize=50, barcolor='gray5', + medians=True, barstd=True, barrange=(-1,1), barzorder=0, boxes=False, capsize=2) + # Supplying error bar data manually ax = axs[2] - m = ax.pcolormesh(data, cmap=('maroon', 'goldenrod'), cmap_kw={'name':'reddish'}) - cmap3 = m.cmap - ax.format(title='From list of colors') - # Breakdowns - f = plot.show_channels(cmap1, cmap2, cmap3, minhue=-180, axwidth=1.2, rgb=False) + boxdata = np.percentile(data, (25,75), axis=0) + bardata = np.percentile(data, (5,95), axis=0) + ax.plot(data.mean(axis=0), boxes=False, marker='o', markersize=5, + edgecolor='k', color='cerulean', boxdata=boxdata, bardata=bardata) + # Formatting + axs[0].format(ylabel='column number', title='Bar plot', ygrid=False) + axs[1].format(title='Scatter plot') + axs[2].format(title='Line plot') + axs[1:].format(xlabel='column number', xticks=1, xgrid=False) -.. image:: tutorial/tutorial_118_0.svg +.. image:: tutorial/tutorial_160_0.svg +Area plots +---------- -.. image:: tutorial/tutorial_118_1.svg +Make area plots with the convenient aliases `~proplot.axes.Axes.area` +and `~proplot.axes.Axes.areax`. These point to the +`~matplotlib.axes.Axes.fill_between` and +`~matplotlib.axes.Axes.fill_betweenx` methods, which are wrapped with +`~proplot.wrappers.fill_between_wrapper` and +`~proplot.wrappers.fill_betweenx_wrapper`. - -Merging and modifying colormaps -------------------------------- - -`~proplot.styletools.Colormap` also lets you merge arbitrary colormaps -and modify existing colormaps. To merge colormaps, simply pass multiple -arguments to the `~proplot.styletools.Colormap` constructor. This -makes it easy to create complex -`SciVisColor `__-style -colormaps, which may be desirable for complex datasets with funky -statistical distributions. The below reconstructs the colormap from -`this -example `__. +The wrappers enable “stacking” successive columns of a 2D input array +like in `pandas`. They also add a new “``negpos``” keyword for +creating area plots that change color when the fill boundaries cross +each other. The most common use case for this is highlighting negative +and positive area underneath a line, as shown below. .. code:: ipython3 import proplot as plot import numpy as np - f, axs = plot.subplots(ncols=2, axwidth=2, panels='b', span=False) - data = np.random.rand(100,100).cumsum(axis=1) - # Make colormap, save as "test1.json" - cmap = plot.Colormap('Green1_r', 'Orange5', 'Blue1_r', 'Blue6', name='test1', save=True) - m = axs[0].contourf(data, cmap=cmap, levels=100) - f.bpanel[0].colorbar(m, locator='none') - # Make colormap, save as "test2.json" - cmap = plot.Colormap('Green1_r', 'Orange5', 'Blue1_r', 'Blue6', ratios=(1,3,5,10), name='test2', save=True) - m = axs[1].contourf(data, cmap=cmap, levels=100) - f.bpanel[1].colorbar(m, locator='none') - axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Merging existing colormaps') - for ax,title in zip(axs, ['Evenly spaced', 'Matching SciVisColor example']): - ax.format(title=title) - + plot.rc.reset() + plot.rc.margin = 0 + f, axs = plot.subplots(array=[[1,2],[3,3]], hratios=(1,0.8), share=0) + axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Area plot demo') + data = np.random.rand(5,3).cumsum(axis=0) + cycle = ('gray3', 'gray5', 'gray7') + ax = axs[0] + ax.areax(np.arange(5), data, data + np.random.rand(5)[:,None], cycle=cycle, alpha=0.5, + legend='uc', legend_kw={'center':True, 'ncols':2, 'labels':['z','y','qqqq']}, + ) + ax.format(title='Fill between columns') + ax = axs[1] + ax.area(np.arange(5), data, stacked=True, cycle=cycle, alpha=0.8, + legend='ul', legend_kw={'center':True, 'ncols':2, 'labels':['z','y','qqqq']}, + ) + ax.format(title='Stack between columns') + ax = axs[2] + data = 5*(np.random.rand(20)-0.5) + ax.area(data, negpos=True, negcolor='blue7', poscolor='red7') + ax.format(title='Negative and positive data', xlabel='xlabel', ylabel='ylabel') + axs.format(grid=False) -.. image:: tutorial/tutorial_121_1.svg +.. image:: tutorial/tutorial_163_0.svg -To modify a diverging colormap by cutting out some central colors, pass -the ``cut`` keyword arg to `~proplot.styletools.Colormap`. This is -great when you want to have a sharper cutoff between negative and -positive values. To cut out colors from the left or right of a colormap, -pass the ``left`` and ``right`` keyword args to -`~proplot.styletools.Colormap`. +Bar plots +--------- -To rotate a cyclic colormap, pass the ``shift`` argument to -`~proplot.styletools.Colormap`. Cyclic colormaps are colormaps for -which ``cyclic=True`` was passed to `~proplot.styletools.Colormap` on -construction. ProPlot ensures the colors at the ends of these maps are -distinct, so that levels don’t blur together. +`~proplot.wrappers.bar_wrapper` and +`~proplot.wrappers.cycle_wrapper` make it easier to generate useful +bar plots. You can now pass 2D arrays to `~matplotlib.axes.Axes.bar` +or `~matplotlib.axes.Axes.barh`, and columns of data will be grouped +or stacked together. And if *x* coordinates are not provided, default +coordinates are applied, just like with `~matplotlib.axes.Axes.plot`. +See `~proplot.wrappers.bar_wrapper` for details. .. code:: ipython3 import proplot as plot import numpy as np - f, axs = plot.subplots([[1,1,2,2,3,3],[0,4,4,5,5,0]], axwidth=1.5) - data = np.random.rand(50,50).cumsum(axis=0) - 50 - # Cutting central colors - for ax,cut in zip(axs[:3],(0, 0.1, 0.2)): - m = ax.contourf(data, cmap='Div', cmap_kw={'cut':cut}, levels=13) - ax.format(xlabel='xlabel', ylabel='ylabel', title=f'cut = {cut}', - suptitle='Slicing existing colormaps') - ax.colorbar(m, loc='b', locator='null') - # Cutting left and right - for ax,cut in zip(axs[3:],(0.2,0.8)): - if cut<0.5: - title, cmap, cmap_kw = f'left={cut}', 'grays', {'left':cut} - else: - title, cmap, cmap_kw = f'right={cut}', 'grays', {'right':cut} - ax.contourf(data, cmap=cmap, cmap_kw=cmap_kw, colorbar='b', colorbar_kw={'locator':'null'}) - ax.format(xlabel='xlabel', ylabel='ylabel', title=title) - # Rotating cyclic - f, axs = plot.subplots(ncols=3, axwidth=1.5) - data = (np.random.rand(50,50)-0.48).cumsum(axis=1).cumsum(axis=0) - 50 - for ax,shift in zip(axs,(0, 90, 180)): - m = ax.contourf(data, cmap='twilight', cmap_kw={'shift':shift}, levels=12) - ax.format(xlabel='x axis', ylabel='y axis', title=f'shift = {shift}', - suptitle='Rotating cyclic colormaps') - ax.colorbar(m, loc='b', locator='null') - - - -.. image:: tutorial/tutorial_123_0.svg - - - -.. image:: tutorial/tutorial_123_1.svg - - -You can also change the “gamma” of any -`~proplot.styletools.PerceptuallyUniformColormap` map on-the-fly. The -“gamma” controls how the luminance and saturation channels vary between -segments of the colormap. A gamma larger than ``1`` emphasizes high -luminance, low saturation colors, and a gamma smaller than ``1`` -emphasizes low luminance, high saturation colors. See -`~proplot.styletools.PerceptuallyUniformColormap` for details. - -.. code:: ipython3 - - import proplot as plot - import numpy as np - name = 'boreal' - # Illustrations - f, axs = plot.subplots(ncols=3, axwidth=1.5, aspect=1) - data = np.random.rand(10,10).cumsum(axis=1) - cmaps = [] - for ax,gamma in zip(axs,(0.8, 1.0, 1.4)): - cmap = plot.Colormap(name, gamma=gamma) - cmap.name = f'gamma={gamma}' - cmaps.append(cmap) - m = ax.pcolormesh(data, cmap=cmap, levels=10, extend='both') - ax.colorbar(m, loc='r', locator='none') - ax.format(title=f'gamma = {gamma}', xlabel='x axis', ylabel='y axis', suptitle='Modifying existing PerceptuallyUniformColormaps') - # Breakdowns - f = plot.show_channels(*cmaps, axwidth=1.2, rgb=False) - - - -.. image:: tutorial/tutorial_125_0.svg - - - -.. image:: tutorial/tutorial_125_1.svg - - -Adding online colormaps ------------------------ - -There are plenty of online interactive tools for generating perceptually -uniform colormaps, including -`HCLWizard `__, -`Chroma.js `__, -`SciVisColor `__, and `HCL -picker `__. - -To add colormaps downloaded from any of these sources, save the colormap -data to a file in your ``~/.proplot/cmaps`` folder, then call -`~proplot.styletools.register_cmaps`. The file should be named -``name.ext``, where ``name`` is the registered colormap name and ``ext`` -is the file extension. See `~proplot.styletools.register_cmaps` for -valid file extensions. - -Registered color cycles ------------------------ - -Use `~proplot.styletools.show_cycles` to generate a table of the color -cycles registered by default and loaded from your ``~/.proplot/cycles`` -folder. You can make your own color cycles using the -`~proplot.styletools.Cycle` constructor function. See -:ref:`Colormaps, cycles, colors, and fonts` for more on the -differences between colormaps and color cycles. - -.. code:: ipython3 - - import proplot as plot - f = plot.show_cycles() + import pandas as pd + plot.rc.titleloc = 'uc' + plot.rc.margin = 0.05 + f, axs = plot.subplots(nrows=2, aspect=2, axwidth=3.5, share=0, hratios=(3,2)) + data = np.random.rand(5,5).cumsum(axis=0).cumsum(axis=1)[:,::-1] + data = pd.DataFrame(data, columns=pd.Index(np.arange(1,6), name='column'), index=pd.Index(['a','b','c','d','e'], name='row idx')) + ax = axs[0] + obj = ax.bar(data, cycle='Reds', colorbar='ul', edgecolor='red9', colorbar_kw={'frameon':False}) + ax.format(xlocator=1, xminorlocator=0.5, ytickminor=False, title='Side-by-side', suptitle='Bar plot wrapper demo') + ax = axs[1] + obj = ax.barh(data.iloc[::-1,:], cycle='Blues', legend='ur', edgecolor='blue9', stacked=True) + ax.format(title='Stacked') + axs.format(grid=False) -.. image:: tutorial/tutorial_130_0.svg +.. image:: tutorial/tutorial_166_0.svg -Making your own color cycles ----------------------------- +Box plots +--------- -You can make new color cycles with ProPlot’s on-the-fly property cycler -generator `~proplot.styletools.Cycle`. ProPlot lets you specify a -property cycle by passing ``cycle`` to plotting commands like -`~matplotlib.axes.Axes.plot` and `~matplotlib.axes.Axes.scatter` -(see `~proplot.wrappers.cycle_wrapper`), which is passed to -`~proplot.styletools.Cycle`, and `~proplot.styletools.Cycle` keyword -args can be specified with ``cycle_kw``. If you want to save your own -color cycle into the ``~/.proplot/cycles`` folder, simply pass -``save=True`` to `~proplot.styletools.Cycle`. Color cycles in this -folder are loaded every time you import ProPlot. If you want to change -the global property cycler, use the ``plot.rc.cycle`` setting (see the -`~proplot.rctools` documentation). +`~matplotlib.axes.Axes.boxplot` and +`~matplotlib.axes.Axes.violinplot` are now wrapped with +`~proplot.wrappers.boxplot_wrapper`, +`~proplot.wrappers.violinplot_wrapper`, and +`~proplot.wrappers.cycle_wrapper`, making it much easier to plot +distributions of data with aesthetically pleasing default settings and +automatic axis labeling. .. code:: ipython3 import proplot as plot import numpy as np - data = (np.random.rand(12,12)-0.45).cumsum(axis=0) - plot.rc.cycle = 'contrast' - lw = 5 - f, axs = plot.subplots(ncols=3, axwidth=1.7) - # Here the default cycle is used + import pandas as pd + N = 500 + f, axs = plot.subplots(ncols=2) + data = np.random.normal(size=(N,5)) + 2*(np.random.rand(N,5)-0.5)*np.arange(5) + data = pd.DataFrame(data, columns=pd.Index(['a','b','c','d','e'], name='xlabel')) ax = axs[0] - ax.plot(data, lw=lw) - # Note that specifying "cycle" does not reset the color cycle + obj1 = ax.boxplot(data, lw=0.7, marker='x', fillcolor='gray5', medianlw=1, mediancolor='k')#, boxprops={'color':'C0'})#, labels=data.columns) + ax.format(title='Box plots', titleloc='uc') ax = axs[1] - ax.plot(data, cycle='qual2', lw=lw) - ax = axs[2] - for i in range(data.shape[1]): - ax.plot(data[:,i], cycle='qual2', lw=lw) - # Format - axs.format(suptitle='Local and global color cycles demo') + obj2 = ax.violinplot(data, lw=0.7, fillcolor='gray7', points=500, bw_method=0.3, means=True) + ax.format(title='Violin plots', titleloc='uc') + axs.format(ymargin=0.1, xmargin=0.1, grid=False, suptitle='Boxes and violins demo') + -.. image:: tutorial/tutorial_133_0.svg +.. image:: tutorial/tutorial_169_1.svg -Colormaps or combinations thereof can be used as sources for making -color cycles. Just pass colormap name(s) to the -`~proplot.styletools.Cycle` constructor, with the last positional -argument indicating the number of samples you want to draw. To exclude -near-white colors on the end of a colormap, pass e.g. ``left=x`` to -`~proplot.styletools.Cycle` (or supply a plotting command with e.g. -``cycle_kw={'left':x}``). See `~proplot.styletools.Colormap` for -details. +Parametric plots +---------------- + +`~matplotlib.axes.Axes.plot` now accepts a ``cmap`` keyword – this +lets you draw line collections that map individual segments of the line +to individual colors. This can be useful for drawing “parametric” plots, +where you want to indicate the time or some other coordinate at each +point on the line. See `~proplot.axes.Axes.cmapline` for details. .. code:: ipython3 import proplot as plot import numpy as np - f, axs = plot.subplots(ncols=2, panels='b', share=0, axwidth=2.2, aspect=1.5) - data = (20*np.random.rand(10,21)-10).cumsum(axis=0) - # Example 1 + plot.rc.reset() + N = 50 + cmap = 'IceFire' + values = np.linspace(-N/2, N/2, N) + f, axs = plot.subplots(share=0, ncols=2, wratios=(2,1), axwidth='6cm', aspect=(2,1)) ax = axs[0] - lines = ax.plot(data[:,:5], cycle='purples', cycle_kw={'left':0.3}, lw=5) - f.bpanel[0].colorbar(lines, values=np.arange(0,len(lines)), label='clabel') - ax.format(title='Simple cycle') - # Example 2 + m = ax.plot((np.random.rand(N)-0.5).cumsum(), np.random.rand(N), cmap=cmap, values=values, lw=7, extend='both') + ax.format(xlabel='xlabel', ylabel='ylabel', title='Line with smooth color gradations', titleweight='bold') + ax.colorbar(m, loc='b', label='parametric coordinate', locator=5) + N = 12 ax = axs[1] - cycle = plot.Cycle('blues', 'reds', 'oranges', 21, left=[0.1]*3) - lines = ax.plot(data, cycle=cycle, lw=5) - f.bpanel[1].colorbar(lines, values=np.arange(0,len(lines)), locator=2, label='clabel') - ax.format(title='Complex cycle', suptitle='Color cycles from colormaps demo') - + values = np.linspace(-N/2, N/2 - 1, N) + radii = np.linspace(1,0.2,N) + angles = np.linspace(0,4*np.pi,N) + x = radii*np.cos(1.4*angles) + y = radii*np.sin(1.4*angles) + m = ax.plot(x, y, values=values, linewidth=15, interp=False, cmap=cmap) + ax.format(xlim=(-1,1), ylim=(-1,1), title='With step gradations', titleweight='bold', xlabel='cosine angle', ylabel='sine angle') + ax.colorbar(m, loc='b', maxn=10, label=f'parametric coordinate') -.. image:: tutorial/tutorial_135_0.svg -`~proplot.styletools.Cycle` can also generate cyclers that change -properties other than color. Below, a single-color dash style cycler is -constructed and applied to the axes locally. To apply it globally, -simply use ``plot.rc['axes.prop_cycle'] = cycle``. -.. code:: ipython3 - - import proplot as plot - import numpy as np - import pandas as pd - f, ax = plot.subplots(axwidth=3, aspect=1.5) - data = (np.random.rand(20,4)-0.5).cumsum(axis=0) - data = pd.DataFrame(data, columns=pd.Index(['a','b','c','d'], name='label')) - ax.format(suptitle='Plot without color cycle') - cycle = plot.Cycle(dashes=[(1,0.5),(1,1.5),(3,0.5),(3,1.5)]) - obj = ax.plot(data, lw=3, cycle=cycle, legend='ul', legend_kw={'ncols':2, 'handlelength':3}) +.. image:: tutorial/tutorial_172_1.svg -.. image:: tutorial/tutorial_137_0.svg - -Adding online color cycles --------------------------- - -There are plenty of online interactive tools for generating and testing -color cycles, including `i want -hue `__, -`coolers `__, and `viz -palette `__. - -To add color cycles downloaded from any of these sources, save the cycle -data to a file in your ``~/.proplot/cycles`` folder, then call -`~proplot.styletools.register_cycles`. The file should be named -``name.ext``, where ``name`` is the registered cycle name and ``ext`` is -the file extension. See `~proplot.styletools.register_cmaps` for valid -file extensions. - -Registered color names +Misc plot enhancements ---------------------- -ProPlot defines new color names from the `XKCD “color -survey” `__, -official `Crayola crayon -colors `__, -and from the `“Open color” `__ -Github project. This was inspired by -`seaborn `__. -Use `~proplot.styletools.show_colors` to generate tables of these -colors, as shown below. Note that the native matplotlib `CSS4 named -colors `__ are -still registered, but I encourage using colors from the tables instead. - -To reduce the number of registered color names to a more manageable -size, XKCD and Crayola colors must have *sufficiently distinct -coordinates* in the HCL perceptually uniform colorspace before they are -added to ProPlot. This makes it a bit easier to pick out colors from a -table generated with `~proplot.styletools.show_colors`. Similar names -were also cleaned up – for example, “reddish” and “reddy” are changed to -“red”. - -.. code:: ipython3 - - import proplot as plot - f = plot.show_colors() - - - -.. image:: tutorial/tutorial_142_0.svg - - - -.. image:: tutorial/tutorial_142_1.svg - - -Individual color sampling -------------------------- - -If you want to draw an individual color from a smooth colormap or a -color cycle, use ``color=(cmapname, position)`` or -``color=(cyclename, index)`` with any command that accepts the ``color`` -keyword! The ``position`` should be between 0 and 1, while the ``index`` -is the index on the list of colors in the cycle. This feature is powered -by the `~proplot.styletools.ColorCacheDict` class. +Thanks to `~proplot.wrappers.scatter_wrapper` and +`~proplot.wrappers.cycle_wrapper`, `~matplotlib.axes.Axes.scatter` +now accepts 2D arrays, just like `~matplotlib.axes.Axes.plot`, and +successive calls to `~matplotlib.axes.Axes.scatter` can apply property +cycle keys other than ``color`` – for example, ``marker`` and +``markersize``. `~matplotlib.axes.Axes.scatter` also now optionally +accepts keywords that look like the `~matplotlib.axes.Axes.plot` +keywords, which is a bit less confusing. You can also pass colormaps to +`~matplotlib.axes.Axes.scatter` just as with matplotlib. + +I am also considering supporting 2D array input and property cycle +iteration for more obscure matplotlib plotting commands like +`~matplotlib.axes.Axes.stem`, `~matplotlib.axes.Axes.step`, +`~matplotlib.axes.Axes.vlines`, and `~matplotlib.axes.Axes.hlines`. +Stay tuned! .. code:: ipython3 import proplot as plot import numpy as np + import pandas as pd plot.rc.reset() - f, axs = plot.subplots(nrows=2, aspect=2, axwidth=3, share=0) - # Drawing from colormap + f, axs = plot.subplots(ncols=2, share=1) + x = (np.random.rand(20)-0).cumsum() + data = (np.random.rand(20,4)-0.5).cumsum(axis=0) + data = pd.DataFrame(data, columns=pd.Index(['a','b','c','d'], name='label')) + # Scatter demo ax = axs[0] - cmap = 'deep' - m = ax.pcolormesh([[0],[1]], cmap=cmap, N=1000) - idxs = plot.arange(0,1,0.2) - np.random.shuffle(idxs) - for idx in idxs: - h = ax.plot((np.random.rand(20)-0.4).cumsum(), lw=5, color=(cmap, idx), - label=f'idx {idx:.1f}', legend='r', legend_kw={'ncols':1}) - ax.colorbar(m, loc='ul', locator=0.2, label='colormap') - ax.format(title='Drawing from the Solar colormap', grid=True) - # Drawing from color cycle + ax.format(title='New prop cycle properties', suptitle='Scatter plot demo') + obj = ax.scatter(x, data, legend='ul', cycle='warm', legend_kw={'ncols':2}, + cycle_kw={'marker':['x','o','x','o'], 'markersize':[5,10,20,30]}) ax = axs[1] - idxs = np.arange(6) - np.random.shuffle(idxs) - for idx in idxs: - h = ax.plot((np.random.rand(20)-0.4).cumsum(), lw=5, color=('qual1', idx), - label=f'idx {idx:.0f}', legend='r', legend_kw={'ncols':1}) - ax.format(title='Drawing from the ggplot color cycle') - axs.format(xlocator='null', abc=True, abcloc='ur', abcformat='A.', - suptitle='Getting individual colors from colormaps and cycles') - - - -.. image:: tutorial/tutorial_145_0.svg - - -Font selection --------------- - -DejaVu Sans is the default matplotlib font, but it’s not very -aesthetically pleasing. ProPlot adds a bunch of sans-serif fonts so that -you have them on every workstation, introduces a -`~proplot.styletools.show_fonts` command to display them (see below), -and makes Helvetica the default, as in MATLAB. Generally speaking, -simple, clean sans-serif fonts are more appropriate for figures than -serif fonts. - -You can register your own fonts by adding ``.ttf`` and ``.otf`` files to -the ``~/.proplot/fonts`` directory and calling -`~proplot.styletools.register_fonts` (which is also called on import). -To change the default font, use the `~proplot.rctools.rc` object or -modify your ``~/.proplotrc``. See the `~proplot.styletools` and -`~proplot.rctools` documentation for more info. - -.. code:: ipython3 - - import proplot as plot - plot.rc.reset() - f = plot.show_fonts() - - + ax.format(title='Scatter plot with cmap') + data = (np.random.rand(2,100)-0.5) + obj = ax.scatter(*data, color=data.sum(axis=0), size=np.random.rand(100), smin=3, smax=30, + marker='o', cmap='dusk', colorbar='lr', colorbar_kw={'locator':0.5, 'label':'label'}) + axs.format(xlabel='xlabel', ylabel='ylabel') -.. image:: tutorial/tutorial_148_0.svg +.. image:: tutorial/tutorial_175_0.svg diff --git a/proplot/axes.py b/proplot/axes.py index 0daf52bfe..7dea565b7 100644 --- a/proplot/axes.py +++ b/proplot/axes.py @@ -1158,7 +1158,8 @@ def legend(self, *args, loc=None, width=None, space=None, **kwargs): self._panel_filled = True # Try to make handles and stuff flush against the axes edge kwargs.setdefault('borderaxespad', 0) - if not kwargs.get('frameon', rc['legend.frameon']): + frameon = _notNone(kwargs.get('frame', None), kwargs.get('frameon', None), rc['legend.frameon']) + if not frameon: kwargs.setdefault('borderpad', 0) # Apply legend location side = self._panel_side @@ -1267,7 +1268,9 @@ def inset_axes(self, bounds, *, transform=None, zorder=5, ax.indicate_inset_zoom(**zoom_kw) return ax - def indicate_inset_zoom(self, alpha=None, linewidth=None, color=None, edgecolor=None, **kwargs): + def indicate_inset_zoom(self, alpha=None, + lw=None, linewidth=None, + color=None, edgecolor=None, **kwargs): """ Called automatically when using `~Axes.inset` with ``zoom=True``. Like `~matplotlib.axes.Axes.indicate_inset_zoom`, but *refreshes* the @@ -1279,11 +1282,9 @@ def indicate_inset_zoom(self, alpha=None, linewidth=None, color=None, edgecolor= ---------- alpha : float, optional The transparency of the zoom box fill. - linewidth : float, optional + lw, linewidth : float, optional The width of the zoom lines and box outline in points. - color : color-spec, optional - The color of the zoom box fill. - edgecolor : color-spec, optional + color, edgecolor : color-spec, optional The color of the zoom lines and box outline. **kwargs Passed to `~matplotlib.axes.Axes.indicate_inset`. @@ -1291,16 +1292,15 @@ def indicate_inset_zoom(self, alpha=None, linewidth=None, color=None, edgecolor= # Should be called from the inset axes parent = self._inset_parent alpha = alpha or 1.0 - linewidth = linewidth or rc['axes.linewidth'] - edgecolor = color or edgecolor or rc['axes.edgecolor'] + linewidth = _notNone(lw, linewidth, rc['axes.linewidth'], names=('lw', 'linewidth')) + edgecolor = _notNone(color, edgecolor, rc['axes.edgecolor'], names=('color', 'edgecolor')) if not parent: raise ValueError(f'{self} is not an inset axes.') - xlim = self.get_xlim() - ylim = self.get_ylim() + xlim, ylim = self.get_xlim(), self.get_ylim() rect = (xlim[0], ylim[0], xlim[1] - xlim[0], ylim[1] - ylim[0]) - # Call inset - kwargs.update({'linewidth':linewidth, 'edgecolor':edgecolor, 'alpha':alpha}) - rectpatch, connects = parent.indicate_inset(rect, self, **kwargs) + # Call indicate_inset + rectpatch, connects = parent.indicate_inset(rect, self, + linewidth=linewidth, edgecolor=edgecolor, alpha=alpha, **kwargs) # Adopt properties from old one if self._inset_zoom_data: rectpatch_old, connects_old = self._inset_zoom_data @@ -1309,6 +1309,7 @@ def indicate_inset_zoom(self, alpha=None, linewidth=None, color=None, edgecolor= for line,line_old in zip(connects,connects_old): visible = line.get_visible() line.update_from(line_old) + line.set_linewidth(line_old.get_linewidth()) line.set_visible(visible) line_old.set_visible(False) # Format zoom data diff --git a/proplot/styletools.py b/proplot/styletools.py index 57f69b639..a990e98ae 100644 --- a/proplot/styletools.py +++ b/proplot/styletools.py @@ -322,8 +322,12 @@ def __getitem__(self, key): pass else: if isinstance(cmap, mcolors.ListedColormap): + if not 0 <= rgb[1] < len(cmap.colors): + raise ValueError(f'Color cycle sample for {rgb[0]!r} cycle must be between 0 and {len(cmap.colors)-1}, got {rgb[1]}.') rgb = cmap.colors[rgb[1]] # draw color from the list of colors, using index else: + if not 0 <= rgb[1] <= 1: + raise ValueError(f'Colormap sample for {rgb[0]!r} colormap must be between 0 and 1, got {rgb[1]}.') rgb = cmap(rgb[1]) # interpolate color from colormap, using key in range 0-1 rgba = mcolors.to_rgba(rgb, alpha) return rgba diff --git a/proplot/subplots.py b/proplot/subplots.py index c8be29cc1..2c38a97f5 100644 --- a/proplot/subplots.py +++ b/proplot/subplots.py @@ -684,7 +684,7 @@ class Figure(mfigure.Figure): may be automatically scaled to preserve subplot aspect ratios.""" def __init__(self, tight=None, - pad=None, axpad=None, panelpad=None, + pad=None, axpad=None, panelpad=None, includepanels=False, autoformat=True, ref=1, order='C', # documented in subplots but needed here subplots_kw=None, gridspec_kw=None, subplots_orig_kw=None, @@ -707,6 +707,9 @@ def __init__(self, Padding between subplots and axes panels, and between "stacked" panels. If float, units are inches. If string, units are interpreted by `~proplot.utils.units`. + includepanels : bool, optional + Whether to include panels when centering spanning *x* axis labels, + *y* axis labels, and figure "super titles". Defaults to ``False``. autoformat : bool, optional Whether to automatically format the axes when a `~pandas.Series`, `~pandas.DataFrame` or `~xarray.DataArray` is passed to a plotting @@ -736,6 +739,7 @@ def __init__(self, self._panelpad = units(_notNone(panelpad, rc['subplots.panelpad'])) self._auto_format = autoformat self._auto_tight_layout = _notNone(tight, rc['tight']) + self._include_panels = includepanels self._order = order # used for configuring panel axes_grids self._ref_num = ref self._axes_main = [] @@ -777,14 +781,20 @@ def _add_axes_panel(self, ax, side, filled=False, **kwargs): if s in 'lr': iratio = (col1 - offset if s == 'l' else col2 + offset) idx1 = slice(row1, row2 + 1) - idx2 = max(0, iratio) + idx2 = iratio else: iratio = (row1 - offset if s == 't' else row2 + offset) - idx1 = max(0, iratio) + idx1 = iratio idx2 = slice(col1, col2 + 1) + gridspec_prev = self._gridspec_main gridspec = self._insert_row_column(side, iratio, width, space, space_orig, figure=False, ) + if gridspec is not gridspec_prev: + if s == 't': + idx1 += 1 + elif s == 'l': + idx2 += 1 # Draw and setup panel with self._unlock(): @@ -1242,6 +1252,8 @@ def _insert_row_column(self, side, idx, for ax in axs: # Get old index # NOTE: Endpoints are inclusive, not exclusive! + if not hasattr(ax, 'get_subplotspec'): + continue if s in 'lr': inserts = (None, None, idx, idx) else: @@ -1274,7 +1286,10 @@ def _get_align_coord(self, side, axs): `x` can be ``'x'`` or ``'y'``.""" # Get position in figure relative coordinates s = side[0] - x = ('x' if s in 'lr' else 'y') + x = ('y' if s in 'lr' else 'x') + extra = ('tb' if s in 'lr' else 'lr') + if self._include_panels: + axs = [iax for ax in axs for iax in ax._iter_panels(extra)] ranges = np.array([ax._range_gridspec(x) for ax in axs]) min_, max_ = ranges[:,0].min(), ranges[:,1].max() axlo = axs[np.where(ranges[:,0] == min_)[0][0]] @@ -1287,6 +1302,7 @@ def _get_align_coord(self, side, axs): pos = (lobox.y1 + hibox.y0)/2 # 'lo' is actually on top, highest up in gridspec # Return axis suitable for spanning position spanax = axs[(np.argmin(ranges[:,0]) + np.argmax(ranges[:,1]))//2] + spanax = spanax._panel_parent or spanax return pos, spanax def _get_align_axes(self, side): @@ -1690,7 +1706,7 @@ def subplots(array=None, ncols=1, nrows=1, align=None, alignx=None, aligny=None, share=None, sharex=None, sharey=None, basemap=False, proj=None, projection=None, proj_kw=None, projection_kw=None, - autoformat=True, + autoformat=True, includepanels=False, ): """ Analogous to `matplotlib.pyplot.subplots`, creates a figure with a single @@ -1836,6 +1852,9 @@ def subplots(array=None, ncols=1, nrows=1, pad, axpad, panelpad : float or str, optional Padding for automatic tight layout adjustments. See `Figure` for details. + includepanels : bool, optional + Whether to include panels when calculating the position of certain + spanning labels. See `Figure` for details. autoformat : bool, optional Whether to automatically format axes when special datasets are passed to plotting commands. See `Figure` for details. @@ -2039,6 +2058,7 @@ def subplots(array=None, ncols=1, nrows=1, ) fig = plt.figure(FigureClass=Figure, tight=tight, figsize=figsize, ref=ref, pad=pad, axpad=axpad, panelpad=panelpad, autoformat=autoformat, + includepanels=includepanels, subplots_orig_kw=subplots_orig_kw, subplots_kw=subplots_kw, gridspec_kw=gridspec_kw) gridspec = fig._gridspec_main