using System;
using XP.Hardware.MotionControl.Abstractions;
namespace XP.Hardware.MotionControl.Services
{
///
/// 几何计算器 | Geometry Calculator
/// 负责 FOD/FDD/放大倍率的正算与反算,支持探测器摆动角度 | Forward and inverse calculation of FOD/FDD/Magnification with detector swing support
///
public class GeometryCalculator
{
///
/// FOD 接近零的阈值(mm)| Threshold for FOD near zero (mm)
///
private const double FodZeroThreshold = 0.001;
///
/// 角度接近零的阈值(度)| Threshold for angle near zero (degrees)
///
private const double AngleZeroThreshold = 0.001;
#region 绝对坐标计算 | Absolute Coordinate Calculation
///
/// 计算绝对坐标 | Calculate absolute coordinate
/// 绝对坐标 = 当前位置 + Origin(配置)| Absolute = current position + Origin (config)
///
/// 当前位置(mm)| Current position (mm)
/// 原点偏移(mm)| Origin offset (mm)
/// 绝对坐标(mm)| Absolute coordinate (mm)
public double CalcAbsolutePosition(double currentPosition, double origin)
{
return currentPosition + origin;
}
#endregion
#region 探测器中心坐标计算 | Detector Center Coordinate Calculation
///
/// 计算探测器感光面中心的 X 坐标 | Calculate detector active area center X coordinate
/// Dx = R × sin(θ)
///
/// 探测器摆动角度(度)| Detector swing angle (degrees)
/// 摆动半径(mm)| Swing radius (mm)
/// 探测器中心 X 坐标(mm)| Detector center X (mm)
public double CalcDetectorCenterX(double swingAngleDeg, double swingRadius)
{
if (swingRadius <= 0 || Math.Abs(swingAngleDeg) < AngleZeroThreshold)
return 0.0;
double thetaRad = swingAngleDeg * Math.PI / 180.0;
return swingRadius * Math.Sin(thetaRad);
}
///
/// 计算探测器感光面中心的 Z 坐标 | Calculate detector active area center Z coordinate
/// Dz = Pz - R × cos(θ),其中 Pz = detectorZAbsolute + swingPivotOffset
/// 探测器感光面在 Pivot 下方(朝向射线源)| Detector active area is below Pivot (toward source)
///
/// 探测器Z轴绝对坐标(mm)| Detector Z absolute (mm)
/// Pivot 相对于 DetectorZ 绝对坐标的偏移(mm)| Pivot offset from DetectorZ absolute (mm)
/// 探测器摆动角度(度)| Detector swing angle (degrees)
/// 摆动半径(mm)| Swing radius (mm)
/// 探测器中心 Z 坐标(mm)| Detector center Z (mm)
public double CalcDetectorCenterZ(double detectorZAbsolute, double swingPivotOffset,
double swingAngleDeg, double swingRadius)
{
double pivotZ = detectorZAbsolute + swingPivotOffset;
if (swingRadius <= 0)
return detectorZAbsolute;
double thetaRad = swingAngleDeg * Math.PI / 180.0;
return pivotZ - swingRadius * Math.Cos(thetaRad);
}
#endregion
#region 正算 | Forward Calculation
///
/// 正算:计算 FDD(考虑探测器摆动)| Forward: Calculate FDD with detector swing
/// FDD = sqrt(Dx² + (Dz - Sz)²)
/// 当 swingRadius = 0 时退化为 |Sz - DetectorZ_abs| | Degrades to |Sz - DetectorZ_abs| when swingRadius = 0
///
/// 射线源Z轴绝对坐标(mm)| Source Z absolute (mm)
/// 探测器Z轴绝对坐标(mm)| Detector Z absolute (mm)
/// Pivot 偏移(mm)| Pivot offset (mm)
/// 摆动角度(度)| Swing angle (degrees)
/// 摆动半径(mm)| Swing radius (mm)
/// FDD 值(mm)| FDD value (mm)
public double CalcFDD(double sourceZAbsolute, double detectorZAbsolute,
double swingPivotOffset, double swingAngleDeg, double swingRadius)
{
// 无摆动时退化为原始公式 | Degrade to original formula when no swing
if (swingRadius <= 0 || Math.Abs(swingAngleDeg) < AngleZeroThreshold)
return Math.Abs(sourceZAbsolute - detectorZAbsolute);
double dx = CalcDetectorCenterX(swingAngleDeg, swingRadius);
double dz = CalcDetectorCenterZ(detectorZAbsolute, swingPivotOffset, swingAngleDeg, swingRadius);
return Math.Sqrt(dx * dx + (dz - sourceZAbsolute) * (dz - sourceZAbsolute));
}
///
/// 正算:计算 FOD(考虑探测器摆动)| Forward: Calculate FOD with detector swing
/// FOD = 射线源到射线束与载物台平面交点的距离 | Distance from source to ray-beam/stage-plane intersection
/// 当 swingRadius = 0 时退化为 |Sz - StageRotationCenterZ| | Degrades when swingRadius = 0
///
/// 射线源Z轴绝对坐标(mm)| Source Z absolute (mm)
/// 旋转中心Z坐标(mm)| Stage rotation center Z (mm)
/// 探测器Z轴绝对坐标(mm)| Detector Z absolute (mm)
/// Pivot 偏移(mm)| Pivot offset (mm)
/// 摆动角度(度)| Swing angle (degrees)
/// 摆动半径(mm)| Swing radius (mm)
/// FOD 值(mm)| FOD value (mm)
public double CalcFOD(double sourceZAbsolute, double stageRotationCenterZ,
double detectorZAbsolute, double swingPivotOffset,
double swingAngleDeg, double swingRadius)
{
// 无摆动时退化为原始公式 | Degrade to original formula when no swing
if (swingRadius <= 0 || Math.Abs(swingAngleDeg) < AngleZeroThreshold)
return Math.Abs(sourceZAbsolute - stageRotationCenterZ);
double dx = CalcDetectorCenterX(swingAngleDeg, swingRadius);
double dz = CalcDetectorCenterZ(detectorZAbsolute, swingPivotOffset, swingAngleDeg, swingRadius);
double deltaZ_SD = dz - sourceZAbsolute;
// 射线源和探测器中心在同一 Z 高度时,射线束水平,无法与载物台平面相交 | Horizontal beam, no intersection
if (Math.Abs(deltaZ_SD) < FodZeroThreshold)
return double.NaN;
// 射线束与载物台平面交点参数 t_stage = (Oz - Sz) / (Dz - Sz) | Ray-plane intersection parameter
double tStage = (stageRotationCenterZ - sourceZAbsolute) / deltaZ_SD;
// 交点 X 坐标 | Intersection X coordinate
double xIntersect = tStage * dx;
// FOD = sqrt(x_intersect² + (Oz - Sz)²) | Euclidean distance from source to intersection
double deltaZ_SO = stageRotationCenterZ - sourceZAbsolute;
return Math.Sqrt(xIntersect * xIntersect + deltaZ_SO * deltaZ_SO);
}
///
/// 正算:计算放大倍率 | Forward: Calculate Magnification
/// M = FDD / FOD,FOD 接近零(< 0.001mm)或 NaN 时返回 double.NaN
///
/// 焦点到探测器距离(mm)| Focus-to-detector distance (mm)
/// 焦点到旋转中心距离(mm)| Focus-to-object distance (mm)
/// 放大倍率,FOD 接近零时返回 NaN | Magnification, NaN when FOD near zero
public double CalcMagnification(double fdd, double fod)
{
if (double.IsNaN(fod) || fod < FodZeroThreshold)
return double.NaN;
return fdd / fod;
}
#endregion
#region 反算 | Inverse Calculation
///
/// 反算:由目标 FOD 和 FDD 计算轴目标位置(考虑探测器摆动)| Inverse: Calculate axis targets from FOD and FDD with swing
/// 返回值为相对坐标(已减去 Origin)| Returns relative coordinates (Origin subtracted)
///
/// 目标 FOD(mm)| Target FOD (mm)
/// 目标 FDD(mm)| Target FDD (mm)
/// 旋转中心绝对Z坐标(mm)| Stage rotation center Z (mm)
/// 射线源Z轴原点偏移(mm)| Source Z origin offset (mm)
/// 探测器Z轴原点偏移(mm)| Detector Z origin offset (mm)
/// Pivot 偏移(mm)| Pivot offset (mm)
/// 摆动角度(度)| Swing angle (degrees)
/// 摆动半径(mm)| Swing radius (mm)
///
/// sourceZTarget: 射线源Z轴相对目标位置(mm)| Source Z relative target (mm)
/// detectorZTarget: 探测器Z轴相对目标位置(mm)| Detector Z relative target (mm)
/// errorMessage: 错误信息,成功时为 null | Error message, null on success
///
public (double sourceZTarget, double detectorZTarget, string errorMessage) CalcAxisTargets(
double targetFOD, double targetFDD,
double stageRotationCenterZ, double sourceZOrigin, double detectorZOrigin,
double swingPivotOffset, double swingAngleDeg, double swingRadius)
{
// 无摆动时使用简化公式 | Use simplified formula when no swing
if (swingRadius <= 0 || Math.Abs(swingAngleDeg) < AngleZeroThreshold)
{
double szAbs = stageRotationCenterZ - targetFOD;
double dzAbs = szAbs + targetFDD;
return (szAbs - sourceZOrigin, dzAbs - detectorZOrigin, null);
}
double thetaRad = swingAngleDeg * Math.PI / 180.0;
double sinTheta = Math.Sin(thetaRad);
double cosTheta = Math.Cos(thetaRad);
// B = R × sin(θ),探测器中心的 X 偏移 | Detector center X offset
double bx = swingRadius * sinTheta;
// A = SwingPivotOffset - R × cos(θ),DetectorZ_abs 到探测器中心的 Z 偏移 | DetectorZ_abs to detector center Z offset
double az = swingPivotOffset - swingRadius * cosTheta;
// 由 targetFOD 求 SourceZ 绝对坐标 | Calculate SourceZ absolute from targetFOD
// 射线源在载物台下方:Sz = Oz - targetFOD | Source below stage
double sourceZAbsolute = stageRotationCenterZ - targetFOD;
// 由 targetFDD 求 DetectorZ 绝对坐标 | Calculate DetectorZ absolute from targetFDD
// FDD² = Bx² + (DetZ_abs + Az - Sz)²
// (DetZ_abs + Az - Sz)² = FDD² - Bx²
double fddSquared = targetFDD * targetFDD;
double bxSquared = bx * bx;
double discriminant = fddSquared - bxSquared;
if (discriminant < 0)
{
return (0, 0,
$"目标 FDD={targetFDD:F3}mm 小于探测器 X 偏移={Math.Abs(bx):F3}mm,目标不可达 | " +
$"Target FDD={targetFDD:F3}mm less than detector X offset={Math.Abs(bx):F3}mm, target unreachable");
}
// DetZ_abs = Sz - Az + sqrt(FDD² - Bx²)(探测器在射线源上方,取正值)
// DetZ_abs = Sz - Az + sqrt(discriminant)
double detectorZAbsolute = sourceZAbsolute - az + Math.Sqrt(discriminant);
// 转换为相对坐标 | Convert to relative coordinates
double sourceZTarget = sourceZAbsolute - sourceZOrigin;
double detectorZTarget = detectorZAbsolute - detectorZOrigin;
return (sourceZTarget, detectorZTarget, null);
}
#endregion
#region 边界检查 | Boundary Validation
///
/// 边界检查:验证目标位置是否在允许范围内 | Boundary validation: Check targets within allowed range
///
/// 射线源Z轴目标位置(mm)| Source Z target (mm)
/// 探测器Z轴目标位置(mm)| Detector Z target (mm)
/// 射线源Z轴最小值(mm)| Source Z minimum (mm)
/// 射线源Z轴最大值(mm)| Source Z maximum (mm)
/// 探测器Z轴最小值(mm)| Detector Z minimum (mm)
/// 探测器Z轴最大值(mm)| Detector Z maximum (mm)
/// 验证结果 | Validation result
public MotionResult ValidateTargets(
double sourceZTarget, double detectorZTarget,
double sourceZMin, double sourceZMax,
double detectorZMin, double detectorZMax)
{
if (sourceZTarget < sourceZMin || sourceZTarget > sourceZMax)
{
return MotionResult.Fail(
$"SourceZ 目标位置 {sourceZTarget:F3} 超出范围 [{sourceZMin:F3}, {sourceZMax:F3}] | " +
$"SourceZ target {sourceZTarget:F3} out of range [{sourceZMin:F3}, {sourceZMax:F3}]");
}
if (detectorZTarget < detectorZMin || detectorZTarget > detectorZMax)
{
return MotionResult.Fail(
$"DetectorZ 目标位置 {detectorZTarget:F3} 超出范围 [{detectorZMin:F3}, {detectorZMax:F3}] | " +
$"DetectorZ target {detectorZTarget:F3} out of range [{detectorZMin:F3}, {detectorZMax:F3}]");
}
return MotionResult.Ok();
}
#endregion
#region 向后兼容的重载 | Backward-Compatible Overloads
///
/// 正算 FOD(无摆动,向后兼容)| Forward FOD (no swing, backward compatible)
///
public double CalcFOD(double sourceZAbsolute, double stageRotationCenterZ)
{
return Math.Abs(sourceZAbsolute - stageRotationCenterZ);
}
///
/// 正算 FDD(无摆动,向后兼容)| Forward FDD (no swing, backward compatible)
///
public double CalcFDD(double sourceZAbsolute, double detectorZAbsolute)
{
return Math.Abs(sourceZAbsolute - detectorZAbsolute);
}
///
/// 反算轴目标(无摆动,向后兼容)| Inverse axis targets (no swing, backward compatible)
///
public (double sourceZTarget, double detectorZTarget) CalcAxisTargetsSimple(
double targetFOD, double targetFDD,
double stageRotationCenterZ, double sourceZOrigin, double detectorZOrigin)
{
double sourceZAbsolute = stageRotationCenterZ - targetFOD;
double detectorZAbsolute = sourceZAbsolute + targetFDD;
return (sourceZAbsolute - sourceZOrigin, detectorZAbsolute - detectorZOrigin);
}
#endregion
}
}