mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-26 04:01:06 -04:00
fix(clamps): test tiny clamps against both arc flattenings
Circumscribing every arc holds all material along convex arcs but drops a sliver along concave ones (a notch), so a tiny clamp 6e-7 into the material beside a notch read as clear. Tiny-clamp checks now test the part flattened both ways (all arcs inscribed, all arcs circumscribed): along any arc one of the two holds the real material, so together they find every real overlap. PM 407f5eb7-329f-41e4-947e-64ad52aa4034 (delta review s6-delta2-review.md)
This commit is contained in:
1 parent
07cba5719f
commit
f2cc471a33
4 files changed
+94
-21
No files matched your search
@@ -286,9 +286,10 @@ public static class PlateOverlapAnalyzer
|
||||
var profile = new ShapeProfile(part.Input.Source.Entities.Select(e => e.Clone()).ToList());
|
||||
if (profile.Perimeter == null)
|
||||
throw new ArgumentException("Drawing has no closed material contour.");
|
||||
var (outer, holes) = KeepOutOverlap.ConservativePolygons(profile, zone);
|
||||
var fine = new OverlapMaterial(outer, holes).Transform(part.Input.Rotation, part.Input.Location);
|
||||
if (KeepOutOverlap.MaterialEnters(fine.Outer, fine.Holes, zone))
|
||||
if (KeepOutOverlap.ConservativePolygons(profile, zone)
|
||||
.Select(polygons => new OverlapMaterial(polygons.Perimeter, polygons.Holes)
|
||||
.Transform(part.Input.Rotation, part.Input.Location))
|
||||
.Any(fine => KeepOutOverlap.MaterialEnters(fine.Outer, fine.Holes, zone)))
|
||||
findings.Add(TinyClampFinding(part, clamp));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -27,33 +27,43 @@ public static class KeepOutOverlap
|
||||
System.Math.Min(ToleranceFreeBelow, System.Math.Min(keepOut.Length, keepOut.Width) / 4);
|
||||
|
||||
/// <summary>
|
||||
/// A part's native contours flattened for a tiny keep-out, never through a grid-rounding
|
||||
/// offset: lines exact, arcs at <see cref="FlatteningTolerance"/>, perimeter arcs
|
||||
/// circumscribed and cutout arcs inscribed, so the polygons hold all real material and
|
||||
/// any real overlap is found. The price is a possible report within that tolerance of
|
||||
/// an arc, never a false clear.
|
||||
/// A part's native contours flattened two ways for a tiny keep-out, never through a
|
||||
/// grid-rounding offset: lines exact, every arc (perimeter and cutouts) inscribed in the
|
||||
/// first and circumscribed in the second, at <see cref="FlatteningTolerance"/>. Along a
|
||||
/// convex arc the circumscribed version holds all real material, along a concave arc
|
||||
/// (a notch, a round hole) the inscribed one does, so together they hold all of it and
|
||||
/// testing both finds any real overlap. The price is a possible report within that
|
||||
/// tolerance of an arc, never a false clear.
|
||||
/// </summary>
|
||||
public static (Polygon Perimeter, List<Polygon> Holes) ConservativePolygons(ShapeProfile profile, Box keepOut)
|
||||
public static IEnumerable<(Polygon Perimeter, List<Polygon> Holes)> ConservativePolygons(
|
||||
ShapeProfile profile, Box keepOut)
|
||||
{
|
||||
var tolerance = FlatteningTolerance(keepOut);
|
||||
var perimeter = profile.Perimeter.ToPolygonWithTolerance(tolerance, circumscribe: true);
|
||||
var holes = new List<Polygon>(profile.Cutouts.Count);
|
||||
foreach (var cutout in profile.Cutouts)
|
||||
holes.Add(cutout.ToPolygonWithTolerance(tolerance));
|
||||
return (perimeter, holes);
|
||||
foreach (var circumscribe in new[] { false, true })
|
||||
{
|
||||
var perimeter = profile.Perimeter.ToPolygonWithTolerance(tolerance, circumscribe);
|
||||
var holes = new List<Polygon>(profile.Cutouts.Count);
|
||||
foreach (var cutout in profile.Cutouts)
|
||||
holes.Add(cutout.ToPolygonWithTolerance(tolerance, circumscribe));
|
||||
yield return (perimeter, holes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="MaterialEnters"/> on <see cref="ConservativePolygons"/>, translated by
|
||||
/// <paramref name="offset"/>.
|
||||
/// True when either of the <see cref="ConservativePolygons"/>, translated by
|
||||
/// <paramref name="offset"/>, enters <paramref name="keepOut"/>.
|
||||
/// </summary>
|
||||
public static bool ProfileEnters(ShapeProfile profile, Vector offset, Box keepOut)
|
||||
{
|
||||
var (perimeter, holes) = ConservativePolygons(profile, keepOut);
|
||||
perimeter.Offset(offset);
|
||||
foreach (var hole in holes)
|
||||
hole.Offset(offset);
|
||||
return MaterialEnters(perimeter, holes, keepOut);
|
||||
foreach (var (perimeter, holes) in ConservativePolygons(profile, keepOut))
|
||||
{
|
||||
perimeter.Offset(offset);
|
||||
foreach (var hole in holes)
|
||||
hole.Offset(offset);
|
||||
if (MaterialEnters(perimeter, holes, keepOut))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -258,4 +258,40 @@ public class TinyClampZoneTests
|
||||
var part = new Part(new Drawing(material, MaterialProgram(material)));
|
||||
Assert.Equal(overlaps, InteractiveClamps.MaterialInKeepOut(part, zone));
|
||||
}
|
||||
// Delta review 2: a concave (notch) arc must not lose material either. The zone's corner
|
||||
// sits 6e-7 into the material beside a semicircular notch, at a flattening vertex.
|
||||
internal static (Program Program, Box Zone) NotchCornerFixture()
|
||||
{
|
||||
var program = new Program();
|
||||
program.MoveTo(0, 0);
|
||||
program.LineTo(20, 0);
|
||||
program.LineTo(20, 20);
|
||||
program.LineTo(12, 20);
|
||||
program.ArcTo(8, 20, 10, 20, RotationType.CW);
|
||||
program.LineTo(0, 20);
|
||||
program.LineTo(0, 0);
|
||||
var arc = new ShapeProfile(OpenNest.Converters.ConvertProgram.ToGeometry(program))
|
||||
.Perimeter.Entities.OfType<Arc>().Single();
|
||||
var native = arc.ToPoints(arc.SegmentsForTolerance(Tiny / 4)).First(v => v.X > 11.9 && v.Y < 19.5);
|
||||
var centre = new Vector(10, 20);
|
||||
var outward = centre + (native - centre) / 2 * (2 + 6e-7);
|
||||
return (program, new Box(outward.X - Tiny, outward.Y, Tiny, Tiny));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConcaveArcMaterialIsNotLostBesideATinyClamp()
|
||||
{
|
||||
var (program, zone) = NotchCornerFixture();
|
||||
var stock = new NestPlateStock("sheet", new Size(25, 25), null, 0, default, 1,
|
||||
new[] { new ClampZone("tiny", zone) });
|
||||
var job = new NestJob(new[] { new NestJobPart("p", PartGeometrySnapshot.FromProgram(program), 1, 0,
|
||||
RotationPolicy.Fixed(0)) }, new[] { stock });
|
||||
NestJobValidator.Validate(job);
|
||||
var result = new NestJobResult(NestJobStatus.Complete, NestJobStopReason.Completed,
|
||||
new[] { new NestJobPlateResult(0, stock, new[] { new NestJobPlacement("p", 0, 0, 0, 0) }) },
|
||||
Array.Empty<PartFulfillment>(), Array.Empty<StockUsage>());
|
||||
|
||||
Assert.NotEmpty(ClampViolations(job, result));
|
||||
Assert.True(InteractiveClamps.MaterialInKeepOut(new Part(new Drawing("notch", program)), zone));
|
||||
}
|
||||
}
|
||||
@@ -256,6 +256,32 @@ public class PlateOverlapAnalyzerClampZoneTests
|
||||
Assert.Equal(overlaps, report.ClampFindings.Count > 0);
|
||||
}
|
||||
|
||||
// Delta review 2: material beside a concave (notch) arc is not lost for a tiny clamp.
|
||||
[Fact]
|
||||
public void TinyClampBesideAConcaveArcIsReported()
|
||||
{
|
||||
var program = new Program();
|
||||
program.MoveTo(0, 0);
|
||||
program.LineTo(20, 0);
|
||||
program.LineTo(20, 20);
|
||||
program.LineTo(12, 20);
|
||||
program.ArcTo(8, 20, 10, 20, RotationType.CW);
|
||||
program.LineTo(0, 20);
|
||||
program.LineTo(0, 0);
|
||||
var arc = new ShapeProfile(OpenNest.Converters.ConvertProgram.ToGeometry(program))
|
||||
.Perimeter.Entities.OfType<Arc>().Single();
|
||||
var native = arc.ToPoints(arc.SegmentsForTolerance(1e-5 / 4)).First(v => v.X > 11.9 && v.Y < 19.5);
|
||||
var centre = new Vector(10, 20);
|
||||
var outward = centre + (native - centre) / 2 * (2 + 6e-7);
|
||||
var zone = new ClampZone("tiny", new Box(outward.X - 1e-5, outward.Y, 1e-5, 1e-5));
|
||||
|
||||
var report = PlateOverlapAnalyzer.Analyze(PlateOverlapAnalyzer.Capture(
|
||||
new List<Part> { new(new Drawing("notch", program), new Vector(0, 0)) }, new[] { zone }));
|
||||
|
||||
Assert.True(report.IsComplete, string.Join("; ", report.Issues.Select(issue => issue.Message)));
|
||||
Assert.NotEmpty(report.ClampFindings);
|
||||
}
|
||||
|
||||
private static string Describe(PlateOverlapReport report) =>
|
||||
string.Join(";", report.Pairs.Select(p => $"{p.PartAId}/{p.PartBId}:{p.Area:R}"))
|
||||
+ "|"
|
||||
|
||||
Reference in new issue
Block a user