feat: initial otko import
Some checks failed
CI / lint (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
Some checks failed
CI / lint (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
This commit is contained in:
commit
612936a00b
540 changed files with 174136 additions and 0 deletions
133
tests/unit/test_diagram_renderer.py
Normal file
133
tests/unit/test_diagram_renderer.py
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
"""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 # noqa: E402
|
||||
|
||||
from otko.core import ( # noqa: E402
|
||||
ElasticBeamColumn,
|
||||
ElasticSection,
|
||||
Node,
|
||||
Project,
|
||||
)
|
||||
from otko.services.element_forces import ( # noqa: E402
|
||||
DiagramData,
|
||||
ForceComponent,
|
||||
extract_diagram_data,
|
||||
)
|
||||
from otko.services.results import StaticResults # noqa: E402
|
||||
from otko.views.canvas3d.diagram_renderer import DiagramRenderer # noqa: E402
|
||||
|
||||
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
|
||||
Loading…
Reference in a new issue