• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#    Copyright 2015-2017 ARM Limited
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15"""This module implements functionality related to
16the arrangement of the plots on the underlying
17plotting backend.
18"""
19
20import matplotlib.pyplot as plt
21from trappy.plotter import AttrConf
22
23
24class PlotLayout(object):
25
26    """
27    :param cols: The number of columns to draw
28    :type cols: int
29
30    :param num_plots: The total number of plots
31    :type num_plots: int
32
33    The linear co-ordinate system :math:`[0, N_{plots}]` is
34    mapped to a 2-D coordinate system with math:`N_{rows}`
35    and :math:`N_{cols}` such that:
36
37    .. math::
38
39        N_{rows} = \\frac{N_{cols}}{N_{plots}}
40    """
41
42    def __init__(self, cols, num_plots, **kwargs):
43
44        self.cols = cols
45        self._attr = {}
46        self.num_plots = num_plots
47        self._single_plot = False
48        if self.num_plots == 0:
49            raise RuntimeError("No plots for the given constraints")
50
51        if self.num_plots < self.cols:
52            self.cols = self.num_plots
53        self.rows = (self.num_plots / self.cols)
54        # Avoid Extra Allocation (shows up in savefig!)
55        if self.num_plots % self.cols != 0:
56            self.rows += 1
57
58        self.usecol = False
59        self.userow = False
60        self._set_defaults()
61
62        for key in kwargs:
63            self._attr[key] = kwargs[key]
64
65        # Scale the plots if there is a single plot and
66        # Set boolean variables
67        if num_plots == 1:
68            self._single_plot = True
69            self._scale_plot()
70        elif self.rows == 1:
71            self.usecol = True
72        elif self.cols == 1:
73            self.userow = True
74            self._scale_plot()
75
76        self._attr["figure"], self._attr["axes"] = plt.subplots(
77            self.rows, self.cols, figsize=(
78                self._attr["width"] * self.cols,
79                self._attr["length"] * self.rows))
80
81        if self._attr['title']:
82            self._attr["figure"].suptitle(
83                    self._attr['title'],
84                    fontsize=AttrConf.TITLE_SIZE,
85                    horizontalalignment='center')
86
87    def _scale_plot(self):
88        """Scale the graph in one
89           plot per line use case"""
90
91        self._attr["width"] = int(self._attr["width"] * 2.5)
92        self._attr["length"] = int(self._attr["length"] * 1.25)
93
94    def _set_defaults(self):
95        """set the default attrs"""
96        self._attr["width"] = AttrConf.WIDTH
97        self._attr["length"] = AttrConf.LENGTH
98
99    def get_2d(self, linear_val):
100        """Convert Linear to 2D coordinates
101
102        :param linear_val: The value in 1-D
103            co-ordinate
104        :type linear_val: int
105
106        :return: Converted 2-D tuple
107        """
108        if self.usecol:
109            return linear_val % self.cols
110
111        if self.userow:
112            return linear_val % self.rows
113
114        val_x = linear_val % self.cols
115        val_y = linear_val / self.cols
116        return val_y, val_x
117
118    def finish(self, plot_index):
119        """Delete the empty cells
120
121        :param plot_index: Linear index at which the
122            last plot was created. This is used to
123            delete the leftover empty plots that
124            were generated.
125        :type plot_index: int
126        """
127        while plot_index < (self.rows * self.cols):
128            self._attr["figure"].delaxes(
129                self._attr["axes"][
130                    self.get_2d(plot_index)])
131            plot_index += 1
132
133    def get_axis(self, plot_index):
134        """Get the axes for the plots
135
136        :param plot_index: The index for
137            which the axis is required. This
138            internally is mapped to a 2-D co-ordinate
139
140        :return: :mod:`matplotlib.axes.Axes`
141            instance is returned
142        """
143        if self._single_plot:
144            return self._attr["axes"]
145        else:
146            return self._attr["axes"][self.get_2d(plot_index)]
147
148    def get_fig(self):
149        """Return the matplotlib figure object
150
151        :return: :mod:`matplotlib.figure`
152        """
153        return self._attr["figure"]
154