diff --git a/OpenNest.IO/CadImportResult.cs b/OpenNest.IO/CadImportResult.cs index 9b0cf39..f3d7555 100644 --- a/OpenNest.IO/CadImportResult.cs +++ b/OpenNest.IO/CadImportResult.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using ACadSharp; using OpenNest.Bending; using OpenNest.Geometry; @@ -38,5 +39,11 @@ namespace OpenNest.IO /// Default drawing name (filename without extension, unless overridden). /// public string Name { get; set; } + + /// + /// The raw CAD document from the source file. Available for callers + /// that need access to non-geometry entities (e.g., text annotations). + /// + public CadDocument Document { get; set; } } } diff --git a/OpenNest.IO/CadImporter.cs b/OpenNest.IO/CadImporter.cs index 921ab1b..6afcea2 100644 --- a/OpenNest.IO/CadImporter.cs +++ b/OpenNest.IO/CadImporter.cs @@ -47,6 +47,7 @@ namespace OpenNest.IO Bounds = dxf.Entities.GetBoundingBox(), SourcePath = path, Name = options.Name ?? Path.GetFileNameWithoutExtension(path), + Document = dxf.Document, }; } diff --git a/OpenNest/Controls/CadText.cs b/OpenNest/Controls/CadText.cs new file mode 100644 index 0000000..d2d5174 --- /dev/null +++ b/OpenNest/Controls/CadText.cs @@ -0,0 +1,16 @@ +using System.Drawing; +using OpenNest.Geometry; + +namespace OpenNest.Controls +{ + public class CadText + { + public Vector Position { get; set; } + public string Value { get; set; } + public double Height { get; set; } + public double Rotation { get; set; } + public string LayerName { get; set; } + public StringAlignment HAlign { get; set; } + public StringAlignment VAlign { get; set; } + } +} diff --git a/OpenNest/Controls/EntityView.cs b/OpenNest/Controls/EntityView.cs index 71593da..53f62d4 100644 --- a/OpenNest/Controls/EntityView.cs +++ b/OpenNest/Controls/EntityView.cs @@ -29,12 +29,14 @@ namespace OpenNest.Controls public List SimplifierToleranceRight { get; set; } public List OriginalEntities { get; set; } public bool ShowEntityLabels { get; set; } + public List Texts { get; set; } = new List(); private readonly Pen gridPen = new Pen(Color.FromArgb(70, 70, 70)); private readonly Dictionary penCache = new Dictionary(); private readonly Font labelFont = new Font("Segoe UI", 7f); private readonly SolidBrush labelBrush = new SolidBrush(Color.FromArgb(220, 255, 255, 200)); private readonly SolidBrush labelBackBrush = new SolidBrush(Color.FromArgb(33, 40, 48)); + private readonly SolidBrush textBrush = new SolidBrush(Color.FromArgb(180, 200, 200, 200)); public event EventHandler LinePicked; public event EventHandler PickCancelled; @@ -116,6 +118,8 @@ namespace OpenNest.Controls DrawEntity(e.Graphics, entity, pen); } + DrawTexts(e.Graphics); + if (ShowEntityLabels) DrawEntityLabels(e.Graphics); @@ -408,6 +412,7 @@ namespace OpenNest.Controls labelFont.Dispose(); labelBrush.Dispose(); labelBackBrush.Dispose(); + textBrush.Dispose(); } base.Dispose(disposing); } @@ -474,6 +479,34 @@ namespace OpenNest.Controls diameter); } + private void DrawTexts(Graphics g) + { + if (Texts == null || Texts.Count == 0) + return; + + using var sf = new StringFormat(); + + foreach (var text in Texts) + { + var pos = PointWorldToGraph(text.Position); + var fontSize = LengthWorldToGui(text.Height); + if (fontSize < 2f) continue; + + var state = g.Save(); + g.TranslateTransform(pos.X, pos.Y); + + if (text.Rotation != 0) + g.RotateTransform((float)OpenNest.Math.Angle.ToDegrees(text.Rotation)); + + sf.Alignment = text.HAlign; + sf.LineAlignment = text.VAlign; + + using var font = new Font("Segoe UI", fontSize, GraphicsUnit.Pixel); + g.DrawString(text.Value, font, textBrush, 0, 0, sf); + g.Restore(state); + } + } + private void DrawPoint(Graphics g, Vector pt, Pen pen) { var pt1 = PointWorldToGraph(pt); diff --git a/OpenNest/Controls/FileListControl.cs b/OpenNest/Controls/FileListControl.cs index 28f7df0..49e9d9c 100644 --- a/OpenNest/Controls/FileListControl.cs +++ b/OpenNest/Controls/FileListControl.cs @@ -22,6 +22,7 @@ namespace OpenNest.Controls public HashSet SuppressedEntityIds { get; set; } public Box Bounds { get; set; } public int EntityCount { get; set; } + public List Texts { get; set; } = new(); } public class FileListControl : Control diff --git a/OpenNest/Forms/CadConverterForm.cs b/OpenNest/Forms/CadConverterForm.cs index b069363..33a9e90 100644 --- a/OpenNest/Forms/CadConverterForm.cs +++ b/OpenNest/Forms/CadConverterForm.cs @@ -92,7 +92,8 @@ namespace OpenNest.Forms Customer = string.Empty, Bends = result.Bends, Bounds = result.Bounds, - EntityCount = result.Entities.Count + EntityCount = result.Entities.Count, + Texts = ExtractTexts(result.Document), }; if (InvokeRequired) @@ -152,6 +153,7 @@ namespace OpenNest.Forms entityView1.Entities.Clear(); entityView1.Entities.AddRange(item.Entities); entityView1.Bends = item.Bends ?? new List(); + entityView1.Texts = item.Texts ?? new List(); item.Entities.ForEach(e => e.IsVisible = true); if (item.Entities.Any(e => e.Layer != null)) @@ -804,6 +806,102 @@ namespace OpenNest.Forms #endregion + private static List ExtractTexts(ACadSharp.CadDocument doc) + { + var texts = new List(); + if (doc == null) return texts; + + foreach (var entity in doc.Entities) + { + switch (entity) + { + case ACadSharp.Entities.MText mtext: + var (mh, mv) = MapAttachmentPoint(mtext.AttachmentPoint); + texts.Add(new CadText + { + Position = new Vector(mtext.InsertPoint.X, mtext.InsertPoint.Y), + Value = ReplaceControlCodes(StripMTextFormatting(mtext.Value)), + Height = mtext.Height, + Rotation = mtext.Rotation, + LayerName = mtext.Layer?.Name, + HAlign = mh, + VAlign = mv, + }); + break; + + case ACadSharp.Entities.TextEntity text: + var useAlignment = text.HorizontalAlignment != 0 + || text.VerticalAlignment != 0; + var pt = useAlignment ? text.AlignmentPoint : text.InsertPoint; + var ha = text.HorizontalAlignment switch + { + ACadSharp.Entities.TextHorizontalAlignment.Center => System.Drawing.StringAlignment.Center, + ACadSharp.Entities.TextHorizontalAlignment.Right => System.Drawing.StringAlignment.Far, + _ => System.Drawing.StringAlignment.Near, + }; + texts.Add(new CadText + { + Position = new Vector(pt.X, pt.Y), + Value = ReplaceControlCodes(text.Value), + Height = text.Height, + Rotation = text.Rotation, + LayerName = text.Layer?.Name, + HAlign = ha, + }); + break; + } + } + + return texts; + } + + private static (System.Drawing.StringAlignment h, System.Drawing.StringAlignment v) MapAttachmentPoint( + ACadSharp.Entities.AttachmentPointType apt) + { + var h = apt switch + { + ACadSharp.Entities.AttachmentPointType.TopCenter + or ACadSharp.Entities.AttachmentPointType.MiddleCenter + or ACadSharp.Entities.AttachmentPointType.BottomCenter => System.Drawing.StringAlignment.Center, + ACadSharp.Entities.AttachmentPointType.TopRight + or ACadSharp.Entities.AttachmentPointType.MiddleRight + or ACadSharp.Entities.AttachmentPointType.BottomRight => System.Drawing.StringAlignment.Far, + _ => System.Drawing.StringAlignment.Near, + }; + var v = apt switch + { + ACadSharp.Entities.AttachmentPointType.MiddleLeft + or ACadSharp.Entities.AttachmentPointType.MiddleCenter + or ACadSharp.Entities.AttachmentPointType.MiddleRight => System.Drawing.StringAlignment.Center, + ACadSharp.Entities.AttachmentPointType.BottomLeft + or ACadSharp.Entities.AttachmentPointType.BottomCenter + or ACadSharp.Entities.AttachmentPointType.BottomRight => System.Drawing.StringAlignment.Far, + _ => System.Drawing.StringAlignment.Near, + }; + return (h, v); + } + + private static string StripMTextFormatting(string text) + { + if (string.IsNullOrEmpty(text)) return text; + var result = System.Text.RegularExpressions.Regex.Replace(text, @"\\[A-Za-z][^;]*;", ""); + result = result.Replace("{", "").Replace("}", ""); + return result.Trim(); + } + + private static string ReplaceControlCodes(string text) + { + if (string.IsNullOrEmpty(text)) return text; + return text + .Replace("%%p", "±") + .Replace("%%P", "±") + .Replace("%%d", "°") + .Replace("%%D", "°") + .Replace("%%c", "⌀") + .Replace("%%C", "⌀") + .Replace("%%%", "%"); + } + private void filterPanel_Paint(object sender, PaintEventArgs e) {