Skip to content
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

support images with reversed axes #3989

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [Unreleased]

- Support images with reversed axes [#3989](https://github.com/MakieOrg/Makie.jl/pull/3989)
- Correct a bug in the `project` function when projecting using a `Scene`. [#3909](https://github.com/MakieOrg/Makie.jl/pull/3909).
- Correct a method ambiguity in `insert!` which was causing `PlotList` to fail on CairoMakie. [#4038](https://github.com/MakieOrg/Makie.jl/pull/4038)

Expand Down
20 changes: 6 additions & 14 deletions CairoMakie/src/primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,10 @@ end

regularly_spaced_array_to_range(arr::AbstractRange) = arr

image_axis(xs::AbstractVector, N) = regularly_spaced_array_to_range(xs)
image_axis(xs::Makie.Interval, N) = range(Makie.endpoints(xs)..., length = N+1)
image_axis(xs, N) = range(extrema(xs)..., length = N+1)

function premultiplied_rgba(a::AbstractArray{<:ColorAlpha})
map(premultiplied_rgba, a)
end
Expand All @@ -796,20 +800,8 @@ function draw_atomic(scene::Scene, screen::Screen{RT}, @nospecialize(primitive::
ctx = screen.context
image = primitive[3][]
xs, ys = primitive[1][], primitive[2][]
if !(xs isa AbstractVector)
l, r = extrema(xs)
N = size(image, 1)
xs = range(l, r, length = N+1)
else
xs = regularly_spaced_array_to_range(xs)
end
if !(ys isa AbstractVector)
l, r = extrema(ys)
N = size(image, 2)
ys = range(l, r, length = N+1)
else
ys = regularly_spaced_array_to_range(ys)
end
xs = image_axis(xs, size(image, 1))
ys = image_axis(ys, size(image, 2))
model = primitive.model[]::Mat4d
interpolate = to_value(primitive.interpolate)

Expand Down
4 changes: 2 additions & 2 deletions GLMakie/src/drawing_primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -629,8 +629,8 @@ end
function draw_atomic(screen::Screen, scene::Scene, plot::Image)
return cached_robj!(screen, scene, plot) do gl_attributes
position = lift(plot, plot[1], plot[2]) do x, y
xmin, xmax = extrema(x)
ymin, ymax = extrema(y)
xmin, xmax = Makie.endpoints(x)
ymin, ymax = Makie.endpoints(y)
rect = Rect2(xmin, ymin, xmax - xmin, ymax - ymin)
return decompose(Point2d, rect)
end
Expand Down
22 changes: 22 additions & 0 deletions ReferenceTests/src/tests/primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -703,3 +703,25 @@ end

fig
end

@reference_test "Reverse heatmap axes" begin
img = [2 0 0 3; 0 0 0 0; 1 1 0 0; 1 1 0 4]

f = Figure()
heatmap(f[1, 1], 1:4, 1:4, img, colormap = :viridis)
heatmap(f[2, 1], 1:4, 4..1, img, colormap = :viridis)
heatmap(f[1, 2], 4:-1:1, 1:4, img, colormap = :viridis)
heatmap(f[2, 2], 4:-1:1, [4, 3, 2, 1], img, colormap = :viridis)
f
end

@reference_test "Reverse image axes" begin
img = [2 0 0 3; 0 0 0 0; 1 1 0 0; 1 1 0 4]

f = Figure()
image(f[1, 1], 1:4, 1:4, img, colormap = :viridis)
image(f[2, 1], 1:4, 4..1, img, colormap = :viridis)
image(f[1, 2], 4:-1:1, 1:4, img, colormap = :viridis)
image(f[2, 2], 4:-1:1, [4, 3, 2, 1], img, colormap = :viridis)
f
end
8 changes: 5 additions & 3 deletions src/conversions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,9 @@ function print_range_warning(side::String, value)
end

to_interval(x::Tuple{<: Real, <: Real}) = float_convert(x[1]) .. float_convert(x[2])
function to_interval(x::Union{Interval,AbstractVector,ClosedInterval})
return float_convert(minimum(x)) .. float_convert(maximum(x))
to_interval(x::Interval) = float_convert(leftendpoint(x)) .. float_convert(rightendpoint(x))
function to_interval(x::Union{AbstractVector})
return float_convert(first(x)) .. float_convert(last(x))
end


Expand Down Expand Up @@ -648,7 +649,8 @@ end
# Helper Functions #
################################################################################

to_linspace(interval, N) = range(minimum(interval), stop = maximum(interval), length = N)
to_linspace(interval::Interval, N) = range(leftendpoint(interval), stop = rightendpoint(interval), length = N)
to_linspace(x, N) = range(first(x), stop = last(x), length = N)
Comment on lines +656 to +657
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SimonDanisch @jkrumbiegel Is it fine to switch away from minimum and maximum to enable reverse image/heatmap axes? Should we consider this breaking?

(This is fixing the points I made in the last comment)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this change is fine we can merge this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that in the code path, that already warns for vectors?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I guess it is... So we don't allow vectors for images anymore anyways 🤷
It's not yet erroring, but should already warn to not use it.
So shouldn't be breaking

Copy link
Collaborator

@ffreyer ffreyer Jul 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have two functions with that change here:

  • to_interval affecting Image, Volume (errors in data_limits with reversed axis though)
  • to_linspace affecting PointBased w/ (Interval, Interval, Matrix), heatmap

What changed as far as I can tell:

  • heatmap no longer errors for reverse intervals but otherwise works the same (is the RangeLike convert not getting hit?)
  • image no longer errors on reverse intervals and allows for reversing, which it previously didn't. This is visually different
  • PointBased no longer errors with (Interval, Interval, Matrix), but still with something like (Interval, Vector)
  • volume errors in data_limits instead of convert_arguments with reversed limits


"""
Converts the element array type to `T1` without making a copy if the element type matches
Expand Down
4 changes: 2 additions & 2 deletions src/layouting/data_limits.jl
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ scalarmax(x, y) = max(x, y)
scalarmin(x::Union{Tuple, AbstractArray}, y::Union{Tuple, AbstractArray}) = min.(x, y)
scalarmin(x, y) = min(x, y)

extrema_nan(itr::Pair) = (itr[1], itr[2])
extrema_nan(itr::ClosedInterval) = (minimum(itr), maximum(itr))
extrema_nan(itr::Pair) = minmax(itr[1], itr[2])
extrema_nan(itr::ClosedInterval) = minmax(endpoints(itr)...)
function extrema_nan(itr)
vs = iterate(itr)
vs === nothing && return (NaN, NaN)
Expand Down
5 changes: 5 additions & 0 deletions test/boundingboxes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ end
@test bb.origin ≈ Point3f(0)
@test bb.widths ≈ Vec3f(10.0, 10.0, 0)

fig, ax, p = image(1..0, 1:10, rand(10, 10))
bb = boundingbox(p)
@test bb.origin ≈ Point3f(0, 1, 0)
@test bb.widths ≈ Vec3f(1.0, 9.0, 0)

# text transforms to pixel space atm (TODO)
fig = Figure(size = (400, 400))
ax = Axis(fig[1, 1])
Expand Down
4 changes: 4 additions & 0 deletions test/conversions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,11 @@ end

v1 = collect(1:10)
v2 = collect(1:6)
v3 = reverse(v1)

i1 = 1..10
i2 = 1..6
i3 = 10..1

o3 = Float32.(m3)

Expand All @@ -339,6 +341,8 @@ end
@test convert_arguments(Image, m3) == (0f0..10f0, 0f0..6f0, o3)
@test convert_arguments(Image, v1, r2, m3) == (1f0..10f0, 1f0..6f0, o3)
@test convert_arguments(Image, i1, v2, m3) == (1f0..10f0, 1f0..6f0, o3)
@test convert_arguments(Image, v3, i1, m3) == (1.0..10.0, 1.0..10.0, o3)
@test convert_arguments(Image, v1, i3, m3) == (1.0..10.0, 10.0..1.0, o3)
@test convert_arguments(Image, m1, m2, m3) === (m1, m2, m3)
@test convert_arguments(Heatmap, m1, m2) === (m1, m2)
end
Expand Down