GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — 3d plots with aspect='equal' · Issue #1077 · matplotlib/matplotlib

3d plots with aspect='equal' #1077

New issue
Copy link
New issue
Copy link
Closed
Closed
3d plots with aspect='equal'#1077
Copy link
Assignees
WeatherGod
Labels
Documentationtopic: mplot3d
Milestone
v3.1.0

Description

@akhmerov
akhmerov
opened on Aug 12, 2012
Issue body actions

aspect='equal' argument is completely misused by axes3D.
To verify:

import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
fig = plt.figure()
ax = fig.add_subplot((111), aspect='equal', projection='3d')
ax.scatter((1, 2), (1, 1.2), (1, 2)) 
  • The figure is rendered with all axes scaled to the data scale, thus ignoring the aspect setting.
  • If one tries to rotate the figure, using GUI, it is rescaled to fix into a very uneven rectangle, presumably calculated from x-scale vs y-scale, and hence with aspect ratio 1:5.
Reacted by Seunghoon Park, Juan Luis Cano Rodríguez, Samuel Ainsworth, Pavel Haluza, Tomas Petricek, morriswmz, Bas Nijholt, Daniel Sank, Tianye Li, barabanus and 25 more
Reacted by Tomomas

Activity

Next
  1. WeatherGod commented on Aug 20, 2012

    @WeatherGod
    WeatherGod
    on Aug 20, 2012
    Member
    More actions

    an "aspect" kwarg doesn't make a lot of sense for 3d plots, which is why even matlab doesn't do it. I have an old branch that provides some very basic functionality in this direction, but I never finished. See here:

    http://matplotlib.1069221.n5.nabble.com/mplot3d-and-daspect-tp11521p11523.html

    PRs against this branch would be welcomed!

    Reacted by Thomas A Caswell
    Reacted by Danny Wang, Mateen Ulhaq, shengdie, tazr, chuyicui, Yuchen Zhong, Daniel Soutar, Morten Lind, Salih Karagoz, Jimmy Paulos and 39 more
  2. akhmerov commented on Aug 20, 2012

    @akhmerov
    akhmerov
    on Aug 20, 2012
    ContributorAuthor
    More actions

    Why doesn't it make sense? If I want to e.g. mark coordinates in a building, or any system where three axes have the same dimension, I do want that the same axes is chosen for each of the axes (that's where I actually encountered the bug). And of course regardless of that, the current handling is bugged.

    Reacted by Samuel Ainsworth, Peter Dijkstra, Alex, chuyicui, barabanus, Javier Dehesa, Daniel Soutar, Morten Lind, Coelancanth, Tal Golan and 49 more
    Reacted by Roman Kovtyukh, Tim, Rafael de la Fuente Herrezuelo, Pan Yu and BUGMAN
    Reacted by Pan Yu
  3. vallettea commented on Jan 23, 2013

    @vallettea
    vallettea
    on Jan 23, 2013
    More actions

    I would also be interested by this feature.
    By the way, did you found a way to tackle the problem?

  4. astrofrog commented on Feb 24, 2013

    @astrofrog
    astrofrog
    on Feb 24, 2013
    Contributor
    More actions

    We also came across this issue - if I run the following code:

    from mpl_toolkits.mplot3d import Axes3D
    import matplotlib.pyplot as plt
    import numpy as np
    from itertools import product, combinations
    
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1,projection='3d')
    
    #draw cube
    r = [-1, 1]
    for s, e in combinations(np.array(list(product(r,r,r))), 2):
        if np.sum(np.abs(s-e)) == r[1]-r[0]:
            ax.plot3D(*zip(s,e), color="b")
    

    I get:

    cube

    and adding

    ax.set_aspect('equal')
    

    then gives

    cube_equal

    which is simply wrong, not just sub-optimal. The cube lines should always be parallel to the axes. In my opinion, it should look more like

    cube_shouldbe

    But at the very least, if set_aspect('equal') is going to do the wrong thing, it would be better if it raised a NotImplemented error.

    (this issue was originally pointed out to me by @koepferl)

    Reacted by Andrew, Juan Luis Cano Rodríguez, Nick LaFarge, Samuel Ainsworth, Peter Dijkstra, Mateen Ulhaq, Jonathan King, darkdragon-001, barabanus, Daniel Soutar and 13 more
  5. tehingo commented on Mar 25, 2013

    @tehingo
    tehingo
    on Mar 25, 2013
    More actions

    My research entails lots of 3D plotting on unit spheres. Needless to say, a unit sphere is a perfect sphere and to display it properly all axes need to have the same scale. This is another use case that requires this functionality.

    I would love to help out, but don't have the time to dig into the code right now. I really hope this feature gets some priority for future releases.

    Reacted by Mateen Ulhaq, barabanus, chtapodi, Thomas Mansencal, Juliano Ferrari Gianlupi, Jacek Grzybowski, RachelChen, Max Schwendeler, laurap81, ZhouWJ and 7 more
  6. akhmerov commented on Mar 25, 2013

    @akhmerov
    akhmerov
    on Mar 25, 2013
    ContributorAuthor
    More actions

    Just in case somebody encounters this bug, and needs a workaround: the way I handle this is by manually calling ax.auto_scale_xyz before rendering.

    Reacted by gfant and Nico Gastellu
  7. tacaswell
    added this to the v1.4.x milestone on Aug 18, 2014
  8. tacaswell
    assigned
    WeatherGod
    on Aug 18, 2014
  9. tacaswell
    modified the milestones: v1.4.x, This milestone has been deleted on Aug 18, 2014
  10. Hanlin-Dong commented on Jan 6, 2016

    @Hanlin-Dong
    Hanlin-Dong
    on Jan 6, 2016
    More actions

    Actually there are several ways of showing a 3D model on a 2D screen, mainly including axonometric projection and perspective projection. There are also many kinds of axonometric projections. So even the unit length of three axises are the same, they look different on the screen on many circumstances. A cubic box may look not that cubic if drawn in different ways. So if the 3D plot class is to be improved, projection method should be determined beforehand.

  11. akhmerov commented on Jan 6, 2016

    @akhmerov
    akhmerov
    on Jan 6, 2016
    ContributorAuthor
    More actions

    @Hanlin-Dong I agree, but no matter how the projection is done, the projection for the data must match the projection for the axes. In the example above it doesn't, hence the bug report. Also the bug report isn't about the projection as much as the impossibility to correctly set the aspect ratios.

  12. solarjoe commented on Jan 18, 2016

    @solarjoe
    solarjoe
    on Jan 18, 2016
    More actions

    I also think that this feature is quite important for 3d plots.

    True, it might not be that important if you visualize data without
    an underlying real-world geometry. But I have to visualize the
    heat distribution on motor parts in 3D and this looks quite strange if the
    aspect ratio is incorrect. I guess the same it true with the buildings that
    @akhmerov mentioned.

    @WeatherGod mentioned above that Matlab does not provide this feature, but
    I am quite sure it does.
    This is the manpage, it also shows 3D examples for "normal" and "equal".
    http://de.mathworks.com/help/matlab/creating_plots/aspect-ratio-for-2-d-axes.html
    More information on how it is implemented as axis property:
    http://de.mathworks.com/help/matlab/ref/axes-properties.html#prop_DataAspectRatio

    I guess most of it is already implemented and working in matplotlib but I came across
    a small issue, see my post and example code in #1104.

  13. samuela commented on May 6, 2018

    @samuela
    samuela
    on May 6, 2018
    More actions

    screen shot 2018-05-05 at 8 30 52 pm

    I just got this plot after calling ax.set_aspect('equal'). Definitely not equal based on the ticks!

  14. timhoffm commented on May 6, 2018

    @timhoffm
    timhoffm
    on May 6, 2018
    Member
    More actions

    Related and possibly a first step for fixing this: #8896

    Reacted by Anton Akhmerov
  15. 11 remaining items

  16. jklymak commented on Mar 3, 2019

    @jklymak
    jklymak
    on Mar 3, 2019
    Member
    More actions

    Closing due to #13474 and the fact that the non-optimal behaviour is discussed in previous issues and PRs references above...

  17. jklymak
    closed this as completedon Mar 3, 2019
  18. akhmerov commented on Mar 3, 2019

    @akhmerov
    akhmerov
    on Mar 3, 2019
    ContributorAuthor
    More actions

    @jklymak what do you mean by the non-optimal behavior? Does closing this issue mean that 3D plots with equal aspect ratio aren't on the roadmap?

    Reacted by Corey, Mateen Ulhaq, Daniel Soutar, bokorn, jkfindeisen, batbone, Alan Covarrubias, Lars Funke and Dazhi Cheng
  19. solarjoe commented on Mar 3, 2019

    @solarjoe
    solarjoe
    on Mar 3, 2019
    More actions

    I hope he is closing because of (still open) #8896 just to clean the issue tracker, as #13474 is only a workaround on how to deal with the behaviour until it is implemented.

    Reacted by Anton Akhmerov
  20. jklymak commented on Mar 3, 2019

    @jklymak
    jklymak
    on Mar 3, 2019
    Member
    More actions

    Correct. OTOH I don't know of anyone actively developing mplot3d, so if someone wants this feature, it would require a volunteer..

    Reacted by Elan Ernest
  21. zh-plus commented on Aug 14, 2019

    @zh-plus
    zh-plus
    on Aug 14, 2019 · Hidden as outdated
    show commentMore actions
  22. solarjoe commented on Aug 14, 2019

    @solarjoe
    solarjoe
    on Aug 14, 2019
    More actions

    The original link is https://stackoverflow.com/questions/13685386/matplotlib-equal-unit-length-with-equal-aspect-ratio-z-axis-is-not-equal-to and has already been mentioned above.

    Reacted by Alfredo Canziani
  23. aarondettmann
    added 2 commits that reference this issue on Oct 18, 2019

    Removed dependency on specific matplotlib version

    e87847a

    Removed dependency on specific matplotlib version

    e9eb8fc
  24. ftherrien
    added a commit that references this issue on Jan 15, 2020

    Fixed the matplotlib 3D set_aspect('equal') issue

    25c4dbd
  25. jondo commented on Jun 2, 2021

    @jondo
    jondo
    on Jun 2, 2021
    More actions

    In order to get a future notification of aspect='equal' working, which open issue (or unmerged PR) should I subscribe to?

    Reacted by Samuel Ainsworth
  26. tacaswell commented on Jun 3, 2021

    @tacaswell
    tacaswell
    on Jun 3, 2021
    Member
    More actions

    Please see the note at https://matplotlib.org/stable/users/prev_whats_new/whats_new_3.3.0.html#axes3d-no-longer-distorts-the-3d-plot-to-match-the-2d-aspect-ratio (from #17515) for how to get this effect.

    If setting this with a string is important please open a new issue with what you would expect "equal" to do under various conditions / adjustable settings.


    The code that would need to be changed to implement this is

    matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py

    Lines 275 to 347 in a3fbf18

    def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
    """
    Set the aspect ratios.
    Axes 3D does not current support any aspect but 'auto' which fills
    the axes with the data limits.
    To simulate having equal aspect in data space, set the ratio
    of your data limits to match the value of `.get_box_aspect`.
    To control box aspect ratios use `~.Axes3D.set_box_aspect`.
    Parameters
    ----------
    aspect : {'auto'}
    Possible values:
    ========= ==================================================
    value description
    ========= ==================================================
    'auto' automatic; fill the position rectangle with data.
    ========= ==================================================
    adjustable : None
    Currently ignored by Axes3D
    If not *None*, this defines which parameter will be adjusted to
    meet the required aspect. See `.set_adjustable` for further
    details.
    anchor : None or str or 2-tuple of float, optional
    If not *None*, this defines where the Axes will be drawn if there
    is extra space due to aspect constraints. The most common way to
    to specify the anchor are abbreviations of cardinal directions:
    ===== =====================
    value description
    ===== =====================
    'C' centered
    'SW' lower left corner
    'S' middle of bottom edge
    'SE' lower right corner
    etc.
    ===== =====================
    See `.set_anchor` for further details.
    share : bool, default: False
    If ``True``, apply the settings to all shared Axes.
    See Also
    --------
    mpl_toolkits.mplot3d.axes3d.Axes3D.set_box_aspect
    """
    if aspect != 'auto':
    raise NotImplementedError(
    "Axes3D currently only supports the aspect argument "
    f"'auto'. You passed in {aspect!r}."
    )
    if share:
    axes = {*self._shared_x_axes.get_siblings(self),
    *self._shared_y_axes.get_siblings(self),
    *self._shared_z_axes.get_siblings(self),
    }
    else:
    axes = {self}
    for ax in axes:
    ax._aspect = aspect
    ax.stale = True
    if anchor is not None:
    self.set_anchor(anchor, share=share)

    and

    matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py

    Lines 407 to 422 in a3fbf18

    def apply_aspect(self, position=None):
    if position is None:
    position = self.get_position(original=True)
    # in the superclass, we would go through and actually deal with axis
    # scales and box/datalim. Those are all irrelevant - all we need to do
    # is make sure our coordinate system is square.
    trans = self.get_figure().transSubfigure
    bb = mtransforms.Bbox.from_bounds(0, 0, 1, 1).transformed(trans)
    # this is the physical aspect of the panel (or figure):
    fig_aspect = bb.height / bb.width
    box_aspect = 1
    pb = position.frozen()
    pb1 = pb.shrunk_to_aspect(box_aspect, pb, fig_aspect)
    self._set_position(pb1.anchored(self.get_anchor(), pb), 'active')

    and likely

    matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py

    Lines 365 to 405 in a3fbf18

    def set_box_aspect(self, aspect, *, zoom=1):
    """
    Set the axes box aspect.
    The box aspect is the ratio of height to width in display
    units for each face of the box when viewed perpendicular to
    that face. This is not to be confused with the data aspect
    (which for Axes3D is always 'auto'). The default ratios are
    4:4:3 (x:y:z).
    To simulate having equal aspect in data space, set the box
    aspect to match your data range in each dimension.
    *zoom* controls the overall size of the Axes3D in the figure.
    Parameters
    ----------
    aspect : 3-tuple of floats or None
    Changes the physical dimensions of the Axes3D, such that the ratio
    of the axis lengths in display units is x:y:z.
    If None, defaults to 4:4:3
    zoom : float
    Control overall size of the Axes3D in the figure.
    """
    if aspect is None:
    aspect = np.asarray((4, 4, 3), dtype=float)
    else:
    orig_aspect = aspect
    aspect = np.asarray(aspect, dtype=float)
    if aspect.shape != (3,):
    raise ValueError(
    "You must pass a 3-tuple that can be cast to floats. "
    f"You passed {orig_aspect!r}"
    )
    # default scale tuned to match the mpl32 appearance.
    aspect *= 1.8294640721620434 * zoom / np.linalg.norm(aspect)
    self._box_aspect = aspect
    self.stale = True

    It is not obvious to me what the correct heuristics are to adjust the data limits / box aspect are under the different adjustable regimes (or if the adjustable values should be the same as in 2D?).

    I suspect that this is 10-30 (apply the standard factor of pi, but would also be very happy to be over estimating) hours of work (most of it reading the mplot3d code) to implement for someone who knows Python well.

    Reacted by Robert Pollak
  27. tacaswell commented on Jun 21, 2021

    @tacaswell
    tacaswell
    on Jun 21, 2021
    Member
    More actions

    @SirSharpest Can you please explain why the process in https://matplotlib.org/stable/users/prev_whats_new/whats_new_3.3.0.html#axes3d-no-longer-distorts-the-3d-plot-to-match-the-2d-aspect-ratio is insufficient?

  28. logari81 commented on Aug 2, 2022

    @logari81
    logari81
    on Aug 2, 2022
    More actions

    all what axis('equal') should do is this:

    xlim = ax.get_xlim3d()
    ylim = ax.get_ylim3d()
    zlim = ax.get_zlim3d()
    ax.set_box_aspect((xlim[1]-xlim[0], ylim[1]-ylim[0], zlim[1]-zlim[0]))
    
    Reacted by Joao Cunha, Yunfeng Wang, lkbarth, vadimcn, alient12 and Jesse Mehami
  29. oscargus commented on Aug 2, 2022

    @oscargus
    oscargus
    on Aug 2, 2022
    Member
    More actions

    There is a PR that hopefully will be included in 3.6. #23409

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

  • @WeatherGod
    WeatherGod

Labels

Documentationtopic: mplot3d

Type

No type

Projects

No projects

    Milestone

    • v3.1.0

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.