feat: add BestFitFilter and TileEvaluator for pair filtering and tiling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
46
OpenNest.Engine/BestFit/BestFitFilter.cs
Normal file
46
OpenNest.Engine/BestFit/BestFitFilter.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenNest.Engine.BestFit
|
||||
{
|
||||
public class BestFitFilter
|
||||
{
|
||||
public double MaxPlateWidth { get; set; }
|
||||
public double MaxPlateHeight { get; set; }
|
||||
public double MaxAspectRatio { get; set; } = 5.0;
|
||||
public double MinUtilization { get; set; } = 0.3;
|
||||
|
||||
public void Apply(List<BestFitResult> results)
|
||||
{
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (!result.Keep)
|
||||
continue;
|
||||
|
||||
if (result.ShortestSide > System.Math.Min(MaxPlateWidth, MaxPlateHeight))
|
||||
{
|
||||
result.Keep = false;
|
||||
result.Reason = "Exceeds plate dimensions";
|
||||
continue;
|
||||
}
|
||||
|
||||
var aspect = result.LongestSide / result.ShortestSide;
|
||||
|
||||
if (aspect > MaxAspectRatio)
|
||||
{
|
||||
result.Keep = false;
|
||||
result.Reason = string.Format("Aspect ratio {0:F1} exceeds max {1}", aspect, MaxAspectRatio);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result.Utilization < MinUtilization)
|
||||
{
|
||||
result.Keep = false;
|
||||
result.Reason = string.Format("Utilization {0:P0} below minimum", result.Utilization);
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Reason = "Valid";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
OpenNest.Engine/BestFit/Tiling/TileEvaluator.cs
Normal file
62
OpenNest.Engine/BestFit/Tiling/TileEvaluator.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.Math;
|
||||
|
||||
namespace OpenNest.Engine.BestFit.Tiling
|
||||
{
|
||||
public class TileEvaluator
|
||||
{
|
||||
public TileResult Evaluate(BestFitResult bestFit, Plate plate)
|
||||
{
|
||||
var plateWidth = plate.Size.Width - plate.EdgeSpacing.Left - plate.EdgeSpacing.Right;
|
||||
var plateHeight = plate.Size.Height - plate.EdgeSpacing.Top - plate.EdgeSpacing.Bottom;
|
||||
|
||||
var result1 = TryTile(bestFit, plateWidth, plateHeight, false);
|
||||
var result2 = TryTile(bestFit, plateWidth, plateHeight, true);
|
||||
return result1.PartsNested >= result2.PartsNested ? result1 : result2;
|
||||
}
|
||||
|
||||
private TileResult TryTile(BestFitResult bestFit, double plateWidth, double plateHeight, bool rotatePair)
|
||||
{
|
||||
var pairWidth = rotatePair ? bestFit.BoundingHeight : bestFit.BoundingWidth;
|
||||
var pairHeight = rotatePair ? bestFit.BoundingWidth : bestFit.BoundingHeight;
|
||||
var spacing = bestFit.Candidate.Spacing;
|
||||
|
||||
var cols = (int)System.Math.Floor((plateWidth + spacing) / (pairWidth + spacing));
|
||||
var rows = (int)System.Math.Floor((plateHeight + spacing) / (pairHeight + spacing));
|
||||
var pairsNested = cols * rows;
|
||||
var partsNested = pairsNested * 2;
|
||||
|
||||
var usedArea = partsNested * (bestFit.TrueArea / 2);
|
||||
var plateArea = plateWidth * plateHeight;
|
||||
|
||||
var placements = new List<PairPlacement>();
|
||||
|
||||
for (var row = 0; row < rows; row++)
|
||||
{
|
||||
for (var col = 0; col < cols; col++)
|
||||
{
|
||||
placements.Add(new PairPlacement
|
||||
{
|
||||
Position = new Vector(
|
||||
col * (pairWidth + spacing),
|
||||
row * (pairHeight + spacing)),
|
||||
PairRotation = rotatePair ? Angle.HalfPI : 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new TileResult
|
||||
{
|
||||
BestFit = bestFit,
|
||||
PairsNested = pairsNested,
|
||||
PartsNested = partsNested,
|
||||
Rows = rows,
|
||||
Columns = cols,
|
||||
Utilization = plateArea > 0 ? usedArea / plateArea : 0,
|
||||
Placements = placements,
|
||||
PairRotated = rotatePair
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
23
OpenNest.Engine/BestFit/Tiling/TileResult.cs
Normal file
23
OpenNest.Engine/BestFit/Tiling/TileResult.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Engine.BestFit.Tiling
|
||||
{
|
||||
public class TileResult
|
||||
{
|
||||
public BestFitResult BestFit { get; set; }
|
||||
public int PairsNested { get; set; }
|
||||
public int PartsNested { get; set; }
|
||||
public int Rows { get; set; }
|
||||
public int Columns { get; set; }
|
||||
public double Utilization { get; set; }
|
||||
public List<PairPlacement> Placements { get; set; }
|
||||
public bool PairRotated { get; set; }
|
||||
}
|
||||
|
||||
public class PairPlacement
|
||||
{
|
||||
public Vector Position { get; set; }
|
||||
public double PairRotation { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user