-
Notifications
You must be signed in to change notification settings - Fork 616
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
New Visual: Bar #2394
base: main
Are you sure you want to change the base?
New Visual: Bar #2394
Conversation
Wow, this seems very simple for such a nice feature. I'm wondering how you think this compares to some of the logic in the existing HistogramVisual: https://github.com/vispy/vispy/blob/main/vispy/visuals/histogram.py Could/should this new Visual be used by the HistogramVisual? I notice the HistogramVisual allows for orientation (even though it is currently backwards from my experiments, see #2372). How hard would it be to add that here? |
Also, don't worry about the Otherwise, I forgot to mention, would it be possible to add a unit test for this new visual? Maybe something simple that shows that it is able to draw. You could even check the colors at certain points in the rendered result. For example, if the first bar is should fill the whole SceneCanvas then make sure that a color near the top of the image is the right color. And if the last bar is 50% of the canvas then make sure the colors are correct at the top and bottom half of the image. |
@@ -0,0 +1,32 @@ | |||
import numpy as np |
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.
This example will need a docstring like the other examples in this gallery directory. Formatting and content is important since it appears in the final website.
As I started with vispy ploting I found it very strage that there where no bar visuals even though there is a histogram visual, seems kind assbackwards, no offense. However it familiarized me with vispy ploting and I did use it to guide me to create #2287 - now I'm much more proficient with vispy ploting, I'm personally more interested in the Candle visual this is just a by-product. Short anwser is yes it would probebly make sense to use bars in the histogram. However I dont use histograms so I'm hardly an authority on the matter - people who actually use the histogram component should make the decision. If I remember correctly in a vispy histogram there is no option for spaces between bins and its a single mesh, which in my opinion looks kind of outdated. It might be useful to have a single mesh for a histogram as I sad I dont use histograms. Regaarding vertical bars probebly just switch width and height And for unit tests i've never done them, so I have no experience |
examples/scene/bar_plot.py
Outdated
# ----------------------------------------------------------------------------- | ||
""" | ||
Bar plot with Axis | ||
===================== |
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.
The underline must be the same number of characters (width) as the title.
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.
Okay, fixed
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.
Looks really good in general! I proposed some small change for clarity's sake, but as far as I can tell it makes sense. I really thionk we need some comments about what each part of calc_vertices
is doing, othwerwise we won't know how to maintain it a month from now, since it's quite cryptic.
Other than that, I think we could merge this, and think about using it as our histogram visual at a later time!
vispy/visuals/bar.py
Outdated
def update_data(self, height, bottom=None, width=0.8, shift=0.0, color='w'): | ||
if bottom is None: | ||
bottom = np.zeros(height.shape[0]) | ||
rr, tris = calc_vertices(height, bottom, width, shift=shift) |
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.
Missing orientation here
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.
Obviously yes, it was late yesterday
vispy/visuals/bar.py
Outdated
Orientation of the histogram - 'h' is default | ||
""" | ||
|
||
def __init__(self, height, bottom=None, width=0.8, shift=0.0, color='w', orientation='h'): |
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.
Would be awesome to be able to pass a per-bar color as well!
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.
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.
Yeah no need to do this here, I just pointed out it would be cool to add in the future :)
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.
Well I just looked into it, it should be not to problematic
vispy/visuals/bar.py
Outdated
vertices = np.array([ | ||
[-width/2 + shift, 1, 0], | ||
[width/2 + shift, 1, 0], | ||
[width/2 + shift, 0, 0], | ||
[-width/2 + shift, 0, 0]]) |
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.
What's the point of the 0
at index 2? I'm having a hard time following the flow.
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.
Well I thought that vertices would have to be x,y,z and the resulting vertices for the mesh should be in the shape (n,3), since its a 2d plot the z axis is zero. I'm not married to it if you want to change it it would be fine with me
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.
Ok, I see why I was confused: this resulted in a deprecation warning from numpy due to a staggered sequence in my testing, because I was passing an array to width
rather than a single value... My bad :P
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.
But yeah, I think you could remove the zero there and it would be fine, meshes work well with 2D data and we do it in several other places as well.
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.
Only argument for not removing the zero would be if it avoids an unnecessary copy. Another thing to consider performance-wise would be to create one giant array at the beginning and fill it in later. This is typically faster since we don't have to keep allocating new numpy arrays, but may only be true for really large arrays.
vispy/visuals/bar.py
Outdated
|
||
base_faces = np.tile(base_faces, (height.shape[0], 1)) | ||
|
||
faces = stack_n_x2*4 + base_faces |
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.
ok, maybe this use of stack_n_x2
counters my proposal of using repeat(4)
above, if I'm following.
vispy/visuals/bar.py
Outdated
orientation : {'h', 'v'} | ||
Orientation of the histogram - 'h' is default |
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 find the current behaviour backwards: I would think h
means that the bars are horizontal, but here it seems to refer to the orientation of the "baseline" of the plot. Is this typical?
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.
What a philosophical question, on the one hand is it called horizontal if the bars got vertical up and are alined along the horizontal axis or is it called vertical.
I was thinking along which axis are the bars alined as opposed to the direction of the bars
I'm have no clue, I''ll use the vispy philosophical bend so its inline with other visuals
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.
This is why I mentioned #2372. I assumed we were talking about the direction a single bar "grows". So if the bin dimension (width) is on the x-axis, then the bars grow on the y-axis and the plot is "vertical".
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.
Still small suggestion left, but otherwise This is looking great for me! @djhoese anything to add?
vispy/visuals/bar.py
Outdated
if color_array is not None and color_array.shape[0] == height.shape[0] and color_array.shape[1] == 3: | ||
MeshVisual.__init__(self, rr, tris, face_colors=np.repeat(color_array, 2, axis=0)) | ||
else: | ||
MeshVisual.__init__(self, rr, tris, color=color) |
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.
Great! To get rid of this if clause (and get extra perks like string inputs and aplha values) I would use the ColorArray
just like you did in the Candle
PR.
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.
@Bliss3d you think you can get this to work with a single color
argument? I think it would be more intuitive, kinda like Markers
does in these few lines:
vispy/vispy/visuals/markers.py
Lines 607 to 613 in f13d7da
edge_color = ColorArray(edge_color).rgba | |
if len(edge_color) == 1: | |
edge_color = edge_color[0] | |
face_color = ColorArray(face_color).rgba | |
if len(face_color) == 1: | |
face_color = face_color[0] |
As you can see, you can use the ColorArray
object from vispy.color
, which automatically takes care of all the weird cases (a string like 'w'
, a (n, 3)
rgb array, or an rgba
, and so on). You can then simply check if it's a single color, and if so pass it to color
, otherwise pass it to face_colors
.
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'm guessing something like this
vispy/visuals/bar.py
Outdated
color_array = None | ||
|
||
if isinstance(color, np.ndarray): | ||
if len(color.shape) == 2: | ||
if color.shape[1] == 3: | ||
if color.shape[0] > 1 and color.shape[0] != 0: | ||
color_array = np.repeat(color, 2, axis=0) | ||
color = None | ||
else: | ||
raise ValueError('If you pass an numpy array as color it needs to be rgb i.e. 3 columns') | ||
elif isinstance(color, ColorArray): | ||
pass | ||
elif isinstance(color, str): | ||
pass | ||
else: | ||
raise ValueError("Color has to be either a vispy ColorArray, numpy array or a string for example: 'w'") |
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.
Sorry, maybe I wasn't very clear: using the ColorArray
is specifically to get rid of all this extra logic (and unnecessary restrictions like passing only rgb). You can do this:
color_array = None | |
if isinstance(color, np.ndarray): | |
if len(color.shape) == 2: | |
if color.shape[1] == 3: | |
if color.shape[0] > 1 and color.shape[0] != 0: | |
color_array = np.repeat(color, 2, axis=0) | |
color = None | |
else: | |
raise ValueError('If you pass an numpy array as color it needs to be rgb i.e. 3 columns') | |
elif isinstance(color, ColorArray): | |
pass | |
elif isinstance(color, str): | |
pass | |
else: | |
raise ValueError("Color has to be either a vispy ColorArray, numpy array or a string for example: 'w'") | |
color = ColorArray(color).rgba | |
if len(color_array): | |
color = color_array[0] | |
color_array = None | |
else: | |
color = None |
and then later
MeshVisual.set_data(self, rr, tris, color=color, face_colors=color_array)
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'm not sure how this is supposed to work, color_array is not defiened in that context
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.
sorry, wrote that in a rush. Fixed:
color = ColorArray(color).rgba
if len(color):
color = color[0]
color_array = None
else:
color = None
color_array = color
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 modified it a bit, but I hope it works for you
Yes, this looks good to me, thanks @Bliss3d! It wouyld be great to also have at least some basic tests before we merge. I don't think we need to do the |
Yes @brisvag. Here's what I said early on that I think would be good for tests:
|
Well I have pretty much no clue how to run or create those test... :( |
Well there's no time like the present! VisPy has some basic documentation for writing tests (really glad past-Dave wrote this): https://vispy.org/dev_guide/contributor_guide.html#adding-tests In your case, you're writing tests for a Visual object so a new test module should be added here: https://github.com/vispy/vispy/tree/main/vispy/visuals/tests You'll find other tests in that directory to use as a starting point, but keep in mind that some of them do things like vispy/vispy/visuals/tests/test_mesh.py Lines 194 to 213 in 859fcae
Let me know if any of this isn't making sense. |
Will do, also I'll transfer the feedback to #2388 since there is quite a bit of overlap, but at the moment I need a change of pace from vertices and I still got the date axis labels in the works - and it is quite good and I need to flush it out before I dump all that time and date stuff out of my brain. I had this PR and #2388 lying around for mouths... |
This PR replaces #2287, this contains fixes for Bar.meshdata.get_vertices() which error out in the previous PR and is probebly a better solution and contains an example
As ever your input is very welcome :)
python.2022-10-02.16-07-02-03.mp4