mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-27 18:31:08 -04:00
Circles packs every part as its minimum enclosing circle at centers tangent to two of the work-area walls, placed discs and clamp keep-outs, bottom-left and left-bottom, with hexagonal and square lattice seeds. It bids only when every part fills at least 0.8 of its enclosing circle and otherwise returns everything unplaced at once. Default runs it as a third candidate. Rectangles' sheet-by-sheet stock loop moves to NestingEngines/SheetByStock (Rectangles output unchanged) and Default's candidate scoring and commit forwarding become shared helpers. Circles also runs the whole job preferring each offered stock and keeps the best result. Contract tests take shapes an engine can override. Synthetic round jobs, salvage-credited: Circles wins or ties all six with valid layouts; Default cost falls about 4% versus Irregular alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
36 lines
1.6 KiB
C#
36 lines
1.6 KiB
C#
#nullable enable
|
|
using System.Linq;
|
|
using OpenNest.Engine.Jobs;
|
|
|
|
namespace OpenNest.Engine.NestingEngines;
|
|
|
|
/// <summary>Ranks whole-job candidate results: a valid layout before an invalid one, then the
|
|
/// fewest unplaced parts, then the lowest <see cref="NestJobCost"/>; remaining ties keep
|
|
/// candidate order.</summary>
|
|
internal sealed record CandidateScore(NestJobResult Result, bool Valid, int Unplaced, double Cost, int Order, bool Priced)
|
|
{
|
|
public static CandidateScore Of(NestJob job, NestJobResult result, int order)
|
|
{
|
|
var valid = NestLayoutCheck.Violations(job, result).Count == 0;
|
|
var placed = result.Plates.Sum(p => p.Placements.Count);
|
|
var unplaced = System.Math.Max(0, job.Parts.Sum(p => p.Quantity) - placed);
|
|
// Cost needs every placement to name a known part, which only a valid result guarantees.
|
|
var cost = valid ? NestJobCost.Evaluate(job, result) : double.PositiveInfinity;
|
|
return new CandidateScore(result, valid, unplaced, cost, order, NestJobCost.UsesExplicitCosts(job));
|
|
}
|
|
|
|
public bool IsBetterThan(CandidateScore other)
|
|
{
|
|
if (Valid != other.Valid)
|
|
return Valid;
|
|
if (Unplaced != other.Unplaced)
|
|
return Unplaced < other.Unplaced;
|
|
if (Priced && Cost != other.Cost)
|
|
return Cost < other.Cost;
|
|
var scale = System.Math.Max(1, System.Math.Max(System.Math.Abs(Cost), System.Math.Abs(other.Cost)));
|
|
if (double.IsFinite(Cost) && double.IsFinite(other.Cost) && System.Math.Abs(Cost - other.Cost) > 1e-9 * scale)
|
|
return Cost < other.Cost;
|
|
return Order < other.Order;
|
|
}
|
|
}
|