using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using OpenNest.CNC;
using OpenNest.CNC.CuttingPlanning;
using OpenNest.Geometry;
using OpenNest.Support;
namespace OpenNest.Engine.CuttingPlanning;
///
/// Capture-time support tabs for one regenerated part (docs/support-tabs.md): with a support
/// bed and on, a part the bed
/// cannot hold level has its perimeter tabbed where the tab steadies it. A supported part, a
/// pinned perimeter (the drawing wins), a missing tab configuration or material the analyzer
/// cannot read leave the preparation as it was.
///
internal static class SupportTabCapture
{
internal static PreparedContours Prepare(PreparedContours prepared, Program ownedClean, Vector location,
CuttingPlanRequest request, CancellationToken token)
{
var parameters = request.ConfirmedParameters;
if (prepared == null || request.SupportBed == null || parameters?.SupportTabs != true
|| parameters.TabConfig == null || prepared.IsPinned(prepared.PerimeterOrdinal))
return prepared;
token.ThrowIfCancellationRequested();
// The clean program is the drawing's own, rotated as placed: its material at the
// part's location is the material that drops onto the bed. The tab keeps clear of
// inside corners by twice its size.
var support = SupportAnalyzer.Analyze(request.SupportBed, request.SupportQuadrant,
SupportMaterial.FromProgram(ownedClean, location),
SupportAnalyzer.InsideCornerClearance(parameters.TabConfig.Size));
if (!support.IsComplete || support.IsSupported || support.BestTabPoint == null)
return prepared;
return prepared.WithSupportTab(support, location, parameters.TabConfig);
}
///
/// Notes for a planned support-tabbed part whose tab, at its perimeter entry, cannot hold it
/// level or sits in an inside corner. Advisory: they never refuse or downgrade a plan.
///
internal static IEnumerable Notes(FixedProgramPlacement planned)
{
var prepared = planned.Prepared;
if (prepared?.Support is not { } support
|| planned.ContourChoices.FirstOrDefault(c => c.ContourOrdinal == prepared.PerimeterOrdinal) is not { } entry)
yield break;
var tab = entry.Point + planned.Location;
var at = FormattableString.Invariant($"({tab.X:0.####}, {tab.Y:0.####})");
if (!support.TabCanSupport)
yield return JointCuttingPlanSearch.Finding(planned, null,
$"The support grid cannot hold it level, and no single tab can: its tab at {at} steadies it "
+ "as far as one tab can, but it may still tip. Add tabs by hand or move the part, then replan.");
else if (!prepared.HoldsLevelAt(entry))
yield return JointCuttingPlanSearch.Finding(planned, null,
$"The support grid cannot hold it level, and no lead-in fits where a tab would: its tab at {at} "
+ "is the steadiest that fits, but it may still tip. Shorten the lead-in in Cutting Settings or "
+ "space the parts farther apart, then replan.");
if (!prepared.IsClearOfInsideCorners(entry))
yield return JointCuttingPlanSearch.Finding(planned, null,
$"Its support tab at {at} is in an inside corner, because no lead-in fits clear of one; "
+ "its nub will be hard to grind flush.");
}
}