部分修改了烟幕对指示器的影响
This commit is contained in:
parent
6e990f65a4
commit
5a480a9969
@ -19,7 +19,7 @@ namespace ThreatSource.Guidance
|
||||
/// - 事件发布
|
||||
/// 是其他具体制导系统的基类
|
||||
/// </remarks>
|
||||
public class BasicGuidanceSystem : SimulationElement, IGuidanceSystem, IJammable
|
||||
public class BaseGuidanceSystem : SimulationElement, IGuidanceSystem, IJammable
|
||||
{
|
||||
/// <summary>
|
||||
/// 干扰处理组件
|
||||
@ -104,7 +104,7 @@ namespace ThreatSource.Guidance
|
||||
/// - 配置制导参数
|
||||
/// - 初始化干扰处理
|
||||
/// </remarks>
|
||||
public BasicGuidanceSystem(string id, double maxAcceleration, double proportionalNavigationCoefficient, ISimulationManager simulationManager)
|
||||
public BaseGuidanceSystem(string id, double maxAcceleration, double proportionalNavigationCoefficient, ISimulationManager simulationManager)
|
||||
: base(id, new MotionParameters(), simulationManager)
|
||||
{
|
||||
HasGuidance = false;
|
||||
@ -15,7 +15,7 @@ namespace ThreatSource.Guidance
|
||||
/// - 制导加速度生成
|
||||
/// 用于实现红外指令制导的导弹控制
|
||||
/// </remarks>
|
||||
public class InfraredCommandGuidanceSystem : BasicGuidanceSystem
|
||||
public class InfraredCommandGuidanceSystem : BaseGuidanceSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 跟踪器到导弹的方向向量 (使用可空类型)
|
||||
|
||||
@ -19,7 +19,7 @@ namespace ThreatSource.Guidance
|
||||
/// - 比例导引控制
|
||||
/// 用于实现自主寻的制导
|
||||
/// </remarks>
|
||||
public class InfraredImagingGuidanceSystem : BasicGuidanceSystem
|
||||
public class InfraredImagingGuidanceSystem : BaseGuidanceSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 工作模式枚举
|
||||
|
||||
@ -17,7 +17,7 @@ namespace ThreatSource.Guidance
|
||||
/// - 非线性增益控制
|
||||
/// 用于实现高精度的制导控制
|
||||
/// </remarks>
|
||||
public class LaserBeamRiderGuidanceSystem : BasicGuidanceSystem
|
||||
public class LaserBeamRiderGuidanceSystem : BaseGuidanceSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置激光源位置 (使用可空类型)
|
||||
|
||||
@ -19,7 +19,7 @@ namespace ThreatSource.Guidance
|
||||
/// - 比例导引控制
|
||||
/// 用于实现精确制导打击
|
||||
/// </remarks>
|
||||
public class LaserSemiActiveGuidanceSystem : BasicGuidanceSystem
|
||||
public class LaserSemiActiveGuidanceSystem : BaseGuidanceSystem
|
||||
{
|
||||
private readonly LaserSemiActiveGuidanceConfig config;
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ namespace ThreatSource.Guidance
|
||||
/// - 比例导引控制
|
||||
/// 用于实现全天候制导打击
|
||||
/// </remarks>
|
||||
public class MillimeterWaveGuidanceSystem : BasicGuidanceSystem
|
||||
public class MillimeterWaveGuidanceSystem : BaseGuidanceSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 工作模式枚举
|
||||
|
||||
@ -2,6 +2,11 @@ using ThreatSource.Utils;
|
||||
using ThreatSource.Jammer;
|
||||
using ThreatSource.Simulation;
|
||||
using ThreatSource.Jammable;
|
||||
using ThreatSource.Equipment;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace ThreatSource.Indicator
|
||||
{
|
||||
@ -17,11 +22,21 @@ namespace ThreatSource.Indicator
|
||||
/// </remarks>
|
||||
public abstract class BaseIndicator : SimulationElement, IIndicator, IJammable
|
||||
{
|
||||
/// <summary>
|
||||
/// 视觉遮挡阈值 (0.0 - 1.0)
|
||||
/// </summary>
|
||||
protected const double TargetObscurationThreshold = 0.8;
|
||||
|
||||
/// <summary>
|
||||
/// 干扰处理组件
|
||||
/// </summary>
|
||||
protected readonly JammableComponent _jammingComponent;
|
||||
|
||||
/// <summary>
|
||||
/// 获取目标是否被烟幕遮挡 (由任何烟幕引起)
|
||||
/// </summary>
|
||||
protected bool IsTargetObscured { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 获取设备支持的干扰类型
|
||||
/// </summary>
|
||||
@ -141,16 +156,19 @@ namespace ThreatSource.Indicator
|
||||
/// <remarks>
|
||||
/// 更新过程:
|
||||
/// - 检查激活状态
|
||||
/// - 更新干扰状态
|
||||
/// - 更新干扰状态 (包括JammableComponent和烟幕遮挡)
|
||||
/// - 更新最后已知目标方向(如果未被遮挡)
|
||||
/// - 处理指示器特定逻辑
|
||||
/// </remarks>
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
// 更新干扰状态
|
||||
_jammingComponent.UpdateJammingStatus(deltaTime);
|
||||
|
||||
if (IsActive)
|
||||
{
|
||||
// Always check obscuration status in each update cycle
|
||||
CheckOverallObscuration();
|
||||
|
||||
UpdateIndicator(deltaTime);
|
||||
}
|
||||
}
|
||||
@ -177,5 +195,82 @@ namespace ThreatSource.Indicator
|
||||
/// 包括位置、姿态、目标关联和干扰状态等
|
||||
/// </remarks>
|
||||
public abstract IndicatorRunningState GetRunningState();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 检查所有活动的烟幕,确定目标是否被任何一个遮挡
|
||||
/// </summary>
|
||||
private void CheckOverallObscuration()
|
||||
{
|
||||
bool currentlyObscured = false;
|
||||
if (string.IsNullOrEmpty(TargetId))
|
||||
{
|
||||
IsTargetObscured = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var target = SimulationManager.GetEntityById(TargetId) as BaseEquipment;
|
||||
if (target == null)
|
||||
{
|
||||
IsTargetObscured = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var activeSmokeGrenades = SimulationManager.GetEntitiesByType<SmokeGrenade>() // Get all smoke grenades
|
||||
.Where(sg => sg.IsActive); // Filter for active ones
|
||||
|
||||
if (!activeSmokeGrenades.Any())
|
||||
{
|
||||
IsTargetObscured = false; // No active smoke, not obscured
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3D observerPos = this.Position;
|
||||
Vector3D targetCenter = target.Position;
|
||||
Orientation targetOrient = target.Orientation;
|
||||
Vector3D targetDims = new(target.Properties.Length, target.Properties.Height, target.Properties.Width);
|
||||
|
||||
foreach (var smokeGrenade in activeSmokeGrenades)
|
||||
{
|
||||
if (smokeGrenade == null) continue;
|
||||
|
||||
try
|
||||
{
|
||||
Vector3D smokeCenter = smokeGrenade.Position;
|
||||
Orientation smokeOrient = smokeGrenade.Orientation;
|
||||
Vector3D smokeDims;
|
||||
if (smokeGrenade.config.SmokeType == Jammer.SmokeScreenType.Cloud)
|
||||
{
|
||||
smokeDims = new(smokeGrenade.config.CloudDiameter, smokeGrenade.config.Thickness, smokeGrenade.config.CloudDiameter);
|
||||
}
|
||||
else
|
||||
{
|
||||
smokeDims = new(smokeGrenade.config.WallWidth, smokeGrenade.config.WallHeight, smokeGrenade.config.Thickness);
|
||||
}
|
||||
|
||||
double ratio = ObscurationUtils.CalculateProjectedOverlapRatio(
|
||||
observerPos,
|
||||
smokeCenter, smokeDims, smokeOrient,
|
||||
targetCenter, targetDims, targetOrient
|
||||
);
|
||||
|
||||
if (ratio >= TargetObscurationThreshold)
|
||||
{
|
||||
currentlyObscured = true;
|
||||
break; // Found an obscuring smoke, no need to check others
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error checking obscuration by smoke {smokeGrenade.Id} for indicator {Id}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if (IsTargetObscured != currentlyObscured)
|
||||
{
|
||||
IsTargetObscured = currentlyObscured;
|
||||
Debug.WriteLine($"Indicator {Id}: Overall obscuration status updated. IsTargetObscured: {IsTargetObscured}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,22 +83,24 @@ namespace ThreatSource.Indicator
|
||||
/// <param name="deltaTime">时间步长,单位:秒</param>
|
||||
/// <remarks>
|
||||
/// 实现红外测角仪特定的更新逻辑:
|
||||
/// - 只有在未被干扰时才执行跟踪
|
||||
/// - 只有在未被电子干扰且目标未被烟幕遮挡时才执行跟踪
|
||||
/// - 更新跟踪状态
|
||||
/// - 处理目标跟踪
|
||||
/// </remarks>
|
||||
protected override void UpdateIndicator(double deltaTime)
|
||||
{
|
||||
// 只有在未被干扰时才执行跟踪
|
||||
if (!IsJammed)
|
||||
// 检查电子干扰和烟幕遮挡
|
||||
if (IsJammed || IsTargetObscured)
|
||||
{
|
||||
UpdateTracking();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 在干扰状态下,确保停止跟踪
|
||||
// 在干扰或遮挡状态下,确保停止跟踪
|
||||
StopTracking();
|
||||
// 可以选择性地在此处添加日志或状态更新
|
||||
if(IsTargetObscured) Debug.WriteLine($"InfraredTracker {Id}: Target obscured by smoke, stopping tracking.");
|
||||
return; // 不执行后续更新逻辑
|
||||
}
|
||||
|
||||
// 未被干扰且未被遮挡,执行正常跟踪
|
||||
UpdateTracking();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -119,7 +121,12 @@ namespace ThreatSource.Indicator
|
||||
var missile = SimulationManager.GetEntityById(MissileId);
|
||||
|
||||
if (target is not SimulationElement targetElement ||
|
||||
missile is not InfraredCommandGuidedMissile missileElement) return;
|
||||
missile is not InfraredCommandGuidedMissile missileElement)
|
||||
{
|
||||
// 如果目标或导弹无效,停止跟踪
|
||||
StopTracking();
|
||||
return;
|
||||
}
|
||||
|
||||
IsTracking = true;
|
||||
|
||||
@ -132,7 +139,8 @@ namespace ThreatSource.Indicator
|
||||
// 计算测角仪到导弹的向量
|
||||
Vector3D trackerToMissile = missileElement.Position - Position;
|
||||
|
||||
// 计算测角仪到目标的向量
|
||||
// 计算测角仪到目标的向量 (使用最后已知方向,以防目标瞬间无效)
|
||||
// Note: This uses the current target position. If obscured, UpdateTracking shouldn't be called.
|
||||
Vector3D trackerToTarget = targetElement.Position - Position;
|
||||
|
||||
// 发送制导指令事件
|
||||
@ -307,6 +315,7 @@ namespace ThreatSource.Indicator
|
||||
/// - 工作类型和状态
|
||||
/// - 位置和姿态信息
|
||||
/// - 激活状态
|
||||
/// - 干扰状态 (包括电子干扰和烟幕遮挡)
|
||||
/// </remarks>
|
||||
public override IndicatorRunningState GetRunningState()
|
||||
{
|
||||
@ -319,7 +328,7 @@ namespace ThreatSource.Indicator
|
||||
Position = Position,
|
||||
Orientation = Orientation,
|
||||
IsActive = IsActive,
|
||||
JammingState = IsJammed ? JammingState.Jammed : JammingState.Normal
|
||||
JammingState = (IsJammed || IsTargetObscured) ? JammingState.Jammed : JammingState.Normal
|
||||
};
|
||||
}
|
||||
|
||||
@ -334,17 +343,21 @@ namespace ThreatSource.Indicator
|
||||
/// - 目标关联状态
|
||||
/// - 工作状态
|
||||
/// - 跟踪状态
|
||||
/// - 干扰状态
|
||||
/// - 干扰/遮挡状态
|
||||
/// </remarks>
|
||||
public override string GetStatus()
|
||||
{
|
||||
string jammingStatusString = "正常";
|
||||
if (IsJammed) jammingStatusString = "受电子干扰";
|
||||
if (IsTargetObscured) jammingStatusString = IsJammed ? "受电子干扰和烟幕遮挡" : "目标被烟幕遮挡";
|
||||
|
||||
return $"红外测角仪 {Id}:\n" +
|
||||
$" 位置: {Position}\n" +
|
||||
$" 跟踪导弹: {MissileId ?? "无"}\n" +
|
||||
$" 目标: {TargetId ?? "无"}\n" +
|
||||
$" 状态: {(IsActive ? "活动" : "已销毁")}\n" +
|
||||
$" 跟踪状态: {(IsTracking ? "跟踪中" : "未跟踪")}\n" +
|
||||
$" 干扰状态: {(IsJammed ? "受干扰" : "正常")}\n";
|
||||
$" 干扰/遮挡状态: {jammingStatusString}\n";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -168,21 +168,52 @@ namespace ThreatSource.Indicator
|
||||
/// <param name="deltaTime">时间步长,单位:秒</param>
|
||||
/// <remarks>
|
||||
/// 更新过程:
|
||||
/// - 检查干扰状态
|
||||
/// - 更新激光指向
|
||||
/// - 发布状态更新事件
|
||||
/// - 检查电子干扰和烟幕遮挡状态
|
||||
/// - 更新激光指向 (仅当未被干扰/遮挡时)
|
||||
/// - 发布状态更新事件 (仅当未被干扰/遮挡且方向更新时)
|
||||
/// </remarks>
|
||||
protected override void UpdateIndicator(double deltaTime)
|
||||
{
|
||||
if (IsBeamOn && !IsJammed)
|
||||
// 检查电子干扰和烟幕遮挡
|
||||
if (IsJammed || IsTargetObscured)
|
||||
{
|
||||
// 更新驾束仪的激光指向
|
||||
// 如果被干扰或遮挡,不更新激光方向,也不发布事件
|
||||
// 激光束本身不一定关闭,除非干扰处理逻辑明确要求(HandleJammingApplied)
|
||||
// 或者产品设计要求在遮挡时关闭光束(可以添加到 StopBeamIllumination 调用)
|
||||
if(IsTargetObscured) Debug.WriteLine($"LaserBeamRider {Id}: Target obscured by smoke, direction update skipped.");
|
||||
return; // 不执行后续更新逻辑
|
||||
}
|
||||
|
||||
// 未被干扰/遮挡,且光束开启,则更新方向并发布事件
|
||||
if (IsBeamOn)
|
||||
{
|
||||
// 更新驾束仪的激光指向 (使用基类维护的 LastKnownTargetDirection)
|
||||
// We rely on BaseIndicator.UpdateTargetDirectionIfNotObscured()
|
||||
// to keep LastKnownTargetDirection updated when not obscured.
|
||||
// If it becomes obscured, UpdateIndicator stops calling this section,
|
||||
// and LaserDirection retains the value from the last successful update.
|
||||
|
||||
// Alternative: Directly use LastKnownTargetDirection if always desired when not obscured.
|
||||
// LaserDirection = LastKnownTargetDirection;
|
||||
|
||||
// Original logic: Recalculate based on current target position if needed
|
||||
// This check happens *after* the IsObscured check, so target should be visible.
|
||||
if (TargetId != null && SimulationManager.GetEntityById(TargetId) is SimulationElement target)
|
||||
{
|
||||
Vector3D targetPosition = target.Position;
|
||||
LaserDirection = (targetPosition - Position).Normalize();
|
||||
Debug.WriteLine($"激光驾束仪 {Id} 更新激光指向: {LaserDirection}");
|
||||
PublishLaserBeamEvent();
|
||||
Vector3D newDirection = (targetPosition - Position).Normalize();
|
||||
// Only update and publish if the direction actually changes significantly (optional optimization)
|
||||
if ((newDirection - LaserDirection).MagnitudeSquared() > 1e-9)
|
||||
{
|
||||
LaserDirection = newDirection;
|
||||
Debug.WriteLine($"激光驾束仪 {Id} 更新激光指向: {LaserDirection}");
|
||||
PublishLaserBeamEvent(); // Publish event only when direction is updated
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Target lost or invalid, maybe stop beam?
|
||||
// StopBeamIllumination();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -201,14 +232,13 @@ namespace ThreatSource.Indicator
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
IsActive = true;
|
||||
// 先订阅干扰事件
|
||||
// IsActive = true; // Base class handles IsActive now
|
||||
SimulationManager.SubscribeToEvent<LaserJammingEvent>(OnLaserJamming);
|
||||
Debug.WriteLine($"激光驾束仪 {Id} 已激活");
|
||||
// 激光驾束仪开始照射
|
||||
StartBeamIllumination();
|
||||
base.Activate(); // Call base Activate LAST to ensure subscriptions are set up before potential events
|
||||
}
|
||||
base.Activate();
|
||||
// base.Activate(); // Should be called within the if block
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -225,16 +255,16 @@ namespace ThreatSource.Indicator
|
||||
{
|
||||
if (IsActive)
|
||||
{
|
||||
IsActive = false;
|
||||
// IsActive = false; // Base class handles IsActive now
|
||||
if (IsBeamOn)
|
||||
{
|
||||
StopBeamIllumination();
|
||||
}
|
||||
// 取消订阅干扰事件
|
||||
SimulationManager.UnsubscribeFromEvent<LaserJammingEvent>(OnLaserJamming);
|
||||
base.Deactivate(); // Call base Deactivate FIRST to handle unsubscriptions before local cleanup
|
||||
Debug.WriteLine($"激光驾束仪 {Id} 已停用");
|
||||
}
|
||||
base.Deactivate();
|
||||
// base.Deactivate(); // Should be called within the if block
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -352,7 +382,7 @@ namespace ThreatSource.Indicator
|
||||
/// - 工作类型和状态
|
||||
/// - 位置和姿态信息
|
||||
/// - 激活状态
|
||||
/// - 干扰状态
|
||||
/// - 干扰/遮挡状态
|
||||
/// </remarks>
|
||||
public override IndicatorRunningState GetRunningState()
|
||||
{
|
||||
@ -365,7 +395,7 @@ namespace ThreatSource.Indicator
|
||||
Position = Position,
|
||||
Orientation = Orientation,
|
||||
IsActive = IsActive,
|
||||
JammingState = IsJammed ? JammingState.Jammed : JammingState.Normal
|
||||
JammingState = (IsJammed || IsTargetObscured) ? JammingState.Jammed : JammingState.Normal
|
||||
};
|
||||
}
|
||||
|
||||
@ -379,15 +409,20 @@ namespace ThreatSource.Indicator
|
||||
/// - 激光参数(功率、发散角等)
|
||||
/// - 工作状态(激活、照射等)
|
||||
/// - 制导范围参数
|
||||
/// - 干扰/遮挡状态
|
||||
/// </remarks>
|
||||
public override string GetStatus()
|
||||
{
|
||||
string jammingStatusString = "正常";
|
||||
if (IsJammed) jammingStatusString = "受电子干扰";
|
||||
if (IsTargetObscured) jammingStatusString = IsJammed ? "受电子干扰和烟幕遮挡" : "目标被烟幕遮挡";
|
||||
|
||||
return $"激光驾束仪 {Id}:\n" +
|
||||
$" 位置: {Position}\n" +
|
||||
$" 方向: {LaserDirection}\n" +
|
||||
$" 激活状态: {(IsActive ? "激活" : "未激活")}\n" +
|
||||
$" 激光束状态: {(IsBeamOn ? "开启" : "关闭")}\n" +
|
||||
$" 干扰状态: {(IsJammed ? "受干扰" : "正常")}\n" +
|
||||
$" 干扰/遮挡状态: {jammingStatusString}\n" +
|
||||
$" 激光功率: {LaserPower} W\n" +
|
||||
$" 发散角: {BeamDivergence} rad\n" +
|
||||
$" 控制场直径: {ControlFieldDiameter} m\n" +
|
||||
|
||||
@ -127,19 +127,33 @@ namespace ThreatSource.Indicator
|
||||
/// <remarks>
|
||||
/// 更新过程:
|
||||
/// - 检查激活状态
|
||||
/// - 检查干扰状态
|
||||
/// - 检查电子干扰和烟幕遮挡状态
|
||||
/// - 更新照射状态
|
||||
/// - 发布状态事件
|
||||
/// - 更新指示器朝向 (仅当未被干扰/遮挡时)
|
||||
/// - 发布状态事件 (仅当未被干扰/遮挡且照射开启时)
|
||||
/// </remarks>
|
||||
protected override void UpdateIndicator(double deltaTime)
|
||||
{
|
||||
if (!IsJammed)
|
||||
// 检查电子干扰和烟幕遮挡
|
||||
if (IsJammed || IsTargetObscured)
|
||||
{
|
||||
UpdateOrientation();
|
||||
if (IsIlluminationOn)
|
||||
{
|
||||
PublishIlluminationUpdateEvent();
|
||||
}
|
||||
// 如果被干扰或遮挡,不更新朝向,也不发布更新事件
|
||||
// 激光照射本身不一定停止,除非干扰处理逻辑明确要求(HandleJammingApplied)
|
||||
// 或设计要求在遮挡时停止照射 (可以添加到 StopLaserIllumination 调用)
|
||||
if(IsTargetObscured) Debug.WriteLine($"LaserDesignator {Id}: Target obscured by smoke, orientation update skipped.");
|
||||
// Still need to publish *if* illumination is on but orientation is frozen?
|
||||
// Current plan: Do not publish update event if obscured/jammed.
|
||||
return; // 不执行后续更新逻辑
|
||||
}
|
||||
|
||||
// 未被干扰/遮挡,执行正常更新
|
||||
// 更新朝向
|
||||
UpdateOrientation(); // This now happens only if not jammed/obscured
|
||||
|
||||
// 如果正在照射,发布更新事件
|
||||
if (IsIlluminationOn)
|
||||
{
|
||||
PublishIlluminationUpdateEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,6 +165,9 @@ namespace ThreatSource.Indicator
|
||||
if (target != null)
|
||||
{
|
||||
Vector3D direction = (target.Position - Position).Normalize();
|
||||
// Check if direction is valid before calculating angles
|
||||
if (direction.MagnitudeSquared() < 1e-9) return;
|
||||
|
||||
double yaw = Math.PI + Math.Atan2(direction.Z, direction.X);
|
||||
double pitch = -Math.Asin(direction.Y);
|
||||
Orientation = new Orientation(yaw, pitch, 0);
|
||||
@ -274,13 +291,12 @@ namespace ThreatSource.Indicator
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
IsActive = true;
|
||||
// 先订阅事件
|
||||
// IsActive = true; // Base class handles IsActive
|
||||
SimulationManager.SubscribeToEvent<LaserJammingEvent>(OnLaserJamming);
|
||||
// 再开始照射
|
||||
StartLaserIllumination();
|
||||
base.Activate(); // Call base Activate LAST
|
||||
}
|
||||
base.Activate();
|
||||
// base.Activate(); // Should be inside the if block
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -298,11 +314,12 @@ namespace ThreatSource.Indicator
|
||||
{
|
||||
if (IsActive)
|
||||
{
|
||||
IsActive = false;
|
||||
// IsActive = false; // Base class handles IsActive
|
||||
StopLaserIllumination();
|
||||
SimulationManager.UnsubscribeFromEvent<LaserJammingEvent>(OnLaserJamming);
|
||||
base.Deactivate(); // Call base Deactivate FIRST
|
||||
}
|
||||
base.Deactivate();
|
||||
// base.Deactivate(); // Should be inside the if block
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -376,7 +393,7 @@ namespace ThreatSource.Indicator
|
||||
/// - 工作类型和状态
|
||||
/// - 位置和姿态信息
|
||||
/// - 激活状态
|
||||
/// - 干扰状态
|
||||
/// - 干扰/遮挡状态
|
||||
/// </remarks>
|
||||
public override IndicatorRunningState GetRunningState()
|
||||
{
|
||||
@ -389,7 +406,7 @@ namespace ThreatSource.Indicator
|
||||
Position = Position,
|
||||
Orientation = Orientation,
|
||||
IsActive = IsActive,
|
||||
JammingState = IsJammed ? JammingState.Jammed : JammingState.Normal
|
||||
JammingState = (IsJammed || IsTargetObscured) ? JammingState.Jammed : JammingState.Normal
|
||||
};
|
||||
}
|
||||
|
||||
@ -403,11 +420,15 @@ namespace ThreatSource.Indicator
|
||||
/// - 位置信息
|
||||
/// - 目标关联状态
|
||||
/// - 工作状态
|
||||
/// - 干扰状态
|
||||
/// - 干扰/遮挡状态
|
||||
/// - 激光参数
|
||||
/// </remarks>
|
||||
public override string GetStatus()
|
||||
{
|
||||
string jammingStatusString = "正常";
|
||||
if (IsJammed) jammingStatusString = "受电子干扰";
|
||||
if (IsTargetObscured) jammingStatusString = IsJammed ? "受电子干扰和烟幕遮挡" : "目标被烟幕遮挡";
|
||||
|
||||
return $"激光目标指示器 {Id}:\n" +
|
||||
$" 位置: {Position}\n" +
|
||||
$" 目标: {TargetId}\n" +
|
||||
@ -415,7 +436,7 @@ namespace ThreatSource.Indicator
|
||||
$" 朝向: {Orientation}\n" +
|
||||
$" 激活状态: {(IsActive ? "激活" : "未激活")}\n" +
|
||||
$" 照射状态: {(IsIlluminationOn ? "正在照射" : "未照射")}\n" +
|
||||
$" 干扰状态: {(IsJammed ? "受干扰" : "正常")}\n" +
|
||||
$" 干扰/遮挡状态: {jammingStatusString}\n" +
|
||||
$" 编码状态: {(LaserCodeConfig != null ? "启用" : "禁用")}\n" +
|
||||
$" 激光功率: {LaserPower:E} W";
|
||||
}
|
||||
|
||||
184
ThreatSource/src/Utils/ObscurationUtils.cs
Normal file
184
ThreatSource/src/Utils/ObscurationUtils.cs
Normal file
@ -0,0 +1,184 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ThreatSource.Simulation; // Assuming SimulationElement, Vector3D, Orientation are here or accessible
|
||||
|
||||
namespace ThreatSource.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods for calculating visual obscuration between simulation elements.
|
||||
/// Assumes objects can be represented by an oriented bounding box (OBB).
|
||||
/// </summary>
|
||||
public static class ObscurationUtils
|
||||
{
|
||||
// Represents a 2D Axis-Aligned Bounding Box
|
||||
private struct Rect
|
||||
{
|
||||
public double MinU, MinV, MaxU, MaxV;
|
||||
public double Width => MaxU - MinU;
|
||||
public double Height => MaxV - MinV;
|
||||
public bool IsValid => Width >= 0 && Height >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the ratio of the background object's projected area that is obscured by the foreground object,
|
||||
/// as seen from the observer's position. Both objects are treated as Oriented Bounding Boxes (OBBs).
|
||||
/// </summary>
|
||||
/// <param name="observerPos">The position of the observer.</param>
|
||||
/// <param name="foregroundCenter">World position of the foreground object's center.</param>
|
||||
/// <param name="foregroundDims">Dimensions (e.g., Length, Height, Width) of the foreground object aligned with its local axes.</param>
|
||||
/// <param name="foregroundOrient">Orientation of the foreground object.</param>
|
||||
/// <param name="backgroundCenter">World position of the background object's center.</param>
|
||||
/// <param name="backgroundDims">Dimensions of the background object aligned with its local axes.</param>
|
||||
/// <param name="backgroundOrient">Orientation of the background object.</param>
|
||||
/// <returns>A ratio from 0.0 (no obscuration) to 1.0 (fully obscured).</returns>
|
||||
public static double CalculateProjectedOverlapRatio(
|
||||
Vector3D observerPos,
|
||||
Vector3D foregroundCenter,
|
||||
Vector3D foregroundDims,
|
||||
Orientation foregroundOrient,
|
||||
Vector3D backgroundCenter,
|
||||
Vector3D backgroundDims,
|
||||
Orientation backgroundOrient)
|
||||
{
|
||||
// --- Input Validation ---
|
||||
if (backgroundDims.X <= 0 || backgroundDims.Y <= 0 || backgroundDims.Z <= 0 ||
|
||||
foregroundDims.X <= 0 || foregroundDims.Y <= 0 || foregroundDims.Z <= 0)
|
||||
{
|
||||
// Invalid dimensions
|
||||
System.Diagnostics.Debug.WriteLine("Warning: Invalid dimensions for obscuration calculation.");
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// --- Define Projection Plane ---
|
||||
Vector3D los = (backgroundCenter - observerPos).Normalize();
|
||||
if (los.MagnitudeSquared() < 1e-9) return 1.0; // Observer at background object center, fully obscured conceptually
|
||||
|
||||
// Create basis vectors for the 2D projection plane (perpendicular to Line of Sight)
|
||||
Vector3D upApprox = (Math.Abs(Vector3D.DotProduct(los, Vector3D.UnitY)) < 0.99) ? Vector3D.UnitY : Vector3D.UnitX;
|
||||
Vector3D uAxis = Vector3D.CrossProduct(upApprox, los).Normalize();
|
||||
Vector3D vAxis = Vector3D.CrossProduct(los, uAxis).Normalize();
|
||||
|
||||
if (uAxis.MagnitudeSquared() < 1e-9 || vAxis.MagnitudeSquared() < 1e-9)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Warning: Failed to create projection basis vectors.");
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// --- Project Objects and Get 2D AABBs ---
|
||||
Rect backgroundRect = CalculateProjectedAABB(observerPos, backgroundCenter, backgroundDims, backgroundOrient, uAxis, vAxis);
|
||||
Rect foregroundRect = CalculateProjectedAABB(observerPos, foregroundCenter, foregroundDims, foregroundOrient, uAxis, vAxis);
|
||||
|
||||
if (!backgroundRect.IsValid)
|
||||
{
|
||||
return 0.0; // Background projection has no area
|
||||
}
|
||||
|
||||
// --- Calculate Intersection and Ratio ---
|
||||
double backgroundArea = backgroundRect.Width * backgroundRect.Height;
|
||||
if (backgroundArea < 1e-9)
|
||||
{
|
||||
// If background has no projected area, it cannot be obscured.
|
||||
// Or consider it fully obscured if foreground *does* have area?
|
||||
// Safest is 0 obscuration if background isn't visible.
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double intersectionArea = CalculateRectangleIntersectionArea(backgroundRect, foregroundRect);
|
||||
|
||||
return Math.Clamp(intersectionArea / backgroundArea, 0.0, 1.0);
|
||||
}
|
||||
|
||||
|
||||
// === Geometric Helper Methods ===
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the 8 world-space corners of an Oriented Bounding Box (OBB).
|
||||
/// </summary>
|
||||
private static List<Vector3D> GetWorldOBBCorners(Vector3D center, Vector3D dimensions, Orientation orientation)
|
||||
{
|
||||
Vector3D halfDim = dimensions * 0.5;
|
||||
// Assuming Orientation class can provide basis vectors or a rotation matrix.
|
||||
// Using a hypothetical GetBasisVectors() returning [localX, localY, localZ] in world space.
|
||||
// TODO: Replace with actual Orientation method.
|
||||
(Vector3D xAxis, Vector3D yAxis, Vector3D zAxis) = GetOrientationBasisVectors(orientation);
|
||||
|
||||
var corners = new List<Vector3D>(8);
|
||||
corners.Add(center - xAxis * halfDim.X - yAxis * halfDim.Y - zAxis * halfDim.Z);
|
||||
corners.Add(center + xAxis * halfDim.X - yAxis * halfDim.Y - zAxis * halfDim.Z);
|
||||
corners.Add(center + xAxis * halfDim.X + yAxis * halfDim.Y - zAxis * halfDim.Z);
|
||||
corners.Add(center - xAxis * halfDim.X + yAxis * halfDim.Y - zAxis * halfDim.Z);
|
||||
corners.Add(center - xAxis * halfDim.X - yAxis * halfDim.Y + zAxis * halfDim.Z);
|
||||
corners.Add(center + xAxis * halfDim.X - yAxis * halfDim.Y + zAxis * halfDim.Z);
|
||||
corners.Add(center + xAxis * halfDim.X + yAxis * halfDim.Y + zAxis * halfDim.Z);
|
||||
corners.Add(center - xAxis * halfDim.X + yAxis * halfDim.Y + zAxis * halfDim.Z);
|
||||
return corners;
|
||||
}
|
||||
|
||||
// Placeholder for getting Orientation axes
|
||||
private static (Vector3D, Vector3D, Vector3D) GetOrientationBasisVectors(Orientation orientation)
|
||||
{
|
||||
// This needs to be implemented based on the actual Orientation class definition.
|
||||
// Example: If Orientation has ToRotationMatrix() returning a 3x3 matrix:
|
||||
// var matrix = orientation.ToRotationMatrix();
|
||||
// return (matrix.GetColumn(0), matrix.GetColumn(1), matrix.GetColumn(2));
|
||||
|
||||
// Example: If Orientation directly stores axes:
|
||||
// return (orientation.XAxis, orientation.YAxis, orientation.ZAxis);
|
||||
|
||||
// Fallback: Assume identity orientation if method not available
|
||||
System.Diagnostics.Debug.WriteLine("Warning: Using identity orientation basis vectors.");
|
||||
return (Vector3D.UnitX, Vector3D.UnitY, Vector3D.UnitZ);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the 2D Axis-Aligned Bounding Box (AABB) of an object's projected corners.
|
||||
/// </summary>
|
||||
private static Rect CalculateProjectedAABB(
|
||||
Vector3D observerPos,
|
||||
Vector3D objectCenter,
|
||||
Vector3D objectDimensions,
|
||||
Orientation objectOrientation,
|
||||
Vector3D uAxis, // Projection plane U axis (world space)
|
||||
Vector3D vAxis) // Projection plane V axis (world space)
|
||||
{
|
||||
List<Vector3D> worldCorners = GetWorldOBBCorners(objectCenter, objectDimensions, objectOrientation);
|
||||
|
||||
double minU = double.MaxValue, maxU = double.MinValue;
|
||||
double minV = double.MaxValue, maxV = double.MinValue;
|
||||
|
||||
foreach (var corner in worldCorners)
|
||||
{
|
||||
Vector3D observerToCorner = corner - observerPos;
|
||||
double u = Vector3D.DotProduct(observerToCorner, uAxis);
|
||||
double v = Vector3D.DotProduct(observerToCorner, vAxis);
|
||||
|
||||
minU = Math.Min(minU, u);
|
||||
maxU = Math.Max(maxU, u);
|
||||
minV = Math.Min(minV, v);
|
||||
maxV = Math.Max(maxV, v);
|
||||
}
|
||||
|
||||
if (minU > maxU || minV > maxV)
|
||||
{
|
||||
// No valid projection (e.g., object is behind observer or has zero size)
|
||||
return new Rect { MinU = 0, MaxU = -1, MinV = 0, MaxV = -1 }; // Indicate invalid rect
|
||||
}
|
||||
|
||||
return new Rect { MinU = minU, MinV = minV, MaxU = maxU, MaxV = maxV };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the intersection area of two 2D AABB rectangles.
|
||||
/// </summary>
|
||||
private static double CalculateRectangleIntersectionArea(Rect r1, Rect r2)
|
||||
{
|
||||
if (!r1.IsValid || !r2.IsValid) return 0;
|
||||
|
||||
double xOverlap = Math.Max(0, Math.Min(r1.MaxU, r2.MaxU) - Math.Max(r1.MinU, r2.MinU));
|
||||
double yOverlap = Math.Max(0, Math.Min(r1.MaxV, r2.MaxV) - Math.Max(r1.MinV, r2.MinV));
|
||||
|
||||
return xOverlap * yOverlap;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user