| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Addresses Issue matplotlib#28408 - matplotlibrc: add axes3d.mouserotationstyle and axes3d.trackballsize - lib/matplotlib/rcsetup.py: add validation for axes3d.mouserotationstyle and axes3d.trackballsize - axes3d.py: implement various mouse rotation styles - update test_axes3d.py::test_rotate() - view_angles.rst: add documentation for the mouse rotation styles - update next_whats_new/mouse_rotation.rst
There was a problem hiding this comment.
Left some comments on the code & docs, but I think this overall looks good.
The main discussion to have I think is whether we want to provide all these options. I can see uses for locking roll in the 'azel' method, and I can see uses for having roll control (and agree that should be the default), but the four roll control option seems to accomplish pretty much the same thing. To that end, 'Holroyd' seems strictly better than any of 'trackball', 'arcball', 'Shoemake' - better than the first two because of the path independence, and better than 'Shoemake' because of avoiding the "edges" in the control region that you and the paper point out. It definitely feels the nicest to me experimenting with it locally.
Are there specific use cases that you see for the other three roll control methods? I think it would make more sense to provide users just the "best" options rather than all the possible options. Eliminates some sources of potential confusion, and less code reduces the maintenance burden.
Sorry, something went wrong.
Well I imagine that different people have different preferences, or they already are used to something specific. Why force a particular approach on them, when it is easy enough to provide a choice? The maintenance burden is relatively minor, eliminating all the trackballs-but-one would only save a handful of lines. |
Sorry, something went wrong.
|
@scottshambaugh - an updated PR is waiting for your review |
Sorry, something went wrong.
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
I don't feel too strongly about this, and it wouldn't really hurt. I just don't immediately see any uses where the alternate rotation methods with roll control are valuable. Would appreciate if another maintainer could weigh in with their opinion. I think we should definitely be very choosey about the default though - can keep talking about that in the comment chain higher up. |
Sorry, something went wrong.
|
Maintenance burden is only on side, and may not be too bad here. But options and choice are also mental load, both for users and maintainers. It makes a difference whether you have to understand two options or four. If there are good reasons/use cases for all the options, we should take them. If they are just added because we've found more mathematical ways to describe rotations, leave them out. |
Sorry, something went wrong.
|
I'll make a proposal then: 1) we'll make the width of the transition region in @scottshambaugh 's proposed arcball variant adjustable, rather than ad hoc have it fixed at 60°. In this way, you can have either a) just a true arc ball (with zero transition width but and abrupt edge) or b) a smoothed edge one. The need for the funny hyperbolic Holroyd arcball then all but disappears, so 2) we drop that. This leaves us with 4 variants, each with a clear use case/reason for their existence:
And then anyone can pick their poison, fair is fair. |
Sorry, something went wrong.
Gavin Bell appears to be the accurate reference/attribution, not Holroyd, replace it.
| The angles are not sensitive to the quaternion's norm(). | ||
| """ | ||
| qw = self.scalar | ||
| qx, qy, qz = self.vector[..., :] |
There was a problem hiding this comment.
It won't let me add this comment to the unmodified line below, but in playing with this PR I do infrequently run into a domain error on arcsin due to floating point errors. A little hard to reproduce reliably, but it does pop up. Clipping the inside value to [-1, 1] should fix this.
/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py:4028: RuntimeWarning: invalid value encountered in arcsin elev = np.arcsin( 2*( qw*qy+qz*qx)/(qw*qw+qx*qx+qy*qy+qz*qz)) # noqa E201 posx and posy should be finite values
Sorry, something went wrong.
There was a problem hiding this comment.
/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py:4028: RuntimeWarning: invalid value encountered in arcsin elev = np.arcsin( 2*( qw*qy+qz*qx)/(qw*qw+qx*qx+qy*qy+qz*qz)) # noqa E201 posx and posy should be finite values
I'm just so curious about the values of qw, qx, qy, and qz that cause this. I can understand the posx and posy should be finite values complaint, as a consequence of the arcsin argument getting out of range. But the invalid value encountered in arcsin is still mysterious...
Could you try for me sometime replacing the offending statement with the following:
try:
elev = np.arcsin( 2*( qw*qy+qz*qx)/(qw*qw+qx*qx+qy*qy+qz*qz)) # noqa E201
except:
print(repr(qw), repr(qx), repr(qy), repr(qz))
print(repr(qw*qy), repr(qz*qx), repr(qw*qw), repr(qx*qx), repr(qy*qy), repr(qz*qz) )
print(repr( 2*( qw*qy+qz*qx) ), repr(qw*qw+qx*qx+qy*qy+qz*qz) )
print(repr( 2*( qw*qy+qz*qx)/(qw*qw+qx*qx+qy*qy+qz*qz) ))
(I thought the error merits an attempt at diagnosing; not that I'm opposed to the np.clip(), I just thought it would be good to understand what is going on, how this comes about.)
Sorry, something went wrong.
There was a problem hiding this comment.
I just spent a few minutes trying to reproduce but couldn't - it's a tricky edge case to trigger. When I ran across it before, the value inside arcsin was only 1e-16 (or thereabouts) larger than 1, so it's just a result of numerical round-off. My guess would be a conditioning issue, where one of the values is very small relative to the others and gets rounded off to 0 in the denominator, but is big enough to still impact the numerator. I'll leave the debug lines in and let you know if it happens again, but am not concerned - this stuff end up happening fairly regularly across the codebase.
Sorry, something went wrong.
There was a problem hiding this comment.
I think the np.clip got missed in your latest commit, possible to add that quickly before merge?
Sorry, something went wrong.
There was a problem hiding this comment.
I think the np.clip got missed in your latest commit, possible to add that quickly before merge?
Oh sorry, of course; I put the np.clip() back in.
Sorry, something went wrong.
There was a problem hiding this comment.
Thanks! Need to move the close paren to fix the test failure but the MR is looking good.
Sorry, something went wrong.
There was a problem hiding this comment.
I just spent a few minutes trying to reproduce but couldn't - it's a tricky edge case to trigger.
You had mentioned before that there were floating point round-off errors on the macos github CI runners (#28823 (comment)); I think that was the original motivation to put the np.clip() in in the first place. Any chance we can trigger the edge case there once again?
(Not that I'm against the np.clip(), but I'm still surprised by the occurrence of the quotient >1. I thought the IEEE 754 standard for floating-point arithmetic requires that multiplication and addition should be correctly rounded, so I thought we would be in the clear... unless macos would not conform to IEEE 754, which would be also quite remarkable... Or I don't quite understand all of the correctly, and I should go read some more Kahan)
Sorry, something went wrong.
There was a problem hiding this comment.
If I happen to catch it again I'm happy to add it as a test! I think I lost the specific test case in the other MR that was erroring, and it was dependent on the specific math operations being performed so it likely wouldn't translate to this new code.
To be clear, I don't think it's a macos-specific problem, the issue is more that there isn't perfect determinism across different platforms / python versions / etc (even with perfect determinism within a configuration). Everything is being rounded "correctly", it's just that if we're right on the edge of floating point tolerance, complier/processor/implementation/optimization differences can have different results, and values infinitesimally on the wrong side of 1 will stack up unfavorably with further operations. For a simple concrete example, some systems in calculating (a + b)*c might distribute that multiplication to a*c + b*c, and those can result in different rounding of the results. Especially if for example a >> b and so b doesn't make it into the mantissa of a + b (addition and subtraction are not associative in floating point math, which is pretty unintuitive IMO).
Sorry, something went wrong.
There was a problem hiding this comment.
[...] it's just that if we're right on the edge of floating point tolerance, complier/processor/implementation/optimization differences can have different results, and values infinitesimally on the wrong side of 1 will stack up unfavorably with further operations. For a simple concrete example, some systems in calculating (a + b)*c might distribute that multiplication to a*c + b*c, and those can result in different rounding of the results.
I'm just trying to wrap my head around what the optimization difference could have been that would lead to the observed result. '(a+b)c' -> 'ac + b*c' does not quite fit the bill... My hope is that an actual example would shed light on it...
Sorry, something went wrong.
|
I can see how the "single speed" roll that tracks the mouse can be a useful property. I think that's a good argument for including it as an option! So I'm on board with your idea to have the four options of 'azel', 'trackball', 'arcball', and 'matplotlib' (Shoemake-like with the fillet, whatever we call it), and scrapping 'Holroyd'/'Bell'. Unfortunately it is mutually exclusive with the path independence property. With our differing opinions on which is more intuitive to use (for what it's worth, Usability Analysis of 3D Rotation Techniques found people equally fast with trackball & shoemake), how to rank them? We need a default which will make life easiest for the 99% of users who won't touch this setting. I think the strongest argument is that path independence allows for easily "undoing" a rotation that the user didn't intend, which fulfills the UX Principle of Forgiveness. It's very easy to otherwise get "lost" in rotation space, especially if your mouse accidentally leaves the axes box and reenters it elsewhere. I also like that you can reach the full sphere of orientations with a single-click, rather than just the front hemisphere as you can with arcball. But that's a personal preference. Edit: I did also happen to see this thread on twitter pop up today about several people complaining about 3D precession :) https://x.com/rms80/status/1843338041957687578 2024-10-06.20-21-34-1.mp4 |
Sorry, something went wrong.
|
@scottshambaugh What is your current opinion on this vs. #28823? If the only sticking points here are default/docstring here then in discussing with @tacaswell and @QuLogic, we are comfortable sorting those out in follow up PRs during the RC (Though an issue to track that follow up would be good). We have also said that we are comfortable with you doing a one review merge on this topic if you are satisfied. |
Sorry, something went wrong.
- revise _arcball() to soften the edge, according to border width parameter - drop _arcball() 'style' function parameter - add trackballborder rcParam - _arcball() 'border' case: normalize, so result vector is on unit sphere - math.sin/cos/sqrt -> np.sin/cos/sqrt: use numpy instead of math for consistency - remove 'Bell' style - rename arcball -> sphere, Shoemake -> arcball - update documentation - update test
|
@MischaMegens2 thank you for those updates, I think this is good to go now! Appreciate the collaboration polishing this up. I think I'm going to try to write up a blog post on the new methods, since it doesn't look like there's been any innovation in these control methods over the past 20 years and I think we came up with some good ideas. @ksunden I think this is good to merge now. If there's time pressure to get rc1 out, then I'm happy to solo-merge, but want to give other people a last chance to check it out. We can always refine docstrings down the line. I won't be able to make the weekly meeting today - maybe float it for a last look? |
Sorry, something went wrong.
Add np.clip() to avoid floating point round-off errors on the macos github CI runners, see also matplotlib#28823 (comment)
With proper parentheses
Thank you too! It does get considerably better with two pairs of eyes, looking at it :) |
Sorry, something went wrong.
I think the safest bet is trackball, it is easiest to understand, no surprises, it works the same in the middle and at the edge of the screen, no guesswork as to what it does. |
Sorry, something went wrong.
Documentation (view_angles.rst): - Fix: only one of :: and .. code:: is needed - Fix: :rc:`axes3d.trackballsize`/2 + :rc:`axes3d.trackballborder` does not work as a formula, use `trackballsize/2 + trackballborder` instead -Fix: $\sqrt 2 \approx 1.414$ now reads :math:`\sqrt 2 \approx 1.414` matplotlibrc: axes3d.mouserotationstyle: trackball The style 'trackball' as default is likely to make life easier for 99% of the users who don't touch this setting (it is easiest to understand, no surprises, it works the same in the middle and at the edge of the screen, no guesswork as to how it works/what it does).
|
@scottshambaugh: There were still a few glitches in the view_angles.rst documentation, I've tried to fix them; and I took the liberty to set the default style to trackball - but if you really think that that is a terrible idea then we can revert that... |
Sorry, something went wrong.
|
I really don't think having a default with hysteresis is a good user experience. If you think arcball is too confusing for users then I would rather keep the default as azel. |
Sorry, something went wrong.
Change default mouse rotation style to arcball
All right, then let's keep the arcball, it is a nice idea, and it is better not to underestimate our users ;) |
Sorry, something went wrong.
- single ` -> double `` - add description of the border (circular arc instead of hyperbola)
|
Remaining test failures are unrelated to this change, all comments have been resolved, @ksunden has given the go-ahead for a solo-review merge in the interest of getting this into 3.10, and I pinged the devs in the gitter channel last week to make sure people had a chance to review. With all that resolved I am merging this in - thanks again @MischaMegens2 for the work that went into the new 3d view control methods! I started putting together a skeleton of a blog post but it's going to take some time to flesh out. Will definitely let you know when it's done. |
Sorry, something went wrong.
|
@MischaMegens2 I finished up my post doing a deep dive into virtual trackballs, curious to hear your thoughts! |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
PR summary
Makes the plot3d mouse rotation style adjustable (various styles);
addresses Issue #28408; an attempt to improve on PR #28236.
It also incorporates some of the improvements proposed in PR #28236; what it does not include (yet) is the introduction of np.clip() to circumvent floating point round-off errors on the macos github CI runners.
PR checklist