Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed ellipse radial angle error | Added DxfHatch #13

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/IxMilia.Converters.Test/DxfToSvgTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Xml.Linq;
using IxMilia.Dxf;
Expand Down Expand Up @@ -170,6 +171,45 @@ public void RenderEllipseTest()
AssertXElement(expected, actual);
}

[Fact]
public void RenderEllipse2Test()
{
var ellipse = new DxfEllipse(new DxfPoint(1.0, 2.0, 3.0), new DxfVector(1.0, 0.0, 0.0), 0.5)
{
StartParameter = 0.0,
EndParameter = Math.PI / 2.0 // 90 degrees
};
var path = ellipse.GetSvgPath2();
Assert.Equal(10, path.Segments.Count);

var move = (SvgMoveToPath)path.Segments[0];
AssertClose(2.0, move.LocationX);
AssertClose(2.0, move.LocationY);

var arcSegment = (SvgEllipseLineToPath)path.Segments.Last();
AssertClose(1.0, arcSegment.LocationX);
AssertClose(2.5, arcSegment.LocationY);
//Assert.Equal(1.0, arcSegment.RadiusX); -> not available any more
//Assert.Equal(0.5, arcSegment.RadiusY); -> not available any more
//Assert.Equal(0.0, arcSegment.XAxisRotation); -> not available any more
//Assert.False(arcSegment.IsLargeArc); -> not available any more
//Assert.True(arcSegment.IsCounterClockwiseSweep); -> not available any more

var expected = new XElement(DxfToSvgConverter.Xmlns + "path",
new XAttribute("d", path.ToString()),
new XAttribute("fill-opacity", "0"),
new XAttribute("stroke-width", "1.0px"),
new XAttribute("vector-effect", "non-scaling-stroke"));
var actual = ellipse.ToXElement2();

AssertXElement(expected, actual);

//comment in to see the result
//now it's just an approximation with a resolution of 10 degrees
//expected.SetAttributeValue("stroke", "red");
//expected.AssertExpected(actual, MethodBase.GetCurrentMethod().Name);
}

