Examples

3-D Euler spiral (Clothoid)

using QuadGK
z = range(-5, 5, 200)
fx(z) = sin(z^2)
fy(z) = cos(z^2)
x = [quadgk(fx, 0, t)[1] for t in z]
y = [quadgk(fy, 0, t)[1] for t in z]
splot("""unset zeroaxis
         set tics border
         set xyplane at -5 
         set view 65,35
         set border 4095
         set xtics offset 0, -0.5""",
         x, y, z, "w l lc 'black' lw 1.5")

Waterfall

Inspired by this Julia Discourse discussion.

x = -15:0.1:15
y = 0:30
u1data = [exp(-(x-0.5*(y-15))^2) for x in x, y in y]
Zf = fill(0.0, length(x))
f = Figure()
Gaston.set!(f(1), """set zrange [0:1.5]
               set tics out
               set ytics border
               set xyplane at 0
               set view 45,17
               set xlabel 'ξ'
               set ylabel 't' offset -2.5
               set zlabel '|u|' offset -0.85
               set border 21
               set size 1, 1.3""")
for i in reverse(eachindex(y))
    Y = fill(y[i], length(x))
    Z = u1data[:,i]
    splot!(x, Y, Z, Zf, Z, "w zerrorfill lc 'black' fillstyle solid 1.0 fc 'white'")
end
f

Vector field

Inspired by this post in gnuplotting.org.

xr = 15  # samples in x direction
yr = 15  # samples in y direction

# parameters
x01 = 1
y01 = 0
q1  = 1
x02 = -1
y02 = 0
q2  = -1
scaling = 0.22

# equations
r(x,y)     = sqrt(x*x+y*y)
v1(x,y)    = q1/(r(x-x01,y-y01))
v2(x,y)    = q2/(r(x-x02,y-y02))
v(x,y)     = v1(x,y)+v2(x,y)
e1x(x,y)   = q1*x/r(x,y)^3
e1y(x,y)   = q1*y/r(x,y)^3
e2x(x,y)   = q2*x/r(x,y)^3
e2y(x,y)   = q2*y/r(x,y)^3
ex(x,y)    = e1x(x-x01,y-y01)+e2x(x-x02,y-y02)
ey(x,y)    = e1y(x-x01,y-y01)+e2y(x-x02,y-y02)
enorm(x,y) = sqrt(ex(x,y)^2 + ey(x,y)^2)
dx(x,y)    = scaling*ex(x,y)/enorm(x,y)
dy(x,y)    = scaling*ey(x,y)/enorm(x,y)

# initialize data vectors
d1 = zeros(xr*yr)
d2 = zeros(xr*yr)
d3 = zeros(xr*yr)
d4 = zeros(xr*yr)
d5 = zeros(xr*yr)

# calculations
for X in range(-2, 2, length=xr)
    for Y in range(-1.8, 1.8, length=yr)
         push!(d1, X-dx(X,Y)/2)
         push!(d2, Y-dy(X,Y)/2)
         push!(d3, dx(X,Y))
         push!(d4, dy(X,Y))
         push!(d5, v(X,Y))
    end
end

@plot({palette = :linear_kry_5_95_c72_n256}, :nocb,
      d1, d2, d3, d4, d5,
      "with vectors head size 0.08,20,60 filled lc palette")

Line color from palette

x = -2π:0.05:2π
@plot {palette = :ice} x sin.(3x) x "w l notitle lw 3 lc palette"

Variable marker size and color

x = 0:0.1:6π
splot("unset colorbox",
      x, cos.(x), sin.(x), x./10,
      "w p", "ps variable", "pt 7", "lc palette")

Filled curves

Filled transparent curves in 2-D

pois(λ, k) =^k)*exp(-λ)/factorial(k)
s = "set style fill transparent solid 0.4 noborder \nset title 'Poisson PMF'"
plot(s, 0:15, k -> pois(4, k), "w filledcu x1 lc 'cyan' t 'λ = 4'")
plot!(0:15, k -> pois(6, k), "w filledcu x1 lc 'blue' t 'λ = 6'")
plot!(0:15, k -> pois(8, k), "w filledcu x1 lc 'pink' t 'λ = 8'")

Fill between two curves

x = range(-10, 10, 100)
y1 = sin.(x) .- 0.5
y2 = sin.(x) .+ 0.5
plot(x, y1, y2, "w filledcu lc 'turquoise'")

Filled curve in 3-D

x = 0.:0.05:3;
y = 0.:0.05:3;
z = @. sin(x) * exp(-(x+y))
@gpkw splot(:labels, {style = "fill transparent solid 0.3", xyplane = "at 0", grid, lt = :Set1_5},
            x, y, z, z.*0, z,
            "w zerror t 'Data'")
