initial commit
This commit is contained in:
36
tests/TraceCad.Tests/DxfExporterTests.cs
Normal file
36
tests/TraceCad.Tests/DxfExporterTests.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using TraceCad.Core.Geometry;
|
||||
using TraceCad.Core.Model;
|
||||
using TraceCad.Dxf;
|
||||
using Xunit;
|
||||
|
||||
namespace TraceCad.Tests;
|
||||
|
||||
public sealed class DxfExporterTests
|
||||
{
|
||||
[Fact]
|
||||
public void ExportCreatesDxfFile()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
document.AddEntity(new LineEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(0, 0), new Point2(100, 0)));
|
||||
document.AddEntity(new CircleEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(20, 20), 10));
|
||||
document.AddEntity(new ArcEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(40, 40), 12, 0, 180, false));
|
||||
|
||||
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.dxf");
|
||||
|
||||
try
|
||||
{
|
||||
new NetDxfExporter().Export(document, path);
|
||||
|
||||
Assert.True(File.Exists(path));
|
||||
Assert.True(new FileInfo(path).Length > 0);
|
||||
Assert.Contains("SECTION", File.ReadAllText(path));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
80
tests/TraceCad.Tests/GeometryTests.cs
Normal file
80
tests/TraceCad.Tests/GeometryTests.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using TraceCad.Core.Geometry;
|
||||
using TraceCad.Core.Model;
|
||||
using Xunit;
|
||||
|
||||
namespace TraceCad.Tests;
|
||||
|
||||
public sealed class GeometryTests
|
||||
{
|
||||
[Fact]
|
||||
public void LineLengthUsesMillimetres()
|
||||
{
|
||||
var line = new LineEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(0, 0), new Point2(3, 4));
|
||||
|
||||
Assert.Equal(5.0, line.Length, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CircleFromThreePointsComputesCenterAndRadius()
|
||||
{
|
||||
var success = GeometryHelpers.TryCreateCircleFromThreePoints(
|
||||
new Point2(1, 0),
|
||||
new Point2(0, 1),
|
||||
new Point2(-1, 0),
|
||||
out var circle);
|
||||
|
||||
Assert.True(success);
|
||||
Assert.Equal(0.0, circle.Center.X, 9);
|
||||
Assert.Equal(0.0, circle.Center.Y, 9);
|
||||
Assert.Equal(1.0, circle.Radius, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CircleFromCollinearPointsFails()
|
||||
{
|
||||
var success = GeometryHelpers.TryCreateCircleFromThreePoints(
|
||||
new Point2(0, 0),
|
||||
new Point2(1, 1),
|
||||
new Point2(2, 2),
|
||||
out _);
|
||||
|
||||
Assert.False(success);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArcFromThreePointsPreservesPointOnCounterClockwiseSweep()
|
||||
{
|
||||
var success = GeometryHelpers.TryCreateArcFromThreePoints(
|
||||
new Point2(1, 0),
|
||||
new Point2(0, 1),
|
||||
new Point2(-1, 0),
|
||||
out var arc);
|
||||
|
||||
Assert.True(success);
|
||||
Assert.False(arc.IsClockwise);
|
||||
Assert.Equal(0.0, arc.StartAngleDeg, 9);
|
||||
Assert.Equal(180.0, arc.EndAngleDeg, 9);
|
||||
Assert.Equal(1.0, arc.Radius, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArcFromCollinearPointsFails()
|
||||
{
|
||||
var success = GeometryHelpers.TryCreateArcFromThreePoints(
|
||||
new Point2(0, 0),
|
||||
new Point2(5, 0),
|
||||
new Point2(10, 0),
|
||||
out _);
|
||||
|
||||
Assert.False(success);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(360, 0)]
|
||||
[InlineData(-90, 270)]
|
||||
[InlineData(725, 5)]
|
||||
public void AngleNormalizationWrapsToPositiveDegrees(double input, double expected)
|
||||
{
|
||||
Assert.Equal(expected, GeometryHelpers.NormalizeAngleDeg(input), 9);
|
||||
}
|
||||
}
|
||||
53
tests/TraceCad.Tests/SerializationTests.cs
Normal file
53
tests/TraceCad.Tests/SerializationTests.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using TraceCad.Core.Geometry;
|
||||
using TraceCad.Core.Model;
|
||||
using TraceCad.Core.Serialization;
|
||||
using Xunit;
|
||||
|
||||
namespace TraceCad.Tests;
|
||||
|
||||
public sealed class SerializationTests
|
||||
{
|
||||
[Fact]
|
||||
public void SketchDocumentRoundTripsEntitiesAndLayers()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
var lineId = Guid.NewGuid();
|
||||
var circleId = Guid.NewGuid();
|
||||
var arcId = Guid.NewGuid();
|
||||
|
||||
document.AddEntity(new LineEntity(lineId, Layer.Cut.Name, new Point2(10, 20), new Point2(40, 50)));
|
||||
document.AddEntity(new CircleEntity(circleId, Layer.Cut.Name, new Point2(15, 15), 8));
|
||||
document.AddEntity(new ArcEntity(arcId, Layer.Cut.Name, new Point2(0, 0), 12, 0, 90, false));
|
||||
|
||||
var json = SketchDocumentSerializer.Serialize(document);
|
||||
var reloaded = SketchDocumentSerializer.Deserialize(json);
|
||||
|
||||
Assert.Equal("mm", reloaded.Units);
|
||||
Assert.Equal(document.Layers.Count, reloaded.Layers.Count);
|
||||
Assert.Contains(reloaded.Entities, entity => entity.Id == lineId);
|
||||
Assert.Contains(reloaded.Entities, entity => entity.Id == circleId);
|
||||
Assert.Contains(reloaded.Entities, entity => entity.Id == arcId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SketchDocumentRoundTripsReferenceImage()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
document.Reference = new ReferenceImage(
|
||||
"reference.png",
|
||||
0.42,
|
||||
Locked: false,
|
||||
new ReferenceTransform(12.5, 20.0, 0.25, 0.25, 8.0));
|
||||
|
||||
var json = SketchDocumentSerializer.Serialize(document);
|
||||
var reloaded = SketchDocumentSerializer.Deserialize(json);
|
||||
|
||||
Assert.NotNull(reloaded.Reference);
|
||||
Assert.Equal("reference.png", reloaded.Reference.ImagePath);
|
||||
Assert.Equal(0.42, reloaded.Reference.Opacity, 9);
|
||||
Assert.False(reloaded.Reference.Locked);
|
||||
Assert.Equal(12.5, reloaded.Reference.Transform.OriginX, 9);
|
||||
Assert.Equal(0.25, reloaded.Reference.Transform.ScaleX, 9);
|
||||
Assert.Equal(8.0, reloaded.Reference.Transform.RotationDeg, 9);
|
||||
}
|
||||
}
|
||||
60
tests/TraceCad.Tests/SheetCalibratorTests.cs
Normal file
60
tests/TraceCad.Tests/SheetCalibratorTests.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using OpenCvSharp;
|
||||
using OpenCvSharp.Aruco;
|
||||
using TraceCad.Vision.Calibration;
|
||||
using Xunit;
|
||||
|
||||
namespace TraceCad.Tests;
|
||||
|
||||
public sealed class SheetCalibratorTests
|
||||
{
|
||||
[Fact]
|
||||
public void CalibrateDetectsSyntheticA4SheetMarkers()
|
||||
{
|
||||
const double pixelsPerMillimetre = 4.0;
|
||||
var template = ReferenceSheetTemplate.DefaultA4();
|
||||
using var sheet = new Mat(
|
||||
(int)Math.Round(template.HeightMm * pixelsPerMillimetre),
|
||||
(int)Math.Round(template.WidthMm * pixelsPerMillimetre),
|
||||
MatType.CV_8UC3,
|
||||
Scalar.White);
|
||||
var dictionary = CvAruco.GetPredefinedDictionary(PredefinedDictionaryName.Dict5X5_1000);
|
||||
|
||||
foreach (var marker in template.Markers)
|
||||
{
|
||||
using var markerImage = new Mat();
|
||||
dictionary.GenerateImageMarker(
|
||||
marker.Id,
|
||||
(int)Math.Round(marker.SizeMm * pixelsPerMillimetre),
|
||||
markerImage,
|
||||
1);
|
||||
using var markerColor = new Mat();
|
||||
Cv2.CvtColor(markerImage, markerColor, ColorConversionCodes.GRAY2BGR);
|
||||
|
||||
var target = new Rect(
|
||||
(int)Math.Round(marker.TopLeftMm.X * pixelsPerMillimetre),
|
||||
(int)Math.Round(marker.TopLeftMm.Y * pixelsPerMillimetre),
|
||||
markerColor.Width,
|
||||
markerColor.Height);
|
||||
markerColor.CopyTo(new Mat(sheet, target));
|
||||
}
|
||||
|
||||
var tempDirectory = Directory.CreateTempSubdirectory("easytrace-calibration-test-").FullName;
|
||||
try
|
||||
{
|
||||
var imagePath = Path.Combine(tempDirectory, "sheet.png");
|
||||
Cv2.ImWrite(imagePath, sheet);
|
||||
|
||||
var result = new SheetCalibrator().Calibrate(imagePath, tempDirectory, template);
|
||||
|
||||
Assert.True(result.Success, result.Message);
|
||||
Assert.Equal(8, result.MatchedMarkerCount);
|
||||
Assert.Equal(0.125, result.MmPerPixel, 9);
|
||||
Assert.Equal("perspective + marker residual warp", result.CorrectionMode);
|
||||
Assert.True(File.Exists(result.CorrectedImagePath));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(tempDirectory, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
tests/TraceCad.Tests/TraceCad.Tests.csproj
Normal file
19
tests/TraceCad.Tests/TraceCad.Tests.csproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\TraceCad.Core\TraceCad.Core.csproj" />
|
||||
<ProjectReference Include="..\..\src\TraceCad.Dxf\TraceCad.Dxf.csproj" />
|
||||
<ProjectReference Include="..\..\src\TraceCad.Vision\TraceCad.Vision.csproj" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user