[Fact]
public void EllipsePathOf180DegreesTest()
{
Expand Down
43 changes: 43 additions & 0 deletions src/IxMilia.Converters.Test/MathExtensionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace IxMilia.Converters.Test
{
public class MathExtensionTests
{
[Theory]
[MemberData(nameof(CalcXAxisRotation_TestData))]
public void CalcXAxisRotation_Test(double majorAxisX, double majorAxisY, double expected_radian, double expected_degree)
{
double radian = 0;
double degree = 0;

Check warning on line 18 in src/IxMilia.Converters.Test/MathExtensionTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

The variable 'degree' is assigned but its value is never used

Check warning on line 18 in src/IxMilia.Converters.Test/MathExtensionTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

The variable 'degree' is assigned but its value is never used

Check warning on line 18 in src/IxMilia.Converters.Test/MathExtensionTests.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

The variable 'degree' is assigned but its value is never used

Check warning on line 18 in src/IxMilia.Converters.Test/MathExtensionTests.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

The variable 'degree' is assigned but its value is never used
MathExtensions.CalcXAxisRotation(majorAxisX, majorAxisY, out radian);

Assert.Equal(expected_radian, radian);
Assert.Equal(expected_degree, radian.ToDegree());



}

public static IEnumerable<object[]> CalcXAxisRotation_TestData => GetCalcXAxisRotation_TestData();

private static IEnumerable<object[]> GetCalcXAxisRotation_TestData()
{

yield return new object[] { 1, 0, 0, 0 };
yield return new object[] { 1, 1, Math.PI / 4, 45 };
yield return new object[] { 0, 1, 2 * Math.PI / 4, 90 };
yield return new object[] { -1, 1, 3 * Math.PI / 4, 135 };
yield return new object[] { -1, 0, Math.PI, 180 };
yield return new object[] { -1, -1, 5 * Math.PI / 4, 225 };
yield return new object[] { 0, -1, 6 * Math.PI / 4, 270 };
yield return new object[] { 1, -1, 7 * Math.PI / 4, 315 };
}
}
}
80 changes: 80 additions & 0 deletions src/IxMilia.Converters.Test/TestExtentions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Xunit;

namespace IxMilia.Converters.Test
{
internal static class TestExtentions
{
public static void WriteToFile(this XElement xElement, string nameOfTest, string? alternativeFilename = null)

Check warning on line 15 in src/IxMilia.Converters.Test/TestExtentions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 15 in src/IxMilia.Converters.Test/TestExtentions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 15 in src/IxMilia.Converters.Test/TestExtentions.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 15 in src/IxMilia.Converters.Test/TestExtentions.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{

var svgXml = new XElement(DxfToSvgConverter.Xmlns + "svg"
//new XAttribute("style", "background: black;")
//new XAttribute("width", "10"),
//new XAttribute("height", "10")
);

var svgG = new XElement(DxfToSvgConverter.Xmlns + "g",
new XAttribute("transform", " scale(1)")
);

svgG.Add(xElement);

svgXml.Add(svgG);

using (MemoryStream stream = new MemoryStream())
{
svgXml.SaveTo(stream);

// Optionally, you can convert the MemoryStream to a byte array or perform other actions.
byte[] svgBytes = stream.ToArray();
string path = Assembly.GetExecutingAssembly().Location;
path = Path.Combine(Path.GetDirectoryName(path), "UnitTests", $"{nameOfTest}");
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}

alternativeFilename = alternativeFilename != null ? $"{alternativeFilename}_" : "";

string filePath = @$"{Path.Combine(path, nameOfTest)}_{alternativeFilename}.svg";
File.WriteAllBytes(filePath, svgBytes);
}
}




public static void RemoveAttribute(this XElement element, string attributeName)
{
// Find and remove the attribute by name
XAttribute attributeToRemove = element.Attribute(attributeName);
attributeToRemove?.Remove();
}
public static void AssertExpected(this XElement expected, XElement actual, string nameOfTest, string? alternativeFilename = null)

Check warning on line 61 in src/IxMilia.Converters.Test/TestExtentions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 61 in src/IxMilia.Converters.Test/TestExtentions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 61 in src/IxMilia.Converters.Test/TestExtentions.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 61 in src/IxMilia.Converters.Test/TestExtentions.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
var g = new XElement(DxfToSvgConverter.Xmlns + "g");

actual.SetAttributeValue("id", "actual");
g.Add(actual);

expected.SetAttributeValue("id", "expected");
expected.SetAttributeValue("stroke", "green");

g.Add(expected);

g.WriteToFile(nameOfTest, alternativeFilename);

var expectedVal = expected.Attributes().ToList().Where(s => s.Name == "d").FirstOrDefault().Value;
var actualVal = actual.Attributes().ToList().Where(s => s.Name == "d").FirstOrDefault().Value;
Assert.Equal(expectedVal, actualVal);
}
}
}
59 changes: 57 additions & 2 deletions src/IxMilia.Converters/DxfExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using IxMilia.Dwg;
using IxMilia.Dwg.Objects;
using IxMilia.Dxf;
Expand All @@ -11,7 +11,7 @@
public static class DxfExtensions
{
public const double DegreesToRadians = Math.PI / 180.0;

public static DxfPoint GetPointFromAngle(this DxfCircle circle, double angleInDegrees)
{
var angleInRadians = angleInDegrees * DegreesToRadians;
Expand Down Expand Up @@ -241,5 +241,60 @@
throw new ArgumentOutOfRangeException(nameof(drawingUnits));
}
}

public static XElement? GetPattern(this DxfHatch arc, string patternId)