splot!(x.*0, y, z, "w l lw 3")
splot!(x, y.*0, z, "w l lw 3")

Here, Set1_5 is a color scheme from ColorSchemes.jl.

Spheres

Wireframe

Θ = range(0, 2π, length = 100)
Φ = range(0, π, length = 20)
rd = 0.8
x = [rd*cos(θ)*sin(ϕ) for θ in Θ, ϕ in Φ]
y = [rd*sin(θ)*sin(ϕ) for θ in Θ, ϕ in Φ]
z = [rd*cos(ϕ)        for θ in Θ, ϕ in Φ]
@gpkw splot({view = "equal xyz", pm3d = "depthorder", hidden3d},
            x, y, z,
            {w = "l", lc = Q"turquoise"})

Surface

Θ = range(0, 2π, length = 100)
Φ = range(0, π, length = 100)
rd = 0.8
x = [rd*cos(θ)*sin(ϕ) for θ in Θ, ϕ in Φ]
y = [rd*sin(θ)*sin(ϕ) for θ in Θ, ϕ in Φ]
z = [rd*cos(ϕ)        for θ in Θ, ϕ in Φ]
@splot({style = "fill transparent solid 1",
        palette = :summer,
        view = "equal xyz",
        pm3d = "depthorder"},
       x, y, z,
       "w pm3d")

Torus

U = range(-π, π, length = 50)
V = range(-π, π, length = 100)
rd = 0.5
x = [1+cos(u)+rd*cos(u)*cos(v) for u in U, v in V]
y = [rd*sin(v)                 for u in U, v in V]
z = [sin(u)+rd*sin(u)*cos(v)   for u in U, v in V]
settings = """set object rectangle from screen 0,0 to screen 1,1 behind fillcolor 'black' fillstyle solid noborder
              set pm3d depthorder
              set style fill transparent solid 0.5
              set pm3d lighting primary 0.05 specular 0.2
              set view 108,2
              unset border
              set xyplane 0
              unset tics
              unset colorbox"""
@splot(settings, {palette = :cool}, x, y, z, "w pm3d")

Interlocking torii

