Coverage for /usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/axis3d.py: 89%

297 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1# axis3d.py, original mplot3d version by John Porter 

2# Created: 23 Sep 2005 

3# Parts rewritten by Reinier Heeres <reinier@heeres.eu> 

4 

5import inspect 

6 

7import numpy as np 

8 

9import matplotlib as mpl 

10from matplotlib import ( 

11 _api, artist, lines as mlines, axis as maxis, patches as mpatches, 

12 transforms as mtransforms, colors as mcolors) 

13from . import art3d, proj3d 

14 

15 

16@_api.deprecated("3.6", alternative="a vendored copy of _move_from_center") 

17def move_from_center(coord, centers, deltas, axmask=(True, True, True)): 

18 """ 

19 For each coordinate where *axmask* is True, move *coord* away from 

20 *centers* by *deltas*. 

21 """ 

22 return _move_from_center(coord, centers, deltas, axmask=axmask) 

23 

24 

25def _move_from_center(coord, centers, deltas, axmask=(True, True, True)): 

26 """ 

27 For each coordinate where *axmask* is True, move *coord* away from 

28 *centers* by *deltas*. 

29 """ 

30 coord = np.asarray(coord) 

31 return coord + axmask * np.copysign(1, coord - centers) * deltas 

32 

33 

34@_api.deprecated("3.6", alternative="a vendored copy of _tick_update_position") 

35def tick_update_position(tick, tickxs, tickys, labelpos): 

36 """Update tick line and label position and style.""" 

37 _tick_update_position(tick, tickxs, tickys, labelpos) 

38 

39 

40def _tick_update_position(tick, tickxs, tickys, labelpos): 

41 """Update tick line and label position and style.""" 

42 

43 tick.label1.set_position(labelpos) 

44 tick.label2.set_position(labelpos) 

45 tick.tick1line.set_visible(True) 

46 tick.tick2line.set_visible(False) 

47 tick.tick1line.set_linestyle('-') 

48 tick.tick1line.set_marker('') 

49 tick.tick1line.set_data(tickxs, tickys) 

50 tick.gridline.set_data(0, 0) 

51 

52 

53class Axis(maxis.XAxis): 

54 """An Axis class for the 3D plots.""" 

55 # These points from the unit cube make up the x, y and z-planes 

56 _PLANES = ( 

57 (0, 3, 7, 4), (1, 2, 6, 5), # yz planes 

58 (0, 1, 5, 4), (3, 2, 6, 7), # xz planes 

59 (0, 1, 2, 3), (4, 5, 6, 7), # xy planes 

60 ) 

61 

62 # Some properties for the axes 

63 _AXINFO = { 

64 'x': {'i': 0, 'tickdir': 1, 'juggled': (1, 0, 2), 

65 'color': (0.95, 0.95, 0.95, 0.5)}, 

66 'y': {'i': 1, 'tickdir': 0, 'juggled': (0, 1, 2), 

67 'color': (0.90, 0.90, 0.90, 0.5)}, 

68 'z': {'i': 2, 'tickdir': 0, 'juggled': (0, 2, 1), 

69 'color': (0.925, 0.925, 0.925, 0.5)}, 

70 } 

71 

72 def _old_init(self, adir, v_intervalx, d_intervalx, axes, *args, 

73 rotate_label=None, **kwargs): 

74 return locals() 

75 

76 def _new_init(self, axes, *, rotate_label=None, **kwargs): 

77 return locals() 

78 

79 def __init__(self, *args, **kwargs): 

80 params = _api.select_matching_signature( 

81 [self._old_init, self._new_init], *args, **kwargs) 

82 if "adir" in params: 

83 _api.warn_deprecated( 

84 "3.6", message=f"The signature of 3D Axis constructors has " 

85 f"changed in %(since)s; the new signature is " 

86 f"{inspect.signature(type(self).__init__)}", pending=True) 

87 if params["adir"] != self.axis_name: 

88 raise ValueError(f"Cannot instantiate {type(self).__name__} " 

89 f"with adir={params['adir']!r}") 

90 axes = params["axes"] 

91 rotate_label = params["rotate_label"] 

92 args = params.get("args", ()) 

93 kwargs = params["kwargs"] 

94 

95 name = self.axis_name 

96 

97 # This is a temporary member variable. 

98 # Do not depend on this existing in future releases! 

99 self._axinfo = self._AXINFO[name].copy() 

