using System.Numerics;
using AcDream.Core.Physics;
namespace AcDream.App.Rendering;
/// Same-frame render facts consumed by the developer DebugVM.
internal readonly record struct DebugVmRenderFacts(
int VisibleLandblocks,
int TotalLandblocks,
float NearestObjectDistance,
string NearestObjectLabel,
bool Colliding)
{
public static DebugVmRenderFacts Initial => new(
VisibleLandblocks: 0,
TotalLandblocks: 0,
NearestObjectDistance: float.PositiveInfinity,
NearestObjectLabel: "-",
Colliding: false);
}
internal interface IDebugVmRenderFactsSource
{
DebugVmRenderFacts DebugVmFacts { get; }
}
///
/// Owns render-derived DebugVM facts without retaining the physics registry,
/// camera, player controller, world state, or any other canonical runtime owner.
/// The pass-local diagnostics owner publishes through this focused query surface.
///
internal sealed class DebugVmRenderFactsPublisher : IDebugVmRenderFactsSource
{
internal const float PlayerCollisionRadius = 0.48f;
internal const float ContactThreshold = 0.05f;
public DebugVmRenderFacts DebugVmFacts { get; private set; } =
DebugVmRenderFacts.Initial;
///
/// Publishes the facts immediately before private presentation. A disabled
/// consumer leaves the prior snapshot untouched and does not enumerate the
/// borrowed shadow-object sequence.
///
public void PublishDebugVmFacts(
bool consumerActive,
int visibleLandblocks,
int totalLandblocks,
Vector3 nearestOrigin,
IEnumerable? shadowObjects)
{
if (!consumerActive)
return;
ArgumentNullException.ThrowIfNull(shadowObjects);
float nearestDistance = float.PositiveInfinity;
string nearestLabel = "-";
foreach (ShadowEntry shadow in shadowObjects)
{
float dx = shadow.Position.X - nearestOrigin.X;
float dy = shadow.Position.Y - nearestOrigin.Y;
float distance = MathF.Sqrt(dx * dx + dy * dy)
- shadow.Radius
- PlayerCollisionRadius;
if (distance < nearestDistance)
{
nearestDistance = distance;
nearestLabel = $"0x{shadow.EntityId:X8} {shadow.CollisionType}";
}
}
DebugVmFacts = new DebugVmRenderFacts(
visibleLandblocks,
totalLandblocks,
nearestDistance < 0f ? 0f : nearestDistance,
nearestLabel,
nearestDistance < ContactThreshold);
}
}