visualdynamics.viz.waterfall¶
waterfall
¶
The 3-D view of a data object: its records as a waterfall.
Many channels on one 2-D axis hide each other where it matters most: resonances line up, and the tenth curve is drawn over the first nine. The waterfall spreads the records along a depth axis instead, one polyline per record at its own station, with the same label the grids and the legend use, and colored by level so that a peak can be followed across the whole set.
It uses numpy and PyVista only, with no Qt, like the rest of viz. Two
choices set how large a scene can be:
- Each record's points are peak-decimated before VTK gets them
(
decimate.peak_decimate, the same reduction the 2-D plot and the report use), so a million-sample time history arrives as a few thousand points that keep every peak. - All of the curves are one PolyData under one actor, whatever the record count, because an actor per record is what makes a large scene slow.
The scene is drawn on a fixed stage (STAGE) instead of in data
coordinates, because a PSD's level spans decades while its frequency
spans kilohertz, and drawn in raw coordinates it would be a thin needle.
The axes are relabeled with the real ranges (axes_ranges). The
vertical axis is in log10 for data read on a log axis, and its title
says so.
Functions:
| Name | Description |
|---|---|
waterfall_groups |
Every (dimension, hint) group among |
waterfall_group |
Which of the requested records are drawn together, and which are |
records_per_page |
How many records fit in one scene, from the vertices each one |
quantity_label |
The word the quantity box shows for one (dimension, hint) group. |
waterfall_arrays |
The waterfall's numbers, before VTK is involved. |
nice_axis |
(low, high, label count): the range widened to round tick values, |
drawn_span |
(low, high) of the abscissa where something is drawn, over the |
stage_curves |
The waterfall's curves placed on the stage. |
add_waterfall |
Draw the waterfall into a plotter: one mesh, the channel labels |
waterfall_scene |
Build a PyVista plotter showing the waterfall, or add it to |
place_camera |
Place the camera at the waterfall's home view. |
stage_basis |
(right, up): the stage's home view, as unit vectors for a |
ribbon |
A surface between two curves over one path on the stage, as a |
finish_scene |
Finish a stage's headless call, the same way for every stage: |
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 records (all of them when
None), as [(key, record indices)].
In the object's own order. These are what the quantity box offers,
and what waterfall_group chooses from. The grouping key is the 2-D
plot's, without the per-plot axis preferences that stacked axes need
and a single stage 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 requested records are drawn together, and which are left out.
One vertical axis cannot hold accelerations and volts at once. The
2-D plot uses one stacked axis per quantity; a scene has one stage,
so the waterfall draws one (dimension, hint) group, the one
quantity names or else the largest (the earliest on a tie), and
reports the rest for the quantity box to offer. A quantity that is
not present falls back to the largest group instead of raising,
because it is a view choice kept from an earlier selection and not a
statement 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 the vertices each one costs.
samples is the number of abscissa values and shape the drawing
shape (plot.drawing_shape). A stepped record ('steps') costs two
points per bin and is never thinned, so a 16k-line PSD is 32k
vertices whatever the budget. Any other record is thinned to
points first, so it costs at most that. The count comes from
vertices and not from a fixed number of records because line counts
vary by orders of magnitude: 300 records of 16k lines and 4800 of 1k
lines are the same size of scene. budget is the scene's vertex
limit, and the result is at most drawn_count.
points must be the decimation budget actually in use, which is not
always this module's: the report draws the same stage with a much
smaller one.
Never returns zero. One record is always drawn, even when it alone is over the budget.
Source code in src/visualdynamics/viz/waterfall.py
quantity_label
¶
The word the quantity box shows for one (dimension, hint) group.
It is the response quantity of the dimension, in the words the interface uses: a PSD group of acceleration**2/frequency shows 'acceleration', and an FRF group shows what it measures. A group whose units are undefined is named the same way from its hint, with '[units undefined]' after it as the axes show, or is 'units undefined' when it has no hint.
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 VTK is involved.
Everything the scene draws, in a form a test can check without a
render window: the per-record curves in display units, decimated to
budget points (a stepped spectrum is not decimated), the log
mapping and the labels. component ('magnitude', 'real', 'imag' or
'phase') applies to complex frequency data, as in the 2-D plot.
quantity picks the group drawn (waterfall_group). The records
are split into pages of as many as fit in scene_budget vertices
(SCENE_BUDGET by default, records_per_page), and page picks
one, clipped to the pages there are. Returns a dict:
curves: one(x, z)pair per drawn record.zis log10 of the value whenlog_scaled, with non-positive values as NaN gaps, andxis log10 of the abscissa whenlog_abscissalabels: each drawn record'srecord_labeldrawn/left_out: record indices;drawnis this page of the group fromwaterfall_grouplog_scaled/log_abscissa: whetherzandxare log10 valuesxlabel/zlabel: axis titles, in plain text for VTKpage,pages,per_page: the page drawn, how many there are, and the records on eachof_quantity: how many records the group has over all pages
Source code in src/visualdynamics/viz/waterfall.py
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 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | |
nice_axis
¶
(low, high, label count): the range widened to round tick values, when that adds little empty axis.
The cube axes place their labels at even divisions of the range, so a 0.4998 s record divided five ways has ticks at 0.125 and 0.375, which a one-decimal format prints as 0.1 and 0.4, values that are not where they are drawn. Widened to 0 to 0.5 with six labels, every tick is round and correct.
The range is widened only when that adds at most budget of the
span as empty axis. A 1024 Hz Nyquist would widen to 1200, leaving a
fifth of the stage with no data, so it keeps its exact range with
five labels, and the label format prints those positions as they
are. A range that is empty or not finite is returned unchanged.
Source code in src/visualdynamics/viz/waterfall.py
drawn_span
¶
(low, high) of the abscissa where something is drawn, over the
points whose level is finite, never over the whole abscissa a curve
is carried on. A PSD written the way a controller writes a
specification (every line to Nyquist, zero outside its band) is
drawn only over its band, since zero is a gap on a log level, and
the stage spans only that band. curves are (x, z) pairs, and
default is returned when nothing is finite.
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 stage.
Takes the output of waterfall_arrays and gives the position of
every point in stage coordinates (abscissa across, records receding,
level up), the real ranges the stage stands for, and the runs of
finite values each curve breaks into. A gap stays a gap, since a
polyline drawn through one would show a value that is not there.
The abscissa range is widened to round ticks (nice_axis). When
ordinate_limits is given and the level is not log-scaled, it fixes
the vertical range, as the 2-D plot fixes a coherence to 0 to 1.05.
This is separate from add_waterfall because the report draws the
same stage in a canvas, and two normalizations would give two
pictures of one measurement. Returns a dict, with no VTK in it:
runs: each{'record', 'station', 'points', 'levels', 'first'}, wherefirstis the run's first index in its curveextents: (x0, x1, z0, z1), the real rangesstations: how many records are on the stagex_label_count: how many abscissa labels to drawlabels,xlabel,zlabel,log_abscissa: from the arraysxn,zn: functions taking real values to stage coordinates
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, the channel labels and the axes.
The arguments are those of waterfall_arrays. quantity picks
which (dimension, hint) group of a mixed object is drawn, as the
quantity box does; None means the largest. Past LABEL_LIMIT
records, only every nth record gets a channel label, so that at most
LABEL_LIMIT are labeled; every curve is still drawn.
A log abscissa is labeled in decades (marks.add_decade_axis).
color draws the curves in one flat color instead of on the level
colormap. That is for a record shown as the reference for something
drawn over it, as the filter preview does with the raw record. The
extents, the axes and the stations are unchanged, so what is drawn
over it lands in the same space.
Returns the dict from waterfall_arrays with these added:
points (how many vertices the scene has), named (which drawn
positions got a channel label), extents ((x0, x1, z0, z1), the
real ranges the stage stands for, so marks drawn later land at the
data positions they describe), stations (how many records are
on the stage) and, for a log abscissa, decades (the decade
labels drawn).
Source code in src/visualdynamics/viz/waterfall.py
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 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | |
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, marks: str | None = None, per_octave: int | None = None) -> Any
Build a PyVista plotter showing the waterfall, or add it to
plotter.
Called the same way as geometry_scene: theme is 'light', 'dark'
or a colors dict, and the plotter is returned, to .show(), or to
.screenshot() when it is off screen. records, component,
budget and quantity are those of waterfall_arrays. The camera
is placed here, once (place_camera). marks draws one of the
record's readings over the stage (viz.marks.add_stage_reading),
with per_octave for 'octave'.
Source code in src/visualdynamics/viz/waterfall.py
place_camera
¶
Place the camera at the waterfall's home view.
From the front-left and above, with the abscissa reading left to right and the records receding, and far enough out that the vertical axis title stays inside the frame (one step closer cut it off). The window calls this when the displayed object changes and at no other time, so a redraw never moves the camera. 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 unit vectors for a parallel projection.
Computed from the camera place_camera sets, so everything that
draws the stage without a camera (the app icon's tile, the report's
3-D figure) shows the same view as the app, not a second isometric
view that looks similar. It is parallel, not the camera's
perspective, because both of those figures are small, and at that
size perspective looks like distortion rather than depth.
Source code in src/visualdynamics/viz/waterfall.py
ribbon
¶
A surface between two curves over one path on the stage, as a
PolyData of quads: the zone fill of the banded and sine stages,
built here once so the two stay the same. xn, z_low and z_high
have one value per point, and yn is one depth or one per point.
The caller sets its color and opacity.
Source code in src/visualdynamics/viz/waterfall.py
finish_scene
¶
Finish a stage's headless call, the same way for every stage:
with a screenshot path, render to it, close the plotter and return
the image; otherwise show the plotter when show is true, and
return the plotter.
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, marks: str | None = None, per_octave: int | None = None) -> Any
Show the waterfall interactively, or render it to screenshot.
The script version of the plot bar's 3-D view, as plot_geometry
is for the geometry scene. records, component and quantity
are those of waterfall_arrays. Returns the pane (its .plotter is
the PyVista one), or the image array when rendering to a file.
marks draws one of the record's readings on the stage, as the plot
bar's toggles do in 3-D: 'averaging', 'shocks', 'truncation' or
'filter' over a time history, each taken from the history's own
setting or else from what its detector suggests, or 'octave' over a
PSD, banded per_octave.