Collision.HasOverlap only needs the verdict, but it went through Check, which also collected crossing points. Triangulation, clipping and hole subtraction now live in one private OverlapRegions method shared by Check and HasOverlap, so verdict arithmetic stays single-sourced; Check output is unchanged. Tests: a frozen copy of the previous Collision is the oracle. 50,000 seeded HasOverlap verdicts and 2,400 bitwise Check results match it, plus containment, contact, hole and input-immutability cases. A Debug-only PerfCounters.CrossingPointScans counter proves HasOverlap no longer scans. Malformed polygons with null outer vertices still throw when the bounding boxes overlap (now ArgumentNullException from triangulation rather than NullReferenceException from ToLines). Measured (Release, same harness in both trees): about 44% less time per overlap-only polygon check, allocations 10.0 -> 7.9 MB per 155-pair sweep. The 169-part serialized corpus layout is byte-identical.
68 lines
3.0 KiB
C#
68 lines
3.0 KiB
C#
using System.Diagnostics;
|
|
using System.Threading;
|
|
|
|
namespace OpenNest
|
|
{
|
|
/// <summary>
|
|
/// Debug-only call counters for the fill hot paths. They confirm that a cache or shortcut
|
|
/// actually removes the work it claims to; Release builds compile the increments away.
|
|
/// </summary>
|
|
public static class PerfCounters
|
|
{
|
|
private static long findBestFits;
|
|
private static long offsetPerimeterEntities;
|
|
private static long partIntersects;
|
|
private static long fillScoreComputations;
|
|
private static long partBoundaryPreparations;
|
|
private static long partBoundsUpdates;
|
|
private static long featureBitmaskCells;
|
|
private static long crossingPointScans;
|
|
|
|
public static long FindBestFits => Interlocked.Read(ref findBestFits);
|
|
public static long OffsetPerimeterEntities => Interlocked.Read(ref offsetPerimeterEntities);
|
|
public static long PartIntersects => Interlocked.Read(ref partIntersects);
|
|
public static long FillScoreComputations => Interlocked.Read(ref fillScoreComputations);
|
|
public static long PartBoundaryPreparations => Interlocked.Read(ref partBoundaryPreparations);
|
|
public static long PartBoundsUpdates => Interlocked.Read(ref partBoundsUpdates);
|
|
public static long FeatureBitmaskCells => Interlocked.Read(ref featureBitmaskCells);
|
|
public static long CrossingPointScans => Interlocked.Read(ref crossingPointScans);
|
|
|
|
[Conditional("DEBUG")]
|
|
public static void CountFindBestFits() => Interlocked.Increment(ref findBestFits);
|
|
|
|
[Conditional("DEBUG")]
|
|
public static void CountOffsetPerimeterEntities() =>
|
|
Interlocked.Increment(ref offsetPerimeterEntities);
|
|
|
|
[Conditional("DEBUG")]
|
|
public static void CountPartIntersects() => Interlocked.Increment(ref partIntersects);
|
|
|
|
[Conditional("DEBUG")]
|
|
public static void CountFillScoreComputation() => Interlocked.Increment(ref fillScoreComputations);
|
|
|
|
[Conditional("DEBUG")]
|
|
public static void CountPartBoundaryPreparation() => Interlocked.Increment(ref partBoundaryPreparations);
|
|
|
|
[Conditional("DEBUG")]
|
|
public static void CountPartBoundsUpdate() => Interlocked.Increment(ref partBoundsUpdates);
|
|
|
|
[Conditional("DEBUG")]
|
|
public static void CountFeatureBitmaskCell() => Interlocked.Increment(ref featureBitmaskCells);
|
|
|
|
[Conditional("DEBUG")]
|
|
public static void CountCrossingPointScan() => Interlocked.Increment(ref crossingPointScans);
|
|
|
|
public static void Reset()
|
|
{
|
|
Interlocked.Exchange(ref findBestFits, 0);
|
|
Interlocked.Exchange(ref offsetPerimeterEntities, 0);
|
|
Interlocked.Exchange(ref partIntersects, 0);
|
|
Interlocked.Exchange(ref fillScoreComputations, 0);
|
|
Interlocked.Exchange(ref partBoundaryPreparations, 0);
|
|
Interlocked.Exchange(ref partBoundsUpdates, 0);
|
|
Interlocked.Exchange(ref featureBitmaskCells, 0);
|
|
Interlocked.Exchange(ref crossingPointScans, 0);
|
|
}
|
|
}
|
|
}
|