Skip to content

Instantly share code, notes, and snippets.

@asinghvi17
Created March 14, 2023 16:47
Show Gist options
  • Save asinghvi17/896b7851f6f91fae29242edfa6e08579 to your computer and use it in GitHub Desktop.
Save asinghvi17/896b7851f6f91fae29242edfa6e08579 to your computer and use it in GitHub Desktop.
A way to display images on meshes which are pretransformed in Makie.jl
using Makie: GeometryBasics
using Makie.GeometryBasics: StructArrays
@recipe(MeshImage) do scene
Attributes(
npoints = 100,
space = :data,
)
end
# this inherits all conversions for `Image`,
# if no specialized conversion for `MeshImage` is found
Makie.conversion_trait(::Type{<: MeshImage}) = Image
function apply_transform!(f, structarray::StructArrays.StructArray{<: VecTypes{N}}, space = :data) where N
backing_arrays = getproperty.((structarray,), 1:N)
for i in 1:length(structarray)
transformed_point = Makie.apply_transform(f, structarray[i], :data)
for j in 1:N
backing_arrays[j][i] = transformed_point[j]
end
end
return structarray
end
function apply_transform!(fs::Tuple{<: Function, <: Function}, structarray::StructArrays.StructArray{<: VecTypes{N}}, space = :data) where N
backing_arrays = getproperty.((structarray,), 1:N)
backing_arrays[1][:] .= (fs[1]).(backing_arrays[1])
backing_arrays[2][:] .= (fs[2]).(backing_arrays[2])
return structarray
end
function Makie.plot!(plot::MeshImage)
points_observable = Observable{StructArrays.StructArray{Point2{Float64}, 1}}()
faces_observable = Observable{Vector{Makie.GLTriangleFace}}()#=GeometryBasics.QuadFace{Int}=#
uv_observable = Observable{Vector{Vec2f}}()
# lift(plot.npoints; ignore_equal_values = true, priority = 100) do npoints
# # could try GLTriangleFace, but that might not turn out well
# uv = map(x-> Vec2f(1f0 - x[2], 1f0 - x[1]), decompose_uv(rect))
# uvm = GeometryBasics.Mesh(GeometryBasics.meta(ps; uv=uv), faces)
# return GeometryBasics.normal_mesh(uvm)
# end
old_npoints = typemin(Int)#plot.npoints[]
# handle the transformation
lift(plot[1], plot[2], plot.transformation.transform_func, plot.npoints, plot.space; ignore_equal_values = true) do x_in, y_in, tfunc, npoints, space
@show tfunc
if npoints != old_npoints
points_observable.val = StructArrays.StructArray{Point2{Float64}}(undef, npoints^2)
rect = GeometryBasics.Tesselation(Rect2f(0, 0, 1, 1), (npoints, npoints))
faces_observable[] = GeometryBasics.decompose(
Makie.GLTriangleFace,#GeometryBasics.QuadFace{Int},
rect
)
uv_observable[] = map(x-> Vec2f(1f0 - x[2], 1f0 - x[1]), GeometryBasics.decompose_uv(rect))
end
xs = LinRange(extrema(x_in)..., npoints)
ys = LinRange(extrema(y_in)..., npoints)
poval = points_observable.val
pox = getproperty(poval, 1)
poy = getproperty(poval, 2)
# NOTE collect(tuple.(1:2, (1:2)'))[:] works fine
for (linear_ind, cartesian_ind) in enumerate(CartesianIndices((npoints, npoints)))
pox[linear_ind] = xs[cartesian_ind[1]]
poy[linear_ind] = ys[cartesian_ind[2]]
end
# apply the transformation to all points
# you can override this function to make it effective
# eg if transformation is known,
# OR is linearly separable,
# OR has a convenient C-api to transform arrays in place.
space === :data && apply_transform!(tfunc, poval, :data)
notify(points_observable)
if npoints != old_npoints
notify(faces_observable)
old_npoints = npoints
end
end
final_mesh = lift(points_observable, faces_observable, uv_observable; ignore_equal_values = true#=, priority = -100=#) do points, faces, uv
return GeometryBasics.normal_mesh(GeometryBasics.Mesh(GeometryBasics.meta(points; uv=uv), faces))
end
mesh!(plot, final_mesh; color = plot[3], space = :transformed, shading = false)
end
function Makie.data_limits(plot::MeshImage)
mini_maxi = Makie.extrema_nan.((plot[1][], plot[2][]))
mini = Vec3f(first.(mini_maxi)..., 0)
maxi = Vec3f(last.(mini_maxi)..., 0)
return Rect3f(mini, maxi .- mini)
end
@asinghvi17
Copy link
Author

download-9

julia> fig, ax1, plt1 = meshimage(rotr90(Makie.FileIO.load(Makie.assetpath("logo.png"))), shading = false, axis = (xscale = log10, yscale = log), npoints = 500)
tfunc = identity
tfunc = (log10, log)

julia> ax2, plt2 = image(fig[1, 2], 1:1473, 1:514, rotr90(Makie.FileIO.load(Makie.assetpath("logo.png"))), shading = false, axis = (xscale = log10, yscale = log), interpolate = false)
Makie.AxisPlot(Axis (1 plots), Image{Tuple{Vector{Float32}, Vector{Float32}, Matrix{ColorTypes.RGBA{Float32}}}})

julia> fig

julia> ax1.title = "meshimage"
"meshimage"

julia> ax2.title = "image"
"image"

julia> fig

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment