-
-
Notifications
You must be signed in to change notification settings - Fork 321
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
Heatmap / Image / Surface API clarification and nonlinear grids #748
Comments
2D contour plots would be |
That is a very important issue that I think should be addressed more seriously at the level of mesh types. I've been working on Meshes.jl where I have this in mind as well. Currently the two available meshes Rectilinear and curvilinear meshes, also deserve their own types, and can be written compactly on top of primitives and other types of geometries. |
Let's maybe separate mesh representation issues from the issue here, which is just about drawing these things correctly. Conversions from special types can come later I think, because the underlying representation on the GPU will be the same for all of these. |
I am not familiar with the traits that are implemented in the Makie.jl stack, but it would certainly be useful to have them aligned with a collection of mesh types. I agree that the issue can be brainstormed regardless of the mesh representation. |
GLMakie currently just draw one textured Rectangle for both To me the difference between I would suggest that image(img::Matrix)
image(origin::Point, widths::Vec, img)
image(origin::Point, endpoint::Point, img)
image([x_min, x_max], [y_min, y_max], img)
image([x_min, x_max], [y_min, y_max], [z_min, z_max], img)
image(r::Rect, img) corresponding to |
I thought the difference between image and heatmap was that an image comes with color data per pixel, and not numerical values that get interpreted via colormap. But I agree that intuitively, one thinks more of the default rectangular image. In your view, should pixel [1, 1] of an image starting at x=1 be centered at 1 or 1.5? That's the difference between specifying the boundaries or specifying the boundary cell centers. I'm not sure if it's good if heatmaps and images differ in that regard. I think I favor the version where pixel centers are what you specify, because people often want to mark positions in images, and that makes more sense if you can just count the pixels. |
fwiw the cellsurfacelike behaviour you are suggesting is the same as that for 2d bar plots (where you supplement the mid point) and vertex-surfacelike the one for histograms (where you supply the bin edges). Bonus info: Plots offers the double api for bar plot where you can supply either the mid points OR n+1 limits between bars (which I think is not a clean design btw). |
Currently
Having an image pixel-centered is weird to me, but I do see the point. Probably best to keep things very adjustable then.
I was thinking about that too. If we were to allow both options this would be a way to handle it. Though it would be unclear how to handle 2-element arrays. The other option would be to have an attribute to specify where cell centers are, which sounds better to me. Maybe then we should have image([xs, ys], img_or_mat[, align=:outside, interpolate=true, colormap=[:black, :white]])
heatmap([xs, ys], img_or_mat[, align=:center, interpolate=false, colormap=:viridis]) with |
I think that might be a bit brittle, and it's better to be explicit about the choice of centers vs. edges. I would favor defaulting to centers (as least for heatmaps, but probably also for images). The error message when passing n+1 points as centers can give a hint about the relevant keyword to pass edges, or even suggest using Also, here my recipe for centered heatmaps (just for working around this issue in the meantime, not as a suggestion for implementation):
|
First of, thanks all involved for looking into these! I'd like to help this move forward however I can, so I'll chime in to synthesize my understanding of the issue and hopefully someone can draw on it, correct my mistakes, and maybe that helps the devs to decide how to implement some changes! 😄 Currently, there are 2 truly flat 1
My understanding is that this issue is driven by the syntactic sugar we are accustomed to for these types of plots. When I run one of image(rand(5,3))
heatmap(rand(5,3))
heatmap(1:5, 1:3, rand(5,3)) I expect the plotting package to figure out the location and shape of the cells/pixels under the hood, and this issue is about doing just that, deciding what to do with different incomplete inputs. About the 2-element vector confusion, I propose appending @jkrumbiegel's original post table with one row for image((x_min, x_max), (y_min, y_max), img)
image((x_min, x_max), (y_min, y_max), (z_min, z_max), img) Some of the issues referenced above request the feature to expand these to allow for less regular meshes. For example, one might want a heatmap where cells are in an irregularly-spaced rectilinear grid, in a curvilinear grid, or an otherwise generally unstructured mesh. I feel like these apply only to FWIW, my uneducated feeling is that @juliohm is right that having this discussion at the mesh level is the best approach to clearly delineate the different plot types/behaviors on that topic. But maybe I am missing something. So, for # if z is a m×n 2D array
heatmap(x::Tuple, y::Tuple, z::Array{<:Number,2}) = heatmap(x[1]..x[2], y[1]..y[2], z) # so same as ClosedInterval case
function heatmap(x::Vector, y::Vector, z::Array{<:Number,2}) # if `x` and `y` are vectors
if (length(x), length(y)) == size(z)
# x and y are cell centers
elseif (length(x), length(y)) == size(z) .+ 1
# x and y are cell edges
else
error("Sizes must be...")
end
end
heatmap(x::Array{<:Number,2}, y::Array{<:Number,2}, z::Array{<:Number,2}) = ... # regular mesh (can be cartesian, rectilinear, or curvilinear)
heatmap(x::Vector, y::Vector, z::Vector) # unstructured mesh Hopefully someone finds this useful 🙏 1: By truly flat I mean that is not thought of as embedded in a 3D space. For instance, I think |
Okay I have a new idea about this. Our base issue is that heatmaps / images have an unusual behavior in that they go edge-to-edge and are not centered on their cells. Their colors are given per cell and not at the vertices, unlike typical meshes. Images are actually not different from heatmaps, only that they are more constrained. For the implementation it does not really matter. I don't really like having a "do we have I would propose adding a simple type In the backend, everything would work via Usage would look like this: heatmap(rand(3, 3)) # centered on 1 to centered on 3 in both dimensions
heatmap(1:3, 1:3, rand(3, 3)) # same
heatmap(1..3, 1..3, rand(3, 3)) # same
heatmap([1, 2, 4], [1, 3, 5], rand(3, 3)) # unequal cell sizes, still giving the centers
heatmap(Edges(1..3), Edges(1..3), rand(3, 3)) # now the edges go from 1 to 3, not the centers
heatmap(Edges(1..3, 1..3), rand(3, 3)) # same, maybe more convenient with only one `Edges`
heatmap(Edges([1, 2, 4, 5], [2, 5, 6, 7]), rand(3, 3)) # unequal cell sizes The "normal" signatures would be immediately converted to |
[EDIT: I just wanted to note that I started this comment a while ago and just finished it now without seeing @jkrumbiegel's latest comment. So sorry if I just added some noise 😓] [EDIT 2: Adding that I like (love?) @jkrumbiegel's idea above] I would really like to see this move forward, so I'm pushing with another supportive comment. Again, I hope this is somewhat useful. My understanding at this point is that we have 4 types of grids/meshes in the end: Right now, only cartesian grids are supported, except I do think that, in the long run, being able to plot any of So what is needed for the rectilinear case? Well, it seems that the backends only need to be able to deal with the more general rectilinear meshes. So IMHO the default should be that at the AbstractPlotting level, plot types returned are actually defining the edges of the cells. So I would assume we could implement the following rules (which is slightly different take from what @jkrumbiegel — who is much more knowledgeable than me — suggested):
I'm not saying that the end product (at the backend level), everything should be of the rectilinear, cells-defined-by-edges type. My uneducated guess is that depending on the type of plot returned by AbstractPlotting, e.g., a |
I'm personally usually for doing it "right" immediately, so if there's great need for the more complex scenarios we should incorporate those as well I guess. In the unstructured example we don't even have a grid-like structure anymore though, does that still make sense for heatmap/image? That looks very mesh-y to me. Or is this still about coloring each face separately instead of the vertices? |
I think plots like those would fit more into Also, how would we differentiate a curvelinear heatmap from normal ones? If there's no clear way to differentiate them, then curvelinear heatmaps should probably be their own separate thing. (Having different plot types for these might also be helpful in GLMakie. A curvelinear heatmap requires a different shader and maybe also a different primitive, I think.) I'd suggest we get rectlinear heatmaps done first and leave the other fancy cases for later. I'm not a big user of heatmaps so I don't have a strong opinion on what the front end of this should look like. I'm fine with doing the |
Is there a level or two of abstraction between "rectilinear" and "unstructured", for Euclidean uniform tilings and more general uniform tilings? |
https://github.com/JuliaGeometry/VoronoiCells.jl might be useful for unstructured tiling? |
You guys are really giving Makie a push towards broader application, maybe my code contains some lines of inspiration: (File was edited to include autoticks.)
|
This was more less implemented by #3106 |
We had multiple issues about nonlinear grids for heatmaps, but also about the bin centering of heatmaps and images (basically that pixel [1, 1] is not centered on point [1, 1]).
These discussions have mostly looked at implementing things in the backends, but I think it's an AbstractPlotting abstraction issue. Right now, there is a
SurfaceLike
trait which heatmap, image and surface use to convert their arguments. I think that this is incorrect, because they are different, even if only in a subtle way.The difference is that a surface colors the vertices of a grid, while image / heatmap colors the cells of a grid. I therefore suggest to remove
SurfaceLike
and instead addVertexSurfaceLike
andCellSurfaceLike
or something else to that effect. Maybe there are better words for this.Then the question is how the coordinates of these grids should be defined. Let's say our color data are always in the form of a matrix, then there are multiple different ways of defining the coordinates of vertices / cells.
Here's what I would suggest:
VertexSurfaceLike
CellSurfaceLike
1.0..10.0
Matrices would then just be extensions of the vector case.
One thing that this doesn't cover is a plot where the coordinates given are vertices, but the colors are for the cells. One could dispatch to that method by checking if the length of heatmap coordinates are n+1, but that might be a bit brittle?
The text was updated successfully, but these errors were encountered: