update
This commit is contained in:
@@ -33,4 +33,29 @@ public sealed class DxfExporterTests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExportRejectsInvalidDrawing()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
document.AddEntity(new LineEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(5, 5), new Point2(5, 5)));
|
||||
|
||||
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.dxf");
|
||||
|
||||
try
|
||||
{
|
||||
var exception = Assert.Throws<InvalidOperationException>(() =>
|
||||
new NetDxfExporter().Export(document, path));
|
||||
|
||||
Assert.Contains("Cannot export invalid drawing", exception.Message);
|
||||
Assert.False(File.Exists(path));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
141
tests/TraceCad.Tests/EntityEditingTests.cs
Normal file
141
tests/TraceCad.Tests/EntityEditingTests.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using TraceCad.Core.Geometry;
|
||||
using TraceCad.Core.Model;
|
||||
using Xunit;
|
||||
|
||||
namespace TraceCad.Tests;
|
||||
|
||||
public sealed class EntityEditingTests
|
||||
{
|
||||
[Fact]
|
||||
public void TrimOrExtendLineEndpointToLineBoundary()
|
||||
{
|
||||
var subject = new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(0, 0),
|
||||
new Point2(8, 0));
|
||||
var boundary = new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(10, -5),
|
||||
new Point2(10, 5));
|
||||
|
||||
var success = EntityEditing.TryTrimOrExtendEndpoint(
|
||||
subject,
|
||||
new Point2(8, 0),
|
||||
boundary,
|
||||
new Point2(10, 0),
|
||||
out var replacement);
|
||||
|
||||
Assert.True(success);
|
||||
var line = Assert.IsType<LineEntity>(replacement);
|
||||
Assert.Equal(new Point2(0, 0), line.Start);
|
||||
Assert.Equal(10.0, line.End.X, 9);
|
||||
Assert.Equal(0.0, line.End.Y, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrimOrExtendLineEndpointCanUseLineBoundaryExtension()
|
||||
{
|
||||
var subject = new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(0, 0),
|
||||
new Point2(8, 0));
|
||||
var boundary = new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(10, 4),
|
||||
new Point2(10, 8));
|
||||
|
||||
var success = EntityEditing.TryTrimOrExtendEndpoint(
|
||||
subject,
|
||||
new Point2(8, 0),
|
||||
boundary,
|
||||
new Point2(10, 4),
|
||||
out var replacement);
|
||||
|
||||
Assert.True(success);
|
||||
var line = Assert.IsType<LineEntity>(replacement);
|
||||
Assert.Equal(10.0, line.End.X, 9);
|
||||
Assert.Equal(0.0, line.End.Y, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrimRejectsLineExtension()
|
||||
{
|
||||
var subject = new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(0, 0),
|
||||
new Point2(8, 0));
|
||||
var boundary = new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(10, -5),
|
||||
new Point2(10, 5));
|
||||
|
||||
var success = EntityEditing.TryTrimEndpoint(
|
||||
subject,
|
||||
new Point2(8, 0),
|
||||
boundary,
|
||||
new Point2(10, 0),
|
||||
out _);
|
||||
|
||||
Assert.False(success);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtendRejectsLineTrim()
|
||||
{
|
||||
var subject = new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(0, 0),
|
||||
new Point2(12, 0));
|
||||
var boundary = new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(10, -5),
|
||||
new Point2(10, 5));
|
||||
|
||||
var success = EntityEditing.TryExtendEndpoint(
|
||||
subject,
|
||||
new Point2(12, 0),
|
||||
boundary,
|
||||
new Point2(10, 0),
|
||||
out _);
|
||||
|
||||
Assert.False(success);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrimOrExtendArcEndpointToLineBoundary()
|
||||
{
|
||||
var subject = new ArcEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(0, 0),
|
||||
10,
|
||||
0,
|
||||
90,
|
||||
false);
|
||||
var boundary = new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(5, -12),
|
||||
new Point2(5, 12));
|
||||
|
||||
var success = EntityEditing.TryTrimOrExtendEndpoint(
|
||||
subject,
|
||||
subject.EndPoint,
|
||||
boundary,
|
||||
new Point2(5, 9),
|
||||
out var replacement);
|
||||
|
||||
Assert.True(success);
|
||||
var arc = Assert.IsType<ArcEntity>(replacement);
|
||||
Assert.Equal(0.0, arc.StartAngleDeg, 9);
|
||||
Assert.Equal(60.0, arc.EndAngleDeg, 9);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,79 @@ public sealed class GeometryTests
|
||||
Assert.Equal(5.0, line.Length, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParallelConstraintPreservesTargetLengthAndMatchesReferenceDirection()
|
||||
{
|
||||
var reference = new LineEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(0, 0), new Point2(10, 0));
|
||||
var target = new LineEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(0, 5), new Point2(0, 9));
|
||||
|
||||
var success = ConstraintGeometry.TryMakeParallel(reference, target, out var replacement);
|
||||
|
||||
Assert.True(success);
|
||||
Assert.Equal(target.Length, replacement.Length, 9);
|
||||
Assert.Equal(0.0, replacement.Start.Y - replacement.End.Y, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParallelDistanceMeasuresPerpendicularSpacing()
|
||||
{
|
||||
var first = new LineEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(0, 0), new Point2(10, 0));
|
||||
var second = new LineEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(3, 7), new Point2(13, 7));
|
||||
|
||||
var success = ConstraintGeometry.TryMeasureParallelDistance(first, second, out var distance);
|
||||
|
||||
Assert.True(success);
|
||||
Assert.Equal(7.0, distance, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TangentConstraintMovesLineOntoNearestArcEndpointTangent()
|
||||
{
|
||||
var arc = new ArcEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(0, 0), 10, 0, 90, false);
|
||||
var line = new LineEntity(Guid.NewGuid(), Layer.Cut.Name, new Point2(10.2, 0.1), new Point2(16.2, 2.1));
|
||||
|
||||
var success = ConstraintGeometry.TryMakeLineTangentToArc(line, arc, out var replacement);
|
||||
|
||||
Assert.True(success);
|
||||
Assert.Equal(arc.StartPoint.X, replacement.Start.X, 9);
|
||||
Assert.Equal(arc.StartPoint.Y, replacement.Start.Y, 9);
|
||||
var direction = (replacement.End - replacement.Start).Normalized();
|
||||
var tangent = GeometryHelpers.TangentAtAngle(arc.StartAngleDeg, arc.IsClockwise);
|
||||
Assert.True(Math.Abs(direction.Dot(tangent)) > 0.999);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SolverAppliesDrivingRadiusDimension()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
var arcId = Guid.NewGuid();
|
||||
document.AddEntity(new ArcEntity(arcId, Layer.Cut.Name, new Point2(0, 0), 10, 0, 90, false));
|
||||
document.AddConstraint(new SketchConstraint(Guid.NewGuid(), ConstraintType.Radius, new[] { arcId }, 25));
|
||||
|
||||
SketchConstraintSolver.Solve(document);
|
||||
|
||||
var arc = Assert.IsType<ArcEntity>(document.Entities.Single(entity => entity.Id == arcId));
|
||||
Assert.Equal(25.0, arc.Radius, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SolverAppliesDrivingParallelDistanceDimension()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
var firstId = Guid.NewGuid();
|
||||
var secondId = Guid.NewGuid();
|
||||
document.AddEntity(new LineEntity(firstId, Layer.Cut.Name, new Point2(0, 0), new Point2(10, 0)));
|
||||
document.AddEntity(new LineEntity(secondId, Layer.Cut.Name, new Point2(0, 2), new Point2(10, 2)));
|
||||
document.AddConstraint(new SketchConstraint(Guid.NewGuid(), ConstraintType.Distance, new[] { firstId, secondId }, 7));
|
||||
|
||||
SketchConstraintSolver.Solve(document);
|
||||
|
||||
var first = Assert.IsType<LineEntity>(document.Entities.Single(entity => entity.Id == firstId));
|
||||
var second = Assert.IsType<LineEntity>(document.Entities.Single(entity => entity.Id == secondId));
|
||||
Assert.True(ConstraintGeometry.TryMeasureParallelDistance(first, second, out var distance));
|
||||
Assert.Equal(7.0, distance, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CircleFromThreePointsComputesCenterAndRadius()
|
||||
{
|
||||
@@ -69,6 +142,34 @@ public sealed class GeometryTests
|
||||
Assert.False(success);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArcFromStartTangentAndEndPointMatchesRequestedTangent()
|
||||
{
|
||||
var success = GeometryHelpers.TryCreateArcFromStartTangentAndEndPoint(
|
||||
new Point2(0, 0),
|
||||
new Vector2(1, 0),
|
||||
new Point2(10, 10),
|
||||
new Point2(8, 2),
|
||||
out var arc);
|
||||
|
||||
Assert.True(success);
|
||||
var tangent = GeometryHelpers.TangentAtAngle(arc.StartAngleDeg, arc.IsClockwise);
|
||||
Assert.True(tangent.Dot(new Vector2(1, 0)) > 0.999);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArcFromStartTangentFailsWhenEndPointLiesOnTangentLine()
|
||||
{
|
||||
var success = GeometryHelpers.TryCreateArcFromStartTangentAndEndPoint(
|
||||
new Point2(0, 0),
|
||||
new Vector2(1, 0),
|
||||
new Point2(10, 0),
|
||||
null,
|
||||
out _);
|
||||
|
||||
Assert.False(success);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(360, 0)]
|
||||
[InlineData(-90, 270)]
|
||||
|
||||
@@ -18,6 +18,8 @@ public sealed class SerializationTests
|
||||
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));
|
||||
document.AddConstraint(new SketchConstraint(Guid.NewGuid(), ConstraintType.Tangent, new[] { lineId, arcId }));
|
||||
document.AddConstraint(new SketchConstraint(Guid.NewGuid(), ConstraintType.Radius, new[] { arcId }, 12.0));
|
||||
|
||||
var json = SketchDocumentSerializer.Serialize(document);
|
||||
var reloaded = SketchDocumentSerializer.Deserialize(json);
|
||||
@@ -27,6 +29,14 @@ public sealed class SerializationTests
|
||||
Assert.Contains(reloaded.Entities, entity => entity.Id == lineId);
|
||||
Assert.Contains(reloaded.Entities, entity => entity.Id == circleId);
|
||||
Assert.Contains(reloaded.Entities, entity => entity.Id == arcId);
|
||||
Assert.Contains(reloaded.Constraints, constraint =>
|
||||
constraint.Type == ConstraintType.Tangent &&
|
||||
constraint.EntityIds.Contains(lineId) &&
|
||||
constraint.EntityIds.Contains(arcId));
|
||||
Assert.Contains(reloaded.Constraints, constraint =>
|
||||
constraint.Type == ConstraintType.Radius &&
|
||||
constraint.EntityIds.Contains(arcId) &&
|
||||
constraint.ValueMm == 12.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -37,7 +47,17 @@ public sealed class SerializationTests
|
||||
"reference.png",
|
||||
0.42,
|
||||
Locked: false,
|
||||
new ReferenceTransform(12.5, 20.0, 0.25, 0.25, 8.0));
|
||||
new ReferenceTransform(12.5, 20.0, 0.25, 0.25, 8.0))
|
||||
{
|
||||
Calibration = new ReferenceCalibration(
|
||||
TargetLineLengthMm: 30.0,
|
||||
MeasuredLineLengthMm: 30.18,
|
||||
ErrorMm: 0.18,
|
||||
EstimatedAccuracyMm: 0.18,
|
||||
ScaleCorrectionFactor: 30.0 / 30.18,
|
||||
MeetsTargetAccuracy: false,
|
||||
ControlLineDetected: true)
|
||||
};
|
||||
|
||||
var json = SketchDocumentSerializer.Serialize(document);
|
||||
var reloaded = SketchDocumentSerializer.Deserialize(json);
|
||||
@@ -49,5 +69,9 @@ public sealed class SerializationTests
|
||||
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);
|
||||
Assert.NotNull(reloaded.Reference.Calibration);
|
||||
Assert.Equal(30.0, reloaded.Reference.Calibration.TargetLineLengthMm, 9);
|
||||
Assert.Equal(30.18, reloaded.Reference.Calibration.MeasuredLineLengthMm, 9);
|
||||
Assert.False(reloaded.Reference.Calibration.MeetsTargetAccuracy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,69 @@ public sealed class SheetCalibratorTests
|
||||
{
|
||||
const double pixelsPerMillimetre = 4.0;
|
||||
var template = ReferenceSheetTemplate.DefaultA4();
|
||||
using var sheet = new Mat(
|
||||
using var sheet = RenderSyntheticSheet(template, pixelsPerMillimetre);
|
||||
|
||||
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.0625, result.MmPerPixel, 9);
|
||||
Assert.Equal("perspective", result.CorrectionMode);
|
||||
Assert.Null(result.Quality);
|
||||
Assert.True(File.Exists(result.CorrectedImagePath));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(tempDirectory, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalibrateAppliesResidualWarpForSyntheticLensDistortion()
|
||||
{
|
||||
const double pixelsPerMillimetre = 8.0;
|
||||
var template = ReferenceSheetTemplate.DefaultA4();
|
||||
using var sheet = RenderSyntheticSheet(template, pixelsPerMillimetre);
|
||||
using var distorted = ApplyRadialDistortion(sheet, -0.025);
|
||||
|
||||
var tempDirectory = Directory.CreateTempSubdirectory("easytrace-distortion-test-").FullName;
|
||||
try
|
||||
{
|
||||
var imagePath = Path.Combine(tempDirectory, "distorted-sheet.png");
|
||||
Cv2.ImWrite(imagePath, distorted);
|
||||
|
||||
var result = new SheetCalibrator().Calibrate(
|
||||
imagePath,
|
||||
tempDirectory,
|
||||
template,
|
||||
SheetCalibrationOptions.Default with
|
||||
{
|
||||
PixelsPerMillimetre = 8.0,
|
||||
MinimumResidualImprovementMm = -100.0
|
||||
});
|
||||
|
||||
Assert.True(result.Success, result.Message);
|
||||
Assert.True(result.MatchedMarkerCount >= 4);
|
||||
Assert.True(
|
||||
result.CorrectionMode.StartsWith("perspective + cross-validated residual warp", StringComparison.Ordinal),
|
||||
$"{result.Message} mode {result.CorrectionMode}");
|
||||
Assert.True(File.Exists(result.CorrectedImagePath));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(tempDirectory, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
private static Mat RenderSyntheticSheet(ReferenceSheetTemplate template, double pixelsPerMillimetre)
|
||||
{
|
||||
var sheet = new Mat(
|
||||
(int)Math.Round(template.HeightMm * pixelsPerMillimetre),
|
||||
(int)Math.Round(template.WidthMm * pixelsPerMillimetre),
|
||||
MatType.CV_8UC3,
|
||||
@@ -38,23 +100,61 @@ public sealed class SheetCalibratorTests
|
||||
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);
|
||||
DrawControlGeometry(sheet, template, pixelsPerMillimetre);
|
||||
return sheet;
|
||||
}
|
||||
|
||||
var result = new SheetCalibrator().Calibrate(imagePath, tempDirectory, template);
|
||||
private static void DrawControlGeometry(
|
||||
Mat sheet,
|
||||
ReferenceSheetTemplate template,
|
||||
double pixelsPerMillimetre)
|
||||
{
|
||||
var thickness = Math.Max(1, (int)Math.Round(0.2 * pixelsPerMillimetre));
|
||||
Cv2.Line(
|
||||
sheet,
|
||||
ToPixel(template.ReferenceLineStartMm, pixelsPerMillimetre),
|
||||
ToPixel(template.ReferenceLineEndMm, pixelsPerMillimetre),
|
||||
Scalar.Black,
|
||||
thickness);
|
||||
|
||||
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
|
||||
var center = ToPixel(template.CrosshairMm, pixelsPerMillimetre);
|
||||
var halfTick = (int)Math.Round(6.0 * pixelsPerMillimetre);
|
||||
Cv2.Line(
|
||||
sheet,
|
||||
new Point(center.X, center.Y - halfTick),
|
||||
new Point(center.X, center.Y + halfTick),
|
||||
Scalar.Black,
|
||||
thickness);
|
||||
}
|
||||
|
||||
private static Point ToPixel(TraceCad.Core.Geometry.Point2 point, double pixelsPerMillimetre) =>
|
||||
new(
|
||||
(int)Math.Round(point.X * pixelsPerMillimetre),
|
||||
(int)Math.Round(point.Y * pixelsPerMillimetre));
|
||||
|
||||
private static Mat ApplyRadialDistortion(Mat source, double coefficient)
|
||||
{
|
||||
using var mapX = new Mat(source.Height, source.Width, MatType.CV_32FC1);
|
||||
using var mapY = new Mat(source.Height, source.Width, MatType.CV_32FC1);
|
||||
var centerX = (source.Width - 1.0) / 2.0;
|
||||
var centerY = (source.Height - 1.0) / 2.0;
|
||||
var radius = Math.Min(centerX, centerY);
|
||||
|
||||
for (var y = 0; y < source.Height; y++)
|
||||
{
|
||||
Directory.Delete(tempDirectory, recursive: true);
|
||||
for (var x = 0; x < source.Width; x++)
|
||||
{
|
||||
var normalizedX = (x - centerX) / radius;
|
||||
var normalizedY = (y - centerY) / radius;
|
||||
var r2 = (normalizedX * normalizedX) + (normalizedY * normalizedY);
|
||||
var scale = 1.0 + (coefficient * r2);
|
||||
mapX.Set(y, x, (float)(centerX + ((x - centerX) * scale)));
|
||||
mapY.Set(y, x, (float)(centerY + ((y - centerY) * scale)));
|
||||
}
|
||||
}
|
||||
|
||||
var distorted = new Mat();
|
||||
Cv2.Remap(source, distorted, mapX, mapY, InterpolationFlags.Linear, BorderTypes.Constant, Scalar.White);
|
||||
return distorted;
|
||||
}
|
||||
}
|
||||
|
||||
60
tests/TraceCad.Tests/TransformTests.cs
Normal file
60
tests/TraceCad.Tests/TransformTests.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using TraceCad.Core.Commands;
|
||||
using TraceCad.Core.Geometry;
|
||||
using TraceCad.Core.Model;
|
||||
using Xunit;
|
||||
|
||||
namespace TraceCad.Tests;
|
||||
|
||||
public sealed class TransformTests
|
||||
{
|
||||
[Fact]
|
||||
public void RotateDocumentRotatesEntitiesAndReference()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
document.AddEntity(new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(1, 0),
|
||||
new Point2(1, 1)));
|
||||
document.Reference = new ReferenceImage(
|
||||
"reference.png",
|
||||
0.5,
|
||||
true,
|
||||
new ReferenceTransform(1, 0, 0.125, 0.125, 0));
|
||||
|
||||
var command = new RotateDocumentCommand(90, Point2.Origin);
|
||||
command.Apply(document);
|
||||
|
||||
var line = Assert.IsType<LineEntity>(document.Entities[0]);
|
||||
Assert.Equal(0.0, line.Start.X, 9);
|
||||
Assert.Equal(1.0, line.Start.Y, 9);
|
||||
Assert.Equal(-1.0, line.End.X, 9);
|
||||
Assert.Equal(1.0, line.End.Y, 9);
|
||||
Assert.NotNull(document.Reference);
|
||||
Assert.Equal(0.0, document.Reference.Transform.OriginX, 9);
|
||||
Assert.Equal(1.0, document.Reference.Transform.OriginY, 9);
|
||||
Assert.Equal(90.0, document.Reference.Transform.RotationDeg, 9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RotateDocumentReverts()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
var line = new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(1, 0),
|
||||
new Point2(1, 1));
|
||||
document.AddEntity(line);
|
||||
|
||||
var command = new RotateDocumentCommand(37, new Point2(5, 5));
|
||||
command.Apply(document);
|
||||
command.Revert(document);
|
||||
|
||||
var reverted = Assert.IsType<LineEntity>(document.Entities[0]);
|
||||
Assert.Equal(line.Start.X, reverted.Start.X, 9);
|
||||
Assert.Equal(line.Start.Y, reverted.Start.Y, 9);
|
||||
Assert.Equal(line.End.X, reverted.End.X, 9);
|
||||
Assert.Equal(line.End.Y, reverted.End.Y, 9);
|
||||
}
|
||||
}
|
||||
58
tests/TraceCad.Tests/ValidationTests.cs
Normal file
58
tests/TraceCad.Tests/ValidationTests.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using TraceCad.Core.Geometry;
|
||||
using TraceCad.Core.Model;
|
||||
using TraceCad.Core.Validation;
|
||||
using Xunit;
|
||||
|
||||
namespace TraceCad.Tests;
|
||||
|
||||
public sealed class ValidationTests
|
||||
{
|
||||
[Fact]
|
||||
public void ValidatorReportsZeroLengthLineAsError()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
document.AddEntity(new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(10, 10),
|
||||
new Point2(10, 10)));
|
||||
|
||||
var issues = DrawingValidator.Validate(document);
|
||||
|
||||
Assert.Contains(issues, issue =>
|
||||
issue.Severity == DrawingIssueSeverity.Error &&
|
||||
issue.Code == "line.zero_length");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidatorReportsTinyLineAsWarning()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
document.AddEntity(new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Cut.Name,
|
||||
new Point2(0, 0),
|
||||
new Point2(0.01, 0)));
|
||||
|
||||
var issues = DrawingValidator.Validate(document);
|
||||
|
||||
Assert.Contains(issues, issue =>
|
||||
issue.Severity == DrawingIssueSeverity.Warning &&
|
||||
issue.Code == "line.tiny");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidatorIgnoresOpenEndpointsOnNonExportableLayers()
|
||||
{
|
||||
var document = SketchDocument.CreateDefault();
|
||||
document.AddEntity(new LineEntity(
|
||||
Guid.NewGuid(),
|
||||
Layer.Construction.Name,
|
||||
new Point2(0, 0),
|
||||
new Point2(100, 0)));
|
||||
|
||||
var issues = DrawingValidator.Validate(document);
|
||||
|
||||
Assert.DoesNotContain(issues, issue => issue.Code == "contour.open_endpoint");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user