100 if mpl.rcParams['_internal.classic_mode']: 

101 self._axinfo.update({ 

102 'label': {'va': 'center', 'ha': 'center'}, 

103 'tick': { 

104 'inward_factor': 0.2, 

105 'outward_factor': 0.1, 

106 'linewidth': { 

107 True: mpl.rcParams['lines.linewidth'], # major 

108 False: mpl.rcParams['lines.linewidth'], # minor 

109 } 

110 }, 

111 'axisline': {'linewidth': 0.75, 'color': (0, 0, 0, 1)}, 

112 'grid': { 

113 'color': (0.9, 0.9, 0.9, 1), 

114 'linewidth': 1.0, 

115 'linestyle': '-', 

116 }, 

117 }) 

118 else: 

119 self._axinfo.update({ 

120 'label': {'va': 'center', 'ha': 'center'}, 

121 'tick': { 

122 'inward_factor': 0.2, 

123 'outward_factor': 0.1, 

124 'linewidth': { 

125 True: ( # major 

126 mpl.rcParams['xtick.major.width'] if name in 'xz' 

127 else mpl.rcParams['ytick.major.width']), 

128 False: ( # minor 

129 mpl.rcParams['xtick.minor.width'] if name in 'xz' 

130 else mpl.rcParams['ytick.minor.width']), 

131 } 

132 }, 

133 'axisline': { 

134 'linewidth': mpl.rcParams['axes.linewidth'], 

135 'color': mpl.rcParams['axes.edgecolor'], 

136 }, 

137 'grid': { 

138 'color': mpl.rcParams['grid.color'], 

139 'linewidth': mpl.rcParams['grid.linewidth'], 

140 'linestyle': mpl.rcParams['grid.linestyle'], 

141 }, 

142 }) 

143 

144 super().__init__(axes, *args, **kwargs) 

145 

146 # data and viewing intervals for this direction 

147 if "d_intervalx" in params: 

148 self.set_data_interval(*params["d_intervalx"]) 

149 if "v_intervalx" in params: 

150 self.set_view_interval(*params["v_intervalx"]) 

151 self.set_rotate_label(rotate_label) 

152 self._init3d() # Inline after init3d deprecation elapses. 

153 

154 __init__.__signature__ = inspect.signature(_new_init) 

155 adir = _api.deprecated("3.6", pending=True)( 

156 property(lambda self: self.axis_name)) 

157 

158 def _init3d(self): 

159 self.line = mlines.Line2D( 

160 xdata=(0, 0), ydata=(0, 0), 

161 linewidth=self._axinfo['axisline']['linewidth'], 

162 color=self._axinfo['axisline']['color'], 

163 antialiased=True) 

164 

165 # Store dummy data in Polygon object 

166 self.pane = mpatches.Polygon( 

167 np.array([[0, 0], [0, 1]]), closed=False) 

168 self.set_pane_color(self._axinfo['color']) 

169 

170 self.axes._set_artist_props(self.line) 

171 self.axes._set_artist_props(self.pane) 

172 self.gridlines = art3d.Line3DCollection([]) 

173 self.axes._set_artist_props(self.gridlines) 

174 self.axes._set_artist_props(self.label) 

175 self.axes._set_artist_props(self.offsetText) 

176 # Need to be able to place the label at the correct location 

177 self.label._transform = self.axes.transData 

178 self.offsetText._transform = self.axes.transData 

179 

180 @_api.deprecated("3.6", pending=True) 

181 def init3d(self): # After deprecation elapses, inline _init3d to __init__. 

182 self._init3d() 

183 

184 def get_major_ticks(self, numticks=None): 

185 ticks = super().get_major_ticks(numticks) 

186 for t in ticks: 

187 for obj in [ 

188 t.tick1line, t.tick2line, t.gridline, t.label1, t.label2]: 

189 obj.set_transform(self.axes.transData) 

190 return ticks 

191 

192 def get_minor_ticks(self, numticks=None): 

193 ticks = super().get_minor_ticks(numticks) 

194 for t in ticks: 

195 for obj in [ 

196 t.tick1line, t.tick2line, t.gridline, t.label1, t.label2]: 

197 obj.set_transform(self.axes.transData) 

198 return ticks 

199 

200 @_api.deprecated("3.6") 

201 def set_pane_pos(self, xys): 

202 self._set_pane_pos(xys) 

203 

