Skip to content

Interactive rendering defaults for PyVista and Plotly (fixes #337) - #366

Open
SLT2k wants to merge 1 commit into
masterfrom
fix/issue-337-rendering-defaults
Open

Interactive rendering defaults for PyVista and Plotly (fixes #337)#366
SLT2k wants to merge 1 commit into
masterfrom
fix/issue-337-rendering-defaults

Conversation

@SLT2k

@SLT2k SLT2k commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Addresses every point in #337 (default rendering configuration tuning for interactive
plots), plus several rendering defects found while testing on a campus case (19k faces,
9,000 vegetation points) and a denser 800 m neighbourhood case (72k faces).

PyVista axes

  • Nice-number 1/2/5 tick locator replaces the fixed 9-point linspace: round tick
    labels with adaptive decimals, at most 8 per axis.
  • The z axis gets intermediate ticks; its base tick is dropped (its label collided
    with the y labels at the shared corner). The exclusion is span-relative because STL
    float noise puts nominally flat ground fractionally below zero.
  • z ticks, labels and title are drawn along a screen-level direction: pure -x
    projects diagonally under the default camera, and diagonal tick stubs read as kinks
    on the (exactly vertical) z-axis line.
  • All axis text is centre-justified on its anchor (the default left/bottom
    justification visibly offset labels from their ticks, worst on the x axis); x/y
    labels hang below their tick tips; the duplicated x/y "0" labels merge into a
    single label below the shared origin corner.
  • Labels and titles sit further from the axes, and fonts scale with the trame still
    ratio so supersampled notebook frames keep their apparent text size.

Rendering quality (PyVista)

  • 16-sample MSAA anti-aliasing (FXAA fallback for GL stacks without multisample
    support) and scene-scaled SSAO contact shading.
  • Notebook stills stream at 1.5x with JPEG quality 95: ratio-1 streaming is
    undersampled on any display with OS scaling above 100%.

Ground/building classification (both backends)

  • Faces now count as ground within a 1 mm height tolerance instead of a strict
    z > 0 test. STL float noise scattered most ground triangles into the buildings
    group, painting the ground in the building colour and leaving mismatched halos
    around building bases.

Vegetation

  • plot_veg draws each vegetation point as an opaque voxel filling its grid cell
    (stretched z spacings included), replacing size-2, opacity-0.2 dots that were
    invisible against the geometry; color and opacity are keyword arguments.
  • New plot_veg_outline (UDVis method + sim.plot_veg_outline wrapper): the same
    vegetation voxels over the outline-style base - the most readable vegetation view
    on fine meshes. Reuses the outline scene and vegetation loading via shared helpers
    rather than duplicating them.

Wireframes and lines

  • Full-wireframe overlays render thin and semi-transparent in PyVista, so fine
    meshes no longer merge into a solid black mass. LineSet gains an opacity
    field plus optional Plotly-specific width/opacity overrides: Plotly's WebGL
    lines rasterize too differently for shared values, so Plotly keeps its original
    bold opaque look.
  • The PyVista mesh path now honours MeshPrimitive.opacity (previously
    Plotly-only).

Plotly

  • Directional Lambert shading is baked into per-face colours: plotly.js does not
    apply its lighting model to Mesh3d facecolor arrays, so uniformly coloured
    meshes rendered as unshaded silhouettes.
  • Explicit axis lines (Plotly 3-D axes draw none by default, leaving the z axis
    invisible) with outside ticks long enough to keep labels clear of the plotted
    field.

API hardening

  • UDBase.plot_veg/plot_trees/plot_veg_outline forward keyword arguments to
    the visualization facade; plot_trees(backend=...) previously raised
    TypeError.

Testing

  • tools/python/tests/test_udbase_vis.py: 44 tests including 15 new ones (tick
    locator, z-tick policy, voxel mesh geometry incl. watertightness, Plotly axis
    contract, alias forwarding, plot_veg_outline render path); PyVista render paths
    exercised off-screen on both backends.
  • Full unittest discover over tools/python/tests matches the clean-master
    baseline locally (the only reds are pre-existing Windows-environment View3D
    config tests, identical without this change).
  • Verified interactively on cases with 19k and 72k facets in the trame notebook
    widget, the native desktop window, and the browser (Plotly), including a case
    without vegetation for the graceful-degradation paths.

Fixes #337

🤖 Generated with [Claude Code]

…337)

PyVista axes:
- Replace fixed 9-point linspace ticks with a 1/2/5 nice-number locator
  (_nice_ticks): round labels, adaptive decimals, at most 8 ticks per axis.
- Give the z axis intermediate ticks and drop its base tick, whose label
  collided with the y labels at the shared corner (span-relative threshold:
  STL float noise puts nominally flat ground fractionally below zero).
- Draw z ticks/labels/title along a screen-level direction so they no
  longer read as kinks on the vertical axis line; centre all axis text on
  its anchor (the default left/bottom justification visibly offset labels
  from their ticks); hang x/y labels below their tick tips; merge the
  duplicated x/y origin labels into one label below the shared corner.
- Move labels and titles clear of the axes and scale fonts with the trame
  still ratio so supersampled notebook frames keep their apparent text
  size.

Rendering quality (PyVista):
- 16-sample MSAA anti-aliasing with FXAA fallback, plus scene-scaled SSAO
  contact shading.
- Stream notebook stills at 1.5x with JPEG quality 95: ratio-1 streaming
  is undersampled on displays with OS scaling above 100%.

Ground/building classification:
- Classify faces as ground within a 1 mm height tolerance instead of a
  strict z > 0 test: STL float noise scattered most ground triangles into
  the buildings group, painting the ground in the building colour and
  leaving mismatched halos around building bases in both backends.

Vegetation:
- plot_veg draws each vegetation point as an opaque voxel filling its
  grid cell (stretched z spacings included) instead of size-2, opacity-0.2
  points that vanished against the geometry; colour and opacity are now
  keyword arguments.
- New plot_veg_outline: the same vegetation voxels over the outline-style
  base, the most readable vegetation view on fine meshes.

Wireframes and lines:
- Full-wireframe overlays render thin and semi-transparent in PyVista so
  fine meshes no longer merge into a solid black mass; LineSet gains an
  opacity field and optional Plotly-specific width/opacity overrides,
  since Plotly WebGL lines rasterize too differently for shared values
  (Plotly keeps its original bold opaque lines).
- The PyVista mesh path now honours MeshPrimitive.opacity
  (previously Plotly-only).

Plotly:
- Bake directional Lambert shading into per-face colours: plotly.js does
  not apply its lighting model to Mesh3d facecolor arrays, so uniformly
  coloured meshes rendered as unshaded silhouettes.
- Draw explicit axis lines (Plotly 3-D axes draw none by default, leaving
  the z axis invisible) with outside ticks for label standoff.

API hardening:
- UDBase.plot_veg/plot_trees/plot_veg_outline forward keyword arguments
  to the visualization facade; plot_trees(backend=...) previously raised
  TypeError.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@SLT2k SLT2k self-assigned this Aug 15, 2026
@SLT2k SLT2k added the enhancement New feature or request label Aug 15, 2026
@SLT2k SLT2k removed their assignment Aug 15, 2026
@SLT2k SLT2k removed the enhancement New feature or request label Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Visualisation: default rendering configuration needs tuning for interactive plots (PyVista & Plotly)

1 participant