Powered by AppSignal & Oban Pro

Basic Streamtube Plot

21_basic_streamtube_plot.livemd

Basic Streamtube Plot

Mix.install([
  {:plotly_ex, "~> 0.3"},
  {:kino, "~> 0.18"}
])

Section

Streamtube.new(x:, y:, z:, u:, v:, w:) traces flow lines through a 3D vector field. The x/y/z arrays define a regular grid; u/v/w are the corresponding vector components. Plotly automatically seeds tube starting positions. sizeref: scales tube radius; colorscale: encodes flow magnitude (speed).

alias Plotly.{Figure, Streamtube}

# Regular grid: x,y each [-4..4 step 1], z [0..3 step 1]
xs_vals = Enum.to_list(-4..4)
ys_vals = Enum.to_list(-4..4)
zs_vals = Enum.to_list(0..3)

grid = for x <- xs_vals, y <- ys_vals, z <- zs_vals, do: {x, y, z}

xs = Enum.map(grid, &elem(&1, 0))
ys = Enum.map(grid, &elem(&1, 1))
zs = Enum.map(grid, &elem(&1, 2))

# Vortex field with upward drift: u = -y/(r²), v = x/(r²), w = 0.3
us = Enum.map(grid, fn {x, y, _z} ->
  r2 = x * x + y * y
  if r2 < 0.01, do: 0.0, else: -y / r2
end)

vs = Enum.map(grid, fn {x, y, _z} ->
  r2 = x * x + y * y
  if r2 < 0.01, do: 0.0, else: x / r2
end)

ws = List.duplicate(0.3, length(grid))

Figure.new()
|> Figure.add_trace(
  Streamtube.new(
    x: xs, y: ys, z: zs,
    u: us, v: vs, w: ws,
    colorscale: "Portland",
    sizeref: 0.5,
    showscale: true
  )
)
|> Figure.update_layout(
  title: "Basic Streamtube — Rotational Field",
  scene: %{
    xaxis: %{title: "X"},
    yaxis: %{title: "Y"},
    zaxis: %{title: "Z"},
    camera: %{eye: %{x: 1.5, y: 1.5, z: 0.5}}
  }
)
|> Plotly.show()