"""scene/materials.py の仕様ベーステスト.
仕様出典:
- docs/04_SPEC/SPEC_02_シーン定義仕様.md(materials セクション)
- docs/03_PLAN/PLAN_02_開発ステップ.md(Step 4: scene/materials.py の役割)
build_material_dict の戻り値 dict 構造のみを検証する.Mitsuba の load_dict は呼ばない.
"""
from __future__ import annotations
import pytest
from sm_tias_lightsim.scene.materials import build_material_dict
from sm_tias_lightsim.scene.schema import (
DielectricMaterial,
DiffuseMaterial,
ThinDielectricMaterial,
)
# ---------------------------------------------------------------------------
# DiffuseMaterial
# ---------------------------------------------------------------------------
def test_diffuse_material_type_is_diffuse():
"""DiffuseMaterial の dict type が "diffuse" であること.
仕様: type: diffuse → Mitsuba BSDF "diffuse" (SPEC_02 materials セクション)
"""
mat = DiffuseMaterial(type="diffuse", reflectance=0.5)
result = build_material_dict(mat)
assert result["type"] == "diffuse"
def test_diffuse_scalar_reflectance_expands_to_rgb():
"""スカラ reflectance が RGB 3 要素リストに展開されること.
仕様: reflectance スカラ可 (SPEC_02 materials セクション white_lambert: 0.95 例)
"""
mat = DiffuseMaterial(type="diffuse", reflectance=0.95)
result = build_material_dict(mat)
assert result["reflectance"]["type"] == "rgb"
assert result["reflectance"]["value"] == pytest.approx([0.95, 0.95, 0.95])
def test_diffuse_rgb_reflectance_preserved():
"""RGB reflectance がそのまま保持されること.
仕様: reflectance: [0.05, 0.05, 0.05] の RGB 表記 (SPEC_02 materials セクション)
"""
mat = DiffuseMaterial(type="diffuse", reflectance=(0.05, 0.05, 0.05))
result = build_material_dict(mat)
assert result["reflectance"]["type"] == "rgb"
assert result["reflectance"]["value"] == pytest.approx([0.05, 0.05, 0.05])
def test_diffuse_reflectance_has_rgb_type_key():
"""DiffuseMaterial の reflectance dict に type="rgb" キーが存在すること.
仕様: Mitsuba RGB スペクトル型の構造 (SPEC_01 scene/materials 責務)
"""
mat = DiffuseMaterial(type="diffuse", reflectance=1.0)
result = build_material_dict(mat)
assert "type" in result["reflectance"]
assert "value" in result["reflectance"]
# ---------------------------------------------------------------------------
# DielectricMaterial
# ---------------------------------------------------------------------------
def test_dielectric_material_type_is_dielectric():
"""DielectricMaterial の dict type が "dielectric" であること.
仕様: type: dielectric → Mitsuba BSDF "dielectric" (SPEC_02 materials セクション)
"""
mat = DielectricMaterial(type="dielectric", int_ior=1.5, ext_ior=1.0)
result = build_material_dict(mat)
assert result["type"] == "dielectric"
def test_dielectric_int_ior_in_dict():
"""DielectricMaterial の int_ior が dict に格納されること.
仕様: dielectric → {"type": "dielectric", "int_ior": ..., "ext_ior": ...}
(SPEC_01 scene/materials 責務)
"""
mat = DielectricMaterial(type="dielectric", int_ior=1.5, ext_ior=1.0)
result = build_material_dict(mat)
assert result["int_ior"] == pytest.approx(1.5)
def test_dielectric_ext_ior_in_dict():
"""DielectricMaterial の ext_ior が dict に格納されること.
仕様: dielectric → {"type": "dielectric", "int_ior": ..., "ext_ior": ...}
(SPEC_01 scene/materials 責務)
"""
mat = DielectricMaterial(type="dielectric", int_ior=1.5, ext_ior=1.0)
result = build_material_dict(mat)
assert result["ext_ior"] == pytest.approx(1.0)
def test_dielectric_ior_values_preserved():
"""DielectricMaterial の IOR 値が正確に dict に格納されること.
仕様: PMMA 屈折率 int_ior: 1.49 例 (SPEC_02 materials セクション)
"""
mat = DielectricMaterial(type="dielectric", int_ior=1.49, ext_ior=1.0003)
result = build_material_dict(mat)
assert result["int_ior"] == pytest.approx(1.49)
assert result["ext_ior"] == pytest.approx(1.0003)
# ---------------------------------------------------------------------------
# ThinDielectricMaterial
# ---------------------------------------------------------------------------
def test_thindielectric_material_type_is_thindielectric():
"""ThinDielectricMaterial の dict type が "thindielectric" であること.
仕様: type: thindielectric → Mitsuba BSDF "thindielectric" (SPEC_02 materials セクション)
"""
mat = ThinDielectricMaterial(type="thindielectric", int_ior=1.49, ext_ior=1.0)
result = build_material_dict(mat)
assert result["type"] == "thindielectric"
def test_thindielectric_int_ior_in_dict():
"""ThinDielectricMaterial の int_ior が dict に格納されること.
仕様: thindielectric → {"type": "thindielectric", "int_ior": ..., "ext_ior": ...}
(SPEC_01 scene/materials 責務)
"""
mat = ThinDielectricMaterial(type="thindielectric", int_ior=1.49, ext_ior=1.0)
result = build_material_dict(mat)
assert result["int_ior"] == pytest.approx(1.49)
def test_thindielectric_ext_ior_in_dict():
"""ThinDielectricMaterial の ext_ior が dict に格納されること.
仕様: thindielectric → {"type": "thindielectric", "int_ior": ..., "ext_ior": ...}
(SPEC_01 scene/materials 責務)
"""
mat = ThinDielectricMaterial(type="thindielectric", int_ior=1.49, ext_ior=1.0)
result = build_material_dict(mat)
assert result["ext_ior"] == pytest.approx(1.0)
def test_thindielectric_does_not_have_reflectance():
"""ThinDielectricMaterial の dict に reflectance キーが含まれないこと.
仕様: thindielectric は IOR のみで反射率を持たない (SPEC_02 materials セクション)
"""
mat = ThinDielectricMaterial(type="thindielectric", int_ior=1.49, ext_ior=1.0)
result = build_material_dict(mat)
assert "reflectance" not in result