204 def _set_pane_pos(self, xys): 

205 xys = np.asarray(xys) 

206 xys = xys[:, :2] 

207 self.pane.xy = xys 

208 self.stale = True 

209 

210 def set_pane_color(self, color, alpha=None): 

211 """ 

212 Set pane color. 

213 

214 Parameters 

215 ---------- 

216 color : color 

217 Color for axis pane. 

218 alpha : float, optional 

219 Alpha value for axis pane. If None, base it on *color*. 

220 """ 

221 color = mcolors.to_rgba(color, alpha) 

222 self._axinfo['color'] = color 

223 self.pane.set_edgecolor(color) 

224 self.pane.set_facecolor(color) 

225 self.pane.set_alpha(color[-1]) 

226 self.stale = True 

227 

228 def set_rotate_label(self, val): 

229 """ 

230 Whether to rotate the axis label: True, False or None. 

231 If set to None the label will be rotated if longer than 4 chars. 

232 """ 

233 self._rotate_label = val 

234 self.stale = True 

235 

236 def get_rotate_label(self, text): 

237 if self._rotate_label is not None: 

238 return self._rotate_label 

239 else: 

240 return len(text) > 4 

241 

242 def _get_coord_info(self, renderer): 

243 mins, maxs = np.array([ 

244 self.axes.get_xbound(), 

245 self.axes.get_ybound(), 

246 self.axes.get_zbound(), 

247 ]).T 

248 

249 # Get the mean value for each bound: 

250 centers = 0.5 * (maxs + mins) 

251 

252 # Add a small offset between min/max point and the edge of the 

253 # plot: 

254 deltas = (maxs - mins) / 12 

255 mins -= 0.25 * deltas 

256 maxs += 0.25 * deltas 

257 

258 # Project the bounds along the current position of the cube: 

259 bounds = mins[0], maxs[0], mins[1], maxs[1], mins[2], maxs[2] 

260 bounds_proj = self.axes.tunit_cube(bounds, self.axes.M) 

261 

262 # Determine which one of the parallel planes are higher up: 

263 means_z0 = np.zeros(3) 

264 means_z1 = np.zeros(3) 

265 for i in range(3): 

266 means_z0[i] = np.mean(bounds_proj[self._PLANES[2 * i], 2]) 

267 means_z1[i] = np.mean(bounds_proj[self._PLANES[2 * i + 1], 2]) 

268 highs = means_z0 < means_z1 

269 

270 # Special handling for edge-on views 

271 equals = np.abs(means_z0 - means_z1) <= np.finfo(float).eps 

272 if np.sum(equals) == 2: 

273 vertical = np.where(~equals)[0][0] 

274 if vertical == 2: # looking at XY plane 

275 highs = np.array([True, True, highs[2]]) 

276 elif vertical == 1: # looking at XZ plane 

277 highs = np.array([True, highs[1], False]) 

278 elif vertical == 0: # looking at YZ plane 

279 highs = np.array([highs[0], False, False]) 

280 

281 return mins, maxs, centers, deltas, bounds_proj, highs 

282 

283 def _get_axis_line_edge_points(self, minmax, maxmin): 

284 """Get the edge points for the black bolded axis line.""" 

285 # When changing vertical axis some of the axes has to be 

286 # moved to the other plane so it looks the same as if the z-axis 

287 # was the vertical axis. 

288 mb = [minmax, maxmin] 

289 mb_rev = mb[::-1] 

290 mm = [[mb, mb_rev, mb_rev], [mb_rev, mb_rev, mb], [mb, mb, mb]] 

291 mm = mm[self.axes._vertical_axis][self._axinfo["i"]] 

292 

293 juggled = self._axinfo["juggled"] 

294 edge_point_0 = mm[0].copy() 

295 edge_point_0[juggled[0]] = mm[1][juggled[0]] 

296 

297 edge_point_1 = edge_point_0.copy() 

298 edge_point_1[juggled[1]] = mm[1][juggled[1]] 

299 

300 return edge_point_0, edge_point_1 

301 

302 def _get_tickdir(self): 

303 """ 

304 Get the direction of the tick. 

305 

306 Returns 

307 ------- 

308 tickdir : int 

309 Index which indicates which coordinate the tick line will 

310 align with. 

311 """ 

312 # TODO: Move somewhere else where it's triggered less: 

313 tickdirs_base = [v["tickdir"] for v in self._AXINFO.values()] 