U = LinRange(-π, pi, 100)
V = LinRange(-π, pi, 20)
x = [cos(u) + .5 * cos(u) * cos(v) for u in U, v in V]
y = [sin(u) + .5 * sin(u) * cos(v) for u in U, v in V]
z = [.5 * sin(v)                   for u in U, v in V]
@gpkw surf({palette = :dense,
        pm3d = "depthorder",
        colorbox = false,
        key = :false,
        tics = :false,
        border = 0,
        view = "60, 30, 1.5, 0.9",
        style = "fill transparent solid 0.7"},
       x', y', z')
x = [1 + cos(u) + .5 * cos(u) * cos(v) for u in U, v in V]
y = [.5 * sin(v)                       for u in U, v in V]
z = [sin(u) + .5 * sin(u) * cos(v)     for u in U, v in V]
surf!(x', y', z')

See more torus examples in the included Pluto notebook.

Contours

Surface with contours

x = y = -10:0.5:10
f1(x, y) = cos.(x./2).*sin.(y./2)
surf("""set hidden3d
        set contour base
        set cntrparam levels 10
        unset key""",
     x, y, f1,
     "lc 'turquoise'")

Egg-shaped contours

x = -1:0.05:1
y = -1.5:0.05:2
egg(x,y) = x^2 + y^2/(1.4 + y/5)^2
segg = [egg(x,y) for x in x, y in y]
@gpkw contour({palette = :cool,
               cntrparam = "levels incremental 0,0.01,1",
               auto = "fix",
               xrange = (-1.2, 1.2),
               yrange = (-1.5, 2),
               cbrange = (0, 1),
               xlabel = "'x'",
               ylabel = "'y'",
               size = "ratio -1"},
              x, y, segg',
              "w l lc palette",
              labels = false)

3D Tubes

Wireframe

U = range(0, 10π, length = 80)
V = range(0, 2π, length = 10)
x = [(1-0.1*cos(v))*cos(u)     for u in U, v in V]
y = [(1-0.1*cos(v))*sin(u)     for u in U, v in V]
z = [0.2*(sin(v) + u/1.7 - 10) for u in U, v in V]
settings = @gpkw {pm3d = "depthorder",
                  style = "fill transparent solid 1",
                  view = "equal xyz",
                  xyplane = -0.05,
                  palette = :ice,
                  xrange = (-1.2, 1.2),
                  yrange = (-1.2, 1.2),
                  colorbox = false,
                  hidden3d,
                  view = (70, 79)}
@splot(settings, x, y, z, "w l lc 'turquoise'")

Surface

@splot(settings, x, y, z, "w pm3d")

Animations

Lorenz attractor

This example is adapted from https://docs.makie.org/stable/#example. A few notes on the adaptation to Gaston:

  • The camera animation is achieved by changing the view setting every frame.
  • Each frame, Npoints coordinates are added to the plot. The animation consists of Nframes frames.
  • The coloring of the attractor is different than in most 3-D plots. Normally, the color of a point depends on its z coordinate. In this case, the z coordinate is not an amplitude, since the curve lives in a state space. Therefore, the color palette will be applied along the length of the curve, with points nearer the start given colors at the start of the palette. To achieve this, we use ColorSchemes.resample to create a new palette with the same number of colors as there are points in the curve. Then, Gaston.hex is used to convert these colors to decimal numbers. Finally, the colors are used as a fourth column of data and the line color is set to lc rgb variable.
using ColorSchemes

Base.@kwdef mutable struct Lorenz
    dt::Float64 = 0.01
    σ::Float64 = 10
    ρ::Float64 = 28
    β::Float64 = 8/3
    x::Float64 = 1
    y::Float64 = 1
    z::Float64 = 1
end

function step!(l::Lorenz)
    dx = l.σ * (l.y - l.x)
    dy = l.x * (l.ρ - l.z) - l.y
    dz = l.x * l.y - l.β * l.z
    l.x += l.dt * dx
    l.y += l.dt * dy
    l.z += l.dt * dz
    return (l.x, l.y, l.z)
end

Nframes = 120
Npoints = 50
attractor = Lorenz()
x = Float64[];
y = Float64[];
z = Float64[];

s = @gpkw {xrange = (-30, 30),
           yrange = (-30, 30),
           zrange = (0, 60),
           xtics = "offset -1.2,0",
           xtics = "add ('' -30, '' 30)",
           ytics = "offset 1.2,0",
           ytics = "add ('' -30, '' 30)",
           origin = "-0.1, -0.1",
           size = "1.2, 1.2",
           object = "rectangle from screen 0,0 to screen 1,1 fillcolor 'black' behind",
           border = "back lc rgb '#eeeeee' lt 1 lw 1.5",
           view = "equal xyz",
           xyplane = "at 0"}

f = splot(s, 1, 1, 1)

for i = 1:Nframes
    for j = 1:Npoints
        step!(attractor)
        push!(x, attractor.x);
        push!(y, attractor.y);
        push!(z, attractor.z)
    end
    cs = resample(ColorSchemes.inferno, length(x))
    splot(f[i],
          s, "set view 70, $(45 + 17 * sin(2pi * i / Nframes))",
          x, y, z, Gaston.hex(cs),
          "w l notitle lc rgb variable")
end
save(f, filename = "lorenz.webp", term = "webp animate loop 0 size 640,480")

3-D spiral

z = 0:0.1:10pi
step = 5
cc = "lc 'turquoise' lw 3 notitle"
ac = @gpkw {zrange = (0,30), xrange = (-1.2, 1.2), yrange = (-1.2, 1.2)}
F = scatter3(ac, :notics, :labels, cos.(z[1:step]), sin.(z[1:step]), z[1:step], cc)
frame = Figure()
for i = 2:60
    frame = scatter3(ac, :notics, :labels, cos.(z[1:i*step]), sin.(z[1:i*step]), z[1:i*step], cc)
    push!(F, frame)
end
for i = 60:-1:1
    frame = scatter3(ac, :notics, :labels, cos.(z[1:i*step]), sin.(z[1:i*step]), z[1:i*step], cc)
    push!(F, frame)
end
save(F, filename = "3dspiral.webp", term = "webp animate loop 0 size 640,480")

Splash

x = y = -15:0.4:15
ac = @gpkw {title = Q"Splash",
            palette  = :cool,
            cbrange  = (-0.2, 1),
            zrange   = (-0.3, 1),
            hidden3d = true}
F = splot(ac, x, y, (x, y) -> sin(sqrt(x*x+y*y))/sqrt(x*x+y*y), "w pm3d")
frame = Figure()
for i = 1:-0.1:-1
    frame = splot(ac, x, y, (x,y) -> i*sin(sqrt(x*x+y*y))/sqrt(x*x+y*y), "w pm3d");
    push!(F, frame)
end
for i = -0.9:0.1:1
    frame = splot(ac, x, y, (x,y) -> i*sin(sqrt(x*x+y*y))/sqrt(x*x+y*y), "w pm3d");
    push!(F, frame)
end
save(F, filename = "3dsplash.webp", term = "webp animate loop 0 size 640,480")