using InABox.Core; using netDxf; using netDxf.Blocks; using netDxf.Entities; using netDxf.Tables; using Syncfusion.XPS; using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using FontStyle = System.Drawing.FontStyle; using SVec2 = System.Numerics.Vector2; using TAlignment = netDxf.Entities.TextAlignment; namespace InABox.Dxf; internal interface IDxfObject { void Draw(DrawData data); RectangleF? GetBounds(TransformData data); } internal class DxfLine : IDxfObject { public Line Line { get; set; } public void Draw(DrawData data) { if (!data.Data.ShouldDraw(Line)) return; data.PushTransform(); //data.ArbitraryAxis(Line.Normal); var colour = data.ResolveColour(Line.Color, Line); var thickness = (float)Line.Thickness; if(Line.Linetype.Segments.Count > 0) { var pos = DrawData.ConvertPoint(Line.StartPoint).ToVector2(); var end = DrawData.ConvertPoint(Line.EndPoint).ToVector2(); var remainingLength = SVec2.Distance(pos, end); if(remainingLength > 0) { var dir = SVec2.Normalize(end - pos); while(remainingLength > 0) { foreach(var segment in Line.Linetype.Segments) { if(segment is LinetypeSimpleSegment simple) { if(simple.Length > 0) { var length = Math.Min(remainingLength, (float)(simple.Length * Line.LinetypeScale)); data.Graphics.DrawLine( colour, thickness, new PointF(pos), new PointF(pos + dir * length)); remainingLength -= length; pos += dir * length; } else if(simple.Length == 0) { data.Graphics.DrawPoint(colour, thickness, new PointF(pos)); } else { var length = (float)(-simple.Length * Line.LinetypeScale); remainingLength -= length; pos += dir * length; } } if(remainingLength <= 0) { break; } } } } } else { data.Graphics.DrawLine( colour, thickness, DrawData.ConvertPoint(Line.StartPoint), DrawData.ConvertPoint(Line.EndPoint)); } data.PopTransform(); } public RectangleF? GetBounds(TransformData data) { if (!data.Data.ShouldDraw(Line)) return null; return Utils.RectangleFromPoints( data.TransformPoint(Line.StartPoint), data.TransformPoint(Line.EndPoint)); } } internal class DxfInsert : IDxfObject { public Insert Insert { get; set; } public List Objects { get; set; } public DxfInsert(Insert insert) { Insert = insert; Objects = insert.Block.Entities.Select(DxfUtils.ConvertEl).NotNull().ToList(); } public void Draw(DrawData data) { if (!data.Data.ShouldDraw(Insert)) return; // var transformation = Insert.GetTransformation(); // var translation = Insert.Position - transformation * Insert.Block.Origin; data.PushTransform(); data.Translate(new PointF((float)Insert.Position.X, (float)Insert.Position.Y)); data.ArbitraryAxis(Insert.Normal); data.Rotate((float)Insert.Rotation); data.Scale((float)Insert.Scale.X, (float)Insert.Scale.Y); data.Translate(new PointF(-(float)Insert.Block.Origin.X, -(float)Insert.Block.Origin.Y)); data.PushBlockColour(Insert.Color, Insert); foreach(var entity in Objects) { entity.Draw(data); } data.PopBlockColour(); data.PopTransform(); // foreach(var entity in Insert.Explode()) // { // // var ent = entity.Clone() as EntityObject; // // ent.TransformBy(transformation, translation); // DxfUtils.ConvertEl(entity)?.Draw(data); // } // foreach(var obj in Insert.Explode()) // { // DxfUtils.ConvertEl(obj)?.Draw(data); // } } public RectangleF? GetBounds(TransformData data) { if (!data.Data.ShouldDraw(Insert)) return null; data.PushTransform(); data.Translate(new PointF((float)Insert.Position.X, (float)Insert.Position.Y)); data.ArbitraryAxis(Insert.Normal); data.Rotate((float)Insert.Rotation); data.Scale((float)Insert.Scale.X, (float)Insert.Scale.Y); data.Translate(new PointF(-(float)Insert.Block.Origin.X, -(float)Insert.Block.Origin.Y)); var bounds = Utils.CombineBounds(Objects.Select(x => x.GetBounds(data))); data.PopTransform(); return bounds; } } internal class DxfArc : IDxfObject { public Arc Arc { get; set; } public DxfArc(Arc arc) { Arc = arc; } public void Draw(DrawData data) { if (!data.Data.ShouldDraw(Arc)) return; foreach(var obj in Arc.ToPolyline2D(100).Explode()) { DxfUtils.ConvertEl(obj)?.Draw(data); } } public RectangleF? GetBounds(TransformData data) { if (!data.Data.ShouldDraw(Arc)) return null; var halfSize = new netDxf.Vector3(Arc.Radius, Arc.Radius, 0); return Utils.RectangleFromPoints( data.TransformPoint(Arc.Center - halfSize), data.TransformPoint(Arc.Center + halfSize)); } } internal class DxfEllipse : IDxfObject { public Ellipse Ellipse { get; set; } public DxfEllipse(Ellipse ellipse) { Ellipse = ellipse; } public void Draw(DrawData data) { if (!data.Data.ShouldDraw(Ellipse)) return; foreach(var obj in Ellipse.ToPolyline2D(100).Explode()) { DxfUtils.ConvertEl(obj)?.Draw(data); } // var center = DrawData.ConvertPoint(Ellipse.Center); // var size = new SizeF((float)Ellipse.MajorAxis, (float)Ellipse.MinorAxis); // var startAngle = (float)(Ellipse.StartAngle); // var endAngle = (float)(Ellipse.EndAngle); // data.Graphics.DrawArc(new Pen(Color.Black, data.ConvertThickness((float)Ellipse.Thickness)), center.X - size.Width / 2, center.Y - size.Height / 2, size.Width, size.Height, startAngle, Utils.Mod(endAngle - startAngle, 360)); } public RectangleF? GetBounds(TransformData data) { if (!data.Data.ShouldDraw(Ellipse)) return null; var halfSize = new netDxf.Vector3(Ellipse.MajorAxis / 2, Ellipse.MinorAxis / 2, 0); return Utils.RectangleFromPoints( data.TransformPoint(Ellipse.Center - halfSize), data.TransformPoint(Ellipse.Center + halfSize)); } } internal class DxfSolid : IDxfObject { public Solid Solid { get; set; } public DxfSolid(Solid solid) { Solid = solid; } public void Draw(DrawData data) { if (!data.Data.ShouldDraw(Solid)) return; var vertices = new Vector2[] { Solid.FirstVertex, Solid.SecondVertex, Solid.FourthVertex, // Apparently the third and fourth are the wrong way round, so I've mirrored that here. Solid.ThirdVertex }; data.Graphics.FillPolygon(data.ResolveColour(Solid.Color, Solid), vertices.ToArray(x => DrawData.ConvertPoint(x))); } public RectangleF? GetBounds(TransformData data) { if (!data.Data.ShouldDraw(Solid)) return null; var vertices = new Vector2[] { Solid.FirstVertex, Solid.SecondVertex, Solid.FourthVertex, // Apparently the third and fourth are the wrong way round, so I've mirrored that here. Solid.ThirdVertex }; return Utils.RectangleFromPoints( vertices.ToArray(data.TransformPoint)); } } internal class DxfPolyline2D : IDxfObject { public Polyline2D Polyline { get; set; } public DxfPolyline2D(Polyline2D polyline) { Polyline = polyline; } public void Draw(DrawData data) { if (!data.Data.ShouldDraw(Polyline)) return; var entities = Polyline.Explode(); foreach(var entity in entities) { DxfUtils.ConvertEl(entity)?.Draw(data); } // if(Polyline.SmoothType == PolylineSmoothType.NoSmooth) // { // var vertices = Polyline.Vertexes.ToArray(x => new PointF((float)x.Position.X, (float)x.Position.Y)); // if (Polyline.IsClosed) // { // data.Graphics.DrawPolygon( // new Pen(Color.Black, data.ConvertThickness((float)Polyline.Thickness)), // vertices); // } // else // { // data.Graphics.DrawLines( // new Pen(Color.Black, data.ConvertThickness((float)Polyline.Thickness)), // vertices); // } // } // else // { // } } public RectangleF? GetBounds(TransformData data) { if (!data.Data.ShouldDraw(Polyline)) return null; var entities = Polyline.Explode(); return Utils.CombineBounds(entities.Select(x => DxfUtils.ConvertEl(x)?.GetBounds(data))); } } internal class DxfText : IDxfObject { public Text Text { get; set; } public DxfText(Text text) { Text = text; } private Font GetFont() { FontFamily fontFamily; if (Text.Style.FontFamilyName.IsNullOrWhiteSpace()) { fontFamily = SystemFonts.DefaultFont.FontFamily; } else { fontFamily = new FontFamily(Text.Style.FontFamilyName); } return new Font(fontFamily, (float)Text.Height, Text.Style.FontStyle switch { netDxf.Tables.FontStyle.Bold => FontStyle.Bold, netDxf.Tables.FontStyle.Italic => FontStyle.Italic, netDxf.Tables.FontStyle.Regular or _ => FontStyle.Regular, }); } private string GetText() { return Text.Value; } private static Bitmap _placeholderBitmap = new Bitmap(1, 1); private void Transform(TransformData data, Font font, string text, Graphics g) { data.Translate(new PointF((float)Text.Position.X, (float)Text.Position.Y)); data.Rotate((float)Text.Rotation); data.Scale(1, -1); var size = g.MeasureString(text, font, new PointF(), StringFormat.GenericTypographic); if(Text.Alignment == TAlignment.Aligned) { data.Scale((float)(Text.Width / size.Width)); } else if(Text.Alignment == TAlignment.Fit) { data.Scale((float)(Text.Width / size.Width), 1); } else { switch (Text.Alignment) { case TAlignment.MiddleLeft: case TAlignment.MiddleCenter: case TAlignment.Middle: case TAlignment.MiddleRight: data.Translate(new PointF(0, -size.Height / 2)); break; case TAlignment.BottomLeft: case TAlignment.BottomCenter: case TAlignment.BottomRight: data.Translate(new PointF(0, -size.Height)); break; default: var ascent = font.FontFamily.GetCellAscent(font.Style); var lineSpace = font.FontFamily.GetLineSpacing(font.Style); var baseline = ascent * font.Height / font.FontFamily.GetEmHeight(font.Style); var ratio = font.GetHeight(g) / lineSpace; data.Translate(new PointF(0, -baseline + ascent * ratio)); break; } switch (Text.Alignment) { case TAlignment.TopLeft: case TAlignment.MiddleLeft: case TAlignment.BottomLeft: break; case TAlignment.TopCenter: case TAlignment.MiddleCenter: case TAlignment.Middle: case TAlignment.BottomCenter: data.Translate(new PointF(-(float)size.Width / 2, 0)); break; case TAlignment.TopRight: case TAlignment.MiddleRight: case TAlignment.BottomRight: data.Translate(new PointF(-(float)size.Width, 0)); break; } } } public void Draw(DrawData data) { if (!data.Data.ShouldDraw(Text)) return; var text = GetText(); if (Text.Style.FontFamilyName.IsNullOrWhiteSpace()) { data.Graphics.SetFont((float)Text.Height, Text.Style.FontStyle switch { netDxf.Tables.FontStyle.Bold => FontStyle.Bold, netDxf.Tables.FontStyle.Italic => FontStyle.Italic, netDxf.Tables.FontStyle.Regular or _ => FontStyle.Regular, }); } else { data.Graphics.SetFont(Text.Style.FontFamilyName, (float)Text.Height, Text.Style.FontStyle switch { netDxf.Tables.FontStyle.Bold => FontStyle.Bold, netDxf.Tables.FontStyle.Italic => FontStyle.Italic, netDxf.Tables.FontStyle.Regular or _ => FontStyle.Regular, }); } if(Text.Alignment == TAlignment.Aligned || Text.Alignment == TAlignment.Fit) { // Need to calculate a rectangle to draw the text in. var scale = new PointF(Text.IsBackward ? -1 : 1, Text.IsUpsideDown ? -1 : 1); if(Text.Alignment == TAlignment.Aligned) { data.Graphics.DrawText(text, data.ResolveColour(Text.Color, Text), DrawData.ConvertPoint(Text.Position), (float)Text.Rotation, new SizeF((float)(Text.Width * Text.WidthFactor), (float)Text.Height), scale); } else { data.Graphics.DrawText(text, data.ResolveColour(Text.Color, Text), DrawData.ConvertPoint(Text.Position), (float)Text.Rotation, (float)(Text.Width * Text.WidthFactor), scale); } } else { var format = new TextFormat { LineAlignment = Text.Alignment switch { TAlignment.Aligned => TextLineAlignment.Baseline, TAlignment.Fit => TextLineAlignment.Baseline, TAlignment.MiddleLeft or TAlignment.MiddleCenter or TAlignment.MiddleRight or TAlignment.Middle => TextLineAlignment.Center, TAlignment.BottomLeft or TAlignment.BottomCenter or TAlignment.BottomRight => TextLineAlignment.Bottom, TAlignment.BaselineLeft or TAlignment.BaselineRight or TAlignment.BaselineCenter => TextLineAlignment.Baseline, _ => TextLineAlignment.Top, }, Alignment = Text.Alignment switch { TAlignment.Aligned => TextAlignment.Justify, TAlignment.Fit => TextAlignment.Fit, TAlignment.TopCenter or TAlignment.MiddleCenter or TAlignment.BottomCenter or TAlignment.Middle or TAlignment.BaselineCenter => TextAlignment.Center, TAlignment.TopRight or TAlignment.MiddleRight or TAlignment.BottomRight or TAlignment.BaselineRight => TextAlignment.Right, _ => TextAlignment.Left, } }; data.PushTransform(); data.Translate(DrawData.ConvertPoint(Text.Position)); data.Rotate((float)Text.Rotation); data.Scale((float)Text.WidthFactor, 1); data.Scale(Text.IsBackward ? -1 : 1, Text.IsUpsideDown ? -1 : 1); data.Graphics.DrawText(text, data.ResolveColour(Text.Color, Text), new(), 0, format); data.PopTransform(); } } public RectangleF? GetBounds(TransformData data) { if (!data.Data.ShouldDraw(Text)) return null; var font = GetFont(); var text = GetText(); data.PushTransform(); using var g = Graphics.FromImage(_placeholderBitmap); Transform(data, font, text, g); var size = g.MeasureString(text, font, new PointF(), StringFormat.GenericTypographic); var bounds = Utils.RectangleFromPoints( data.TransformPoint(0, 0), data.TransformPoint(size.Width, size.Height)); data.PopTransform(); return bounds; } } internal class DxfMText : IDxfObject { public MText MText { get; set; } public DxfMText(MText text) { MText = text; } private Font GetFont() { FontFamily fontFamily; if (MText.Style.FontFamilyName.IsNullOrWhiteSpace()) { fontFamily = SystemFonts.DefaultFont.FontFamily; } else { fontFamily = new FontFamily(MText.Style.FontFamilyName); } return new Font(fontFamily, (float)MText.Height, MText.Style.FontStyle switch { netDxf.Tables.FontStyle.Bold => FontStyle.Bold, netDxf.Tables.FontStyle.Italic => FontStyle.Italic, netDxf.Tables.FontStyle.Regular or _ => FontStyle.Regular, }); } private string GetText() { return MText.PlainText().Replace("^M", ""); } private static Bitmap _placeholderBitmap = new Bitmap(1, 1); private void Transform(TransformData data, Font font, string text, Graphics g) { data.Translate(new PointF((float)MText.Position.X, (float)MText.Position.Y)); data.Rotate((float)MText.Rotation); data.Scale(1, -1); var size = g.MeasureString(text, font, new PointF(), StringFormat.GenericTypographic); switch (MText.AttachmentPoint) { case MTextAttachmentPoint.MiddleLeft: case MTextAttachmentPoint.MiddleCenter: case MTextAttachmentPoint.MiddleRight: data.Translate(new PointF(0, -size.Height / 2)); break; case MTextAttachmentPoint.BottomLeft: case MTextAttachmentPoint.BottomCenter: case MTextAttachmentPoint.BottomRight: data.Translate(new PointF(0, -size.Height)); break; default: var ascent = font.FontFamily.GetCellAscent(font.Style); var lineSpace = font.FontFamily.GetLineSpacing(font.Style); var baseline = ascent * font.Height / font.FontFamily.GetEmHeight(font.Style); var ratio = font.GetHeight(g) / lineSpace; data.Translate(new PointF(0, -baseline + ascent * ratio)); break; } switch (MText.AttachmentPoint) { case MTextAttachmentPoint.TopLeft: case MTextAttachmentPoint.MiddleLeft: case MTextAttachmentPoint.BottomLeft: break; case MTextAttachmentPoint.TopCenter: case MTextAttachmentPoint.MiddleCenter: case MTextAttachmentPoint.BottomCenter: data.Translate(new PointF(-(float)size.Width / 2, 0)); break; case MTextAttachmentPoint.TopRight: case MTextAttachmentPoint.MiddleRight: case MTextAttachmentPoint.BottomRight: data.Translate(new PointF(-(float)size.Width, 0)); break; } } public void Draw(DrawData data) { if (!data.Data.ShouldDraw(MText)) return; var text = GetText(); if (MText.Style.FontFamilyName.IsNullOrWhiteSpace()) { data.Graphics.SetFont((float)MText.Height, MText.Style.FontStyle switch { netDxf.Tables.FontStyle.Bold => FontStyle.Bold, netDxf.Tables.FontStyle.Italic => FontStyle.Italic, netDxf.Tables.FontStyle.Regular or _ => FontStyle.Regular, }); } else { data.Graphics.SetFont(MText.Style.FontFamilyName, (float)MText.Height, MText.Style.FontStyle switch { netDxf.Tables.FontStyle.Bold => FontStyle.Bold, netDxf.Tables.FontStyle.Italic => FontStyle.Italic, netDxf.Tables.FontStyle.Regular or _ => FontStyle.Regular, }); } var format = new TextFormat { LineAlignment = MText.AttachmentPoint switch { MTextAttachmentPoint.MiddleLeft or MTextAttachmentPoint.MiddleCenter or MTextAttachmentPoint.MiddleRight => TextLineAlignment.Center, MTextAttachmentPoint.BottomLeft or MTextAttachmentPoint.BottomCenter or MTextAttachmentPoint.BottomRight => TextLineAlignment.Bottom, _ => TextLineAlignment.Top, }, Alignment = MText.AttachmentPoint switch { MTextAttachmentPoint.TopCenter or MTextAttachmentPoint.MiddleCenter or MTextAttachmentPoint.BottomCenter => TextAlignment.Center, MTextAttachmentPoint.TopRight or MTextAttachmentPoint.MiddleRight or MTextAttachmentPoint.BottomRight => TextAlignment.Right, _ => TextAlignment.Left, } }; data.Graphics.DrawText(text, data.ResolveColour(MText.Color, MText), DrawData.ConvertPoint(MText.Position), (float)MText.Rotation, format); } public RectangleF? GetBounds(TransformData data) { if (!data.Data.ShouldDraw(MText)) return null; var font = GetFont(); var text = GetText(); data.PushTransform(); using var g = Graphics.FromImage(_placeholderBitmap); Transform(data, font, text, g); var size = g.MeasureString(text, font, new PointF(), StringFormat.GenericTypographic); var bounds = Utils.RectangleFromPoints( data.TransformPoint(0, 0), data.TransformPoint(size.Width, size.Height)); data.PopTransform(); return bounds; } } internal class DxfDimension : IDxfObject { public Dimension Dimension { get; set; } public List Objects { get; set; } public DxfDimension(Dimension dimension) { Dimension = dimension; if(Dimension.Block is null) { Objects = new(); } else { Objects = Dimension.Block.Entities.Select(DxfUtils.ConvertEl).NotNull().ToList(); } } public void Draw(DrawData data) { if (!data.Data.ShouldDraw(Dimension)) return; data.PushBlockColour(Dimension.Color, Dimension); foreach(var entity in Objects) { entity.Draw(data); } data.PopBlockColour(); } public RectangleF? GetBounds(TransformData data) { if (!data.Data.ShouldDraw(Dimension)) return null; return Utils.CombineBounds(Objects.Select(x => x.GetBounds(data))); } }