XIAN-FEM-2026June/3D opticsfem-master/kernel/SBC_NormalUtil.h

54 lines
1.4 KiB
C++

#pragma once
#include "../Eigen/Dense"
#include <cmath>
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