Check warning on line 245 in src/IxMilia.Converters/DxfExtensions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 245 in src/IxMilia.Converters/DxfExtensions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 245 in src/IxMilia.Converters/DxfExtensions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 245 in src/IxMilia.Converters/DxfExtensions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 245 in src/IxMilia.Converters/DxfExtensions.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 245 in src/IxMilia.Converters/DxfExtensions.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 245 in src/IxMilia.Converters/DxfExtensions.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 245 in src/IxMilia.Converters/DxfExtensions.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
if (arc.IsPatternDoubled)
{
throw new NotImplementedException();
}
if (arc.PatternType != DxfHatchPatternType.Predefined)
{
throw new NotImplementedException();

}
if (arc.PatternDefinitionLines.Count != 1)
{
//throw new NotImplementedException();
return null;
}
else
{





string colorString = string.Empty;
if (arc.Color.IsIndex)
{
colorString = arc.Color.ToRGBString();

}

var lineDistance = (0.1 * arc.PatternScale * 2).ToDisplayString();
// Create pattern element
XElement patternElement = new XElement(DxfToSvgConverter.Xmlns + "pattern",
new XAttribute("id", $"{patternId}"),
new XAttribute("patternUnits", "userSpaceOnUse"),
new XAttribute("width", $"{lineDistance}"),
new XAttribute("height", $"{lineDistance}"),
new XAttribute("patternTransform", $"rotate({(arc.PatternDefinitionLines.First().Angle * -1).ToDisplayString()})"));

// Create line element
XElement lineElement = new XElement(DxfToSvgConverter.Xmlns + "line",
new XAttribute("x1", "0"),
new XAttribute("y1", "0"),
new XAttribute("x2", "0"),
new XAttribute("y2", $"{lineDistance}"),
new XAttribute("stroke", $"{colorString}"),
new XAttribute("stroke-width", "1")
);
// Append rect elements to pattern element
patternElement.Add(lineElement);
return patternElement;
}

}
}
}
92 changes: 91 additions & 1 deletion src/IxMilia.Converters/DxfToSvgConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Xml.Linq;
using IxMilia.Dxf;
using IxMilia.Dxf.Entities;
using static IxMilia.Dxf.Entities.DxfHatch;

namespace IxMilia.Converters
{
Expand Down Expand Up @@ -299,7 +300,7 @@
case DxfDimensionBase dim:
return dim.ToXElement(dimStyles, drawingUnits, unitFormat, unitPrecision);
case DxfEllipse ellipse:
return ellipse.ToXElement();
return ellipse.ToXElement2();
case DxfImage image:
return await image.ToXElement(options);
case DxfLine line:
Expand All @@ -316,6 +317,8 @@
return spline.ToXElement();
case DxfText text:
return text.ToXElement();
case DxfHatch hatch:
return hatch.ToXElement();
default:
return null;
}
Expand Down Expand Up @@ -345,6 +348,56 @@
.AddVectorEffect();
}

static int randomId = 1;
public static XElement ToXElement(this DxfHatch arc, bool drawStroke = false)
{
List<XElement> hatchElements = new List<XElement>();

var hatches = new XElement(DxfToSvgConverter.Xmlns + "g");
foreach (var boundaryPath in arc.BoundaryPaths)
{
switch (boundaryPath)
{
case NonPolylineBoundaryPath nonPolylineBoundaryPath:

var paths = nonPolylineBoundaryPath.Edges.GetSvgPath();
string patternId = $"{arc.PatternName.ToUpper()}_{randomId}";
var patternElement = arc.GetPattern(patternId);

if (patternElement is not null)
{

var boundary = new XElement(DxfToSvgConverter.Xmlns + "path",
new XAttribute("d", paths.ToString()));
boundary.Add(new XAttribute("fill-opacity", 1));

if (!drawStroke)
{
boundary.Add(new XAttribute("stroke-width", 0));
}
boundary.Add(new XAttribute("fill", $"url(#{patternId})"));
hatchElements.Add(patternElement);
hatchElements.Add(boundary);
randomId++;
}
break;

case PolylineBoundaryPath polylineBoundaryPath:
throw new NotImplementedException();
break;

Check warning on line 387 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

Unreachable code detected

Check warning on line 387 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

Unreachable code detected

Check warning on line 387 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

Unreachable code detected

Check warning on line 387 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

Unreachable code detected

Check warning on line 387 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

Unreachable code detected

Check warning on line 387 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

Unreachable code detected

Check warning on line 387 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

Unreachable code detected

Check warning on line 387 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

Unreachable code detected

default:
throw new NotImplementedException();

break;

Check warning on line 392 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

Unreachable code detected

Check warning on line 392 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

Unreachable code detected

Check warning on line 392 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

Unreachable code detected

Check warning on line 392 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Debug)

Unreachable code detected

Check warning on line 392 in src/IxMilia.Converters/DxfToSvgConverter.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest, Release)

Unreachable code detected
}

}
hatches.Add(hatchElements);

