Newer
Older
SmTIAS-LightSim / src / sm_tias_lightsim / geometry / subdivide.py
"""メッシュ三角形分割ユーティリティモジュール.

STL メッシュの三角形を最大辺長以下に均一分割する純関数を提供する.
per-face ベイクにおいて巨大三角形のブロック感を解消するために使う.
"""

from __future__ import annotations

import numpy as np


def subdivide_mesh_to_max_edge(
    vertices: np.ndarray,
    faces: np.ndarray,
    max_edge: float,
) -> tuple[np.ndarray, np.ndarray]:
    """メッシュの全辺長が max_edge 以下になるまで三角形を再分割する(決定論的).

    ``trimesh.remesh.subdivide_to_size`` を内部で呼び出す.
    同一の (vertices, faces, max_edge) を与えれば常に同一の出力を返すため,
    baker と viewer が独立に呼んでも face 配列が一致する.

    max_edge の単位は呼び出し側の頂点座標系に従う.
    baker / viewer では mm → m 変換後の頂点配列(m 単位)に対して呼ぶため,
    max_edge も m 単位で渡すこと(例: 10 mm → max_edge=0.010).

    注意: trimesh.remesh.subdivide_to_size は "triangle soup" を返す(頂点共有なし).
    per-face ベイクではフェイスの重心・法線を計算するだけなので共有なしで問題ない.
    ただし bake_smooth(Laplacian 平滑化)は隣接情報に依存するため,
    分割後のトポロジーが変わると近傍関係が変わる点に留意する(精度は許容範囲内).

    Args:
        vertices: 頂点配列 (N_v, 3),呼び出し側の座標系単位(m 推奨).
        faces: 三角形面配列 (N_f, 3),頂点インデックス.
        max_edge: 各辺の最大許容長.この値以下になるまで分割を繰り返す.
            単位は vertices と同じ(m 単位で渡すなら m).

    Returns:
        (new_vertices, new_faces) のタプル.
        - new_vertices: 分割後の頂点配列 (N_v', 3),dtype=float64.
        - new_faces: 分割後の三角形面配列 (N_f', 3),dtype=int64.
          N_f' >= N_f (分割により face 数は増える).

    Raises:
        ValueError: max_edge が 0 以下の場合.
    """
    if max_edge <= 0.0:
        msg = f"max_edge は正の値でなければなりません (got {max_edge})."
        raise ValueError(msg)

    import trimesh

    new_verts, new_faces = trimesh.remesh.subdivide_to_size(
        vertices=vertices.astype(np.float64),
        faces=faces.astype(np.int64),
        max_edge=max_edge,
    )
    return new_verts.astype(np.float64), new_faces.astype(np.int64)