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