#pragma once #include "../Eigen/Dense" #include namespace OpticsFEM { // SBC face normal for 3D first-order Nedelec scatter (FemType=4). // Matches MATLAB assembly_inc.m / assembly_out.m: // inc -> [0,0,1] // out, domain==3 -> [0,0,-1] // other out -> normalize([sum(x)/3, sum(y)/3, 0]) from tet vertices // Fallback when the reference formula is degenerate: // 1) mesh NormOfFace(domain) if non-zero // 2) geometric normal from the boundary triangle inline Eigen::Vector3d computeScatterSBCNormal( bool isInc, int boundaryDomain, const double tetX[4], const double tetY[4], const Eigen::Vector3d& faceP0, const Eigen::Vector3d& faceP1, const Eigen::Vector3d& faceP2, const Eigen::Vector3d* meshNormFallback = nullptr) { using Eigen::Vector3d; if (isInc) return Vector3d(0.0, 0.0, 1.0); // Prefer mesh NormOfFace (matches MATLAB assembly_out / assembly_inc). if (meshNormFallback != nullptr && meshNormFallback->norm() >= 1e-30) return (*meshNormFallback) / meshNormFallback->norm(); if (boundaryDomain == 3) return Vector3d(0.0, 0.0, -1.0); Vector3d n( (tetX[0] + tetX[1] + tetX[2] + tetX[3]) / 3.0, (tetY[0] + tetY[1] + tetY[2] + tetY[3]) / 3.0, 0.0); const double len = n.norm(); if (len >= 1e-30) return n / len; const Vector3d geo = (faceP1 - faceP0).cross(faceP2 - faceP0); if (geo.norm() >= 1e-30) return geo.normalized(); return Vector3d(0.0, 0.0, -1.0); } } // namespace OpticsFEM