Skip to content
This repository was archived by the owner on Jul 13, 2021. It is now read-only.

Commit bec40fe

Browse files
committed
extend streamplot to 3d
1 parent dd2fa78 commit bec40fe

File tree

1 file changed

+50
-39
lines changed

1 file changed

+50
-39
lines changed

src/basic_recipes/basic_recipes.jl

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,80 +1000,90 @@ run_example("streamplot")
10001000
## Theme
10011001
$(ATTRIBUTES)
10021002
"""
1003-
@recipe(StreamPlot, f, xrange, yrange) do scene
1003+
@recipe(StreamPlot, f, limits) do scene
10041004
Theme(
10051005
stepsize = 0.01,
1006-
resolution = (32, 32),
1006+
gridsize = (32, 32, 32),
10071007
colormap = theme(scene, :colormap),
10081008
arrow_size = 0.03
10091009
)
10101010
end
10111011

1012-
function convert_arguments(::Type{<: StreamPlot}, f::Function, x, y)
1013-
(f, x, y)
1012+
function convert_arguments(::Type{<: StreamPlot}, f::Function, xrange, yrange)
1013+
xmin, xmax = extrema(xrange)
1014+
ymin, ymax = extrema(yrange)
1015+
return (f, Rect(xmin, ymin, xmax - xmin, ymax - ymin))
1016+
end
1017+
function convert_arguments(::Type{<: StreamPlot}, f::Function, xrange, yrange, zrange)
1018+
xmin, xmax = extrema(xrange)
1019+
ymin, ymax = extrema(yrange)
1020+
zmin, zmax = extrema(yrange)
1021+
mini = Vec3f0(xmin, ymin, zmin)
1022+
maxi = Vec3f0(xmax, ymax, zmax)
1023+
return (f, Rect(mini, maxi .- mini))
1024+
end
1025+
1026+
function convert_arguments(::Type{<: StreamPlot}, f::Function, limits::Rect)
1027+
return (f, limits)
10141028
end
10151029

10161030
"""
10171031
Code adapted from an example implementation by Moritz Schauer (@mschauer)
10181032
from https://github.com/JuliaPlots/Makie.jl/issues/355#issuecomment-504449775
10191033
"""
1020-
function streamplot_impl(CallType, f, xrange, yrange, resolution, stepsize)
1021-
mask = trues(resolution)
1022-
arrow_pos = Point2f0[]
1023-
arrow_dir = Vec2f0[]
1024-
line_points = Point2f0[]
1034+
function streamplot_impl(CallType, f, limits::Rect{N, T}, resolutionND, stepsize) where {N, T}
1035+
resolution = to_ndim(Vec{N, Int}, resolutionND, last(resolutionND))
1036+
mask = trues(resolution...)
1037+
arrow_pos = Point{N, Float32}[]
1038+
arrow_dir = Vec{N, Float32}[]
1039+
line_points = Point{N, Float32}[]
10251040
colors = Float64[]
10261041
line_colors = Float64[]
1027-
xmin, xmax = extrema(xrange)
1028-
ymin, ymax = extrema(yrange)
1029-
limits = Rect(xmin, ymin, xmax - xmin, ymax - ymin)
1030-
dt = Point2f0(to2tuple(stepsize))
1031-
r = (
1032-
LinRange(xmin, xmax, resolution[1] + 1),
1033-
LinRange(ymin, ymax, resolution[2] + 1)
1034-
)
1042+
dt = Point{N, Float32}(stepsize)
1043+
mini, maxi = minimum(limits), maximum(limits)
1044+
r = ntuple(N) do i
1045+
LinRange(mini[i], maxi[i], resolution[i] + 1)
1046+
end
10351047
apply_f(x0, P) = if P <: Point
10361048
f(x0)
10371049
else
1038-
f(x0[1], x0[2])
1050+
f(x0...)
10391051
end
10401052
for c in CartesianIndices(mask)
1041-
i0, j0 = Tuple(c)
1042-
x0 = Point2(
1043-
first(r[1]) + (i0 - 0.5) * step(r[1]),
1044-
first(r[2]) + (j0 - 0.5) * step(r[1])
1045-
)
1053+
x0 = Point(ntuple(N) do i
1054+
first(r[i]) + (c[i] - 0.5) * step(r[i])
1055+
end)
10461056
if mask[c]
10471057
point = apply_f(x0, CallType)
1048-
if !(point isa Point2)
1049-
error("Function passed to streamplot must return Point2")
1058+
if !(point isa Point2 || point isa Point3)
1059+
error("Function passed to streamplot must return Point2 or Point3")
10501060
end
10511061
pnorm = norm(point)
10521062
push!(arrow_pos, x0)
1053-
push!(arrow_dir, point / pnorm)
1063+
push!(arrow_dir, point ./ pnorm)
10541064
push!(colors, pnorm)
10551065
mask[c] == false
10561066
for d in (-1, 1)
10571067
n_linepoints = 1
10581068
x = x0
1059-
push!(line_points, Point2f0(NaN), x)
1069+
push!(line_points, Point{N, Float32}(NaN), x)
10601070
push!(line_colors, 0.0, pnorm)
1061-
i0, j0 = Tuple(c)
10621071
while x in limits && n_linepoints < 500
10631072
point = apply_f(x, CallType)
10641073
pnorm = norm(point)
10651074
x = x .+ d .* dt .* point ./ pnorm
10661075
if !(x in limits)
10671076
break
10681077
end
1069-
i = searchsortedlast(r[1], x[1])
1070-
j = searchsortedlast(r[2], x[2])
1071-
if (i, j) != (i0, j0)
1072-
if !mask[i,j]
1078+
# WHAT? Why does point behave different from tuple in this
1079+
# broadcast
1080+
idx = CartesianIndex(searchsortedlast.(r, Tuple(x)))
1081+
if idx != c
1082+
if !mask[idx]
10731083
break
10741084
end
1075-
mask[i, j] = false
1076-
i0, j0 = i, j
1085+
mask[idx] = false
1086+
c = idx
10771087
end
10781088
push!(line_points, x)
10791089
push!(line_colors, pnorm)
@@ -1092,21 +1102,22 @@ function streamplot_impl(CallType, f, xrange, yrange, resolution, stepsize)
10921102
end
10931103

10941104
function plot!(p::StreamPlot)
1095-
data = lift(p.f, p.xrange, p.yrange, p.resolution, p.stepsize) do f, xrange, yrange, resolution, stepsize
1096-
P = if applicable(f, Point2f0(0))
1105+
data = lift(p.f, p.limits, p.gridsize, p.stepsize) do f, limits, resolution, stepsize
1106+
P = if applicable(f, Point2f0(0)) || applicable(f, Point3f0(0))
10971107
Point
10981108
else
10991109
Number
11001110
end
1101-
streamplot_impl(P, f, xrange, yrange, resolution, stepsize)
1111+
streamplot_impl(P, f, limits, resolution, stepsize)
11021112
end
11031113
lines!(
11041114
p,
11051115
lift(x->x[3], data), color = lift(last, data), colormap = p.colormap
11061116
)
1107-
scatter!(
1117+
N = ndims(p.limits[])
1118+
scatterfun(N)(
11081119
p,
1109-
lift(first, data), markersize = p.arrow_size, marker = '',
1120+
lift(first, data), markersize = p.arrow_size, marker = arrow_head(N, automatic),
11101121
color = lift(x-> x[4], data), rotations = lift(x-> x[2], data),
11111122
colormap = p.colormap,
11121123
)

0 commit comments

Comments
 (0)