314 info_i = [v["i"] for v in self._AXINFO.values()] 

315 

316 i = self._axinfo["i"] 

317 j = self.axes._vertical_axis - 2 

318 # tickdir = [[1, 2, 1], [2, 2, 0], [1, 0, 0]][i] 

319 tickdir = np.roll(info_i, -j)[np.roll(tickdirs_base, j)][i] 

320 return tickdir 

321 

322 def draw_pane(self, renderer): 

323 renderer.open_group('pane3d', gid=self.get_gid()) 

324 

325 mins, maxs, centers, deltas, tc, highs = self._get_coord_info(renderer) 

326 

327 info = self._axinfo 

328 index = info['i'] 

329 if not highs[index]: 

330 plane = self._PLANES[2 * index] 

331 else: 

332 plane = self._PLANES[2 * index + 1] 

333 xys = [tc[p] for p in plane] 

334 self._set_pane_pos(xys) 

335 self.pane.draw(renderer) 

336 

337 renderer.close_group('pane3d') 

338 

339 @artist.allow_rasterization 

340 def draw(self, renderer): 

341 self.label._transform = self.axes.transData 

342 renderer.open_group("axis3d", gid=self.get_gid()) 

343 

344 ticks = self._update_ticks() 

345 

346 # Get general axis information: 

347 info = self._axinfo 

348 index = info["i"] 

349 juggled = info["juggled"] 

350 

351 mins, maxs, centers, deltas, tc, highs = self._get_coord_info(renderer) 

352 

353 minmax = np.where(highs, maxs, mins) 

354 maxmin = np.where(~highs, maxs, mins) 

355 

356 # Create edge points for the black bolded axis line: 

357 edgep1, edgep2 = self._get_axis_line_edge_points(minmax, maxmin) 

358 

359 # Project the edge points along the current position and 

360 # create the line: 

361 pep = proj3d.proj_trans_points([edgep1, edgep2], self.axes.M) 

362 pep = np.asarray(pep) 

363 self.line.set_data(pep[0], pep[1]) 

364 self.line.draw(renderer) 

365 

366 # Draw labels 

367 # The transAxes transform is used because the Text object 

368 # rotates the text relative to the display coordinate system. 

369 # Therefore, if we want the labels to remain parallel to the 

370 # axis regardless of the aspect ratio, we need to convert the 

371 # edge points of the plane to display coordinates and calculate 

372 # an angle from that. 

373 # TODO: Maybe Text objects should handle this themselves? 

374 dx, dy = (self.axes.transAxes.transform([pep[0:2, 1]]) - 

375 self.axes.transAxes.transform([pep[0:2, 0]]))[0] 

376 

377 lxyz = 0.5 * (edgep1 + edgep2) 

378 

379 # A rough estimate; points are ambiguous since 3D plots rotate 

380 reltoinches = self.figure.dpi_scale_trans.inverted() 

381 ax_inches = reltoinches.transform(self.axes.bbox.size) 

382 ax_points_estimate = sum(72. * ax_inches) 

383 deltas_per_point = 48 / ax_points_estimate 

384 default_offset = 21. 

385 labeldeltas = ( 

386 (self.labelpad + default_offset) * deltas_per_point * deltas) 

387 axmask = [True, True, True] 

388 axmask[index] = False 

389 lxyz = _move_from_center(lxyz, centers, labeldeltas, axmask) 

390 tlx, tly, tlz = proj3d.proj_transform(*lxyz, self.axes.M) 

391 self.label.set_position((tlx, tly)) 

392 if self.get_rotate_label(self.label.get_text()): 

393 angle = art3d._norm_text_angle(np.rad2deg(np.arctan2(dy, dx))) 

394 self.label.set_rotation(angle) 

395 self.label.set_va(info['label']['va']) 

396 self.label.set_ha(info['label']['ha']) 

397 self.label.draw(renderer) 

398 

399 # Draw Offset text 

400 

401 # Which of the two edge points do we want to 

402 # use for locating the offset text? 

403 if juggled[2] == 2: 

404 outeredgep = edgep1 

405 outerindex = 0 

406 else: 

407 outeredgep = edgep2 

408 outerindex = 1 

409 

410 pos = _move_from_center(outeredgep, centers, labeldeltas, axmask) 

411 olx, oly, olz = proj3d.proj_transform(*pos, self.axes.M) 

