Some checks failed
CI / lint (pull_request) Has been cancelled
CI / type (pull_request) Has been cancelled
CI / test-headless (macos-latest, 3.10) (pull_request) Has been cancelled
CI / test-headless (macos-latest, 3.11) (pull_request) Has been cancelled
CI / test-headless (macos-latest, 3.12) (pull_request) Has been cancelled
CI / test-headless (ubuntu-latest, 3.10) (pull_request) Has been cancelled
CI / test-headless (ubuntu-latest, 3.11) (pull_request) Has been cancelled
CI / test-headless (ubuntu-latest, 3.12) (pull_request) Has been cancelled
CI / test-headless (windows-latest, 3.10) (pull_request) Has been cancelled
CI / test-headless (windows-latest, 3.11) (pull_request) Has been cancelled
CI / test-headless (windows-latest, 3.12) (pull_request) Has been cancelled
CI / test-gui (pull_request) Has been cancelled
CI / test-integration (macos-latest) (pull_request) Has been cancelled
CI / test-integration (ubuntu-latest) (pull_request) Has been cancelled
CI / test-integration (windows-latest) (pull_request) Has been cancelled
Auto-fixes (UP037, UP038, F401, I001, SIM105, RUF100) plus hand-fixes: drop the now-unused `Union` imports, annotate mutable class attributes with ClassVar, raise ValidationError instead of blind Exception in the zero-length test, delete two dead locals, and bind the per-iteration grid-builder closures to their loop variables (B023).
135 lines
4.6 KiB
Python
135 lines
4.6 KiB
Python
"""Smoke tests for the force-diagram overlay renderer.
|
|
|
|
These verify the renderer doesn't crash on representative inputs and
|
|
produces an actor when there's data to draw. Geometry/colour assertions
|
|
are kept minimal — they'd be brittle and the visual is the spec anyway.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
# Force pyvista off-screen before any pyvista import in this module's chain.
|
|
os.environ.setdefault("PYVISTA_OFF_SCREEN", "true")
|
|
|
|
import pyvista as pv
|
|
|
|
from otko.core import (
|
|
ElasticBeamColumn,
|
|
ElasticSection,
|
|
Node,
|
|
Project,
|
|
)
|
|
from otko.services.element_forces import (
|
|
DiagramData,
|
|
ForceComponent,
|
|
extract_diagram_data,
|
|
)
|
|
from otko.services.results import StaticResults
|
|
from otko.views.canvas3d.diagram_renderer import DiagramRenderer
|
|
|
|
pv.OFF_SCREEN = True
|
|
|
|
|
|
@pytest.fixture
|
|
def offscreen_plotter(): # type: ignore[no-untyped-def]
|
|
p = pv.Plotter(off_screen=True)
|
|
yield p
|
|
p.close()
|
|
|
|
|
|
@pytest.fixture
|
|
def project_3d() -> Project:
|
|
return Project(
|
|
nodes=[
|
|
Node(id=1, coords=(0.0, 0.0, 0.0)),
|
|
Node(id=2, coords=(3.0, 0.0, 0.0)),
|
|
Node(id=3, coords=(6.0, 0.0, 0.0)),
|
|
],
|
|
sections=[ElasticSection(id=1, E=200e9, A=0.01, Iz=1e-5, Iy=1e-5)],
|
|
elements=[
|
|
ElasticBeamColumn(id=10, nodes=(1, 2), section_id=1),
|
|
ElasticBeamColumn(id=20, nodes=(2, 3), section_id=1),
|
|
],
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def static_results() -> StaticResults:
|
|
f10 = np.array([[100.0, 5.0, 0.0, 0.0, 0.0, 9.0, -100.0, -5.0, 0.0, 0.0, 0.0, -9.0]])
|
|
f20 = np.array([[-50.0, 2.0, 0.0, 0.0, 0.0, 3.0, 50.0, -2.0, 0.0, 0.0, 0.0, -3.0]])
|
|
return StaticResults(case_id=1, case_name="t", n_steps=1, element_forces={10: f10, 20: f20})
|
|
|
|
|
|
def test_render_axial_creates_actor(offscreen_plotter, project_3d, static_results) -> None: # type: ignore[no-untyped-def]
|
|
r = DiagramRenderer(offscreen_plotter)
|
|
data = extract_diagram_data(project_3d, static_results, ForceComponent.N)
|
|
r.render(project_3d, data, scale=0.01)
|
|
assert r._actor is not None
|
|
|
|
|
|
def test_render_shear_creates_actor(offscreen_plotter, project_3d, static_results) -> None: # type: ignore[no-untyped-def]
|
|
r = DiagramRenderer(offscreen_plotter)
|
|
data = extract_diagram_data(project_3d, static_results, ForceComponent.V2)
|
|
r.render(project_3d, data, scale=0.05)
|
|
assert r._actor is not None
|
|
|
|
|
|
def test_render_moment_creates_actor(offscreen_plotter, project_3d, static_results) -> None: # type: ignore[no-untyped-def]
|
|
r = DiagramRenderer(offscreen_plotter)
|
|
data = extract_diagram_data(project_3d, static_results, ForceComponent.M3)
|
|
r.render(project_3d, data, scale=0.05)
|
|
assert r._actor is not None
|
|
|
|
|
|
def test_render_empty_data_does_not_create_actor(offscreen_plotter, project_3d) -> None: # type: ignore[no-untyped-def]
|
|
r = DiagramRenderer(offscreen_plotter)
|
|
empty = DiagramData(
|
|
component=ForceComponent.N,
|
|
element_ids=np.empty(0, dtype=int),
|
|
values_i=np.empty(0),
|
|
values_j=np.empty(0),
|
|
abs_max=0.0,
|
|
)
|
|
r.render(project_3d, empty, scale=1.0)
|
|
assert r._actor is None
|
|
|
|
|
|
def test_render_zero_magnitude_data_does_not_create_actor(
|
|
offscreen_plotter,
|
|
project_3d,
|
|
) -> None: # type: ignore[no-untyped-def]
|
|
"""A diagram for a component that's identically zero shouldn't render
|
|
an empty mesh + scalar bar — that's misleading visual noise."""
|
|
r = DiagramRenderer(offscreen_plotter)
|
|
zero_for_two_elems = DiagramData(
|
|
component=ForceComponent.T,
|
|
element_ids=np.array([10, 20], dtype=int),
|
|
values_i=np.zeros(2),
|
|
values_j=np.zeros(2),
|
|
abs_max=0.0,
|
|
)
|
|
r.render(project_3d, zero_for_two_elems, scale=1.0)
|
|
assert r._actor is None
|
|
|
|
|
|
def test_clear_removes_actor(offscreen_plotter, project_3d, static_results) -> None: # type: ignore[no-untyped-def]
|
|
r = DiagramRenderer(offscreen_plotter)
|
|
data = extract_diagram_data(project_3d, static_results, ForceComponent.N)
|
|
r.render(project_3d, data, scale=0.01)
|
|
assert r._actor is not None
|
|
r.clear()
|
|
assert r._actor is None
|
|
|
|
|
|
def test_render_replaces_previous_overlay(offscreen_plotter, project_3d, static_results) -> None: # type: ignore[no-untyped-def]
|
|
r = DiagramRenderer(offscreen_plotter)
|
|
data_n = extract_diagram_data(project_3d, static_results, ForceComponent.N)
|
|
r.render(project_3d, data_n, scale=0.01)
|
|
first_actor = r._actor
|
|
data_v = extract_diagram_data(project_3d, static_results, ForceComponent.V2)
|
|
r.render(project_3d, data_v, scale=0.05)
|
|
assert r._actor is not first_actor
|