Bars3D & Trail3D
Create live categorical 3D bar charts and streamed two-series ribbon paths with in-place updates, labels, automatic normalization, projections, and cages.
Categorical 3D bars
var p3d.Scene scene = p3d.newScene()
var p3d.Mesh bars = na
array<float> values = array.from(
volume - volume[1], volume[1] - volume[2], volume[2] - volume[3])
array<string> names = array.from("Current", "Previous", "Older")
if barstate.isfirst
bars := p3d.bars3D(values, 30.0, 30.0, 10.0,
color.blue, color.red, 200.0).barLabels(names)
scene.add(bars)
scene.camera.orbit(215.0, 25.0, 360.0)
if barstate.islast
bars.updateBars(values)
p3d.render(scene, lighting = true)bars3D(values, barWidth, barDepth, spacing, lowCol, highCol, maxHeight) maps height and color, then packs the bars into one mesh while preserving per-bar depth groups.
Updates and labels
- Call
barLabels(names)once at build time. - Call
updateBars(values)to refresh heights, colors, and value-label positions. - Omit custom value labels for automatic
str.tostring()formatting. - Pass custom value strings to
updateBars()only when needed.
Negative values
Negative values extrude below the base plane. Pine3D reverses face winding automatically, so signed PnL, returns, momentum, or delta values render correctly.
Streamed oscillator paths
var p3d.Scene scene = p3d.newScene()
var p3d.Trail3D trail = na
if barstate.isfirst
trail := p3d.trail3D(220.0, 200, color.yellow)
.cage(true)
.axisLabels("RSI", "MFI", color.white)
scene.add(trail)
scene.camera.orbit(215.0, 20.0, 360.0)
float rsiValue = ta.rsi(close, 14)
float mfiValue = ta.mfi(hlc3, 14)
if barstate.isconfirmed
trail.pushSample(rsiValue, mfiValue)
p3d.render(scene)Trail3D visualizes two correlated values through time. Samples are normalized against rolling window minima and maxima, so the ribbon fills its cage without manual scaling.
Trail behavior
| Control | Behavior |
|---|---|
capacity | Rolling sample capacity, clamped internally to 300. |
minSamples | Sample count at which the cage reaches full cube width. |
pushSample(u, v) | Adds one confirmed two-series observation. |
axisLabels() | Adds axis names. |
cage() | Shows or hides the bounding cage. |
moveTo() | Moves the complete trail assembly. |
Use cases
RSI vs. MFI, momentum vs. volatility, phase-space portraits, Lissajous paths, and any evolving pair where the relationship matters more than either series alone.