412 self.offsetText.set_text(self.major.formatter.get_offset()) 

413 self.offsetText.set_position((olx, oly)) 

414 angle = art3d._norm_text_angle(np.rad2deg(np.arctan2(dy, dx))) 

415 self.offsetText.set_rotation(angle) 

416 # Must set rotation mode to "anchor" so that 

417 # the alignment point is used as the "fulcrum" for rotation. 

418 self.offsetText.set_rotation_mode('anchor') 

419 

420 # ---------------------------------------------------------------------- 

421 # Note: the following statement for determining the proper alignment of 

422 # the offset text. This was determined entirely by trial-and-error 

423 # and should not be in any way considered as "the way". There are 

424 # still some edge cases where alignment is not quite right, but this 

425 # seems to be more of a geometry issue (in other words, I might be 

426 # using the wrong reference points). 

427 # 

428 # (TT, FF, TF, FT) are the shorthand for the tuple of 

429 # (centpt[info['tickdir']] <= pep[info['tickdir'], outerindex], 

430 # centpt[index] <= pep[index, outerindex]) 

431 # 

432 # Three-letters (e.g., TFT, FTT) are short-hand for the array of bools 

433 # from the variable 'highs'. 

434 # --------------------------------------------------------------------- 

435 centpt = proj3d.proj_transform(*centers, self.axes.M) 

436 if centpt[info['tickdir']] > pep[info['tickdir'], outerindex]: 

437 # if FT and if highs has an even number of Trues 

438 if (centpt[index] <= pep[index, outerindex] 

439 and np.count_nonzero(highs) % 2 == 0): 

440 # Usually, this means align right, except for the FTT case, 

441 # in which offset for axis 1 and 2 are aligned left. 

442 if highs.tolist() == [False, True, True] and index in (1, 2): 

443 align = 'left' 

444 else: 

445 align = 'right' 

446 else: 

447 # The FF case 

448 align = 'left' 

449 else: 

450 # if TF and if highs has an even number of Trues 

451 if (centpt[index] > pep[index, outerindex] 

452 and np.count_nonzero(highs) % 2 == 0): 

453 # Usually mean align left, except if it is axis 2 

454 align = 'right' if index == 2 else 'left' 

455 else: 

456 # The TT case 

457 align = 'right' 

458 

459 self.offsetText.set_va('center') 

460 self.offsetText.set_ha(align) 

461 self.offsetText.draw(renderer) 

462 

463 if self.axes._draw_grid and len(ticks): 

464 # Grid points where the planes meet 

465 xyz0 = np.tile(minmax, (len(ticks), 1)) 

466 xyz0[:, index] = [tick.get_loc() for tick in ticks] 

467 

468 # Grid lines go from the end of one plane through the plane 

469 # intersection (at xyz0) to the end of the other plane. The first 

470 # point (0) differs along dimension index-2 and the last (2) along 

471 # dimension index-1. 

472 lines = np.stack([xyz0, xyz0, xyz0], axis=1) 

473 lines[:, 0, index - 2] = maxmin[index - 2] 

474 lines[:, 2, index - 1] = maxmin[index - 1] 

475 self.gridlines.set_segments(lines) 

476 gridinfo = info['grid'] 

477 self.gridlines.set_color(gridinfo['color']) 

478 self.gridlines.set_linewidth(gridinfo['linewidth']) 

479 self.gridlines.set_linestyle(gridinfo['linestyle']) 

480 self.gridlines.do_3d_projection() 

481 self.gridlines.draw(renderer) 

482 

483 # Draw ticks: 

484 tickdir = self._get_tickdir() 

485 tickdelta = deltas[tickdir] if highs[tickdir] else -deltas[tickdir] 

486 

487 tick_info = info['tick'] 

488 tick_out = tick_info['outward_factor'] * tickdelta 

489 tick_in = tick_info['inward_factor'] * tickdelta 

490 tick_lw = tick_info['linewidth'] 

491 edgep1_tickdir = edgep1[tickdir] 

492 out_tickdir = edgep1_tickdir + tick_out 

493 in_tickdir = edgep1_tickdir - tick_in 

494 

495 default_label_offset = 8. # A rough estimate 

496 points = deltas_per_point * deltas 

497 for tick in ticks: 

498 # Get tick line positions 

499 pos = edgep1.copy() 

500 pos[index] = tick.get_loc() 

501 pos[tickdir] = out_tickdir 

