Source code for eradiate.test_tools.types
from __future__ import annotations
import mitsuba as mi
import pytest
from ..contexts import KernelContext
from ..kernel import MitsubaObjectWrapper, mi_traverse
from ..scenes.core import CompositeSceneElement, NodeSceneElement, Scene, traverse
[docs]
def check_scene_element(
instance: NodeSceneElement | CompositeSceneElement,
mi_cls=None,
ctx: KernelContext = None,
drop_parameters: bool = True,
) -> MitsubaObjectWrapper:
"""
Perform kernel dictionary checks on a scene element.
This function checks if the scene element can produce a valid kernel
dictionary template, as well as an appropriate parameter table.
The returned Mitsuba object and parameter table can be used to perform
additional checks.
Parameters
----------
instance : :class:`.NodeSceneElement` or :class:`.CompositeSceneElement`
Node scene element to check.
mi_cls : :class:`mitsuba.Object`
Mitsuba class the node scene element expands to. Must be set if
`instance` is a :class:`.NodeSceneElement`; ignored otherwise.
ctx : .KernelContext, optional
If provided, the kernel dictionary context to use. Otherwise, a default
context is created.
drop_parameters : bool, default: True
If ``True``, the Mitsuba scene parameter table will be stripped off from
untracked parameters.
Returns
-------
mi_obj : :class:`mitsuba.Object`
Mitsuba object the scene element was expanded to. See notes for details.
mi_params : dict
Parameter table of the Mitsuba objects generated by the tested scene
element.
Notes
-----
* If `instance` is a :class:`.NodeSceneElement`, the corresponding Mitsuba
object type is checked against `mi_cls`.
* If `instance` is a :class:`.CompositeSceneElement`, the corresponding
Mitsuba objects are automatically encapsulated into a
:class:`mitsuba.Scene` object.
"""
if isinstance(instance, NodeSceneElement):
if mi_cls is None:
raise ValueError("Expected Mitsuba class must be set")
kdict_template, umap_template = traverse(instance)
elif isinstance(instance, CompositeSceneElement):
mi_cls = mi.Scene
kdict_template, umap_template = traverse(Scene(objects={"composite": instance}))
else:
raise RuntimeError(f"Cannot test type '{instance.__class__}'")
# Check if the template can be instantiated
ctx = KernelContext() if ctx is None else ctx
kernel_dict = kdict_template.render(ctx)
try:
mi_obj = mi.load_dict(kernel_dict)
except RuntimeError as e:
pytest.fail(
reason=f"could not load scene dictionary, got RuntimeError: {e}\n"
f"{kernel_dict = }"
)
assert isinstance(mi_obj, mi_cls)
# Collect Mitsuba parameters, resolve update map parameter paths
mi_wrapper = mi_traverse(mi_obj, umap_template)
mi_params = mi_wrapper.parameters
# Check that parameters can all be set
umap = umap_template.render(ctx)
for key, value in umap.items():
try:
mi_params[key] = value
except KeyError as e:
pytest.fail(
reason=f"could not set parameter, got KeyError: {e}\n{mi_params = }"
)
mi_params.update()
# Drop untracked parameters (this will detect param lookup failures)
if drop_parameters:
mi_wrapper.drop_parameters()
return mi_wrapper