Fill a curve in 3D

I want to plot a complex impedance value as a function of frequency.

This results in a line in 3D(real, imaginary, frequency).
How do I fill the area between the line and the z=0 coordinate in 3D.
I tried fillrange and ribbon, but I could not get it to work in Plots.jl

Something like this can be found on page 295:

1 Like

In PGFPlotsX, I would suggest this example as a starting point:

https://kristofferc.github.io/PGFPlotsX.jl/dev/examples/gallery.html#D-Waterfall-1

I am not aware of something similar for Plots.jl, but see the original topic:

1 Like

Don’t know if it applies, but in Gnuplot.jl is quite easy:

x = 0.:0.05:3;
y = 0.:0.05:3;
z = @. sin(x) * exp(-(x+y))

using Gnuplot
@gsp xlab="X" ylab="Y" linetypes(:Set1_5, lw=3) :-
@gsp :- "set style fill transparent solid 0.3" :-
@gsp :- "set xyplane at 0" "set grid" :-
@gsp :- x y z z.*0 z "w zerror t 'Data'" :-
@gsp :- x.*0 y z "w l notit" :-  # projection on x=0
@gsp :- x y.*0 z "w l notit"     # projection on y=0
save(term="pngcairo", output="output.png")

output

13 Likes

Nice, what about increase transparency in the shaded area (area_gradient)? As in here: Area Chart with Gradient | Vega-Lite

Is this possible with a plots backend? I want to draw in 3D space a 2D polygon so that the disk or ellipse can be rotated. All I am able to get is an unfilled line. Same commands create a filled shape if 2D points are used. Where do I need to look?

2 Likes

Right, how to do this with Plots.jl? This can be don in 2D using plot(x, y, fillrange=0), but this doesn’t work in 3D.

2 Likes

I know this is kind of old, but I implemented this functionality using mesh3d:

function fill_between3d(x1, y1, z1, x2, y2, z2)
    n = length(x1)
    x = [x1; x2]
    y = [y1; y2]
    z = [z1; z2]
    bottom_edge = 0:n-2
    top_edge = 1:n-1
    connect1 = [[i for i ∈ bottom_edge]; [2n-i for i ∈ top_edge]]
    connect2 = [[i+1 for i ∈ bottom_edge]; [2n-i-1 for i ∈ top_edge]]
    connect3 = [[n+i+1 for i ∈ bottom_edge]; [n-i-1 for i ∈ top_edge]]
    connect = (connect1, connect2, connect3)
    return (x, y, z, connect)
end

Then with GR backend:

x = 0.:0.05:3;
y = 0.:0.05:3;
z = @. sin(x) * exp(-(x+y))

gr()
plot3d(zero(x), y, z; linecolor=:blue, linewidth=2, label=nothing)
plot3d!(x, y, z; linecolor=:red, linewidth=2, label="data")
meshx, meshy, meshz, connections = fill_between3d(x, y, z, x, y, zero(z))
# linewidth=0. is only necessary for gr() backend it seems...
mesh3d!(meshx, meshy, meshz, connections=connections, color=:red, alpha=0.4, linewidth=0., label=nothing)
# for pgfplotsx():
# mesh3d!(meshx, meshy, meshz, connections=connections, color=:red, fill_opacity=0.4, draw_opacity=0., label=nothing)
plot3d!(x, zero(y), z; linecolor=:green, linewidth=2, label=nothing)

and with some other backends:

plotlyjs():

pythonplot():

pgfplotsx():
This required a couple extra parameters in the call to mesh3d to enable the transparency and hide the mesh grid-lines

mesh3d!(meshx, meshy, meshz, connections=connections, fill_opacity=0.4, draw_opacity=0., color=:red)

3 Likes

Here’s a solution using Makie:

using GLMakie

x = 0:0.05:3;
y = 0:0.05:3;
z = @. sin(x) * exp(-(x+y))

fig = Figure(size=(400, 300))
ax = Axis3(fig[1,1], limits=((0,3), (0,3), (0,0.2)),
           azimuth=0.3, elevation=0.4)
lines!(Point3f.(x, 0, z))
lines!(Point3f.(0, y, z))
band!(Point3f.(x, y, 0), Point3f.(x, y, z), color=(:red, 0.5))
fig

image

I used GLMakie because CairoMakie handles the transparency poorly for some reason:

3 Likes

This is a great thread, and I’m happy to add a different approach using Plots.jl and parametric surfaces, which has the potential to be extended:

Plots_plotlyjs_fill_3D_curve

Plots' plotlyjs() code
using Plots; plotlyjs()

f(x, y) = sin(x) * exp(-(x+y))

x = y = range(0, 3, 500)
z = @. f(x, y)

θ = atand(y[end], x[end])
u, v = hypot.(x, y), range(0, maximum(z), 200)
X = [u * cosd(θ) for u in u, _ in v]
Y = [u * sind(θ) for u in u, _ in v]
Z = [0 ≤ v ≤ f(u*cosd(θ), u*sind(θ)) ? v : NaN  for u in u, v in v]

Plots.surface(X, Y, Z, c=:reds, fillalpha=0.7, colorbar=false)
Plots.plot!(x, y, z, c=:red, lw=3)
Plots.plot!(x, 0*y, z, c=:green, lw=2)
Plots.plot!(0*x, y, z, c=:blue, lw=2)
1 Like