@@ -76,6 +76,8 @@ def __init__(self, *args, **kwargs):
7676
7777 name = self .axis_name
7878
79+ self .position = 'auto'
80+
7981 # This is a temporary member variable.
8082 # Do not depend on this existing in future releases!
8183 self ._axinfo = self ._AXINFO [name ].copy ()
@@ -225,8 +227,7 @@ def _get_coord_info(self, renderer):
225227 # Get the mean value for each bound:
226228 centers = 0.5 * (maxs + mins )
227229
228- # Add a small offset between min/max point and the edge of the
229- # plot:
230+ # Add a small offset between min/max point and the edge of the plot:
230231 deltas = (maxs - mins ) / 12
231232 mins -= 0.25 * deltas
232233 maxs += 0.25 * deltas
@@ -256,19 +257,26 @@ def _get_coord_info(self, renderer):
256257
257258 return mins , maxs , centers , deltas , bounds_proj , highs
258259
259- def _get_axis_line_edge_points (self , minmax , maxmin ):
260+ def _get_axis_line_edge_points (self , minmax , maxmin , position = None ):
260261 """Get the edge points for the black bolded axis line."""
261262 # When changing vertical axis some of the axes has to be
262263 # moved to the other plane so it looks the same as if the z-axis
263264 # was the vertical axis.
264- mb = [minmax , maxmin ]
265+ mb = [minmax , maxmin ] # line from origin to near invisible corner
265266 mb_rev = mb [::- 1 ]
266267 mm = [[mb , mb_rev , mb_rev ], [mb_rev , mb_rev , mb ], [mb , mb , mb ]]
267268 mm = mm [self .axes ._vertical_axis ][self ._axinfo ["i" ]]
268269
269270 juggled = self ._axinfo ["juggled" ]
270- edge_point_0 = mm [0 ].copy ()
271- edge_point_0 [juggled [0 ]] = mm [1 ][juggled [0 ]]
271+ edge_point_0 = mm [0 ].copy () # origin point
272+
273+ if ( (position == 'lower'
274+ and mm [1 ][juggled [- 1 ]] < mm [0 ][juggled [- 1 ]])
275+ or (position == 'upper'
276+ and mm [1 ][juggled [- 1 ]] > mm [0 ][juggled [- 1 ]])):
277+ edge_point_0 [juggled [- 1 ]] = mm [1 ][juggled [- 1 ]]
278+ else :
279+ edge_point_0 [juggled [0 ]] = mm [1 ][juggled [0 ]]
272280
273281 edge_point_1 = edge_point_0 .copy ()
274282 edge_point_1 [juggled [1 ]] = mm [1 ][juggled [1 ]]
@@ -372,7 +380,8 @@ def _draw_ticks(self, renderer, edgep1, deltas_per_point):
372380 tick .draw (renderer )
373381
374382
375- def _draw_offset_text (self , renderer , edgep1 , edgep2 , labeldeltas , pep , dx , dy ):
383+ def _draw_offset_text (self , renderer , edgep1 , edgep2 , labeldeltas , pep ,
384+ dx , dy ):
376385 mins , maxs , centers , deltas , tc , highs = self ._get_coord_info (renderer )
377386
378387 # Get general axis information:
@@ -389,7 +398,8 @@ def _draw_offset_text(self, renderer, edgep1, edgep2, labeldeltas, pep, dx, dy):
389398 outeredgep = edgep2
390399 outerindex = 1
391400
392- pos = _move_from_center (outeredgep , centers , labeldeltas , self ._axmask ())
401+ pos = _move_from_center (outeredgep , centers , labeldeltas ,
402+ self ._axmask ())
393403 olx , oly , olz = proj3d .proj_transform (* pos , self .axes .M )
394404 self .offsetText .set_text (self .major .formatter .get_offset ())
395405 self .offsetText .set_position ((olx , oly ))
@@ -470,19 +480,6 @@ def draw(self, renderer):
470480 # Get general axis information:
471481 mins , maxs , centers , deltas , tc , highs = self ._get_coord_info (renderer )
472482
473- minmax = np .where (highs , maxs , mins )
474- maxmin = np .where (~ highs , maxs , mins )
475-
476- # Create edge points for the black bolded axis line:
477- edgep1 , edgep2 = self ._get_axis_line_edge_points (minmax , maxmin )
478-
479- # Draw the lines
480- # Project the edge points along the current position
481- pep = proj3d ._proj_trans_points ([edgep1 , edgep2 ], self .axes .M )
482- pep = np .asarray (pep )
483- self .line .set_data (pep [0 ], pep [1 ])
484- self .line .draw (renderer )
485-
486483 # Calculate offset distances
487484 # A rough estimate; points are ambiguous since 3D plots rotate
488485 reltoinches = self .figure .dpi_scale_trans .inverted ()
@@ -492,24 +489,53 @@ def draw(self, renderer):
492489 default_offset = 21.
493490 labeldeltas = (
494491 (self .labelpad + default_offset ) * deltas_per_point * deltas )
495- # The transAxes transform is used because the Text object
496- # rotates the text relative to the display coordinate system.
497- # Therefore, if we want the labels to remain parallel to the
498- # axis regardless of the aspect ratio, we need to convert the
499- # edge points of the plane to display coordinates and calculate
500- # an angle from that.
501- # TODO: Maybe Text objects should handle this themselves?
502- dx , dy = (self .axes .transAxes .transform ([pep [0 :2 , 1 ]]) -
503- self .axes .transAxes .transform ([pep [0 :2 , 0 ]]))[0 ]
504-
505- # Draw labels
506- self ._draw_labels (renderer , edgep1 , edgep2 , labeldeltas , dx , dy )
507492
508- # Draw Offset text
509- self ._draw_offset_text (renderer , edgep1 , edgep2 , labeldeltas , pep , dx , dy )
510-
511- # Draw ticks
512- self ._draw_ticks (renderer , edgep1 , deltas_per_point )
493+ # Determine edge points for the axis lines
494+ edgep1s = []
495+ edgep2s = []
496+ minmax = np .where (highs , maxs , mins ) # "origin" point
497+ maxmin = np .where (~ highs , maxs , mins ) # "opposite" corner near camera
498+ if self .position == 'auto' :
499+ edgep1 , edgep2 = self ._get_axis_line_edge_points (minmax , maxmin )
500+ edgep1s = [edgep1 ]
501+ edgep2s = [edgep2 ]
502+ else :
503+ edgep1_l , edgep2_l = self ._get_axis_line_edge_points (minmax , maxmin , position = 'lower' )
504+ edgep1_u , edgep2_u = self ._get_axis_line_edge_points (minmax , maxmin , position = 'upper' )
505+ if self .position in ('lower' , 'both' ):
506+ edgep1s .append (edgep1_l )
507+ edgep2s .append (edgep2_l )
508+ if self .position in ('upper' , 'both' ):
509+ edgep1s .append (edgep1_u )
510+ edgep2s .append (edgep2_u )
511+
512+ for edgep1 , edgep2 in zip (edgep1s , edgep2s ):
513+ # Draw the lines
514+ # Project the edge points along the current position
515+ pep = proj3d ._proj_trans_points ([edgep1 , edgep2 ], self .axes .M )
516+ pep = np .asarray (pep )
517+ self .line .set_data (pep [0 ], pep [1 ])
518+ self .line .draw (renderer )
519+
520+ # The transAxes transform is used because the Text object
521+ # rotates the text relative to the display coordinate system.
522+ # Therefore, if we want the labels to remain parallel to the
523+ # axis regardless of the aspect ratio, we need to convert the
524+ # edge points of the plane to display coordinates and calculate
525+ # an angle from that.
526+ # TODO: Maybe Text objects should handle this themselves?
527+ dx , dy = (self .axes .transAxes .transform ([pep [0 :2 , 1 ]]) -
528+ self .axes .transAxes .transform ([pep [0 :2 , 0 ]]))[0 ]
529+
530+ # Draw labels
531+ self ._draw_labels (renderer , edgep1 , edgep2 , labeldeltas , dx , dy )
532+
533+ # Draw Offset text
534+ self ._draw_offset_text (renderer , edgep1 , edgep2 , labeldeltas , pep ,
535+ dx , dy )
536+
537+ # Draw ticks
538+ self ._draw_ticks (renderer , edgep1 , deltas_per_point )
513539
514540 renderer .close_group ('axis3d' )
515541 self .stale = False
0 commit comments