visualdynamics.viz.waterfall¶
waterfall
¶
The 3-D reading of a data object: its records as a waterfall.
Many channels on one 2-D axis hide each other exactly where it matters most — resonances line up, and the tenth curve lands on the first nine. The waterfall spreads the records along a depth axis instead: one polyline per record at its own station, wearing the same label the grids and the legend use, coloured by level so a peak reads across the whole set at once.
numpy and PyVista only — no Qt, like the rest of viz. Two decisions
carry the size ceiling:
- Points are peak-decimated per record before VTK sees them
(
decimate.peak_decimate, the same reading the 2-D plot and the report use), so a million-sample time history arrives as the few thousand points that keep every peak. - The whole set of curves is one PolyData under one actor, whatever the record count — per-record actors are what make large scenes slow, which the animation learned at 202k nodes.
The scene is drawn on a fixed stage (STAGE) rather than in data
coordinates — a PSD spans decades while its frequency axis spans
kilohertz, and raw coordinates would draw a needle — and the axes are
relabelled with the real ranges (axes_ranges), the vertical one in
log10 for data that reads on a log axis, which the axis title says.
Functions:
| Name | Description |
|---|---|
waterfall_groups |
Every (dimension, hint) group among the records asked for. |
waterfall_group |
Which of the asked-for records draw together, and which wait. |
records_per_page |
How many records fit in one scene, from what each one costs. |
quantity_label |
The word the quantity box shows for one group. |
waterfall_arrays |
The waterfall's numbers, before any VTK touches them. |
nice_axis |
(low, high, label count) — the range widened to round ticks, |
stage_curves |
The waterfall's curves placed on the unit stage. |
add_waterfall |
Draw the waterfall into a plotter: one mesh, labels, axes. |
waterfall_scene |
Build (or add to) a PyVista plotter showing the waterfall. |
place_camera |
The waterfall's home view, placed — never re-placed on a redraw. |
stage_basis |
(right, up) — the stage's home view, as a parallel basis. |
ribbon |
A translucent surface between two curves over one path on the |
finish_scene |
End a stage's headless call the one way every stage does: render |
plot_waterfall |
Show the waterfall interactively, or render it to |
Classes¶
Functions:¶
waterfall_groups
¶
waterfall_groups(
data: DataArray, records: Sequence[int] | None = None
) -> list[tuple[tuple[str, str | None], list[int]]]
Every (dimension, hint) group among the records asked for.
In the object's own order — what the quantity box offers, and what
waterfall_group chooses from. Same grouping key as the 2-D plot,
minus the per-plot axis preferences that stacked axes need and one
floor does not.
Source code in src/visualdynamics/viz/waterfall.py
waterfall_group
¶
waterfall_group(
data: DataArray,
records: Sequence[int] | None = None,
quantity: tuple[str, str | None] | None = None,
) -> tuple[list[int], list[int]]
Which of the asked-for records draw together, and which wait.
One vertical axis cannot honestly hold accelerations and volts at
once. The 2-D plot answers with one stacked axis per quantity; a
scene has one floor, so the waterfall draws one (dimension, hint)
group — the one quantity names, else the largest (the earliest
on a tie) — and reports the rest for the quantity box to offer.
A quantity no longer present falls back to the largest rather
than refusing: it is a sticky view choice outliving the selection
it was made on, not a claim about this object.
Returns (drawn, left_out), both in the object's record order.
Source code in src/visualdynamics/viz/waterfall.py
records_per_page
¶
records_per_page(
data: DataArray,
drawn_count: int,
samples: int,
shape: str,
budget: int = SCENE_BUDGET,
points: int = POINT_BUDGET,
) -> int
How many records fit in one scene, from what each one costs.
A stepped record costs two points per bin and is never thinned, so
a 16k-line PSD is 32k vertices however the budget feels about it.
Anything else is thinned to points first, so it costs at most
that. Derived from vertices rather than fixed at a record count
because line counts vary by orders of magnitude — 300 records of
16k lines and 4800 of 1k lines are the same scene.
points is the decimation budget actually in use, not always this
module's: the report draws the same stage on a far smaller one,
and costing its records at the app's budget answered 26 records
where 48 fit. On screen the two are the same number, which is why
this went unnoticed.
Never returns zero: one record always draws, even if it is alone over budget, because a page showing nothing is not a page.
Source code in src/visualdynamics/viz/waterfall.py
quantity_label
¶
The word the quantity box shows for one group.
The response factor of the dimension, in the interface's own vocabulary — a PSD group of acceleration**2/frequency reads 'acceleration', an FRF group by what it measures. A hinted group keeps the undefined-units flag the axes carry.
Source code in src/visualdynamics/viz/waterfall.py
waterfall_arrays
¶
waterfall_arrays(
data: DataArray,
records: Sequence[int] | None = None,
unit_system: UnitSystem | None = None,
component: str = "magnitude",
budget: int = POINT_BUDGET,
quantity: tuple[str, str | None] | None = None,
page: int = 0,
scene_budget: int | None = None,
) -> dict[str, Any]
The waterfall's numbers, before any VTK touches them.
Decimated per-record curves in display units, the log mapping, the labels — everything the scene draws, in a form a test can hold without a render window. Returns a dict:
curves: one(x, z)pair per drawn record, peak-decimated;zis log10 of the value whenlog_scaled, with non-positive values as NaN gapslabels: each drawn record'srecord_labeldrawn/left_out: record indices, fromwaterfall_grouplog_scaled: whetherzis the log10 readingxlabel/zlabel: axis titles, plain text for VTK
Source code in src/visualdynamics/viz/waterfall.py
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | |
nice_axis
¶
(low, high, label count) — the range widened to round ticks, when the widening is nearly free.
The cube axes place their labels at even divisions of the range, so a 0.4998 s record divided five ways puts ticks at 0.125 and 0.375 — and the default one-decimal format then prints them as 0.1 and 0.4, numbers that do not sit where they claim (Brandon, 2026-08-28: the record read as 0.6 s long against the truncation boxes' honest 0.5). Widening to 0..0.5 with six labels makes every tick round and true.
Only when it costs at most budget of the span in dead axis: a
1024 Hz Nyquist would widen to 1200, a fifth of the stage holding
no data, so it keeps its exact range — the label format prints
those positions truthfully instead.
Source code in src/visualdynamics/viz/waterfall.py
stage_curves
¶
stage_curves(
arrays: dict[str, Any],
ordinate_limits: tuple[float, float] | None = None,
) -> dict[str, Any]
The waterfall's curves placed on the unit stage.
Takes waterfall_arrays' output and answers where every point
stands in stage coordinates — abscissa across, records receding,
level up — plus the real ranges that stage stands in for and the
runs of finite values each curve breaks into (a gap is a gap; a
polyline drawn through one would invent a value).
Here rather than inside add_waterfall because the report draws
the same stage in a canvas, and two normalizations would be two
pictures of one measurement. Returns a dict of runs (each
{'station', 'points', 'levels'}), extents (x0, x1, z0,
z1), stations and labels — plain floats, no VTK.
Source code in src/visualdynamics/viz/waterfall.py
add_waterfall
¶
add_waterfall(
plotter: Any,
data: DataArray,
records: Sequence[int] | None = None,
unit_system: UnitSystem | None = None,
theme: Any = None,
component: str = "magnitude",
budget: int = POINT_BUDGET,
quantity: tuple[str, str | None] | None = None,
page: int = 0,
scene_budget: int | None = None,
color: str | None = None,
) -> dict[str, Any]
Draw the waterfall into a plotter: one mesh, labels, axes.
quantity picks which (dimension, hint) group of a mixed object
stands on the floor — the quantity box's choice; None means the
largest. Returns waterfall_arrays' dict with points (how many
vertices the scene got — the number a size-ceiling test pins) and
named (which drawn positions got a channel label) added.
color draws the ribbons flat in one colour instead of on the
level colormap: the stood-back reading, for a record shown as the
reference for something else drawn over it — what the filter
preview does with the raw record (Brandon, 2026-08-25). The
extents, the axes and the stations are unchanged, so the thing
drawn over it lands in the same space.
Source code in src/visualdynamics/viz/waterfall.py
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 | |
waterfall_scene
¶
waterfall_scene(
data: DataArray,
records: Sequence[int] | None = None,
unit_system: UnitSystem | None = None,
plotter: Any = None,
off_screen: bool = False,
theme: Any = None,
component: str = "magnitude",
budget: int = POINT_BUDGET,
quantity: tuple[str, str | None] | None = None,
) -> Any
Build (or add to) a PyVista plotter showing the waterfall.
Same shape as geometry_scene: theme is 'light', 'dark' or a
colors dict; returns the plotter — .show() it, or .screenshot()
if off_screen. The camera is placed once, here: from the front-left
and above, abscissa reading left to right, records receding.
Source code in src/visualdynamics/viz/waterfall.py
place_camera
¶
The waterfall's home view, placed — never re-placed on a redraw.
From the front-left and above: abscissa left to right, records receding. Far enough out that the vertical axis title clears the frame — one notch closer clipped it. The window calls this when the displayed object changes and at no other time, per the standing camera rule; the stage is a fixed size, so one home view fits every object.
Source code in src/visualdynamics/viz/waterfall.py
stage_basis
¶
(right, up) — the stage's home view, as a parallel basis.
Derived from the very camera place_camera sets, so everything
that draws the stage without a camera — the app icon's tile, the
report's 3-D figure — opens on the app's own view of data rather
than on a second isometric that merely looks similar. Parallel
rather than the camera's perspective: both of those readings are
small, and perspective at that size is a distortion nobody reads
as depth.
Source code in src/visualdynamics/viz/waterfall.py
ribbon
¶
A translucent surface between two curves over one path on the
stage — the banded and sine views' zone fill, built once here so
the two cannot drift apart. yn may be one depth or one per point.
Source code in src/visualdynamics/viz/waterfall.py
finish_scene
¶
End a stage's headless call the one way every stage does: render
to screenshot and hand back the image, or show, or hand back the
plotter for the caller to drive.
Source code in src/visualdynamics/viz/waterfall.py
plot_waterfall
¶
plot_waterfall(
data: DataArray,
records: Sequence[int] | None = None,
*,
screenshot: str | None = None,
unit_system: UnitSystem | None = None,
theme: Any = None,
component: str = "magnitude",
show: bool = True,
quantity: tuple[str, str | None] | None = None,
) -> Any
Show the waterfall interactively, or render it to screenshot.
The scriptable face of the plot bar's 3-D reading, like
plot_geometry for the geometry scene. Returns the pane (its
.plotter is the PyVista one), or the image array when rendering
to a file.