1e81fdcb135d0325e3bc22fae0583555d20aae280Brendan Jackman#    Copyright 2016-2017 ARM Limited
2d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock#
3d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock# Licensed under the Apache License, Version 2.0 (the "License");
4d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock# you may not use this file except in compliance with the License.
5d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock# You may obtain a copy of the License at
6d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock#
7d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock#     http://www.apache.org/licenses/LICENSE-2.0
8d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock#
9d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock# Unless required by applicable law or agreed to in writing, software
10d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock# distributed under the License is distributed on an "AS IS" BASIS,
11d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock# See the License for the specific language governing permissions and
13d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock# limitations under the License.
14d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock#
15d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock"""Base matplotlib plotter module"""
16d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocockfrom abc import abstractmethod, ABCMeta
1766d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocockfrom collections import defaultdict as ddict
18d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocockimport matplotlib.pyplot as plt
19d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocockfrom trappy.plotter import AttrConf
20d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocockfrom trappy.plotter.Constraint import ConstraintManager
21d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocockfrom trappy.plotter.PlotLayout import PlotLayout
22d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocockfrom trappy.plotter.AbstractDataPlotter import AbstractDataPlotter
23d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocockfrom trappy.plotter.ColorMap import ColorMap
24d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
25d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
26d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
27d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocockclass StaticPlot(AbstractDataPlotter):
28d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    """
29d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    This class uses :mod:`trappy.plotter.Constraint.Constraint` to
30d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    represent different permutations of input parameters. These
31d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    constraints are generated by creating an instance of
32d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :mod:`trappy.plotter.Constraint.ConstraintManager`.
33d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
34b8d89134b5ea7157b5b77f7907f569dff955fa1bJavi Merino    :param traces: The input data
354f4b18adce159f73b112ab789d695918ba45ef2fJavi Merino    :type traces: a list of :mod:`trappy.trace.FTrace`,
364f4b18adce159f73b112ab789d695918ba45ef2fJavi Merino        :mod:`trappy.trace.SysTrace`, :mod:`trappy.trace.BareTrace`
374f4b18adce159f73b112ab789d695918ba45ef2fJavi Merino        or :mod:`pandas.DataFrame` or a single instance of them.
38d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
39d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param column: specifies the name of the column to
40d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock           be plotted.
41d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type column: (str, list(str))
42d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
43d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param templates: TRAPpy events
44d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
45d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        .. note::
46d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
47d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock                This is not required if a :mod:`pandas.DataFrame` is
48d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock                used
49d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
50d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type templates: :mod:`trappy.base.Base`
51d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
52d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param filters: Filter the column to be plotted as per the
53d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        specified criteria. For Example:
54d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        ::
55d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
56d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            filters =
57d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock                    {
58d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock                        "pid": [ 3338 ],
59d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock                        "cpu": [0, 2, 4],
60d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock                    }
61d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type filters: dict
62d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
63d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param per_line: Used to control the number of graphs
64d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        in each graph subplot row
65d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type per_line: int
66d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
67d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param concat: Draw all the pivots on a single graph
68d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type concat: bool
69d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
70d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param permute: Draw one plot for each of the traces specified
71d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type permute: bool
72d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
73d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param drawstyle: This argument is forwarded to the matplotlib
74d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        corresponding :func:`matplotlib.pyplot.plot` call
75d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
76d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        drawing style.
77d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
78d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        .. note::
79d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
80d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            step plots are not currently supported for filled
81d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            graphs
82d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
83d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param xlim: A tuple representing the upper and lower xlimits
84d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type xlim: tuple
85d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
86d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param ylim: A tuple representing the upper and lower ylimits
87d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type ylim: tuple
88d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
89d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param title: A title describing all the generated plots
90d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type title: str
91d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
92d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param style: Created pre-styled graphs loaded from
93d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        :mod:`trappy.plotter.AttrConf.MPL_STYLE`
94d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type style: bool
95d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
96d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :param signals: A string of the type event_name:column
97d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        to indicate the value that needs to be plotted
98d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
99d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        .. note::
100d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
101d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            - Only one of `signals` or both `templates` and
102d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock              `columns` should be specified
103d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            - Signals format won't work for :mod:`pandas.DataFrame`
104d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock              input
105d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
106d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    :type signals: str
107d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
1089b4898aa3ea40f2af85157af65ab491e8aaa9b4cMichele Di Giorgio    :param legend_ncol: A positive integer that represents the
1099b4898aa3ea40f2af85157af65ab491e8aaa9b4cMichele Di Giorgio        number of columns in the legend
1109b4898aa3ea40f2af85157af65ab491e8aaa9b4cMichele Di Giorgio    :type legend_ncol: int
111d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    """
112d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    __metaclass__ = ABCMeta
113d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
114f4dbd1a60d428b70ea8fc1f05f59343d1db4900cJohn Pocock    def __init__(self, traces, templates, **kwargs):
115d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._fig = None
116d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._layout = None
117d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        super(StaticPlot, self).__init__(traces=traces,
118d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock                                         templates=templates)
119d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
120d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self.set_defaults()
121d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
122d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        for key in kwargs:
123d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            if key in AttrConf.ARGS_TO_FORWARD:
124d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock                self._attr["args_to_forward"][key] = kwargs[key]
125d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            else:
126d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock                self._attr[key] = kwargs[key]
127d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
128d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        if "signals" in self._attr:
129d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            self._describe_signals()
130d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
131d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._check_data()
132d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
133d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        if "column" not in self._attr:
134d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            raise RuntimeError("Value Column not specified")
135d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
136d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        zip_constraints = not self._attr["permute"]
137d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self.c_mgr = ConstraintManager(traces, self._attr["column"],
138d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock                                       self.templates, self._attr["pivot"],
139e6274bd2296229dae5303df8b4226601cc4abdb6Javi Merino                                       self._attr["filters"],
140e6274bd2296229dae5303df8b4226601cc4abdb6Javi Merino                                       zip_constraints=zip_constraints)
141d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
142d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    def savefig(self, *args, **kwargs):
143d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        """Save the plot as a PNG fill. This calls into
144d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        :mod:`matplotlib.figure.savefig`
145d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        """
146d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
147d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        if self._fig is None:
148d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            self.view()
149d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._fig.savefig(*args, **kwargs)
150d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
151d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    @abstractmethod
152d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    def set_defaults(self):
153d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        """Sets the default attrs"""
154d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["width"] = AttrConf.WIDTH
155d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["length"] = AttrConf.LENGTH
156d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["per_line"] = AttrConf.PER_LINE
157d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["concat"] = AttrConf.CONCAT
158d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["filters"] = {}
159d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["style"] = True
160d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["permute"] = False
161d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["pivot"] = AttrConf.PIVOT
162d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["xlim"] = AttrConf.XLIM
1630121e7156c2f9cc25637b206a6c2827a24ea782bMichele Di Giorgio        self._attr["ylim"] = AttrConf.YLIM
164d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["title"] = AttrConf.TITLE
165d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["args_to_forward"] = {}
166d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._attr["map_label"] = {}
16766d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock        self._attr["_legend_handles"] = []
16866d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock        self._attr["_legend_labels"] = []
1699b4898aa3ea40f2af85157af65ab491e8aaa9b4cMichele Di Giorgio        self._attr["legend_ncol"] = AttrConf.LEGEND_NCOL
170d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
171d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock    def view(self, test=False):
172d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        """Displays the graph"""
173d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
174d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        if test:
175d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            self._attr["style"] = True
176d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            AttrConf.MPL_STYLE["interactive"] = False
177d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
178287241040f9f8c58399f97412383efb36324c63dJohn Pocock        permute = self._attr["permute"] and not self._attr["concat"]
179287241040f9f8c58399f97412383efb36324c63dJohn Pocock        if self._attr["style"]:
180287241040f9f8c58399f97412383efb36324c63dJohn Pocock            with plt.rc_context(AttrConf.MPL_STYLE):
181287241040f9f8c58399f97412383efb36324c63dJohn Pocock                self._resolve(permute, self._attr["concat"])
182d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        else:
183287241040f9f8c58399f97412383efb36324c63dJohn Pocock            self._resolve(permute, self._attr["concat"])
184d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
1855d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock    def make_title(self, constraint, pivot, permute, concat):
1865d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        """Generates a title string for an axis"""
1875d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        if concat:
1885d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock            return str(constraint)
189b24eed7a902022c014c61f7c8c3061adf53f4eacMichele Di Giorgio
1902c3ca82c4b1c8ee6d6e37718696c4b422fe824c4John Pocock        if permute:
19129a12bb53910915378e5fa9b3c1cf35fbe8cfec7Michele Di Giorgio            return constraint.get_data_name()
192b24eed7a902022c014c61f7c8c3061adf53f4eacMichele Di Giorgio        elif pivot != AttrConf.PIVOT_VAL:
193b24eed7a902022c014c61f7c8c3061adf53f4eacMichele Di Giorgio            return "{0}: {1}".format(self._attr["pivot"], self._attr["map_label"].get(pivot, pivot))
1942c3ca82c4b1c8ee6d6e37718696c4b422fe824c4John Pocock        else:
195b24eed7a902022c014c61f7c8c3061adf53f4eacMichele Di Giorgio            return ""
1962c3ca82c4b1c8ee6d6e37718696c4b422fe824c4John Pocock
1979b4898aa3ea40f2af85157af65ab491e8aaa9b4cMichele Di Giorgio    def add_to_legend(self, series_index, handle, constraint, pivot, concat, permute):
1985d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        """
1995d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        Add series handles and names to the legend
2005d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        A handle is returned from a plot on an axis
2015d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        e.g. Line2D from axis.plot()
2025d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        """
2035d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        self._attr["_legend_handles"][series_index] = handle
2045d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        legend_labels = self._attr["_legend_labels"]
2055d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock
2065d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        if concat and pivot == AttrConf.PIVOT_VAL:
2075d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock            legend_labels[series_index] = self._attr["column"]
2085d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        elif concat:
2095d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock            legend_labels[series_index] = "{0}: {1}".format(
2105d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock                self._attr["pivot"],
2115d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock                self._attr["map_label"].get(pivot, pivot)
2125d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock            )
2139b4898aa3ea40f2af85157af65ab491e8aaa9b4cMichele Di Giorgio        elif permute:
21429a12bb53910915378e5fa9b3c1cf35fbe8cfec7Michele Di Giorgio            legend_labels[series_index] = constraint._template.name + ":" + constraint.column
2155d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        else:
2165d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock            legend_labels[series_index] = str(constraint)
21766d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock
218287241040f9f8c58399f97412383efb36324c63dJohn Pocock    def _resolve(self, permute, concat):
219287241040f9f8c58399f97412383efb36324c63dJohn Pocock        """Determine what data to plot on which axis"""
220d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        pivot_vals, len_pivots = self.c_mgr.generate_pivots(permute)
2212c3ca82c4b1c8ee6d6e37718696c4b422fe824c4John Pocock        pivot_vals = list(pivot_vals)
222d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
223287241040f9f8c58399f97412383efb36324c63dJohn Pocock        num_of_axes = len(self.c_mgr) if concat else len_pivots
224287241040f9f8c58399f97412383efb36324c63dJohn Pocock
225d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        # Create a 2D Layout
226d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._layout = PlotLayout(
227d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            self._attr["per_line"],
228287241040f9f8c58399f97412383efb36324c63dJohn Pocock            num_of_axes,
229d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            width=self._attr["width"],
230d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            length=self._attr["length"],
231d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock            title=self._attr['title'])
232d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
233d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        self._fig = self._layout.get_fig()
234d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock
2356b7add538125171d58e9569025062c21dc4d2923Michele Di Giorgio        # Determine what constraint to plot and the corresponding pivot value
236d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        if permute:
23766d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock            legend_len = self.c_mgr._max_len
2382c3ca82c4b1c8ee6d6e37718696c4b422fe824c4John Pocock            pivots = [y for _, y in pivot_vals]
23999afffa3870fcc68ac895fa58ff99e8043ff34dbMichele Di Giorgio            c_dict = {c : str(c) for c in self.c_mgr}
24099afffa3870fcc68ac895fa58ff99e8043ff34dbMichele Di Giorgio            c_list = sorted(c_dict.items(), key=lambda x: (x[1].split(":")[-1], x[1].split(":")[0]))
24199afffa3870fcc68ac895fa58ff99e8043ff34dbMichele Di Giorgio            constraints = [c[0] for c in c_list]
24299afffa3870fcc68ac895fa58ff99e8043ff34dbMichele Di Giorgio            cp_pairs = [(c, p) for c in constraints for p in sorted(set(pivots))]
243d098bd5d11b3305a66c13c705aba65879e7f3adcJohn Pocock        else:
244287241040f9f8c58399f97412383efb36324c63dJohn Pocock            legend_len = len_pivots if concat else len(self.c_mgr)
2452c3ca82c4b1c8ee6d6e37718696c4b422fe824c4John Pocock            pivots = pivot_vals
24666d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock            cp_pairs = [(c, p) for c in self.c_mgr for p in pivots if p in c.result]
2472c3ca82c4b1c8ee6d6e37718696c4b422fe824c4John Pocock
2486b7add538125171d58e9569025062c21dc4d2923Michele Di Giorgio        # Initialise legend data and colormap
24966d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock        self._attr["_legend_handles"] = [None] * legend_len
25066d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock        self._attr["_legend_labels"] = [None] * legend_len
25135e40457aa97167d333835c796d77b7404014b83John Pocock
25235e40457aa97167d333835c796d77b7404014b83John Pocock        if "colors" in self._attr:
25335e40457aa97167d333835c796d77b7404014b83John Pocock            self._cmap = ColorMap.rgb_cmap(self._attr["colors"])
25435e40457aa97167d333835c796d77b7404014b83John Pocock        else:
25535e40457aa97167d333835c796d77b7404014b83John Pocock            self._cmap = ColorMap(legend_len)
2562c3ca82c4b1c8ee6d6e37718696c4b422fe824c4John Pocock
2576b7add538125171d58e9569025062c21dc4d2923Michele Di Giorgio        # Group constraints/series with the axis they are to be plotted on
25866d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock        figure_data = ddict(list)
25966d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock        for i, (constraint, pivot) in enumerate(cp_pairs):
260287241040f9f8c58399f97412383efb36324c63dJohn Pocock            axis = self._layout.get_axis(constraint.trace_index if concat else i)
2615d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock            figure_data[axis].append((constraint, pivot))
2622c3ca82c4b1c8ee6d6e37718696c4b422fe824c4John Pocock
2636b7add538125171d58e9569025062c21dc4d2923Michele Di Giorgio        # Plot each axis
2645d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        for axis, series_list in figure_data.iteritems():
26566d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock            self.plot_axis(
26666d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock                axis,
26766d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock                series_list,
26866d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock                permute,
2695d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock                self._attr["concat"],
2705d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock                self._attr["args_to_forward"]
2715d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock            )
2720121e7156c2f9cc25637b206a6c2827a24ea782bMichele Di Giorgio            if self._attr["xlim"]:
2730121e7156c2f9cc25637b206a6c2827a24ea782bMichele Di Giorgio                axis.set_xlim(self._attr["xlim"])
2740121e7156c2f9cc25637b206a6c2827a24ea782bMichele Di Giorgio            if self._attr["ylim"]:
2750121e7156c2f9cc25637b206a6c2827a24ea782bMichele Di Giorgio                axis.set_ylim(self._attr["ylim"])
2765d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock
2776b7add538125171d58e9569025062c21dc4d2923Michele Di Giorgio        # Show legend
2786b7add538125171d58e9569025062c21dc4d2923Michele Di Giorgio        legend = self._fig.legend(self._attr["_legend_handles"],
2799b4898aa3ea40f2af85157af65ab491e8aaa9b4cMichele Di Giorgio                         self._attr["_legend_labels"],
2809b4898aa3ea40f2af85157af65ab491e8aaa9b4cMichele Di Giorgio                         loc='lower center',
2819b4898aa3ea40f2af85157af65ab491e8aaa9b4cMichele Di Giorgio                         ncol=self._attr["legend_ncol"],
2829b4898aa3ea40f2af85157af65ab491e8aaa9b4cMichele Di Giorgio                         borderaxespad=0.)
2836b7add538125171d58e9569025062c21dc4d2923Michele Di Giorgio        legend.get_frame().set_facecolor('#F4F4F4')
284287241040f9f8c58399f97412383efb36324c63dJohn Pocock
285287241040f9f8c58399f97412383efb36324c63dJohn Pocock        self._layout.finish(num_of_axes)
28666d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock
2875d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock    def plot_axis(self, axis, series_list, permute, concat, args_to_forward):
2885d35796164c77d1350b7dc26329fb0d0abc549b5John Pocock        """Internal Method called to plot data (series_list) on a given axis"""
28966d4e90b9b9fa5db544e4154505e97f8db562aa2John Pocock        raise NotImplementedError("Method Not Implemented")
290