return hatches;
}

public static XElement ToXElement(this DxfDimensionBase dim, Dictionary<string, DxfDimStyle> dimStyles, DxfDrawingUnits drawingUnits, DxfUnitFormat unitFormat, int unitPrecision)
{
if (!dimStyles.TryGetValue(dim.DimensionStyleName, out var dimStyle))
Expand Down Expand Up @@ -473,6 +526,20 @@
.AddVectorEffect();
}

public static XElement ToXElement2(this DxfEllipse ellipse, int angleResolutionInDegree = 10)
{
var path = ellipse.GetSvgPath2(angleResolutionInDegree);

XElement baseShape = new XElement(DxfToSvgConverter.Xmlns + "path",
new XAttribute("d", path.ToString()));

baseShape.Add(new XAttribute("fill-opacity", 0));
return baseShape
.AddStroke(ellipse.Color)
.AddStrokeWidth(0)
.AddVectorEffect();
}

public static async Task<XElement> ToXElement(this DxfImage image, DxfToSvgConverterOptions options)
{
var imageHref = await options.ResolveImageHrefAsync(image.ImageDefinition.FilePath);
Expand Down Expand Up @@ -649,6 +716,15 @@
{
return SvgPath.FromEllipse(ellipse.Center.X, ellipse.Center.Y, ellipse.MajorAxis.X, ellipse.MajorAxis.Y, ellipse.MinorAxisRatio, ellipse.StartParameter, ellipse.EndParameter);
}
internal static SvgPath GetSvgPath2(this DxfEllipse ellipse, int angleResolutionInDegree = 10)
{
return SvgPath.FromEllipse2(ellipse.Center.X, ellipse.Center.Y, ellipse.MajorAxis.X, ellipse.MajorAxis.Y, ellipse.MinorAxisRatio, ellipse.StartParameter, ellipse.EndParameter,angleResolutionInDegree: angleResolutionInDegree);
}

internal static SvgPath GetSvgPath(this IList<BoundaryPathEdgeBase> edges)
{
return SvgPath.FromHatch(edges);
}

internal static SvgPath GetSvgPath(this DxfLwPolyline poly)
{
Expand Down Expand Up @@ -710,6 +786,20 @@
return new SvgPath(segments);
}

public static SvgEllipseLineToPath TransformAngle(this SvgEllipseLineToPath pOriginal, double centerX, double centerY, double alpha)
{
var corrLocationX = pOriginal.LocationX - centerX;
var corrLocationY = pOriginal.LocationY - centerY;

// X-Koordinate
double transX = corrLocationX * Math.Cos(alpha) - corrLocationY * Math.Sin(alpha) + centerX;

// Y-Koordinate
double transY = corrLocationX * Math.Sin(alpha) + corrLocationY * Math.Cos(alpha) + centerY;

return new SvgEllipseLineToPath(pOriginal.AngleRadian, transX, transY);
}

private static XElement AddFill(this XElement element, DxfColor color)
{
if (color.IsIndex)
Expand Down
Loading