-
-
Notifications
You must be signed in to change notification settings - Fork 317
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
Useful for visualizing on a sphere? #72
Comments
Can you give an example? how i imagine it, mapping something to the surface of a sphere, it should be possible ;) |
For the density plot, @MikaelSlevinsky has some nice examples he did using PlotlyJS (https://arxiv.org/pdf/1801.04902.pdf) For vector fields on a sphere, the simplest is just to plot a lot of arrows, a bit nicer would be a stream plot but where the stream lines are on the sphere, with colour used to indicate magnitude. (In my case the vector fields are restricted to the tangent space.) |
Is the code somewhere, so I can get an idea of the interface? |
Yes: https://github.com/JuliaApproximation/SpectralTimeStepping.jl/blob/master/NonlocalSphere/plot_Allen_Cahn.jl s = surface(x=x, y=y, z=z, colorscale = "Viridis", surfacecolor = F, cmin = -1.0, cmax = 1.0, showscale = false) where |
For the arrow plot case, the interface should match quiver(pts, quiver=df) where |
Do you expect the arrows do bend according to the surface of the sphere? |
and will the points / vectors be 2d, and you will indicate with a keyword argument, that they should be on a sphere? |
I "expect" them to not because that's hard to implement, but I wish they would. |
No, 3D. (In principle I'd expect the interface to work for other surfaces too.) |
I will make a pr to make it a bit more user friendly, since right now, you need to map F to an array of colors yourself... But that actually fits well with implementing I just recently added a prototype for quiver, I'll take a look how far it is ;) |
Do you also have a piece of code that gives me |
For f = (x,y,z) -> x*exp(cos(y)*z)
∇f = (x,y,z) -> SVector(exp(cos(y)*z), -sin(y)*z*x*exp(cos(y)*z), x*cos(y)*exp(cos(y)*z))
∇ˢf = (x,y,z) -> ∇f(x,y,z) - SVector(x,y,z)*dot(SVector(x,y,z), ∇f(x,y,z))
θ = [0;(0.5:n-0.5)/n;1]
φ = [(0:2n-2)*2/(2n-1);2]
x = [cospi(φ)*sinpi(θ) for θ in θ, φ in φ]
y = [sinpi(φ)*sinpi(θ) for θ in θ, φ in φ]
z = [cospi(θ) for θ in θ, φ in φ]
pts = vec(SVector.(x, y, z))
∇ˢF = vec(∇ˢf.(x, y, z))
quiver(pts, df=∇ˢF) |
Perfect! (I assume all the arrows are tangential to the sphere... hard to tell without being able to rotate and zoom.) If the arrows could wrap around the sphere it would be even nicer... but I guess that will take some work (and may be too slow in practice)... |
Slowness shouldn't be a problem, but someone will need to put some time into emiting the correct line paths... I'm guessing one would need to project the 3D vectors on the surface of the sphere? They're not really tangential, since |
It should be true that This means the arrow connecting Note I'm assuming this is a unit sphere, so that |
To clarify further: |
ok! using GeometryTypes, Colors, Makie
function arrows(
parent, points::AbstractVector{Pair{Point3f0, Point3f0}};
arrowhead = Pyramid(Point3f0(0, 0, -0.5), 1f0, 1f0), arrowtail = nothing, arrowsize = 0.3,
linecolor = :black, arrowcolor = linecolor, linewidth = 1,
linestyle = nothing, scale = Vec3f0(1)
)
linesegment(parent, points, color = linecolor, linewidth = linewidth, linestyle = linestyle, scale = scale)
rotations = map(points) do p
p1, p2 = p
dir = p2 .- p1
GLVisualize.rotation_between(Vec3f0(0, 0, 1), Vec3f0(dir))
end
meshscatter(
last.(points), marker = arrowhead, markersize = arrowsize, color = arrowcolor,
rotations = rotations, scale = scale
)
end
n = 20
f = (x,y,z) -> x*exp(cos(y)*z)
∇f = (x,y,z) -> Point3f0(exp(cos(y)*z), -sin(y)*z*x*exp(cos(y)*z), x*cos(y)*exp(cos(y)*z))
∇ˢf = (x,y,z) -> ∇f(x,y,z) - Point3f0(x,y,z)*dot(Point3f0(x,y,z), ∇f(x,y,z))
θ = [0;(0.5:n-0.5)/n;1]
φ = [(0:2n-2)*2/(2n-1);2]
x = [cospi(φ)*sinpi(θ) for θ in θ, φ in φ]
y = [sinpi(φ)*sinpi(θ) for θ in θ, φ in φ]
z = [cospi(θ) for θ in θ, φ in φ]
pts = vec(Point3f0.(x, y, z))
∇ˢF = vec(∇ˢf.(x, y, z))
arr = map(pts, ∇ˢF) do p, dir
p => p .+ (dir .* 0.2f0)
end
scene = Scene();
surface(scene , x, y, z)
arrows(scene, arr, arrowsize = 0.03, linecolor = :gray, linewidth = 3) |
Cool! That's good enough for me. |
PS Is it easy to create animated GIFs like on the README? I'll send you some nice movies of evolving functions / vector fields on the sphere once we get it working. |
This is really cool! Care to give ballpark timings? (PlotlyJS worked great up to about 512 x 1024 grids) More of a transforms-related issue, but the grids probably shouldn't cluster at the poles for a quiver plot. |
Is there Julia code for generating uniformly distributed points on the sphere? |
There's Matlab code for minimum energy or maximum determinant nodes https://github.com/gradywright/spherepts/blob/master/docs/NodesGridsMeshesOnSphere.pdf but perhaps HEALPix would be better transforms-wise. |
Oh wait, the min energy and max det use huge look-up tables. https://github.com/gradywright/spherepts/tree/master/nodes |
It’s MIT license so we can just copy the tables |
You can generate uniformly distributed points oh the unit sphere with something like this:
|
I think random points are not a good idea since the arrows will be hard to read. Also you’ll have clustering of points. PS here’s another random points on the sphere which is coordinate independent: qr(randn(3,3))[1][:,1] |
So this works now on master: using GeometryTypes, Colors, Makie
# the surface
scene = Scene()
s = Makie.surface(x, y, z, image = F, colormap = :viridis, colornorm = (-1.0, 1.0))
s[:image] = F .+ 0.1 # update image
# the quiver
n = 20
f = (x,y,z) -> x*exp(cos(y)*z)
∇f = (x,y,z) -> Point3f0(exp(cos(y)*z), -sin(y)*z*x*exp(cos(y)*z), x*cos(y)*exp(cos(y)*z))
∇ˢf = (x,y,z) -> ∇f(x,y,z) - Point3f0(x,y,z)*dot(Point3f0(x,y,z), ∇f(x,y,z))
θ = [0;(0.5:n-0.5)/n;1]
φ = [(0:2n-2)*2/(2n-1);2]
x = [cospi(φ)*sinpi(θ) for θ in θ, φ in φ]
y = [sinpi(φ)*sinpi(θ) for θ in θ, φ in φ]
z = [cospi(θ) for θ in θ, φ in φ]
pts = vec(Point3f0.(x, y, z))
∇ˢF = vec(∇ˢf.(x, y, z))
scene = Scene();
surface(x, y, z)
arr = Makie.arrows(
scene,
pts, ∇ˢF,
arrowsize = 0.03, linecolor = (:gray, 0.7), linewidth = 3
)
arr[:lengthscale] = 0.3
center!(scene)
# record a video
io = VideoStream(scene, joinpath(homedir(), "Desktop"), "test")
arr[:directions] = ∇ˢF
for i = 1:100
arr[:directions] = (to_value(arr, :directions) .+ (rand(Float32, length(∇ˢF)) * 0.05))
recordframe!(io)
sleep(1/30)
end
finish(io, "mp4") # could also be gif, webm or mkv
|
Please let me know about any problems in a new issue ;) |
The code work, and I checked and all the arrows are in the tangent plane, yay! It's a bit clearer when we normalize and scale: scene = Scene(); surface(scene , x, y, z); arr = Makie.arrows(
scene,
pts, normalize.(∇ˢF)./15,
arrowsize = 0.03, linecolor = (:gray, 0.7), linewidth = 3
) Some questions:
|
The code for the density map is above: using GeometryTypes, Colors, Makie
# the surface
scene = Scene()
s = Makie.surface(x, y, z, image = F, colormap = :viridis, colornorm = (-1.0, 1.0))
s[:image] = F .+ 0.1 # update image |
Forgot one question: \3. How do you clear the scene? |
I usually just make a new scene! In the future, I plan to offer a more Plots.jl like api, where plot (without !) will just create a new scene! |
Btw, there are arguments for normalization and scaling: |
2.) For the arrows on the sphere, does the fact that they are (circular) arcs help? I.e., is there an arc analogue to linesegment (used to make the arrows)? We can definitely just draw lines with a high enough granularity, to look like they're bend along the sphere ;) Could you supply me with an example code, using |
At that point we might as well do streamlines. Here's a first attempt (it works but is dreadfully slow, and needs a better point distribution to look good): function sphere_streamline(∇ˢf, pt, h=0.01f0, n=5)
ret = [pt]
df = normalize(∇ˢf(pt...))
push!(ret, normalize(pt .+ h*df))
for k=2:n
cur_pt = last(ret)
push!(ret, cur_pt)
df = normalize(∇ˢf(cur_pt...))
push!(ret, normalize(cur_pt .+ h*df))
end
ret
end
function plotstreamline(scene, ret)
linecolor = :gray
linewidth = 3
scale = Vec3f0(1)
linesegment(scene, ret, color = linecolor, linewidth = linewidth, linestyle = nothing, scale = scale)
Makie.arrows(scene, [ret[end-1]], [ret[end]-ret[end-1]], arrowsize = 0.01, linecolor = (:gray, 0.7), linewidth = 3)
end
lns = sphere_streamline.(∇ˢf, pts, 0.03f0)
scene = Scene();
for ln in lns
plotstreamline(scene, ln)
end |
ok, let's make it fast :) |
Wow, it's slower than I imagined ;) Good news is, it should be easy to fix! |
I didn't expect it to be fast, but there must also be a performance bug, since it seems to become at least quadratically slower |
This seems to be reasonably fast: using Makie, GeometryTypes
function sphere_streamline(linebuffer, ∇ˢf, pt, h=0.01f0, n=5)
push!(linebuffer, pt)
df = normalize(∇ˢf(pt[1], pt[2], pt[3]))
push!(linebuffer, normalize(pt .+ h*df))
for k=2:n
cur_pt = last(linebuffer)
push!(linebuffer, cur_pt)
df = normalize(∇ˢf(cur_pt...))
push!(linebuffer, normalize(cur_pt .+ h*df))
end
return
end
function streamlines(
scene, ∇ˢf, pts::AbstractVector{T};
h=0.01f0, n=5, color = :black, linewidth = 1
) where T
linebuffer = T[]
sub = Scene(
scene,
h = h, n = 5, color = :black, linewidth = 1
)
lines = lift_node(to_node(∇ˢf), to_node(pts), sub[:h], sub[:n]) do ∇ˢf, pts, h, n
empty!(linebuffer)
for point in pts
sphere_streamline(linebuffer, ∇ˢf, point, h, n)
end
linebuffer
end
linesegment(sub, lines, color = sub[:color], linewidth = sub[:linewidth])
sub
end
# needs to be in a function for ∇ˢf to be fast and inferable
function test()
n = 20
f = (x,y,z) -> x*exp(cos(y)*z)
∇f = (x,y,z) -> Point3f0(exp(cos(y)*z), -sin(y)*z*x*exp(cos(y)*z), x*cos(y)*exp(cos(y)*z))
∇ˢf = (x,y,z) -> ∇f(x,y,z) - Point3f0(x,y,z)*dot(Point3f0(x,y,z), ∇f(x,y,z))
θ = [0;(0.5:n-0.5)/n;1]
φ = [(0:2n-2)*2/(2n-1);2]
x = [cospi(φ)*sinpi(θ) for θ in θ, φ in φ]
y = [sinpi(φ)*sinpi(θ) for θ in θ, φ in φ]
z = [cospi(θ) for θ in θ, φ in φ]
pts = vec(Point3f0.(x, y, z))
scene = Scene()
lns = streamlines(scene, ∇ˢf, pts)
# those can be changed interactively:
lns[:color] = :black
lns[:h] = 0.06
lns[:linewidth] = 1.0
for i = linspace(0.01, 0.1, 100)
lns[:h] = i
yield()
end
end
test() |
Hi Hi, I'm trying to play some with the arrows, and I'm using the code Simon put in there,
But I am getting the following error:
I have both Makie and AbstractPlotting checked out. What am I missing? Loic |
In case someone needs an updates version: using GLMakie, Makie, LinearAlgebra
n = 10
θ = [0;(0.5:n-0.5)/n;1]
φ = [(0:2n-2)*2/(2n-1);2]
x = [cospi(φ)*sinpi(θ) for θ in θ, φ in φ]
y = [sinpi(φ)*sinpi(θ) for θ in θ, φ in φ]
z = [cospi(θ) for θ in θ, φ in φ]
f2(x, y, z) = cross([x, y, z], [1.0, 0.0, 0.0])
tans = f2.(vec(x), vec(y), vec(z))
u = [a[1] for a in tans]
v = [a[2] for a in tans]
w = [a[3] for a in tans]
scene = Scene();
surface(x, y, z)
arr = Makie.arrows!(
vec(x), vec(y), vec(z), u, v, w;
arrowsize = 0.1, linecolor = (:gray, 0.7), linewidth = 0.02, lengthscale = 0.1
) |
I'm looking for a "good" method to do visualizations on the sphere, including heat maps and vector fields.
Is this possible in Makie?
The text was updated successfully, but these errors were encountered: