diff --git a/src/ACadSharp.Tests/IO/DXF/DxfWriterSingleObjectTests.cs b/src/ACadSharp.Tests/IO/DXF/DxfWriterSingleObjectTests.cs index a412c49b3..e24381f90 100644 --- a/src/ACadSharp.Tests/IO/DXF/DxfWriterSingleObjectTests.cs +++ b/src/ACadSharp.Tests/IO/DXF/DxfWriterSingleObjectTests.cs @@ -1,5 +1,8 @@ -using ACadSharp.IO; +using ACadSharp.Entities; +using ACadSharp.IO; +using CSMath; using System.IO; +using System.Linq; using Xunit; using Xunit.Abstractions; @@ -51,6 +54,59 @@ public void WriteCasesAC1032(SingleCaseGenerator data) this.writeDxfFile(data, ACadVersion.AC1032); } + [Theory] + [InlineData(-90, 270, -90, 270)] + [InlineData(180, 540, -180, 180)] + [InlineData(-540, -180, -180, 180)] + [InlineData(540, 180, 180, -180)] + [InlineData(-180, -540, 180, -180)] + [InlineData(0, 360, 0, 360)] + [InlineData(0, -360, 0, -360)] + [InlineData(360, 720, 0, 360)] + [InlineData(90, 90, 90, 90)] + [InlineData(0, 90, 0, 90)] + [InlineData(450, 540, 90, 180)] + [InlineData(-540, -450, -180, -90)] + public void WriteHatchNormalizesBoundaryAngles(double startDegrees, double endDegrees, double expectedStartDegrees, double expectedEndDegrees) + { + double startAngle = MathHelper.DegToRad(startDegrees); + double endAngle = MathHelper.DegToRad(endDegrees); + double expectedStartAngle = MathHelper.DegToRad(expectedStartDegrees); + double expectedEndAngle = MathHelper.DegToRad(expectedEndDegrees); + + SingleCaseGenerator data = new SingleCaseGenerator(); + data.CreateHatchFullSweeps(); + + Hatch hatch = Assert.Single(data.Document.Entities.OfType()); + Hatch.BoundaryPath.Arc arc = Assert.Single( + hatch.Paths.SelectMany(p => p.Edges).OfType()); + Hatch.BoundaryPath.Ellipse ellipse = Assert.Single( + hatch.Paths.SelectMany(p => p.Edges).OfType()); + arc.StartAngle = ellipse.StartAngle = startAngle; + arc.EndAngle = ellipse.EndAngle = endAngle; + ellipse.CounterClockWise = false; + + using MemoryStream output = new MemoryStream(); + DxfWriter.Write(output, data.Document); + + using MemoryStream input = new MemoryStream(output.ToArray()); + CadDocument result = DxfReader.Read(input); + Hatch writtenHatch = Assert.Single(result.Entities.OfType()); + arc = Assert.Single(writtenHatch.Paths.SelectMany(p => p.Edges).OfType()); + ellipse = Assert.Single(writtenHatch.Paths.SelectMany(p => p.Edges).OfType()); + + Assert.Equal(expectedStartAngle, arc.StartAngle, 12); + Assert.Equal(expectedEndAngle, arc.EndAngle, 12); + Assert.Equal(expectedStartAngle, ellipse.StartAngle, 12); + Assert.Equal(expectedEndAngle, ellipse.EndAngle, 12); + Assert.InRange(arc.StartAngle, -MathHelper.TwoPI, MathHelper.TwoPI); + Assert.InRange(arc.EndAngle, -MathHelper.TwoPI, MathHelper.TwoPI); + Assert.InRange(ellipse.StartAngle, -MathHelper.TwoPI, MathHelper.TwoPI); + Assert.InRange(ellipse.EndAngle, -MathHelper.TwoPI, MathHelper.TwoPI); + Assert.True(arc.CounterClockWise); + Assert.False(ellipse.CounterClockWise); + } + protected void writeDxfFile(SingleCaseGenerator data, ACadVersion version) { Assert.True(data.HasExecuted, $"The writer has failed during it's execution."); @@ -84,4 +140,4 @@ protected void writeDxfFile(SingleCaseGenerator data, ACadVersion version) } } } -} \ No newline at end of file +} diff --git a/src/ACadSharp.Tests/IO/WriterSingleObjectTests.cs b/src/ACadSharp.Tests/IO/WriterSingleObjectTests.cs index 899e8c31a..35250bcbc 100644 --- a/src/ACadSharp.Tests/IO/WriterSingleObjectTests.cs +++ b/src/ACadSharp.Tests/IO/WriterSingleObjectTests.cs @@ -68,6 +68,7 @@ static WriterSingleObjectTests() Data.Add(new(nameof(SingleCaseGenerator.CreateHatchPolyline))); Data.Add(new(nameof(SingleCaseGenerator.CreateHatch))); Data.Add(new(nameof(SingleCaseGenerator.CreateCircleHatch))); + Data.Add(new(nameof(SingleCaseGenerator.CreateHatchFullSweeps))); Data.Add(new(nameof(SingleCaseGenerator.HatchWithEntities))); Data.Add(new(nameof(SingleCaseGenerator.ChangedEncoding))); Data.Add(new(nameof(SingleCaseGenerator.AddBlockWithAttributes))); @@ -739,6 +740,37 @@ public void CreateHatch() this.Document.Entities.Add(hatch); } + public void CreateHatchFullSweeps() + { + Hatch hatch = new Hatch { IsSolid = true }; + hatch.SeedPoints.Add(XY.Zero); + + Hatch.BoundaryPath arcPath = new Hatch.BoundaryPath(); + arcPath.Edges.Add(new Hatch.BoundaryPath.Arc + { + Center = XY.Zero, + Radius = 2, + StartAngle = -MathHelper.HalfPI, + EndAngle = MathHelper.ThreeHalfPI, + CounterClockWise = true, + }); + hatch.Paths.Add(arcPath); + + Hatch.BoundaryPath ellipsePath = new Hatch.BoundaryPath(); + ellipsePath.Edges.Add(new Hatch.BoundaryPath.Ellipse + { + Center = new XY(5, 0), + MajorAxisEndPoint = new XY(2, 0), + RadiusRatio = 0.5, + StartAngle = Math.PI, + EndAngle = 3 * Math.PI, + CounterClockWise = true, + }); + hatch.Paths.Add(ellipsePath); + + this.Document.Entities.Add(hatch); + } + public void CreateHatchPolyline() { Hatch hatch = new Hatch(); @@ -2014,4 +2046,4 @@ public void XRef() this.Document.Entities.Add(new Insert(record)); } } -} \ No newline at end of file +} diff --git a/src/ACadSharp/IO/DXF/DxfStreamWriter/DxfSectionWriterBase.Entities.cs b/src/ACadSharp/IO/DXF/DxfStreamWriter/DxfSectionWriterBase.Entities.cs index 3debfc00f..aca6880dc 100644 --- a/src/ACadSharp/IO/DXF/DxfStreamWriter/DxfSectionWriterBase.Entities.cs +++ b/src/ACadSharp/IO/DXF/DxfStreamWriter/DxfSectionWriterBase.Entities.cs @@ -516,6 +516,26 @@ private void writeHatch(Hatch hatch) //TODO: Implement HatchGradientPattern } + private void writeHatchBoundaryAngles(double startAngle, double endAngle) + { + double start = MathHelper.RadToDeg(startAngle, normalize: true, absolute: false); + double end = MathHelper.RadToDeg(endAngle, normalize: true, absolute: false); + + if (MathHelper.IsEqual(Math.Abs(endAngle - startAngle), MathHelper.TwoPI)) + { + // Keep a full sweep distinct without writing angles outside -360 to 360. + double sweep = endAngle > startAngle ? 360.0 : -360.0; + if (Math.Abs(start + sweep) > 360.0) + { + start -= sweep; + } + end = start + sweep; + } + + this._writer.Write(50, start); + this._writer.Write(51, end); + } + private void writeHatchBoundaryPathEdge(Hatch.BoundaryPath.Edge edge) { if (edge is not Hatch.BoundaryPath.Polyline) @@ -528,16 +548,14 @@ private void writeHatchBoundaryPathEdge(Hatch.BoundaryPath.Edge edge) case Hatch.BoundaryPath.Arc arc: this._writer.Write(10, arc.Center); this._writer.Write(40, arc.Radius); - this._writer.Write(50, MathHelper.RadToDeg(arc.StartAngle)); - this._writer.Write(51, MathHelper.RadToDeg(arc.EndAngle)); + this.writeHatchBoundaryAngles(arc.StartAngle, arc.EndAngle); this._writer.Write(73, arc.CounterClockWise ? (short)1 : (short)0); break; case Hatch.BoundaryPath.Ellipse ellipse: this._writer.Write(10, ellipse.Center); this._writer.Write(11, ellipse.MajorAxisEndPoint); this._writer.Write(40, ellipse.RadiusRatio); - this._writer.Write(50, MathHelper.RadToDeg(ellipse.StartAngle)); - this._writer.Write(51, MathHelper.RadToDeg(ellipse.EndAngle)); + this.writeHatchBoundaryAngles(ellipse.StartAngle, ellipse.EndAngle); this._writer.Write(73, ellipse.CounterClockWise ? (short)1 : (short)0); break; case Hatch.BoundaryPath.Line line: