@@ -287,9 +287,7 @@ def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
287287 'equalyz' adapt the y and z axes to have equal aspect ratios.
288288 ========= ==================================================
289289
290- adjustable : None
291- Currently ignored by Axes3D
292-
290+ adjustable : None or {'box', 'datalim'}, optional
293291 If not *None*, this defines which parameter will be adjusted to
294292 meet the required aspect. See `.set_adjustable` for further
295293 details.
@@ -320,34 +318,65 @@ def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
320318 """
321319 _api .check_in_list (('auto' , 'equal' , 'equalxy' , 'equalyz' , 'equalxz' ),
322320 aspect = aspect )
321+ if adjustable is None :
322+ adjustable = self ._adjustable
323+ _api .check_in_list (('box' , 'datalim' ), adjustable = adjustable )
323324 super ().set_aspect (
324325 aspect = 'auto' , adjustable = adjustable , anchor = anchor , share = share )
325326 self ._aspect = aspect
326327
327328 if aspect in ('equal' , 'equalxy' , 'equalxz' , 'equalyz' ):
328- if aspect == 'equal' :
329- ax_indices = [0 , 1 , 2 ]
330- elif aspect == 'equalxy' :
331- ax_indices = [0 , 1 ]
332- elif aspect == 'equalxz' :
333- ax_indices = [0 , 2 ]
334- elif aspect == 'equalyz' :
335- ax_indices = [1 , 2 ]
329+ ax_idx = self ._equal_aspect_axis_indices (aspect )
336330
337331 view_intervals = np .array ([self .xaxis .get_view_interval (),
338332 self .yaxis .get_view_interval (),
339333 self .zaxis .get_view_interval ()])
340- mean = np .mean (view_intervals , axis = 1 )
341334 ptp = np .ptp (view_intervals , axis = 1 )
342- delta = max (ptp [ax_indices ])
343- scale = self ._box_aspect [ptp == delta ][0 ]
344- deltas = delta * self ._box_aspect / scale
345-
346- for i , set_lim in enumerate ((self .set_xlim3d ,
347- self .set_ylim3d ,
348- self .set_zlim3d )):
349- if i in ax_indices :
350- set_lim (mean [i ] - deltas [i ]/ 2. , mean [i ] + deltas [i ]/ 2. )
335+ if adjustable == 'datalim' :
336+ mean = np .mean (view_intervals , axis = 1 )
337+ delta = max (ptp [ax_idx ])
338+ scale = self ._box_aspect [ptp == delta ][0 ]
339+ deltas = delta * self ._box_aspect / scale
340+
341+ for i , set_lim in enumerate ((self .set_xlim3d ,
342+ self .set_ylim3d ,
343+ self .set_zlim3d )):
344+ if i in ax_idx :
345+ set_lim (mean [i ] - deltas [i ]/ 2. , mean [i ] + deltas [i ]/ 2. )
346+ else : # 'box'
347+ # Change the box aspect such that the ratio of the length of
348+ # the unmodified axis to the length of the diagonal
349+ # perpendicular to it remains unchanged.
350+ box_aspect = np .array (self ._box_aspect )
351+ box_aspect [ax_idx ] = ptp [ax_idx ]
352+ remaining_ax_idx = {0 , 1 , 2 }.difference (ax_idx )
353+ if remaining_ax_idx :
354+ remaining = remaining_ax_idx .pop ()
355+ old_diag = np .linalg .norm (self ._box_aspect [ax_idx ])
356+ new_diag = np .linalg .norm (box_aspect [ax_idx ])
357+ box_aspect [remaining ] *= new_diag / old_diag
358+ self .set_box_aspect (box_aspect )
359+
360+ def _equal_aspect_axis_indices (self , aspect ):
361+ """
362+ Get the indices for which of the x, y, z axes are constrained to have
363+ equal aspect ratios.
364+
365+ Parameters
366+ ----------
367+ aspect : {'auto', 'equal', 'equalxy', 'equalxz', 'equalyz'}
368+ See descriptions in docstring for `.set_aspect()`.
369+ """
370+ ax_indices = [] # aspect == 'auto'
371+ if aspect == 'equal' :
372+ ax_indices = [0 , 1 , 2 ]
373+ elif aspect == 'equalxy' :
374+ ax_indices = [0 , 1 ]
375+ elif aspect == 'equalxz' :
376+ ax_indices = [0 , 2 ]
377+ elif aspect == 'equalyz' :
378+ ax_indices = [1 , 2 ]
379+ return ax_indices
351380
352381 def set_box_aspect (self , aspect , * , zoom = 1 ):
353382 """
0 commit comments