aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/matplotlib/py2/mpl_toolkits/mplot3d/axis3d.py
diff options
context:
space:
mode:
authorshumkovnd <shumkovnd@yandex-team.com>2023-11-10 14:39:34 +0300
committershumkovnd <shumkovnd@yandex-team.com>2023-11-10 16:42:24 +0300
commit77eb2d3fdcec5c978c64e025ced2764c57c00285 (patch)
treec51edb0748ca8d4a08d7c7323312c27ba1a8b79a /contrib/python/matplotlib/py2/mpl_toolkits/mplot3d/axis3d.py
parentdd6d20cadb65582270ac23f4b3b14ae189704b9d (diff)
downloadydb-77eb2d3fdcec5c978c64e025ced2764c57c00285.tar.gz
KIKIMR-19287: add task_stats_drawing script
Diffstat (limited to 'contrib/python/matplotlib/py2/mpl_toolkits/mplot3d/axis3d.py')
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/mplot3d/axis3d.py484
1 files changed, 484 insertions, 0 deletions
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/mplot3d/axis3d.py b/contrib/python/matplotlib/py2/mpl_toolkits/mplot3d/axis3d.py
new file mode 100644
index 0000000000..50b81df912
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/mplot3d/axis3d.py
@@ -0,0 +1,484 @@
+# axis3d.py, original mplot3d version by John Porter
+# Created: 23 Sep 2005
+# Parts rewritten by Reinier Heeres <reinier@heeres.eu>
+
+from __future__ import (absolute_import, division, print_function,
+ unicode_literals)
+
+import six
+
+import math
+import copy
+
+from matplotlib import lines as mlines, axis as maxis, patches as mpatches
+from matplotlib import rcParams
+from . import art3d
+from . import proj3d
+
+import numpy as np
+
+def get_flip_min_max(coord, index, mins, maxs):
+ if coord[index] == mins[index]:
+ return maxs[index]
+ else:
+ return mins[index]
+
+def move_from_center(coord, centers, deltas, axmask=(True, True, True)):
+ '''Return a coordinate that is moved by "deltas" away from the center.'''
+ coord = copy.copy(coord)
+ for i in range(3):
+ if not axmask[i]:
+ continue
+ if coord[i] < centers[i]:
+ coord[i] -= deltas[i]
+ else:
+ coord[i] += deltas[i]
+ return coord
+
+def tick_update_position(tick, tickxs, tickys, labelpos):
+ '''Update tick line and label position and style.'''
+
+ for (label, on) in [(tick.label1, tick.label1On),
+ (tick.label2, tick.label2On)]:
+ if on:
+ label.set_position(labelpos)
+
+ tick.tick1On, tick.tick2On = True, False
+ tick.tick1line.set_linestyle('-')
+ tick.tick1line.set_marker('')
+ tick.tick1line.set_data(tickxs, tickys)
+ tick.gridline.set_data(0, 0)
+
+class Axis(maxis.XAxis):
+
+ # These points from the unit cube make up the x, y and z-planes
+ _PLANES = (
+ (0, 3, 7, 4), (1, 2, 6, 5), # yz planes
+ (0, 1, 5, 4), (3, 2, 6, 7), # xz planes
+ (0, 1, 2, 3), (4, 5, 6, 7), # xy planes
+ )
+
+ # Some properties for the axes
+ _AXINFO = {
+ 'x': {'i': 0, 'tickdir': 1, 'juggled': (1, 0, 2),
+ 'color': (0.95, 0.95, 0.95, 0.5)},
+ 'y': {'i': 1, 'tickdir': 0, 'juggled': (0, 1, 2),
+ 'color': (0.90, 0.90, 0.90, 0.5)},
+ 'z': {'i': 2, 'tickdir': 0, 'juggled': (0, 2, 1),
+ 'color': (0.925, 0.925, 0.925, 0.5)},
+ }
+
+ def __init__(self, adir, v_intervalx, d_intervalx, axes, *args, **kwargs):
+ # adir identifies which axes this is
+ self.adir = adir
+ # data and viewing intervals for this direction
+ self.d_interval = d_intervalx
+ self.v_interval = v_intervalx
+
+ # This is a temporary member variable.
+ # Do not depend on this existing in future releases!
+ self._axinfo = self._AXINFO[adir].copy()
+ if rcParams['_internal.classic_mode']:
+ self._axinfo.update(
+ {'label': {'va': 'center',
+ 'ha': 'center'},
+ 'tick': {'inward_factor': 0.2,
+ 'outward_factor': 0.1,
+ 'linewidth': rcParams['lines.linewidth'],
+ 'color': 'k'},
+ 'axisline': {'linewidth': 0.75,
+ 'color': (0, 0, 0, 1)},
+ 'grid': {'color': (0.9, 0.9, 0.9, 1),
+ 'linewidth': 1.0,
+ 'linestyle': '-'},
+ })
+ else:
+ self._axinfo.update(
+ {'label': {'va': 'center',
+ 'ha': 'center'},
+ 'tick': {'inward_factor': 0.2,
+ 'outward_factor': 0.1,
+ 'linewidth': rcParams.get(
+ adir + 'tick.major.width',
+ rcParams['xtick.major.width']),
+ 'color': rcParams.get(
+ adir + 'tick.color',
+ rcParams['xtick.color'])},
+ 'axisline': {'linewidth': rcParams['axes.linewidth'],
+ 'color': rcParams['axes.edgecolor']},
+ 'grid': {'color': rcParams['grid.color'],
+ 'linewidth': rcParams['grid.linewidth'],
+ 'linestyle': rcParams['grid.linestyle']},
+ })
+
+ maxis.XAxis.__init__(self, axes, *args, **kwargs)
+ self.set_rotate_label(kwargs.get('rotate_label', None))
+
+ def init3d(self):
+ self.line = mlines.Line2D(
+ xdata=(0, 0), ydata=(0, 0),
+ linewidth=self._axinfo['axisline']['linewidth'],
+ color=self._axinfo['axisline']['color'],
+ antialiased=True)
+
+ # Store dummy data in Polygon object
+ self.pane = mpatches.Polygon(
+ np.array([[0, 0], [0, 1], [1, 0], [0, 0]]),
+ closed=False, alpha=0.8, facecolor='k', edgecolor='k')
+ self.set_pane_color(self._axinfo['color'])
+
+ self.axes._set_artist_props(self.line)
+ self.axes._set_artist_props(self.pane)
+ self.gridlines = art3d.Line3DCollection([])
+ self.axes._set_artist_props(self.gridlines)
+ self.axes._set_artist_props(self.label)
+ self.axes._set_artist_props(self.offsetText)
+ # Need to be able to place the label at the correct location
+ self.label._transform = self.axes.transData
+ self.offsetText._transform = self.axes.transData
+
+ def get_tick_positions(self):
+ majorLocs = self.major.locator()
+ self.major.formatter.set_locs(majorLocs)
+ majorLabels = [self.major.formatter(val, i)
+ for i, val in enumerate(majorLocs)]
+ return majorLabels, majorLocs
+
+ def get_major_ticks(self, numticks=None):
+ ticks = maxis.XAxis.get_major_ticks(self, numticks)
+ for t in ticks:
+ t.tick1line.set_transform(self.axes.transData)
+ t.tick2line.set_transform(self.axes.transData)
+ t.gridline.set_transform(self.axes.transData)
+ t.label1.set_transform(self.axes.transData)
+ t.label2.set_transform(self.axes.transData)
+ return ticks
+
+ def set_pane_pos(self, xys):
+ xys = np.asarray(xys)
+ xys = xys[:,:2]
+ self.pane.xy = xys
+ self.stale = True
+
+ def set_pane_color(self, color):
+ '''Set pane color to a RGBA tuple.'''
+ self._axinfo['color'] = color
+ self.pane.set_edgecolor(color)
+ self.pane.set_facecolor(color)
+ self.pane.set_alpha(color[-1])
+ self.stale = True
+
+ def set_rotate_label(self, val):
+ '''
+ Whether to rotate the axis label: True, False or None.
+ If set to None the label will be rotated if longer than 4 chars.
+ '''
+ self._rotate_label = val
+ self.stale = True
+
+ def get_rotate_label(self, text):
+ if self._rotate_label is not None:
+ return self._rotate_label
+ else:
+ return len(text) > 4
+
+ def _get_coord_info(self, renderer):
+ minx, maxx, miny, maxy, minz, maxz = self.axes.get_w_lims()
+ if minx > maxx:
+ minx, maxx = maxx, minx
+ if miny > maxy:
+ miny, maxy = maxy, miny
+ if minz > maxz:
+ minz, maxz = maxz, minz
+ mins = np.array((minx, miny, minz))
+ maxs = np.array((maxx, maxy, maxz))
+ centers = (maxs + mins) / 2.
+ deltas = (maxs - mins) / 12.
+ mins = mins - deltas / 4.
+ maxs = maxs + deltas / 4.
+
+ vals = mins[0], maxs[0], mins[1], maxs[1], mins[2], maxs[2]
+ tc = self.axes.tunit_cube(vals, renderer.M)
+ avgz = [tc[p1][2] + tc[p2][2] + tc[p3][2] + tc[p4][2]
+ for p1, p2, p3, p4 in self._PLANES]
+ highs = np.array([avgz[2*i] < avgz[2*i+1] for i in range(3)])
+
+ return mins, maxs, centers, deltas, tc, highs
+
+ def draw_pane(self, renderer):
+ renderer.open_group('pane3d')
+
+ mins, maxs, centers, deltas, tc, highs = self._get_coord_info(renderer)
+
+ info = self._axinfo
+ index = info['i']
+ if not highs[index]:
+ plane = self._PLANES[2 * index]
+ else:
+ plane = self._PLANES[2 * index + 1]
+ xys = [tc[p] for p in plane]
+ self.set_pane_pos(xys)
+ self.pane.draw(renderer)
+
+ renderer.close_group('pane3d')
+
+ def draw(self, renderer):
+ self.label._transform = self.axes.transData
+ renderer.open_group('axis3d')
+
+ # code from XAxis
+ majorTicks = self.get_major_ticks()
+ majorLocs = self.major.locator()
+
+ info = self._axinfo
+ index = info['i']
+
+ # filter locations here so that no extra grid lines are drawn
+ locmin, locmax = self.get_view_interval()
+ if locmin > locmax:
+ locmin, locmax = locmax, locmin
+
+ # Rudimentary clipping
+ majorLocs = [loc for loc in majorLocs if
+ locmin <= loc <= locmax]
+ self.major.formatter.set_locs(majorLocs)
+ majorLabels = [self.major.formatter(val, i)
+ for i, val in enumerate(majorLocs)]
+
+ mins, maxs, centers, deltas, tc, highs = self._get_coord_info(renderer)
+
+ # Determine grid lines
+ minmax = np.where(highs, maxs, mins)
+
+ # Draw main axis line
+ juggled = info['juggled']
+ edgep1 = minmax.copy()
+ edgep1[juggled[0]] = get_flip_min_max(edgep1, juggled[0], mins, maxs)
+
+ edgep2 = edgep1.copy()
+ edgep2[juggled[1]] = get_flip_min_max(edgep2, juggled[1], mins, maxs)
+ pep = proj3d.proj_trans_points([edgep1, edgep2], renderer.M)
+ centpt = proj3d.proj_transform(
+ centers[0], centers[1], centers[2], renderer.M)
+ self.line.set_data((pep[0][0], pep[0][1]), (pep[1][0], pep[1][1]))
+ self.line.draw(renderer)
+
+ # Grid points where the planes meet
+ xyz0 = []
+ for val in majorLocs:
+ coord = minmax.copy()
+ coord[index] = val
+ xyz0.append(coord)
+
+ # Draw labels
+ peparray = np.asanyarray(pep)
+ # The transAxes transform is used because the Text object
+ # rotates the text relative to the display coordinate system.
+ # Therefore, if we want the labels to remain parallel to the
+ # axis regardless of the aspect ratio, we need to convert the
+ # edge points of the plane to display coordinates and calculate
+ # an angle from that.
+ # TODO: Maybe Text objects should handle this themselves?
+ dx, dy = (self.axes.transAxes.transform([peparray[0:2, 1]]) -
+ self.axes.transAxes.transform([peparray[0:2, 0]]))[0]
+
+ lxyz = 0.5*(edgep1 + edgep2)
+
+ # A rough estimate; points are ambiguous since 3D plots rotate
+ ax_scale = self.axes.bbox.size / self.figure.bbox.size
+ ax_inches = np.multiply(ax_scale, self.figure.get_size_inches())
+ ax_points_estimate = sum(72. * ax_inches)
+ deltas_per_point = 48. / ax_points_estimate
+ default_offset = 21.
+ labeldeltas = (
+ (self.labelpad + default_offset) * deltas_per_point * deltas)
+ axmask = [True, True, True]
+ axmask[index] = False
+ lxyz = move_from_center(lxyz, centers, labeldeltas, axmask)
+ tlx, tly, tlz = proj3d.proj_transform(lxyz[0], lxyz[1], lxyz[2],
+ renderer.M)
+ self.label.set_position((tlx, tly))
+ if self.get_rotate_label(self.label.get_text()):
+ angle = art3d.norm_text_angle(math.degrees(math.atan2(dy, dx)))
+ self.label.set_rotation(angle)
+ self.label.set_va(info['label']['va'])
+ self.label.set_ha(info['label']['ha'])
+ self.label.draw(renderer)
+
+
+ # Draw Offset text
+
+ # Which of the two edge points do we want to
+ # use for locating the offset text?
+ if juggled[2] == 2 :
+ outeredgep = edgep1
+ outerindex = 0
+ else :
+ outeredgep = edgep2
+ outerindex = 1
+
+ pos = copy.copy(outeredgep)
+ pos = move_from_center(pos, centers, labeldeltas, axmask)
+ olx, oly, olz = proj3d.proj_transform(
+ pos[0], pos[1], pos[2], renderer.M)
+ self.offsetText.set_text( self.major.formatter.get_offset() )
+ self.offsetText.set_position( (olx, oly) )
+ angle = art3d.norm_text_angle(math.degrees(math.atan2(dy, dx)))
+ self.offsetText.set_rotation(angle)
+ # Must set rotation mode to "anchor" so that
+ # the alignment point is used as the "fulcrum" for rotation.
+ self.offsetText.set_rotation_mode('anchor')
+
+ #----------------------------------------------------------------------
+ # Note: the following statement for determining the proper alignment of
+ # the offset text. This was determined entirely by trial-and-error
+ # and should not be in any way considered as "the way". There are
+ # still some edge cases where alignment is not quite right, but this
+ # seems to be more of a geometry issue (in other words, I might be
+ # using the wrong reference points).
+ #
+ # (TT, FF, TF, FT) are the shorthand for the tuple of
+ # (centpt[info['tickdir']] <= peparray[info['tickdir'], outerindex],
+ # centpt[index] <= peparray[index, outerindex])
+ #
+ # Three-letters (e.g., TFT, FTT) are short-hand for the array of bools
+ # from the variable 'highs'.
+ # ---------------------------------------------------------------------
+ if centpt[info['tickdir']] > peparray[info['tickdir'], outerindex] :
+ # if FT and if highs has an even number of Trues
+ if (centpt[index] <= peparray[index, outerindex]
+ and ((len(highs.nonzero()[0]) % 2) == 0)) :
+ # Usually, this means align right, except for the FTT case,
+ # in which offset for axis 1 and 2 are aligned left.
+ if highs.tolist() == [False, True, True] and index in (1, 2) :
+ align = 'left'
+ else :
+ align = 'right'
+ else :
+ # The FF case
+ align = 'left'
+ else :
+ # if TF and if highs has an even number of Trues
+ if (centpt[index] > peparray[index, outerindex]
+ and ((len(highs.nonzero()[0]) % 2) == 0)) :
+ # Usually mean align left, except if it is axis 2
+ if index == 2 :
+ align = 'right'
+ else :
+ align = 'left'
+ else :
+ # The TT case
+ align = 'right'
+
+ self.offsetText.set_va('center')
+ self.offsetText.set_ha(align)
+ self.offsetText.draw(renderer)
+
+ # Draw grid lines
+ if len(xyz0) > 0:
+ # Grid points at end of one plane
+ xyz1 = copy.deepcopy(xyz0)
+ newindex = (index + 1) % 3
+ newval = get_flip_min_max(xyz1[0], newindex, mins, maxs)
+ for i in range(len(majorLocs)):
+ xyz1[i][newindex] = newval
+
+ # Grid points at end of the other plane
+ xyz2 = copy.deepcopy(xyz0)
+ newindex = (index + 2) % 3
+ newval = get_flip_min_max(xyz2[0], newindex, mins, maxs)
+ for i in range(len(majorLocs)):
+ xyz2[i][newindex] = newval
+
+ lines = list(zip(xyz1, xyz0, xyz2))
+ if self.axes._draw_grid:
+ self.gridlines.set_segments(lines)
+ self.gridlines.set_color([info['grid']['color']] * len(lines))
+ self.gridlines.set_linewidth(
+ [info['grid']['linewidth']] * len(lines))
+ self.gridlines.set_linestyle(
+ [info['grid']['linestyle']] * len(lines))
+ self.gridlines.draw(renderer, project=True)
+
+ # Draw ticks
+ tickdir = info['tickdir']
+ tickdelta = deltas[tickdir]
+ if highs[tickdir]:
+ ticksign = 1
+ else:
+ ticksign = -1
+
+ for tick, loc, label in zip(majorTicks, majorLocs, majorLabels):
+ if tick is None:
+ continue
+
+ # Get tick line positions
+ pos = copy.copy(edgep1)
+ pos[index] = loc
+ pos[tickdir] = (
+ edgep1[tickdir]
+ + info['tick']['outward_factor'] * ticksign * tickdelta)
+ x1, y1, z1 = proj3d.proj_transform(pos[0], pos[1], pos[2],
+ renderer.M)
+ pos[tickdir] = (
+ edgep1[tickdir]
+ - info['tick']['inward_factor'] * ticksign * tickdelta)
+ x2, y2, z2 = proj3d.proj_transform(pos[0], pos[1], pos[2],
+ renderer.M)
+
+ # Get position of label
+ default_offset = 8. # A rough estimate
+ labeldeltas = (
+ (tick.get_pad() + default_offset) * deltas_per_point * deltas)
+
+ axmask = [True, True, True]
+ axmask[index] = False
+ pos[tickdir] = edgep1[tickdir]
+ pos = move_from_center(pos, centers, labeldeltas, axmask)
+ lx, ly, lz = proj3d.proj_transform(pos[0], pos[1], pos[2],
+ renderer.M)
+
+ tick_update_position(tick, (x1, x2), (y1, y2), (lx, ly))
+ tick.tick1line.set_linewidth(info['tick']['linewidth'])
+ tick.tick1line.set_color(info['tick']['color'])
+ tick.set_label1(label)
+ tick.set_label2(label)
+ tick.draw(renderer)
+
+ renderer.close_group('axis3d')
+ self.stale = False
+
+ def get_view_interval(self):
+ """return the Interval instance for this 3d axis view limits"""
+ return self.v_interval
+
+ def set_view_interval(self, vmin, vmax, ignore=False):
+ if ignore:
+ self.v_interval = vmin, vmax
+ else:
+ Vmin, Vmax = self.get_view_interval()
+ self.v_interval = min(vmin, Vmin), max(vmax, Vmax)
+
+ # TODO: Get this to work properly when mplot3d supports
+ # the transforms framework.
+ def get_tightbbox(self, renderer) :
+ # Currently returns None so that Axis.get_tightbbox
+ # doesn't return junk info.
+ return None
+
+# Use classes to look at different data limits
+
+class XAxis(Axis):
+ def get_data_interval(self):
+ 'return the Interval instance for this axis data limits'
+ return self.axes.xy_dataLim.intervalx
+
+class YAxis(Axis):
+ def get_data_interval(self):
+ 'return the Interval instance for this axis data limits'
+ return self.axes.xy_dataLim.intervaly
+
+class ZAxis(Axis):
+ def get_data_interval(self):
+ 'return the Interval instance for this axis data limits'
+ return self.axes.zz_dataLim.intervalx