502 x1, y1, z1 = proj3d.proj_transform(*pos, self.axes.M) 

503 pos[tickdir] = in_tickdir 

504 x2, y2, z2 = proj3d.proj_transform(*pos, self.axes.M) 

505 

506 # Get position of label 

507 labeldeltas = (tick.get_pad() + default_label_offset) * points 

508 

509 pos[tickdir] = edgep1_tickdir 

510 pos = _move_from_center(pos, centers, labeldeltas, axmask) 

511 lx, ly, lz = proj3d.proj_transform(*pos, self.axes.M) 

512 

513 _tick_update_position(tick, (x1, x2), (y1, y2), (lx, ly)) 

514 tick.tick1line.set_linewidth(tick_lw[tick._major]) 

515 tick.draw(renderer) 

516 

517 renderer.close_group('axis3d') 

518 self.stale = False 

519 

520 # TODO: Get this to work (more) properly when mplot3d supports the 

521 # transforms framework. 

522 def get_tightbbox(self, renderer=None, *, for_layout_only=False): 

523 # docstring inherited 

524 if not self.get_visible(): 

525 return 

526 # We have to directly access the internal data structures 

527 # (and hope they are up to date) because at draw time we 

528 # shift the ticks and their labels around in (x, y) space 

529 # based on the projection, the current view port, and their 

530 # position in 3D space. If we extend the transforms framework 

531 # into 3D we would not need to do this different book keeping 

532 # than we do in the normal axis 

533 major_locs = self.get_majorticklocs() 

534 minor_locs = self.get_minorticklocs() 

535 

536 ticks = [*self.get_minor_ticks(len(minor_locs)), 

537 *self.get_major_ticks(len(major_locs))] 

538 view_low, view_high = self.get_view_interval() 

539 if view_low > view_high: 

540 view_low, view_high = view_high, view_low 

541 interval_t = self.get_transform().transform([view_low, view_high]) 

542 

543 ticks_to_draw = [] 

544 for tick in ticks: 

545 try: 

546 loc_t = self.get_transform().transform(tick.get_loc()) 

547 except AssertionError: 

548 # Transform.transform doesn't allow masked values but 

549 # some scales might make them, so we need this try/except. 

550 pass 

551 else: 

552 if mtransforms._interval_contains_close(interval_t, loc_t): 

553 ticks_to_draw.append(tick) 

554 

555 ticks = ticks_to_draw 

556 

557 bb_1, bb_2 = self._get_ticklabel_bboxes(ticks, renderer) 

558 other = [] 

559 

560 if self.line.get_visible(): 

561 other.append(self.line.get_window_extent(renderer)) 

562 if (self.label.get_visible() and not for_layout_only and 

563 self.label.get_text()): 

564 other.append(self.label.get_window_extent(renderer)) 

565 

566 return mtransforms.Bbox.union([*bb_1, *bb_2, *other]) 

567 

568 d_interval = _api.deprecated( 

569 "3.6", alternative="get_data_interval", pending=True)( 

570 property(lambda self: self.get_data_interval(), 

571 lambda self, minmax: self.set_data_interval(*minmax))) 

572 v_interval = _api.deprecated( 

573 "3.6", alternative="get_view_interval", pending=True)( 

574 property(lambda self: self.get_view_interval(), 

575 lambda self, minmax: self.set_view_interval(*minmax))) 

576 

577 

578class XAxis(Axis): 

579 axis_name = "x" 

580 get_view_interval, set_view_interval = maxis._make_getset_interval( 

581 "view", "xy_viewLim", "intervalx") 

582 get_data_interval, set_data_interval = maxis._make_getset_interval( 

583 "data", "xy_dataLim", "intervalx") 

584 

585 

586class YAxis(Axis): 

587 axis_name = "y" 

588 get_view_interval, set_view_interval = maxis._make_getset_interval( 

589 "view", "xy_viewLim", "intervaly") 

590 get_data_interval, set_data_interval = maxis._make_getset_interval( 

591 "data", "xy_dataLim", "intervaly") 

592 

593 

594class ZAxis(Axis): 

595 axis_name = "z" 

596 get_view_interval, set_view_interval = maxis._make_getset_interval( 

597 "view", "zz_viewLim", "intervalx") 

598 get_data_interval, set_data_interval = maxis._make_getset_interval( 

599 "data", "zz_dataLim", "intervalx")