diff --git a/OpenNest.IO/Bending/BendDetectorRegistry.cs b/OpenNest.IO/Bending/BendDetectorRegistry.cs new file mode 100644 index 0000000..d78c6b2 --- /dev/null +++ b/OpenNest.IO/Bending/BendDetectorRegistry.cs @@ -0,0 +1,40 @@ +using ACadSharp; +using OpenNest.Bending; +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.IO.Bending +{ + public static class BendDetectorRegistry + { + private static readonly List detectors = new(); + + static BendDetectorRegistry() + { + Register(new SolidWorksBendDetector()); + } + + public static void Register(IBendDetector detector) + { + detectors.Add(detector); + } + + public static IReadOnlyList Detectors => detectors; + + public static IBendDetector GetByName(string name) + { + return detectors.FirstOrDefault(d => d.Name == name); + } + + public static List AutoDetect(CadDocument document) + { + foreach (var detector in detectors) + { + var bends = detector.DetectBends(document); + if (bends.Count > 0) + return bends; + } + return new List(); + } + } +} diff --git a/OpenNest.IO/Bending/IBendDetector.cs b/OpenNest.IO/Bending/IBendDetector.cs new file mode 100644 index 0000000..900b24c --- /dev/null +++ b/OpenNest.IO/Bending/IBendDetector.cs @@ -0,0 +1,12 @@ +using ACadSharp; +using OpenNest.Bending; +using System.Collections.Generic; + +namespace OpenNest.IO.Bending +{ + public interface IBendDetector + { + string Name { get; } + List DetectBends(CadDocument document); + } +} diff --git a/OpenNest.IO/Bending/SolidWorksBendDetector.cs b/OpenNest.IO/Bending/SolidWorksBendDetector.cs new file mode 100644 index 0000000..0ad9a8f --- /dev/null +++ b/OpenNest.IO/Bending/SolidWorksBendDetector.cs @@ -0,0 +1,139 @@ +using ACadSharp; +using ACadSharp.Entities; +using OpenNest.Bending; +using OpenNest.Geometry; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; + +namespace OpenNest.IO.Bending +{ + public class SolidWorksBendDetector : IBendDetector + { + public string Name => "SolidWorks"; + + public double MaxBendRadius { get; set; } = 4.0; + + private static readonly Regex BendNoteRegex = new Regex( + @"\b(?UP|DOWN|DN)\s+(?\d+(\.\d+)?)°?\s*R\s*(?\d+(\.\d+)?)\b", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public List DetectBends(CadDocument document) + { + var bendLines = FindBendLines(document); + var bendNotes = FindBendNotes(document); + + if (bendLines.Count == 0) + return new List(); + + var bends = new List(); + + foreach (var line in bendLines) + { + var start = new Vector(line.StartPoint.X, line.StartPoint.Y); + var end = new Vector(line.EndPoint.X, line.EndPoint.Y); + + var bend = new Bend + { + StartPoint = start, + EndPoint = end, + Direction = BendDirection.Unknown + }; + + var note = FindClosestBendNote(line, bendNotes); + if (note != null) + { + bend.Direction = GetBendDirection(note.Value); + bend.NoteText = note.Value; + ParseBendNote(note.Value, bend); + } + + if (!bend.Radius.HasValue || bend.Radius.Value <= MaxBendRadius) + bends.Add(bend); + } + + return bends; + } + + private List FindBendLines(CadDocument document) + { + return document.Entities + .OfType() + .Where(l => l.Layer?.Name == "BEND" + && (l.LineType?.Name?.Contains("CENTER") == true + || l.LineType?.Name == "CENTERX2")) + .ToList(); + } + + private List FindBendNotes(CadDocument document) + { + return document.Entities + .OfType() + .Where(t => GetBendDirection(t.Value) != BendDirection.Unknown) + .ToList(); + } + + private static BendDirection GetBendDirection(string text) + { + if (string.IsNullOrEmpty(text)) + return BendDirection.Unknown; + + var upper = text.ToUpperInvariant(); + + if (upper.Contains("UP")) + return BendDirection.Up; + + if (upper.Contains("DOWN") || upper.Contains("DN")) + return BendDirection.Down; + + return BendDirection.Unknown; + } + + private static void ParseBendNote(string text, Bend bend) + { + var normalized = text.ToUpperInvariant().Replace("SHARP", "R0"); + var match = BendNoteRegex.Match(normalized); + + if (match.Success) + { + if (double.TryParse(match.Groups["radius"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var radius)) + bend.Radius = radius; + + if (double.TryParse(match.Groups["angle"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var angle)) + bend.Angle = angle; + } + } + + private MText FindClosestBendNote(ACadSharp.Entities.Line bendLine, List notes) + { + if (notes.Count == 0) return null; + + MText closest = null; + var closestDist = double.MaxValue; + + foreach (var note in notes) + { + var notePos = new Vector(note.InsertPoint.X, note.InsertPoint.Y); + var lineStart = new Vector(bendLine.StartPoint.X, bendLine.StartPoint.Y); + var lineEnd = new Vector(bendLine.EndPoint.X, bendLine.EndPoint.Y); + + var geomLine = new OpenNest.Geometry.Line(lineStart, lineEnd); + var perpPoint = geomLine.ClosestPointTo(notePos); + var dist = notePos.DistanceTo(perpPoint); + + var maxAcceptable = note.Height * 2.0; + if (dist > maxAcceptable) continue; + + if (dist < closestDist) + { + closestDist = dist; + closest = note; + } + } + + return closest; + } + } +} diff --git a/OpenNest.Tests/Bending/SolidWorksBendDetectorTests.cs b/OpenNest.Tests/Bending/SolidWorksBendDetectorTests.cs new file mode 100644 index 0000000..db9dc74 --- /dev/null +++ b/OpenNest.Tests/Bending/SolidWorksBendDetectorTests.cs @@ -0,0 +1,30 @@ +using OpenNest.Bending; +using OpenNest.IO.Bending; + +namespace OpenNest.Tests.Bending; + +public class SolidWorksBendDetectorTests +{ + [Fact] + public void SolidWorksDetector_IsRegistered() + { + var detector = BendDetectorRegistry.GetByName("SolidWorks"); + Assert.NotNull(detector); + Assert.Equal("SolidWorks", detector.Name); + } + + [Fact] + public void Registry_ContainsSolidWorksDetector() + { + Assert.Contains(BendDetectorRegistry.Detectors, + d => d.Name == "SolidWorks"); + } + + [Fact] + public void AutoDetect_EmptyDocument_ReturnsEmptyList() + { + var doc = new ACadSharp.CadDocument(); + var bends = BendDetectorRegistry.AutoDetect(doc); + Assert.Empty(bends); + } +}