-
-
Notifications
You must be signed in to change notification settings - Fork 366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix exclusive upper bound for line aggregation #343
Conversation
The previous adjustment for the exlusive upper bound failed to account properly for bounds with float values. This caused more pixels than necessary to be clipped. Instead of performing the upper bound check when we clipped a line segment to the bounding box, we now check at a lower level when we translate a line segment to the actual pixels. This gives us greater control without dealing with float coordinates; at that level we are working with integer coordinates. Fix holoviz#342
datashader/glyphs.py
Outdated
xmin = int(x_mapper(xmin) * sx + tx) | ||
xmax = int(x_mapper(xmax) * sx + tx) | ||
ymin = int(y_mapper(ymin) * sy + ty) | ||
ymax = int(y_mapper(ymax) * sy + ty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'm not reading the code correctly but could this be done once outside the aggregation function somehow? Seems like doing it repeatedly for each line vertex will add some overhead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After a brief look could Line._build_extend
do this and then pass the mapped_bounds
in as an argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was my question as well; seems like this change has the potential to slow things down, which we should be careful to avoid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this could be a performance issue, so as @philippjfr suggested, I pre-calculated the mapped bounds and passed it down to draw_line
.
I refactored the pixel mapping into a separate method similar to draw_line
and extend_line
. This made the code more readable and prevented me from hard-coding a mapped bounds value into the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turned out to be more complicated than I expected, but it does look like a better approach. Have you tested with logarithmic axes?
I haven't. Is there a relevant notebook example? |
No that I know of, no, but that's a good point -- we should have one. And I guess we need one both for lines and for points, since they are using different code here. There are a couple of open issues about logarithmic axes... |
I checked the test cases and found tests for logarithmic axes but only for points. I'll add tests for lines. |
Thanks for the fix @jbcrail. My understanding of this code is still lacking so my feedback may not be as helpful as it could be. The main worry I have are these new conditionals you've had to introduce in the core aggregation function will add additional overhead which may not be necessary. I have the feeling that your original approach of adjusting the bounds would have worked okay but should simply been adjusting them by 1 or 1/2 a pixel width rather than always subtracting 1. I'm not very confident about that though so if you can explain to me and Jim why these checks have to happen in the core aggregation and not before we'll trust you. I'll try to work through it on paper now to convince myself. |
My understanding of the code is still improving also, so any feedback or double-checking is appreciated. I'll describe what I've learned and why I choose the current approach. Maybe this will help us find a better way. Initial approach: exclusive bounding box in In If we made the bounding box exclusive, any point landing on either maximum bound would have to be adjusted, causing the point to be inside the box. Then we could pass this clipped line segment to However, the clipping algorithm assumes an inclusive bounding box and updating the algorithm accordingly turned out to be error prone. I considered using NumPy's float epsilon to decrement the respective point's coordinate so that the point would be just inside but I determined it was a bad idea to change data. Current approach: exclusive bounding box in We leave At this level, the coordinates of the line segment and bounding box are scaled and transformed to integer coordinates. There are four cases to consider: 1) one segment point is on a maximum bound, 2) each segment point is on a separate maximum bound, 3) both segment points are on the same maximum bound (i.e. vertical or horizontal line), and 4) both segment points are inside or on minimum bounds. Since Alternative In Based on @philippjfr's feedback, we could adjust the integer-coordinate bounding box down by one on each side. Then we would run the clipping algorithm unaltered and pass the new line segment coordinates to |
This removes the bound checks in draw_line. It also makes the bounding box clipping integer-based, along with draw_line.
Thanks so much for the detailed summary. The new approach seems like the correct and most optimal approach so I'd be very happy to see this merged. |
Thanks, @philippjfr. I also benchmarked master against this PR using a 1e6 x 1e6 diagonal. No major difference in performance.
|
This reverts to exclusive ranges both manual and auto for all glyphs (points/line) without introducing regressions of holoviz#318, holoviz#330, and holoviz#343. I refactored several tests to make xarray coordinate indices easier to read and more explicit.
The previous adjustment for the exlusive upper bound failed to account properly for bounds with float values. This caused more pixels than necessary to be clipped.
Instead of performing the upper bound check when we clipped a line segment to the bounding box, we now check at a lower level when we translate a line segment to the actual pixels. This gives us greater control without dealing with float coordinates; at that level we are working with integer coordinates.
One test was updated to match the expected result.
Fix #342