diff --git a/CSMath.Tests/Geometry/Arc2DTests.cs b/CSMath.Tests/Geometry/Arc2DTests.cs
new file mode 100644
index 0000000..5536be4
--- /dev/null
+++ b/CSMath.Tests/Geometry/Arc2DTests.cs
@@ -0,0 +1,550 @@
+using CSMath.Geometry;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace CSMath.Tests.Geometry;
+
+public class Arc2DTests
+{
+ private const double Tolerance = 1e-10;
+
+ [Fact]
+ public void Arc2D_DefaultConstructor_CreatesZeroArc()
+ {
+ // Arrange & Act
+ Arc2D arc = new Arc2D();
+
+ // Assert
+ Assert.Equal(0, arc.StartAngle);
+ Assert.Equal(0, arc.EndAngle);
+ Assert.Equal(0, arc.Radius);
+ Assert.Equal(XY.Zero, arc.Center);
+ }
+
+ [Fact]
+ public void Arc2D_Properties_CanBeSetAndRetrieved()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ StartAngle = MathHelper.DegToRad(45),
+ EndAngle = MathHelper.DegToRad(135),
+ Radius = 10.0,
+ Center = new XY(5, 5)
+ };
+
+ // Assert
+ Assert.Equal(MathHelper.DegToRad(45), arc.StartAngle, Tolerance);
+ Assert.Equal(MathHelper.DegToRad(135), arc.EndAngle, Tolerance);
+ Assert.Equal(10.0, arc.Radius, Tolerance);
+ Assert.Equal(new XY(5, 5), arc.Center);
+ }
+
+ [Fact]
+ public void FindIntersections_ArcWithOffsetCenter_ReturnsCorrectPoints()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = new XY(10, 10),
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.TwoPI
+ };
+
+ // Horizontal line through the center
+ Line2D line = new Line2D(new XY(0, 10), new XY(20, 0));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ Assert.Equal(2, intersections.Count);
+ Assert.Contains(intersections, p => Math.Abs(p.X - 15) < Tolerance && Math.Abs(p.Y - 10) < Tolerance);
+ Assert.Contains(intersections, p => Math.Abs(p.X - 5) < Tolerance && Math.Abs(p.Y - 10) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_DiagonalLineThroughArc_ReturnsTwoPoints()
+ {
+ // Arrange - Full circle
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.TwoPI
+ };
+
+ // Diagonal line passing through center (45 degree angle)
+ Line2D line = new Line2D(new XY(-10, -10), new XY(20, 20));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ Assert.Equal(2, intersections.Count);
+
+ // Points should be at 45° and 225°
+ double expectedCoord = 5 / Math.Sqrt(2);
+ Assert.Contains(intersections, p =>
+ Math.Abs(p.X - expectedCoord) < Tolerance &&
+ Math.Abs(p.Y - expectedCoord) < Tolerance);
+ Assert.Contains(intersections, p =>
+ Math.Abs(p.X + expectedCoord) < Tolerance &&
+ Math.Abs(p.Y + expectedCoord) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_FullCircle_LinePassesThrough_ReturnsTwoPoints()
+ {
+ // Arrange - Full circle (0° to 360°)
+ Arc2D arc = new Arc2D
+ {
+ Center = new XY(5, 5),
+ Radius = 3.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.TwoPI
+ };
+
+ // Line passing through circle
+ Line2D line = new Line2D(new XY(5, 0), new XY(0, 10));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ Assert.Equal(2, intersections.Count);
+ // Verify both points are at the correct distance from center
+ foreach (var point in intersections)
+ {
+ double distance = Math.Sqrt(Math.Pow(point.X - 5, 2) + Math.Pow(point.Y - 5, 2));
+ Assert.True(Math.Abs(distance - 3.0) < Tolerance);
+ }
+ }
+
+ [Fact]
+ public void FindIntersections_LargeRadius_ReturnsCorrectPoints()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 1000.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.TwoPI
+ };
+
+ // Horizontal line through center
+ Line2D line = new Line2D(new XY(-2000, 0), new XY(4000, 0));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ Assert.Equal(2, intersections.Count);
+ Assert.Contains(intersections, p => Math.Abs(p.X - 1000) < Tolerance && Math.Abs(p.Y) < Tolerance);
+ Assert.Contains(intersections, p => Math.Abs(p.X + 1000) < Tolerance && Math.Abs(p.Y) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_LineIntersectsArcAtOnePoint_ReturnsOnePoint()
+ {
+ // Arrange - Arc from 45° to 135° (upper half, centered)
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = MathHelper.DegToRad(45),
+ EndAngle = MathHelper.DegToRad(135)
+ };
+
+ // Vertical line through center at x = 0
+ Line2D line = new Line2D(new XY(0, -10), new XY(0, 20));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ // Should intersect at 90° (0, 5)
+ Assert.Single(intersections);
+ Assert.True(Math.Abs(intersections[0].X) < Tolerance);
+ Assert.True(Math.Abs(intersections[0].Y - 5) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_LineIntersectsCircleButNotArc_ReturnsEmpty()
+ {
+ // Arrange - Arc from 0° to 90° (first quadrant only)
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.HalfPI
+ };
+
+ // Horizontal line through center (would intersect at 180° and 0°)
+ // But 180° is outside the arc range
+ Line2D line = new Line2D(new XY(-10, 0), new XY(20, 0));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ // Should only find the point at 0° (5, 0)
+ Assert.Single(intersections);
+ Assert.True(Math.Abs(intersections[0].X - 5) < Tolerance);
+ Assert.True(Math.Abs(intersections[0].Y) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_LineMissesArc_ReturnsEmpty()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.TwoPI
+ };
+
+ // Horizontal line far above the arc
+ Line2D line = new Line2D(new XY(-10, 10), new XY(20, 0));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance);
+
+ // Assert
+ Assert.Empty(intersections);
+ }
+
+ [Fact]
+ public void FindIntersections_LinePassesThroughArc_ReturnsTwoPoints()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = Math.PI // Half circle (0° to 180°)
+ };
+
+ // Horizontal line passing through center
+ Line2D line = new Line2D(new XY(-10, 0), new XY(20, 0));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ Assert.Equal(2, intersections.Count);
+ Assert.Contains(intersections, p => Math.Abs(p.X - 5) < Tolerance && Math.Abs(p.Y) < Tolerance);
+ Assert.Contains(intersections, p => Math.Abs(p.X + 5) < Tolerance && Math.Abs(p.Y) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_LineTangentToArc_ReturnsOnePoint()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.TwoPI // Full circle
+ };
+
+ // Horizontal line tangent at the top
+ Line2D line = new Line2D(new XY(-10, 5), new XY(20, 0));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ Assert.Single(intersections);
+ Assert.True(Math.Abs(intersections[0].X) < Tolerance);
+ Assert.True(Math.Abs(intersections[0].Y - 5) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_SmallArc_LineIntersectsOnce_ReturnsOnePoint()
+ {
+ // Arrange - Small arc from 80° to 100°
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 10.0,
+ StartAngle = MathHelper.DegToRad(80),
+ EndAngle = MathHelper.DegToRad(100)
+ };
+
+ // Vertical line at x = 0 (intersects at 90°)
+ Line2D line = new Line2D(new XY(0, 0), new XY(0, 20));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ Assert.Single(intersections);
+ Assert.True(Math.Abs(intersections[0].X) < Tolerance);
+ Assert.True(Math.Abs(intersections[0].Y - 10) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_VerticalLine_ReturnsCorrectPoints()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.TwoPI
+ };
+
+ // Vertical line at x = 3
+ Line2D line = new Line2D(new XY(3, -10), new XY(0, 20));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ Assert.Equal(2, intersections.Count);
+
+ // Calculate expected Y coordinates: sqrt(r^2 - x^2) = sqrt(25 - 9) = 4
+ double expectedY = 4.0;
+ Assert.Contains(intersections, p => Math.Abs(p.X - 3) < Tolerance && Math.Abs(p.Y - expectedY) < Tolerance);
+ Assert.Contains(intersections, p => Math.Abs(p.X - 3) < Tolerance && Math.Abs(p.Y + expectedY) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_VerySmallRadius_ReturnsCorrectPoints()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 0.001,
+ StartAngle = 0,
+ EndAngle = MathHelper.TwoPI
+ };
+
+ // Horizontal line through center
+ Line2D line = new Line2D(new XY(-1, 0), new XY(2, 0));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ Assert.Equal(2, intersections.Count);
+ Assert.Contains(intersections, p => Math.Abs(p.X - 0.001) < Tolerance && Math.Abs(p.Y) < Tolerance);
+ Assert.Contains(intersections, p => Math.Abs(p.X + 0.001) < Tolerance && Math.Abs(p.Y) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_WrapAroundArc_ReturnsCorrectPoints()
+ {
+ // Arrange - Arc from 315° to 45° (wraps around 0°)
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = MathHelper.DegToRad(315),
+ EndAngle = MathHelper.DegToRad(45)
+ };
+
+ // Horizontal line through center
+ Line2D line = new Line2D(new XY(-10, 0), new XY(20, 0));
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance).ToList();
+
+ // Assert
+ // Should only intersect at 0° (5, 0), not at 180° (-5, 0)
+ Assert.Single(intersections);
+ Assert.True(Math.Abs(intersections[0].X - 5) < Tolerance);
+ Assert.True(Math.Abs(intersections[0].Y) < Tolerance);
+ }
+
+ [Fact]
+ public void FindIntersections_ZeroLengthLine_ReturnsEmpty()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.TwoPI
+ };
+
+ // Zero-length line (point)
+ Line2D line = new Line2D(new XY(5, 0), XY.Zero);
+
+ // Act
+ var intersections = arc.FindIntersections(line, Tolerance);
+
+ // Assert
+ Assert.Empty(intersections);
+ }
+
+ [Fact]
+ public void InAngularRange_CenterOffsetArc_PointInRange_ReturnsTrue()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = new XY(10, 10),
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.HalfPI
+ };
+
+ // Point at 45 degrees from center
+ double angle = MathHelper.DegToRad(45);
+ XY point = new XY(10 + 5 * Math.Cos(angle), 10 + 5 * Math.Sin(angle));
+
+ // Act
+ bool result = arc.InAngularRange(point);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void InAngularRange_PointAtEndAngle_ReturnsTrue()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = MathHelper.DegToRad(30),
+ EndAngle = MathHelper.DegToRad(120)
+ };
+
+ double endAngle = MathHelper.DegToRad(120);
+ XY point = new XY(5 * Math.Cos(endAngle), 5 * Math.Sin(endAngle));
+
+ // Act
+ bool result = arc.InAngularRange(point);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void InAngularRange_PointAtStartAngle_ReturnsTrue()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = MathHelper.DegToRad(30),
+ EndAngle = MathHelper.DegToRad(120)
+ };
+
+ double startAngle = MathHelper.DegToRad(30);
+ XY point = new XY(5 * Math.Cos(startAngle), 5 * Math.Sin(startAngle));
+
+ // Act
+ bool result = arc.InAngularRange(point);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void InAngularRange_PointOutsideRange_ReturnsFalse()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.HalfPI // 90 degrees
+ };
+
+ // Point at 180 degrees
+ XY point = new XY(-5, 0);
+
+ // Act
+ bool result = arc.InAngularRange(point);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void InAngularRange_PointWithinRange_ReturnsTrue()
+ {
+ // Arrange
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = 0,
+ EndAngle = MathHelper.HalfPI // 90 degrees
+ };
+
+ // Point at 45 degrees
+ XY point = new XY(5 * Math.Cos(MathHelper.DegToRad(45)), 5 * Math.Sin(MathHelper.DegToRad(45)));
+
+ // Act
+ bool result = arc.InAngularRange(point);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void InAngularRange_WrapAroundArc_PointInRange_ReturnsTrue()
+ {
+ // Arrange - Arc from 315° to 45° (wraps around 0°)
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = MathHelper.DegToRad(315),
+ EndAngle = MathHelper.DegToRad(45)
+ };
+
+ // Point at 10 degrees (should be in range)
+ double angle = MathHelper.DegToRad(10);
+ XY point = new XY(5 * Math.Cos(angle), 5 * Math.Sin(angle));
+
+ // Act
+ bool result = arc.InAngularRange(point);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void InAngularRange_WrapAroundArc_PointOutsideRange_ReturnsFalse()
+ {
+ // Arrange - Arc from 315° to 45° (wraps around 0°)
+ Arc2D arc = new Arc2D
+ {
+ Center = XY.Zero,
+ Radius = 5.0,
+ StartAngle = MathHelper.DegToRad(315),
+ EndAngle = MathHelper.DegToRad(45)
+ };
+
+ // Point at 180 degrees (should be outside range)
+ XY point = new XY(-5, 0);
+
+ // Act
+ bool result = arc.InAngularRange(point);
+
+ // Assert
+ Assert.False(result);
+ }
+}
\ No newline at end of file
diff --git a/CSMath.Tests/Geometry/Circle2DTests.cs b/CSMath.Tests/Geometry/Circle2DTests.cs
new file mode 100644
index 0000000..31e01ef
--- /dev/null
+++ b/CSMath.Tests/Geometry/Circle2DTests.cs
@@ -0,0 +1,218 @@
+using CSMath.Geometry;
+using System.Linq;
+using Xunit;
+
+namespace CSMath.Tests.Geometry;
+
+public class Circle2DTests
+{
+ [Fact]
+ public void Constructor_WithCenterAndRadius_SetsPropertiesCorrectly()
+ {
+ // Arrange
+ XY center = new XY(5.0, 10.0);
+ double radius = 7.5;
+
+ // Act
+ Circle2D circle = new Circle2D(center, radius);
+
+ // Assert
+ Assert.Equal(center, circle.Center);
+ Assert.Equal(radius, circle.Radius);
+ }
+
+ [Fact]
+ public void Constructor_WithCoordinatesAndRadius_SetsPropertiesCorrectly()
+ {
+ // Arrange
+ double centerX = 3.0;
+ double centerY = 4.0;
+ double radius = 5.0;
+
+ // Act
+ Circle2D circle = new Circle2D(centerX, centerY, radius);
+
+ // Assert
+ Assert.Equal(centerX, circle.Center.X);
+ Assert.Equal(centerY, circle.Center.Y);
+ Assert.Equal(radius, circle.Radius);
+ }
+
+ [Theory]
+ [InlineData(0.0, 0.0, 1.0)]
+ [InlineData(5.0, 5.0, 10.0)]
+ [InlineData(-3.0, 4.0, 2.5)]
+ public void Constructor_WithVariousValues_CreatesCorrectCircle(double x, double y, double radius)
+ {
+ // Act
+ Circle2D circle1 = new Circle2D(x, y, radius);
+ Circle2D circle2 = new Circle2D(new XY(x, y), radius);
+
+ // Assert
+ Assert.Equal(x, circle1.Center.X);
+ Assert.Equal(y, circle1.Center.Y);
+ Assert.Equal(radius, circle1.Radius);
+ Assert.Equal(circle1.Center, circle2.Center);
+ Assert.Equal(circle1.Radius, circle2.Radius);
+ }
+
+ [Fact]
+ public void FindIntersections_WithDiagonalLine_ReturnsTwoIntersections()
+ {
+ // Arrange
+ Circle2D circle = new Circle2D(0.0, 0.0, 5.0);
+ Line2D line = new Line2D(new XY(-10.0, -10.0), new XY(1.0, 1.0)); // Diagonal line y=x
+
+ // Act
+ var intersections = circle.FindIntersections(line).ToArray();
+
+ // Assert
+ Assert.Equal(2, intersections.Length);
+
+ // Both points should be on the circle (distance from center = radius)
+ double distance1 = (intersections[0] - circle.Center).GetLength();
+ double distance2 = (intersections[1] - circle.Center).GetLength();
+ Assert.True(MathHelper.IsEqual(circle.Radius, distance1, 1e-10));
+ Assert.True(MathHelper.IsEqual(circle.Radius, distance2, 1e-10));
+
+ // Both points should satisfy y=x
+ Assert.True(MathHelper.IsEqual(intersections[0].X, intersections[0].Y, 1e-10));
+ Assert.True(MathHelper.IsEqual(intersections[1].X, intersections[1].Y, 1e-10));
+ }
+
+ [Fact]
+ public void FindIntersections_WithLargeCircle_ReturnsPreciseIntersections()
+ {
+ // Arrange
+ Circle2D circle = new Circle2D(0.0, 0.0, 1000.0);
+ Line2D line = new Line2D(new XY(-2000.0, 0.0), new XY(1.0, 0.0)); // Horizontal line through center
+
+ // Act
+ var intersections = circle.FindIntersections(line).ToArray();
+
+ // Assert
+ Assert.Equal(2, intersections.Length);
+
+ // Verify distances are equal to radius
+ double distance1 = intersections[0].GetLength();
+ double distance2 = intersections[1].GetLength();
+ Assert.True(MathHelper.IsEqual(circle.Radius, distance1, 1e-8));
+ Assert.True(MathHelper.IsEqual(circle.Radius, distance2, 1e-8));
+ }
+
+ [Fact]
+ public void FindIntersections_WithLineNegativeDirection_ReturnsTwoIntersections()
+ {
+ // Arrange
+ Circle2D circle = new Circle2D(0.0, 0.0, 5.0);
+ Line2D line = new Line2D(new XY(0.0, 10.0), new XY(0.0, -1.0)); // Vertical line with negative direction
+
+ // Act
+ var intersections = circle.FindIntersections(line).ToArray();
+
+ // Assert
+ Assert.Equal(2, intersections.Length);
+
+ // Both points should be at x=0
+ Assert.True(MathHelper.IsZero(intersections[0].X, 1e-10));
+ Assert.True(MathHelper.IsZero(intersections[1].X, 1e-10));
+
+ // Both points should be on the circle
+ double distance1 = intersections[0].GetLength();
+ double distance2 = intersections[1].GetLength();
+ Assert.True(MathHelper.IsEqual(circle.Radius, distance1, 1e-10));
+ Assert.True(MathHelper.IsEqual(circle.Radius, distance2, 1e-10));
+ }
+
+ [Fact]
+ public void FindIntersections_WithLineThroughCenter_ReturnsTwoIntersections()
+ {
+ // Arrange
+ Circle2D circle = new Circle2D(0.0, 0.0, 5.0);
+ Line2D line = new Line2D(new XY(-10.0, 0.0), new XY(1.0, 0.0)); // Horizontal line through center
+
+ // Act
+ var intersections = circle.FindIntersections(line).ToArray();
+
+ // Assert
+ Assert.Equal(2, intersections.Length);
+ Assert.True(MathHelper.IsEqual(5.0, intersections[0].X, 1e-10) || MathHelper.IsEqual(-5.0, intersections[0].X, 1e-10));
+ Assert.True(MathHelper.IsZero(intersections[0].Y, 1e-10));
+ Assert.True(MathHelper.IsEqual(5.0, intersections[1].X, 1e-10) || MathHelper.IsEqual(-5.0, intersections[1].X, 1e-10));
+ Assert.True(MathHelper.IsZero(intersections[1].Y, 1e-10));
+ }
+
+ [Fact]
+ public void FindIntersections_WithNonIntersectingLine_ReturnsEmpty()
+ {
+ // Arrange
+ Circle2D circle = new Circle2D(0.0, 0.0, 5.0);
+ Line2D line = new Line2D(new XY(10.0, 0.0), new XY(0.0, 1.0)); // Vertical line at x=10, outside circle
+
+ // Act
+ var intersections = circle.FindIntersections(line);
+
+ // Assert
+ Assert.Empty(intersections);
+ }
+
+ [Fact]
+ public void FindIntersections_WithOffsetCircle_ReturnsTwoIntersections()
+ {
+ // Arrange
+ Circle2D circle = new Circle2D(3.0, 4.0, 5.0);
+ Line2D line = new Line2D(new XY(3.0, -10.0), new XY(0.0, 1.0)); // Vertical line through center x=3
+
+ // Act
+ var intersections = circle.FindIntersections(line).ToArray();
+
+ // Assert
+ Assert.Equal(2, intersections.Length);
+
+ // Both points should be on the vertical line x=3
+ Assert.True(MathHelper.IsEqual(3.0, intersections[0].X, 1e-10));
+ Assert.True(MathHelper.IsEqual(3.0, intersections[1].X, 1e-10));
+
+ // Both points should be on the circle
+ double distance1 = (intersections[0] - circle.Center).GetLength();
+ double distance2 = (intersections[1] - circle.Center).GetLength();
+ Assert.True(MathHelper.IsEqual(circle.Radius, distance1, 1e-10));
+ Assert.True(MathHelper.IsEqual(circle.Radius, distance2, 1e-10));
+ }
+
+ [Fact]
+ public void FindIntersections_WithSmallCircle_ReturnsPreciseIntersections()
+ {
+ // Arrange
+ Circle2D circle = new Circle2D(0.0, 0.0, 0.1);
+ Line2D line = new Line2D(new XY(-1.0, 0.0), new XY(1.0, 0.0)); // Horizontal line through center
+
+ // Act
+ var intersections = circle.FindIntersections(line).ToArray();
+
+ // Assert
+ Assert.Equal(2, intersections.Length);
+
+ // Verify distances are equal to radius
+ double distance1 = intersections[0].GetLength();
+ double distance2 = intersections[1].GetLength();
+ Assert.True(MathHelper.IsEqual(circle.Radius, distance1, 1e-10));
+ Assert.True(MathHelper.IsEqual(circle.Radius, distance2, 1e-10));
+ }
+
+ [Fact]
+ public void FindIntersections_WithTangentLine_ReturnsOneIntersection()
+ {
+ // Arrange
+ Circle2D circle = new Circle2D(0.0, 0.0, 5.0);
+ Line2D line = new Line2D(new XY(5.0, -10.0), new XY(0.0, 1.0)); // Vertical tangent line at x=5
+
+ // Act
+ var intersections = circle.FindIntersections(line).ToArray();
+
+ // Assert
+ Assert.Single(intersections);
+ Assert.True(MathHelper.IsEqual(5.0, intersections[0].X, 1e-10));
+ Assert.True(MathHelper.IsZero(intersections[0].Y, 1e-10));
+ }
+}
\ No newline at end of file
diff --git a/CSMath.Tests/Geometry/Segment2DTests.cs b/CSMath.Tests/Geometry/Segment2DTests.cs
new file mode 100644
index 0000000..7233348
--- /dev/null
+++ b/CSMath.Tests/Geometry/Segment2DTests.cs
@@ -0,0 +1,258 @@
+using CSMath.Geometry;
+using Xunit;
+
+namespace CSMath.Tests.Geometry;
+
+public class Segment2DTests
+{
+ [Fact]
+ public void Constructor_WithValidPoints_CreatesSegment()
+ {
+ var origin = new XY(0, 0);
+ var end = new XY(1, 1);
+
+ var segment = new Segment2D(origin, end);
+
+ Assert.Equal(origin, segment.Origin);
+ Assert.Equal(end, segment.End);
+ }
+
+ [Fact]
+ public void Constructor_WithZeroLengthSegment_CreatesSegment()
+ {
+ var point = new XY(5, 5);
+
+ var segment = new Segment2D(point, point);
+
+ Assert.Equal(point, segment.Origin);
+ Assert.Equal(point, segment.End);
+ }
+
+ [Fact]
+ public void Direction_ForZeroLengthSegment_ReturnsZeroVector()
+ {
+ var point = new XY(5, 5);
+ var segment = new Segment2D(point, point);
+
+ var direction = segment.Direction;
+
+ Assert.Equal(XY.Zero, direction);
+ }
+
+ [Fact]
+ public void Direction_ReturnsVectorFromOriginToEnd()
+ {
+ var origin = new XY(1, 2);
+ var end = new XY(4, 6);
+ var segment = new Segment2D(origin, end);
+
+ var direction = segment.Direction;
+
+ Assert.Equal(new XY(3, 4), direction);
+ }
+
+ [Fact]
+ public void Direction_UpdatesWhenPropertiesChange()
+ {
+ var segment = new Segment2D(new XY(0, 0), new XY(1, 1));
+
+ segment.End = new XY(2, 2);
+
+ Assert.Equal(new XY(2, 2), segment.Direction);
+ }
+
+ [Fact]
+ public void Direction_WithNegativeCoordinates_ReturnsCorrectVector()
+ {
+ var origin = new XY(-1, -2);
+ var end = new XY(-4, -6);
+ var segment = new Segment2D(origin, end);
+
+ var direction = segment.Direction;
+
+ Assert.Equal(new XY(-3, -4), direction);
+ }
+
+ [Fact]
+ public void End_CanBeModified()
+ {
+ var segment = new Segment2D(new XY(0, 0), new XY(1, 1));
+ var newEnd = new XY(3, 3);
+
+ segment.End = newEnd;
+
+ Assert.Equal(newEnd, segment.End);
+ }
+
+ [Fact]
+ public void Equals_WithDifferentEnd_ReturnsFalse()
+ {
+ var segment1 = new Segment2D(new XY(0, 0), new XY(1, 1));
+ var segment2 = new Segment2D(new XY(0, 0), new XY(2, 2));
+
+ Assert.False(segment1.Equals(segment2));
+ }
+
+ [Fact]
+ public void Equals_WithDifferentOrigin_ReturnsFalse()
+ {
+ var segment1 = new Segment2D(new XY(0, 0), new XY(1, 1));
+ var segment2 = new Segment2D(new XY(1, 0), new XY(1, 1));
+
+ Assert.False(segment1.Equals(segment2));
+ }
+
+ [Fact]
+ public void Equals_WithSameOriginAndEnd_ReturnsTrue()
+ {
+ var segment1 = new Segment2D(new XY(0, 0), new XY(1, 1));
+ var segment2 = new Segment2D(new XY(0, 0), new XY(1, 1));
+
+ Assert.True(segment1.Equals(segment2));
+ }
+
+ [Fact]
+ public void FindIntersection_WithCollinearSegments_ReturnsNaN()
+ {
+ // Two collinear segments on the same line
+ var segment1 = new Segment2D(new XY(0, 0), new XY(2, 0));
+ var segment2 = new Segment2D(new XY(1, 0), new XY(3, 0));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.True(intersection.IsNaN());
+ }
+
+ [Fact]
+ public void FindIntersection_WithDiagonalSegments_ReturnsIntersectionPoint()
+ {
+ // Segment from (0, 0) to (2, 2)
+ var segment1 = new Segment2D(new XY(0, 0), new XY(2, 2));
+ // Segment from (0, 2) to (2, 0)
+ var segment2 = new Segment2D(new XY(0, 2), new XY(2, 0));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.Equal(new XY(1, 1), intersection);
+ }
+
+ [Fact]
+ public void FindIntersection_WithIntersectingSegment_ReturnsIntersectionPoint()
+ {
+ // Horizontal segment from (0, 1) to (2, 1)
+ var segment1 = new Segment2D(new XY(0, 1), new XY(2, 1));
+ // Vertical segment from (1, 0) to (1, 2)
+ var segment2 = new Segment2D(new XY(1, 0), new XY(1, 2));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.Equal(new XY(1, 1), intersection);
+ }
+
+ [Fact]
+ public void FindIntersection_WithIntersectionAtEndpoint_ReturnsIntersectionPoint()
+ {
+ // Segment from (0, 0) to (2, 0)
+ var segment1 = new Segment2D(new XY(0, 0), new XY(2, 0));
+ // Segment from (2, -1) to (2, 1)
+ var segment2 = new Segment2D(new XY(2, -1), new XY(2, 1));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.Equal(new XY(2, 0), intersection);
+ }
+
+ [Fact]
+ public void FindIntersection_WithIntersectionJustOutsideBounds_ReturnsNaN()
+ {
+ // Segment from (0, 0) to (1, 0)
+ var segment1 = new Segment2D(new XY(0, 0), new XY(1, 0));
+ // Segment from (2, -1) to (2, 1)
+ var segment2 = new Segment2D(new XY(2, -1), new XY(2, 1));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.True(intersection.IsNaN());
+ }
+
+ [Fact]
+ public void FindIntersection_WithIntersectionOutsideSegmentBounds_ReturnsNaN()
+ {
+ // Segment from (0, 1) to (1, 1)
+ var segment1 = new Segment2D(new XY(0, 1), new XY(1, 1));
+ // Vertical segment from (2, 0) to (2, 2)
+ var segment2 = new Segment2D(new XY(2, 0), new XY(2, 2));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.True(intersection.IsNaN());
+ }
+
+ [Fact]
+ public void FindIntersection_WithParallelSegments_ReturnsNaN()
+ {
+ // Two parallel horizontal segments
+ var segment1 = new Segment2D(new XY(0, 1), new XY(2, 1));
+ var segment2 = new Segment2D(new XY(0, 2), new XY(2, 2));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.True(intersection.IsNaN());
+ }
+
+ [Fact]
+ public void GetHashCode_DifferentSegments_MayHaveDifferentHashCode()
+ {
+ var segment1 = new Segment2D(new XY(0, 0), new XY(1, 1));
+ var segment2 = new Segment2D(new XY(2, 1), new XY(2, 2));
+
+ // Not guaranteed to be different, but highly likely
+ Assert.NotEqual(segment1.GetHashCode(), segment2.GetHashCode());
+ }
+
+ [Fact]
+ public void GetHashCode_EqualSegments_HaveSameHashCode()
+ {
+ var segment1 = new Segment2D(new XY(0, 0), new XY(1, 1));
+ var segment2 = new Segment2D(new XY(0, 0), new XY(1, 1));
+
+ Assert.Equal(segment1.GetHashCode(), segment2.GetHashCode());
+ }
+
+ [Fact]
+ public void ObjectEquals_WithDifferentType_ReturnsFalse()
+ {
+ var segment = new Segment2D(new XY(0, 0), new XY(1, 1));
+ object other = "not a segment";
+
+ Assert.False(segment.Equals(other));
+ }
+
+ [Fact]
+ public void ObjectEquals_WithNull_ReturnsFalse()
+ {
+ var segment = new Segment2D(new XY(0, 0), new XY(1, 1));
+
+ Assert.False(segment.Equals(null));
+ }
+
+ [Fact]
+ public void ObjectEquals_WithSameSegment_ReturnsTrue()
+ {
+ var segment1 = new Segment2D(new XY(0, 0), new XY(1, 1));
+ object segment2 = new Segment2D(new XY(0, 0), new XY(1, 1));
+
+ Assert.True(segment1.Equals(segment2));
+ }
+
+ [Fact]
+ public void Origin_CanBeModified()
+ {
+ var segment = new Segment2D(new XY(0, 0), new XY(1, 1));
+ var newOrigin = new XY(2, 2);
+
+ segment.Origin = newOrigin;
+
+ Assert.Equal(newOrigin, segment.Origin);
+ }
+}
\ No newline at end of file
diff --git a/CSMath.Tests/Geometry/Segment3DTests.cs b/CSMath.Tests/Geometry/Segment3DTests.cs
new file mode 100644
index 0000000..15dd16d
--- /dev/null
+++ b/CSMath.Tests/Geometry/Segment3DTests.cs
@@ -0,0 +1,313 @@
+using CSMath.Geometry;
+using Xunit;
+
+namespace CSMath.Tests.Geometry;
+
+public class Segment3DTests
+{
+ [Fact]
+ public void Constructor_WithValidPoints_CreatesSegment()
+ {
+ var origin = new XYZ(0, 0, 0);
+ var end = new XYZ(1, 1, 1);
+
+ var segment = new Segment3D(origin, end);
+
+ Assert.Equal(origin, segment.Origin);
+ Assert.Equal(end, segment.End);
+ }
+
+ [Fact]
+ public void Constructor_WithZeroLengthSegment_CreatesSegment()
+ {
+ var point = new XYZ(5, 5, 5);
+
+ var segment = new Segment3D(point, point);
+
+ Assert.Equal(point, segment.Origin);
+ Assert.Equal(point, segment.End);
+ }
+
+ [Fact]
+ public void Direction_ForZeroLengthSegment_ReturnsZeroVector()
+ {
+ var point = new XYZ(5, 5, 5);
+ var segment = new Segment3D(point, point);
+
+ var direction = segment.Direction;
+
+ Assert.Equal(XYZ.Zero, direction);
+ }
+
+ [Fact]
+ public void Direction_ReturnsVectorFromOriginToEnd()
+ {
+ var origin = new XYZ(1, 2, 3);
+ var end = new XYZ(4, 6, 9);
+ var segment = new Segment3D(origin, end);
+
+ var direction = segment.Direction;
+
+ Assert.Equal(new XYZ(3, 4, 6), direction);
+ }
+
+ [Fact]
+ public void Direction_UpdatesWhenOriginChanges()
+ {
+ var segment = new Segment3D(new XYZ(0, 0, 0), new XYZ(2, 2, 2));
+
+ segment.Origin = new XYZ(1, 1, 1);
+
+ Assert.Equal(new XYZ(1, 1, 1), segment.Direction);
+ }
+
+ [Fact]
+ public void Direction_UpdatesWhenPropertiesChange()
+ {
+ var segment = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+
+ segment.End = new XYZ(2, 2, 2);
+
+ Assert.Equal(new XYZ(2, 2, 2), segment.Direction);
+ }
+
+ [Fact]
+ public void Direction_WithMixedCoordinates_ReturnsCorrectVector()
+ {
+ var origin = new XYZ(-1, 2, -3);
+ var end = new XYZ(4, -6, 9);
+ var segment = new Segment3D(origin, end);
+
+ var direction = segment.Direction;
+
+ Assert.Equal(new XYZ(5, -8, 12), direction);
+ }
+
+ [Fact]
+ public void Direction_WithNegativeCoordinates_ReturnsCorrectVector()
+ {
+ var origin = new XYZ(-1, -2, -3);
+ var end = new XYZ(-4, -6, -9);
+ var segment = new Segment3D(origin, end);
+
+ var direction = segment.Direction;
+
+ Assert.Equal(new XYZ(-3, -4, -6), direction);
+ }
+
+ [Fact]
+ public void End_CanBeModified()
+ {
+ var segment = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+ var newEnd = new XYZ(3, 3, 3);
+
+ segment.End = newEnd;
+
+ Assert.Equal(newEnd, segment.End);
+ }
+
+ [Fact]
+ public void Equals_WithDifferentEnd_ReturnsFalse()
+ {
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+ var segment2 = new Segment3D(new XYZ(0, 0, 0), new XYZ(2, 2, 2));
+
+ Assert.False(segment1.Equals(segment2));
+ }
+
+ [Fact]
+ public void Equals_WithDifferentOrigin_ReturnsFalse()
+ {
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+ var segment2 = new Segment3D(new XYZ(1, 0, 0), new XYZ(1, 1, 1));
+
+ Assert.False(segment1.Equals(segment2));
+ }
+
+ [Fact]
+ public void Equals_WithDifferentZ_ReturnsFalse()
+ {
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+ var segment2 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 2));
+
+ Assert.False(segment1.Equals(segment2));
+ }
+
+ [Fact]
+ public void Equals_WithSameOriginAndEnd_ReturnsTrue()
+ {
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+ var segment2 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+
+ Assert.True(segment1.Equals(segment2));
+ }
+
+ [Fact]
+ public void FindIntersection_WithCollinearSegments_ReturnsNaN()
+ {
+ // Two collinear segments on the same line
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(2, 0, 0));
+ var segment2 = new Segment3D(new XYZ(1, 0, 0), new XYZ(3, 0, 0));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.True(intersection.IsNaN());
+ }
+
+ [Fact]
+ public void FindIntersection_WithDiagonalSegments_ReturnsIntersectionPoint()
+ {
+ // Segment from (0, 0, 0) to (2, 2, 0)
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(2, 2, 0));
+ // Segment from (0, 2, 0) to (2, 0, 0)
+ var segment2 = new Segment3D(new XYZ(0, 2, 0), new XYZ(2, 0, 0));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.Equal(new XYZ(1, 1, 0), intersection);
+ }
+
+ [Fact]
+ public void FindIntersection_WithIntersectingSegmentsIn3D_ReturnsIntersectionPoint()
+ {
+ // Segment from (0, 0, 0) to (2, 2, 2)
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(2, 2, 2));
+ // Segment from (0, 2, 0) to (2, 0, 2)
+ var segment2 = new Segment3D(new XYZ(0, 2, 0), new XYZ(2, 0, 2));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.Equal(new XYZ(1, 1, 1), intersection);
+ }
+
+ [Fact]
+ public void FindIntersection_WithIntersectingSegmentsInXYPlane_ReturnsIntersectionPoint()
+ {
+ // Horizontal segment from (0, 1, 0) to (2, 1, 0)
+ var segment1 = new Segment3D(new XYZ(0, 1, 0), new XYZ(2, 1, 0));
+ // Vertical segment from (1, 0, 0) to (1, 2, 0)
+ var segment2 = new Segment3D(new XYZ(1, 0, 0), new XYZ(1, 2, 0));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.Equal(new XYZ(1, 1, 0), intersection);
+ }
+
+ [Fact]
+ public void FindIntersection_WithIntersectionAtEndpoint_ReturnsIntersectionPoint()
+ {
+ // Segment from (0, 0, 0) to (2, 0, 0)
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(2, 0, 0));
+ // Segment from (2, -1, 0) to (2, 1, 0)
+ var segment2 = new Segment3D(new XYZ(2, -1, 0), new XYZ(2, 1, 0));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.Equal(new XYZ(2, 0, 0), intersection);
+ }
+
+ [Fact]
+ public void FindIntersection_WithIntersectionJustOutsideBounds_ReturnsNaN()
+ {
+ // Segment from (0, 0, 0) to (1, 0, 0)
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 0, 0));
+ // Segment from (2, -1, 0) to (2, 1, 0)
+ var segment2 = new Segment3D(new XYZ(2, -1, 0), new XYZ(2, 1, 0));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.True(intersection.IsNaN());
+ }
+
+ [Fact]
+ public void FindIntersection_WithIntersectionOutsideSegmentBounds_ReturnsNaN()
+ {
+ // Segment from (0, 1, 0) to (1, 1, 0)
+ var segment1 = new Segment3D(new XYZ(0, 1, 0), new XYZ(1, 1, 0));
+ // Vertical segment from (2, 0, 0) to (2, 2, 0)
+ var segment2 = new Segment3D(new XYZ(2, 0, 0), new XYZ(2, 2, 0));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.True(intersection.IsNaN());
+ }
+
+ [Fact]
+ public void FindIntersection_WithParallelSegments_ReturnsNaN()
+ {
+ // Two parallel horizontal segments
+ var segment1 = new Segment3D(new XYZ(0, 1, 0), new XYZ(2, 1, 0));
+ var segment2 = new Segment3D(new XYZ(0, 2, 0), new XYZ(2, 2, 0));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.True(intersection.IsNaN());
+ }
+
+ [Fact]
+ public void FindIntersection_WithSegmentsInDifferentPlanes_MayReturnNaN()
+ {
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(2, 2, 0));
+ var segment2 = new Segment3D(new XYZ(3, 3, 3), new XYZ(1, 1, 3));
+
+ var intersection = segment1.FindIntersection(segment2);
+
+ Assert.True(intersection.IsNaN());
+ }
+
+ [Fact]
+ public void GetHashCode_DifferentSegments_MayHaveDifferentHashCode()
+ {
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+ var segment2 = new Segment3D(new XYZ(1, 1, 1), new XYZ(2, 2, 2));
+
+ // Not guaranteed to be different, but highly likely
+ Assert.NotEqual(segment1.GetHashCode(), segment2.GetHashCode());
+ }
+
+ [Fact]
+ public void GetHashCode_EqualSegments_HaveSameHashCode()
+ {
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+ var segment2 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+
+ Assert.Equal(segment1.GetHashCode(), segment2.GetHashCode());
+ }
+
+ [Fact]
+ public void ObjectEquals_WithDifferentType_ReturnsFalse()
+ {
+ var segment = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+ object other = "not a segment";
+
+ Assert.False(segment.Equals(other));
+ }
+
+ [Fact]
+ public void ObjectEquals_WithNull_ReturnsFalse()
+ {
+ var segment = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+
+ Assert.False(segment.Equals(null));
+ }
+
+ [Fact]
+ public void ObjectEquals_WithSameSegment_ReturnsTrue()
+ {
+ var segment1 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+ object segment2 = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+
+ Assert.True(segment1.Equals(segment2));
+ }
+
+ [Fact]
+ public void Origin_CanBeModified()
+ {
+ var segment = new Segment3D(new XYZ(0, 0, 0), new XYZ(1, 1, 1));
+ var newOrigin = new XYZ(2, 2, 2);
+
+ segment.Origin = newOrigin;
+
+ Assert.Equal(newOrigin, segment.Origin);
+ }
+}
\ No newline at end of file
diff --git a/CSMath/BoundingBox.cs b/CSMath/BoundingBox.cs
index f39353f..681a6d7 100644
--- a/CSMath/BoundingBox.cs
+++ b/CSMath/BoundingBox.cs
@@ -8,28 +8,6 @@ namespace CSMath;
///
public struct BoundingBox
{
- ///
- /// Instance of a null bounding box.
- ///
- public static readonly BoundingBox Null = new BoundingBox(BoundingBoxExtent.Null);
-
- ///
- /// Instance of an infinite bounding box.
- ///
- public static readonly BoundingBox Infinite = new BoundingBox(BoundingBoxExtent.Infinite);
-
- public BoundingBoxExtent Extent { get; }
-
- ///
- /// Get the min corner of the bounding box.
- ///
- public XYZ Min { get; set; }
-
- ///
- /// Get the max corner of the bounding box.
- ///
- public XYZ Max { get; set; }
-
///
/// Center of the box.
///
@@ -41,6 +19,17 @@ public XYZ Center
}
}
+ public BoundingBoxExtent Extent { get; }
+
+ [Obsolete("Use LengthY instead.")]
+ public double Height
+ {
+ get
+ {
+ return Math.Abs(this.Max.Y - this.Min.Y);
+ }
+ }
+
///
/// Gets the length of the bounding box along the X-axis.
///
@@ -74,6 +63,16 @@ public double LengthZ
}
}
+ ///
+ /// Get the max corner of the bounding box.
+ ///
+ public XYZ Max { get; set; }
+
+ ///
+ /// Get the min corner of the bounding box.
+ ///
+ public XYZ Min { get; set; }
+
[Obsolete("Use LengthX instead.")]
public double Width
{
@@ -83,34 +82,15 @@ public double Width
}
}
- [Obsolete("Use LengthY instead.")]
- public double Height
- {
- get
- {
- return Math.Abs(this.Max.Y - this.Min.Y);
- }
- }
+ ///
+ /// Instance of an infinite bounding box.
+ ///
+ public static readonly BoundingBox Infinite = new BoundingBox(BoundingBoxExtent.Infinite);
- private BoundingBox(BoundingBoxExtent extent)
- {
- this.Extent = extent;
- switch (extent)
- {
- case BoundingBoxExtent.Null:
- this.Max = new XYZ(double.NaN);
- this.Min = new XYZ(double.NaN);
- break;
- case BoundingBoxExtent.Infinite:
- this.Min = new XYZ(double.NegativeInfinity);
- this.Max = new XYZ(double.PositiveInfinity);
- break;
- case BoundingBoxExtent.Finite:
- case BoundingBoxExtent.Point:
- default:
- break;
- }
- }
+ ///
+ /// Instance of a null bounding box.
+ ///
+ public static readonly BoundingBox Null = new BoundingBox(BoundingBoxExtent.Null);
///
/// Initializes a new instance of the BoundingBox class that represents a single point.
@@ -162,48 +142,58 @@ public BoundingBox(double minX, double minY, double minZ, double maxX, double ma
this = new BoundingBox(new XYZ(minX, minY, minZ), new XYZ(maxX, maxY, maxZ));
}
+ private BoundingBox(BoundingBoxExtent extent)
+ {
+ this.Extent = extent;
+ switch (extent)
+ {
+ case BoundingBoxExtent.Null:
+ this.Max = new XYZ(double.NaN);
+ this.Min = new XYZ(double.NaN);
+ break;
+ case BoundingBoxExtent.Infinite:
+ this.Min = new XYZ(double.NegativeInfinity);
+ this.Max = new XYZ(double.PositiveInfinity);
+ break;
+ case BoundingBoxExtent.Finite:
+ case BoundingBoxExtent.Point:
+ default:
+ break;
+ }
+ }
+
///
- /// Returns a new bounding box translated by the specified vector.
+ /// Create a bounding box from a collection of points.
///
- /// The vector by which to translate the bounding box. Each component is added to the corresponding coordinates of the
- /// bounding box's minimum and maximum points.
- /// A new BoundingBox instance that is the result of translating the current bounding box by the specified vector.
- public BoundingBox Move(XYZ xyz)
+ ///
+ ///
+ public static BoundingBox FromPoints(IEnumerable points)
{
- return new BoundingBox(this.Min + xyz, this.Max + xyz);
+ BoundingBox boundingBox = Null;
+
+ foreach (var point in points)
+ {
+ boundingBox = boundingBox.Merge(new BoundingBox(point));
+ }
+
+ return boundingBox;
}
///
- /// Merge 2 boxes into the common one.
+ /// Merge Multiple boxes into the common one.
///
- ///
+ ///
/// The merged box.
- public BoundingBox Merge(BoundingBox box)
+ public static BoundingBox Merge(IEnumerable boxes)
{
- if (this.Extent == BoundingBoxExtent.Infinite
- || box.Extent == BoundingBoxExtent.Infinite)
- {
- return BoundingBox.Infinite;
- }
- else if (this.Extent == BoundingBoxExtent.Null)
- {
- return box;
- }
- else if (box.Extent == BoundingBoxExtent.Null)
+ BoundingBox b = BoundingBox.Null;
+
+ foreach (var box in boxes)
{
- return this;
+ b = b.Merge(box);
}
- var min = new XYZ(
- System.Math.Min(this.Min.X, box.Min.X),
- System.Math.Min(this.Min.Y, box.Min.Y),
- System.Math.Min(this.Min.Z, box.Min.Z));
- var max = new XYZ(
- System.Math.Max(this.Max.X, box.Max.X),
- System.Math.Max(this.Max.Y, box.Max.Y),
- System.Math.Max(this.Max.Z, box.Max.Z));
-
- return new BoundingBox(min, max);
+ return b;
}
///
@@ -253,36 +243,46 @@ public bool IsIn(XYZ point)
}
///
- /// Merge Multiple boxes into the common one.
+ /// Merge 2 boxes into the common one.
///
- ///
+ ///
/// The merged box.
- public static BoundingBox Merge(IEnumerable boxes)
+ public BoundingBox Merge(BoundingBox box)
{
- BoundingBox b = BoundingBox.Null;
-
- foreach (var box in boxes)
+ if (this.Extent == BoundingBoxExtent.Infinite
+ || box.Extent == BoundingBoxExtent.Infinite)
{
- b = b.Merge(box);
+ return BoundingBox.Infinite;
+ }
+ else if (this.Extent == BoundingBoxExtent.Null)
+ {
+ return box;
+ }
+ else if (box.Extent == BoundingBoxExtent.Null)
+ {
+ return this;
}
- return b;
+ var min = new XYZ(
+ System.Math.Min(this.Min.X, box.Min.X),
+ System.Math.Min(this.Min.Y, box.Min.Y),
+ System.Math.Min(this.Min.Z, box.Min.Z));
+ var max = new XYZ(
+ System.Math.Max(this.Max.X, box.Max.X),
+ System.Math.Max(this.Max.Y, box.Max.Y),
+ System.Math.Max(this.Max.Z, box.Max.Z));
+
+ return new BoundingBox(min, max);
}
///
- /// Create a bounding box from a collection of points.
+ /// Returns a new bounding box translated by the specified vector.
///
- ///
- ///
- public static BoundingBox FromPoints(IEnumerable points)
+ /// The vector by which to translate the bounding box. Each component is added to the corresponding coordinates of the
+ /// bounding box's minimum and maximum points.
+ /// A new BoundingBox instance that is the result of translating the current bounding box by the specified vector.
+ public BoundingBox Move(XYZ xyz)
{
- BoundingBox boundingBox = Null;
-
- foreach (var point in points)
- {
- boundingBox = boundingBox.Merge(new BoundingBox(point));
- }
-
- return boundingBox;
+ return new BoundingBox(this.Min + xyz, this.Max + xyz);
}
}
\ No newline at end of file
diff --git a/CSMath/BoundingBoxExtent.cs b/CSMath/BoundingBoxExtent.cs
index c1efed3..7d3d259 100644
--- a/CSMath/BoundingBoxExtent.cs
+++ b/CSMath/BoundingBoxExtent.cs
@@ -3,7 +3,10 @@
public enum BoundingBoxExtent
{
Null,
+
Finite,
+
Infinite,
+
Point
-}
+}
\ No newline at end of file
diff --git a/CSMath/CSMath.projitems b/CSMath/CSMath.projitems
index 6b9faa1..b437265 100644
--- a/CSMath/CSMath.projitems
+++ b/CSMath/CSMath.projitems
@@ -11,11 +11,15 @@
+
+
+
+
diff --git a/CSMath/Geometry/Arc2D.cs b/CSMath/Geometry/Arc2D.cs
new file mode 100644
index 0000000..f25923b
--- /dev/null
+++ b/CSMath/Geometry/Arc2D.cs
@@ -0,0 +1,105 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace CSMath.Geometry;
+
+///
+/// Represents a 2D circular arc defined by a center point, radius, and angular range.
+///
+public struct Arc2D
+{
+ ///
+ /// Gets or sets the center point of the arc.
+ ///
+ public XY Center { get; set; }
+
+ ///
+ /// Gets or sets the ending angle of the arc in radians.
+ ///
+ public double EndAngle { get; set; }
+
+ ///
+ /// Gets or sets the radius of the arc.
+ ///
+ public double Radius { get; set; }
+
+ ///
+ /// Gets or sets the starting angle of the arc in radians.
+ ///
+ public double StartAngle { get; set; }
+
+ public Arc2D(XY center, double radius, double startAngle, double endAngle)
+ {
+ this.Center = center;
+ this.Radius = radius;
+ this.StartAngle = startAngle;
+ this.EndAngle = endAngle;
+ }
+
+ ///
+ /// Calculates the intersection points between this arc and a line segment.
+ ///
+ /// The line segment to test for intersection.
+ /// The precision value for the calculation.
+ /// An enumerable collection of intersection points. Returns an empty collection if no intersections exist.
+ public IEnumerable FindIntersections(Line2D line, double precision = MathHelper.Epsilon)
+ {
+ double lengthSquared = line.Direction.GetLengthSquared();
+ XY originOffset = line.Origin - this.Center;
+ XY offsetEnd = originOffset + line.Direction;
+ double crossProduct = originOffset.X * offsetEnd.Y - offsetEnd.X * originOffset.Y;
+ double discriminant = this.Radius * this.Radius * lengthSquared - crossProduct * crossProduct;
+
+ if (discriminant < 0)
+ {
+ return Enumerable.Empty();
+ }
+
+ double invLengthSquared = 1.0 / lengthSquared;
+ double baseX = crossProduct * line.Direction.Y;
+ double baseY = -crossProduct * line.Direction.X;
+
+ if (discriminant <= 0)
+ {
+ XY tangentPoint = new XY(baseX * invLengthSquared, baseY * invLengthSquared) + this.Center;
+ if (this.InAngularRange(tangentPoint))
+ {
+ return new[] { tangentPoint };
+ }
+
+ return Enumerable.Empty();
+ }
+
+ double sqrtDiscriminant = System.Math.Sqrt(discriminant);
+ double sign = line.Direction.Y < 0.0 ? -1.0 : 1.0;
+ double offsetX = sign * line.Direction.X * sqrtDiscriminant;
+ double offsetY = System.Math.Abs(line.Direction.Y) * sqrtDiscriminant;
+
+ List intersections = new List(2);
+ XY firstPoint = new XY((baseX + offsetX) * invLengthSquared, (baseY + offsetY) * invLengthSquared) + this.Center;
+ if (this.InAngularRange(firstPoint))
+ {
+ intersections.Add(firstPoint);
+ }
+
+ XY secondPoint = new XY((baseX - offsetX) * invLengthSquared, (baseY - offsetY) * invLengthSquared) + this.Center;
+ if (this.InAngularRange(secondPoint))
+ {
+ intersections.Add(secondPoint);
+ }
+
+ return intersections;
+ }
+
+ ///
+ /// Determines whether a point lies within the angular range of this arc.
+ ///
+ /// The point to test.
+ /// true if the point's angle from the center falls within the arc's angular range; otherwise, false.
+ public bool InAngularRange(XY point)
+ {
+ XY dir = point - this.Center;
+ double angle = System.Math.Atan2(dir.Y, dir.X);
+ return MathHelper.IsAngleInRange(angle, this.StartAngle, this.EndAngle);
+ }
+}
\ No newline at end of file
diff --git a/CSMath/Geometry/Circle2D.cs b/CSMath/Geometry/Circle2D.cs
new file mode 100644
index 0000000..2e272e7
--- /dev/null
+++ b/CSMath/Geometry/Circle2D.cs
@@ -0,0 +1,99 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace CSMath.Geometry;
+
+///
+/// Represents a circle in 2D space defined by a center point and radius.
+///
+public struct Circle2D
+{
+ ///
+ /// Gets or sets the center point of the circle.
+ ///
+ public XY Center { get; set; }
+
+ ///
+ /// Gets or sets the radius of the circle.
+ ///
+ public double Radius { get; set; }
+
+ ///
+ /// Initializes a new instance of the struct with a center point and radius.
+ ///
+ /// The center point of the circle.
+ /// The radius of the circle.
+ public Circle2D(XY center, double radius)
+ {
+ this.Center = center;
+ this.Radius = radius;
+ }
+
+ ///
+ /// Initializes a new instance of the struct with center coordinates and radius.
+ ///
+ /// The X coordinate of the center point.
+ /// The Y coordinate of the center point.
+ /// The radius of the circle.
+ public Circle2D(double centerX, double centerY, double radius)
+ {
+ this.Center = new XY(centerX, centerY);
+ this.Radius = radius;
+ }
+
+ ///
+ /// Finds the intersection points between this circle and a line.
+ ///
+ /// The line to test for intersection with this circle.
+ ///
+ /// An enumerable collection of intersection points. Returns:
+ /// - Empty enumerable if the line does not intersect the circle.
+ /// - Single point if the line is tangent to the circle.
+ /// - Two points if the line intersects the circle at two locations.
+ ///
+ public IEnumerable FindIntersections(Line2D line)
+ {
+ double lengthSquared = line.Direction.GetLengthSquared();
+ XY relativeOrigin = line.Origin - this.Center;
+ double determinant = XY.Cross(relativeOrigin, line.Direction);
+ double discriminant = this.Radius * this.Radius * lengthSquared - determinant * determinant;
+
+ if (MathHelper.IsZero(discriminant))
+ {
+ double x = determinant * line.Direction.Y / lengthSquared;
+ double y = -determinant * line.Direction.X / lengthSquared;
+
+ return new[]
+ {
+ new XY(x, y) + this.Center
+ };
+ }
+
+ if (discriminant < 0.0)
+ {
+ return Enumerable.Empty();
+ }
+
+ double sqrtDiscriminant = System.Math.Sqrt(discriminant);
+ double sign = line.Direction.Y < 0.0 ? -1.0 : 1.0;
+
+ double baseX = determinant * line.Direction.Y;
+ double baseY = -determinant * line.Direction.X;
+ double offsetX = sign * line.Direction.X * sqrtDiscriminant;
+ double offsetY = System.Math.Abs(line.Direction.Y) * sqrtDiscriminant;
+
+ XY intersection1 = new XY(
+ (baseX + offsetX) / lengthSquared,
+ (baseY + offsetY) / lengthSquared) + this.Center;
+
+ XY intersection2 = new XY(
+ (baseX - offsetX) / lengthSquared,
+ (baseY - offsetY) / lengthSquared) + this.Center;
+
+ return new[]
+ {
+ intersection1,
+ intersection2
+ };
+ }
+}
\ No newline at end of file
diff --git a/CSMath/Geometry/ICurve.cs b/CSMath/Geometry/ICurve.cs
index c7f97a3..a54e687 100644
--- a/CSMath/Geometry/ICurve.cs
+++ b/CSMath/Geometry/ICurve.cs
@@ -17,17 +17,17 @@ public interface ICurve
///
double RadiusRatio { get; }
- ///
- /// Converts the curve in a list of vertexes.
- ///
- /// Number of vertexes generated.
- /// A list vertexes that represents the curve expressed in object coordinate system.
- List PolygonalVertexes(int precision);
-
///
/// Calculate the local point on the curve for a given angle relative to the center.
///
/// Angle in radians.
/// A local point on the curve for the given angle relative to the center.
XYZ PolarCoordinateRelativeToCenter(double angle);
+
+ ///
+ /// Converts the curve in a list of vertexes.
+ ///
+ /// Number of vertexes generated.
+ /// A list vertexes that represents the curve expressed in object coordinate system.
+ List PolygonalVertexes(int precision);
}
\ No newline at end of file
diff --git a/CSMath/Geometry/ILine.cs b/CSMath/Geometry/ILine.cs
index 8380bc0..879bb14 100644
--- a/CSMath/Geometry/ILine.cs
+++ b/CSMath/Geometry/ILine.cs
@@ -4,14 +4,14 @@ public interface ILine
where T : IVector
{
///
- /// Origin point that the line intersects with
+ /// Direction fo the line
///
- public T Origin { get; set; }
+ public T Direction { get; }
///
- /// Direction fo the line
+ /// Origin point that the line intersects with
///
- public T Direction { get; set; }
+ public T Origin { get; }
///
/// Find the intersection between 2 lines.
diff --git a/CSMath/Geometry/Line2D.cs b/CSMath/Geometry/Line2D.cs
index 0f92f7e..156ded9 100644
--- a/CSMath/Geometry/Line2D.cs
+++ b/CSMath/Geometry/Line2D.cs
@@ -2,17 +2,26 @@
namespace CSMath.Geometry;
+///
+/// Represents a 2D line defined by an origin point and a direction vector.
+///
public struct Line2D : ILine, IEquatable
{
///
public XY Direction { get; set; }
+ ///
+ /// Gets the y-intercept of the line, calculated from the origin and slope.
+ ///
public double Offset { get { return Origin.Y - this.Slope * Origin.X; } }
///
public XY Origin { get; set; }
- public double Slope { get { return (this.Direction.Y - this.Direction.Y) / (this.Direction.X - this.Direction.X); } }
+ ///
+ /// Gets the slope of the line based on its direction vector.
+ ///
+ public double Slope { get { return this.Direction.Y / this.Direction.X; } }
public Line2D(XY origin, XY direction)
{
@@ -20,12 +29,39 @@ public Line2D(XY origin, XY direction)
this.Direction = direction;
}
+ ///
+ /// Creates a line from 2 points, the first point is the origin and the second point is used to calculate the direction.
+ ///
+ /// The first point, which will be the origin of the line.
+ /// The second point, used to calculate the direction of the line.
+ /// A new instance of the class.
+ public static Line2D FromPoints(XY pt1, XY pt2)
+ {
+ return new Line2D(pt1, pt2 - pt1);
+ }
+
+ ///
+ /// Creates a line from a 2D segment, using the segment's origin and direction.
+ ///
+ /// The 2D segment used to create the line.
+ /// A new instance of the class.
+ public static Line2D FromSegment2D(Segment2D segment)
+ {
+ return new Line2D(segment.Origin, segment.Direction);
+ }
+
///
public bool Equals(Line2D other)
{
return this.IsPointOnLine(other.Origin) && other.Direction == this.Direction;
}
+ ///
+ public override bool Equals(object obj)
+ {
+ return obj is Line2D && Equals((Line2D)obj);
+ }
+
///
public XY FindIntersection(ILine line)
{
@@ -39,4 +75,20 @@ public XY FindIntersection(ILine line)
double s = (v.X * line.Direction.Y - v.Y * line.Direction.X) / cross;
return this.Origin + s * this.Direction;
}
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.Origin.GetHashCode() ^ this.Direction.GetHashCode();
+ }
+
+ ///
+ /// Determines if a given point lies on the line.
+ ///
+ /// The parameter value along the line.
+ /// The point on the line corresponding to the given parameter value.
+ public XY PointInLine(double lambda)
+ {
+ return this.Origin + lambda * this.Direction;
+ }
}
\ No newline at end of file
diff --git a/CSMath/Geometry/Line3D.cs b/CSMath/Geometry/Line3D.cs
index 33aa62d..aef0e24 100644
--- a/CSMath/Geometry/Line3D.cs
+++ b/CSMath/Geometry/Line3D.cs
@@ -2,14 +2,13 @@
namespace CSMath.Geometry;
-// Eq: pt = origin + a * Direction
public struct Line3D : ILine, IEquatable
{
///
- public XYZ Origin { get; set; }
+ public XYZ Direction { get; set; }
///
- public XYZ Direction { get; set; }
+ public XYZ Origin { get; set; }
///
/// Initialize a new instance of the class.
@@ -29,6 +28,33 @@ public Line3D(XYZ origin, XYZ direction)
this.Direction = direction.Normalize();
}
+ ///
+ /// Creates a line from 2 points, the first point is the origin and the second point is used to calculate the direction.
+ ///
+ /// The first point, which will be the origin of the line.
+ /// The second point, used to calculate the direction of the line.
+ /// A new instance of the class.
+ public static Line3D FromPoints(XYZ pt1, XYZ pt2)
+ {
+ return new Line3D(pt1, pt2 - pt1);
+ }
+
+ ///
+ /// Creates a line from a segment, the origin of the line will be the origin of the segment and the direction will be the direction of the segment.
+ ///
+ /// The 3D segment used to create the line.
+ /// A new instance of the class.
+ public static Line3D FromSegment3D(Segment3D segment)
+ {
+ return new Line3D(segment.Origin, segment.Direction);
+ }
+
+ ///
+ public bool Equals(Line3D other)
+ {
+ return this.IsPointOnLine(other.Origin) && other.Direction == this.Direction;
+ }
+
///
public XYZ FindIntersection(ILine line)
{
@@ -59,10 +85,4 @@ public XYZ FindIntersection(ILine line)
return XYZ.NaN;
}
}
-
- ///
- public bool Equals(Line3D other)
- {
- return this.IsPointOnLine(other.Origin) && other.Direction == this.Direction;
- }
}
\ No newline at end of file
diff --git a/CSMath/Geometry/LineExtensions.cs b/CSMath/Geometry/LineExtensions.cs
index 938feaf..56cdcaf 100644
--- a/CSMath/Geometry/LineExtensions.cs
+++ b/CSMath/Geometry/LineExtensions.cs
@@ -4,6 +4,14 @@ namespace CSMath.Geometry;
public static class LineExtensions
{
+ ///
+ /// Creates a line from 2 points, the first point is the origin and the second point is used to calculate the direction.
+ ///
+ /// The type of the line to create.
+ /// The type of the vector used for the points.
+ /// The first point, which will be the origin of the line.
+ /// The second point, used to calculate the direction of the line.
+ /// A new instance of the specified line type.
public static T CreateFromPoints(R pt1, R pt2)
where T : ILine
where R : IVector, new()
@@ -14,9 +22,9 @@ public static T CreateFromPoints(R pt1, R pt2)
///
/// Determines whether the specified point is on the line, or not.
///
- ///
- ///
- ///
+ /// The line to check against.
+ /// The point to check.
+ /// True if the point is on the line; otherwise, false.
public static bool IsPointOnLine(this ILine line, T point)
where T : IVector
{
@@ -38,14 +46,16 @@ public static bool IsPointOnLine(this ILine line, T point)
///
/// Tries to find the intersection between 2 lines.
///
- ///
- ///
- ///
- ///
- ///
+ /// The type of the first line.
+ /// The type of the second line.
+ /// The type of the vector used for the lines.
+ /// The first line.
+ /// The second line.
+ /// The intersection point, if found.
/// True if the intersection is found.
- public static bool TryFindIntersection(this T line1, T line2, out R intersection)
+ public static bool TryFindIntersection(this T line1, S line2, out R intersection)
where T : ILine
+ where S : ILine
where R : IVector
{
intersection = line1.FindIntersection(line2);
diff --git a/CSMath/Geometry/Segment2D.cs b/CSMath/Geometry/Segment2D.cs
new file mode 100644
index 0000000..12bcdbd
--- /dev/null
+++ b/CSMath/Geometry/Segment2D.cs
@@ -0,0 +1,68 @@
+using System;
+
+namespace CSMath.Geometry;
+
+public struct Segment2D : ILine, IEquatable
+{
+ ///
+ public XY Direction { get { return this.End - this.Origin; } }
+
+ ///
+ /// Gets or sets the end point of the segment in the XY plane.
+ ///
+ public XY End { get; set; }
+
+ ///
+ public XY Origin { get; set; }
+
+ public Segment2D(XY origin, XY end)
+ {
+ this.Origin = origin;
+ this.End = end;
+ }
+
+ ///
+ public bool Equals(Segment2D other)
+ {
+ return this.Origin.Equals(other.Origin) && this.End.Equals(other.End);
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return obj is Segment2D && Equals((Segment2D)obj);
+ }
+
+ ///
+ /// Calculates the intersection point of the current line segment and the specified line.
+ ///
+ /// The line to intersect with.
+ /// The intersection point as an XY object, or XY.NaN if there is no intersection within the bounds of the current
+ /// segment.
+ public XY FindIntersection(ILine line)
+ {
+ var curr = new Line2D(this.Origin, this.Direction);
+ var intersection = curr.FindIntersection(line);
+
+ if (intersection.IsNaN())
+ {
+ return XY.NaN;
+ }
+
+ if (intersection.X < Math.Min(this.Origin.X, this.End.X)
+ || intersection.X > Math.Max(this.Origin.X, this.End.X)
+ || intersection.Y < Math.Min(this.Origin.Y, this.End.Y)
+ || intersection.Y > Math.Max(this.Origin.Y, this.End.Y))
+ {
+ return XY.NaN;
+ }
+
+ return intersection;
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.Origin.GetHashCode() ^ this.End.GetHashCode();
+ }
+}
\ No newline at end of file
diff --git a/CSMath/Geometry/Segment3D.cs b/CSMath/Geometry/Segment3D.cs
new file mode 100644
index 0000000..4ee843b
--- /dev/null
+++ b/CSMath/Geometry/Segment3D.cs
@@ -0,0 +1,68 @@
+using System;
+
+namespace CSMath.Geometry;
+
+public struct Segment3D : ILine, IEquatable
+{
+ ///
+ public XYZ Direction { get { return this.End - this.Origin; } }
+
+ ///
+ /// Gets or sets the end point of the segment in 3D space.
+ ///
+ public XYZ End { get; set; }
+
+ ///
+ public XYZ Origin { get; set; }
+
+ public Segment3D(XYZ origin, XYZ end)
+ {
+ this.Origin = origin;
+ this.End = end;
+ }
+
+ ///
+ public bool Equals(Segment3D other)
+ {
+ return this.Origin.Equals(other.Origin) && this.End.Equals(other.End);
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return obj is Segment3D && Equals((Segment3D)obj);
+ }
+
+ ///
+ /// Calculates the intersection point of the current line segment and the specified line.
+ ///
+ /// The line to intersect with.
+ /// The intersection point as an XYZ object, or XYZ.NaN if there is no intersection within the bounds of the current
+ /// segment.
+ public XYZ FindIntersection(ILine line)
+ {
+ var curr = new Line3D(this.Origin, this.Direction);
+ var intersection = curr.FindIntersection(line);
+
+ if (intersection.IsNaN())
+ {
+ return XYZ.NaN;
+ }
+
+ if (intersection.X < Math.Min(this.Origin.X, this.End.X)
+ || intersection.X > Math.Max(this.Origin.X, this.End.X)
+ || intersection.Y < Math.Min(this.Origin.Y, this.End.Y)
+ || intersection.Y > Math.Max(this.Origin.Y, this.End.Y))
+ {
+ return XYZ.NaN;
+ }
+
+ return intersection;
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.Origin.GetHashCode() ^ this.End.GetHashCode();
+ }
+}
\ No newline at end of file
diff --git a/CSMath/IVector.cs b/CSMath/IVector.cs
index 49cfc5e..59af5d0 100644
--- a/CSMath/IVector.cs
+++ b/CSMath/IVector.cs
@@ -15,4 +15,4 @@ public interface IVector
/// The index.
/// The value of the coordinate at the specified index.
public double this[int index] { get; set; }
-}
+}
\ No newline at end of file
diff --git a/CSMath/MathHelper.cs b/CSMath/MathHelper.cs
index c420705..d8ca7ef 100644
--- a/CSMath/MathHelper.cs
+++ b/CSMath/MathHelper.cs
@@ -20,7 +20,7 @@ public static class MathHelper
///
/// Factor for converting degrees to radians.
///
- public const double DegToRadFactor = (Math.PI / 180);
+ public const double DegToRadFactor = Math.PI / 180;
///
/// Default tolerance
@@ -50,7 +50,7 @@ public static class MathHelper
///
/// Factor for converting radians to degrees.
///
- public const double RadToDegFactor = (180 / Math.PI);
+ public const double RadToDegFactor = 180 / Math.PI;
///
/// Factor for converting radians to gradians.
@@ -108,6 +108,17 @@ public static double FixZero(double number)
return FixZero(number, Epsilon);
}
+ ///
+ /// Replaces the specified number with zero if it is within the defined threshold.
+ ///
+ /// The number to evaluate.
+ /// The threshold value for determining if the number is considered zero.
+ /// Zero if the number is within the threshold; otherwise, the original number.
+ public static double FixZero(double number, double threshold)
+ {
+ return IsZero(number, threshold) ? 0 : number;
+ }
+
///
/// Returns zero if the specified number is within the given threshold of zero; otherwise, returns the original number.
///
@@ -117,12 +128,6 @@ public static double FixZero(double number)
/// The value to evaluate for near-zero equivalence.
/// The tolerance within which the number is considered to be zero. Must be non-negative.
/// Zero if the absolute value of the number is less than or equal to the threshold; otherwise, the original number.
-
- public static double FixZero(double number, double threshold)
- {
- return IsZero(number, threshold) ? 0 : number;
- }
-
///
/// Returns a copy of the specified vector with components that are effectively zero replaced by exact zeros.
///
@@ -195,6 +200,35 @@ public static bool IsAlmostZero(double value)
return false;
}
+ ///
+ /// Determines whether a specified angle falls within a given angular range.
+ ///
+ ///
+ /// This method normalizes all angles to the range [0, 2Ï€) before comparison. It correctly handles ranges that wrap
+ /// around the 0/2Ï€ boundary. For example, if the start angle is greater than the end angle, the range is considered
+ /// to span across 0 radians.
+ ///
+ /// The angle to test, in radians.
+ /// The start of the angular range, in radians.
+ /// The end of the angular range, in radians.
+ /// The precision value for the calculation.
+ ///
+ /// true if the angle falls within the specified range (inclusive); otherwise, false.
+ ///
+ public static bool IsAngleInRange(double angle, double start, double end, double precision = Epsilon)
+ {
+ angle = NormalizeAngleRadians(angle);
+ start = NormalizeAngleRadians(start);
+ end = NormalizeAngleRadians(end);
+
+ if (start > end)
+ {
+ return (angle >= 0.0 && angle <= end + precision) || (angle >= start - precision && angle <= TwoPI);
+ }
+
+ return angle >= start - precision && angle <= end + precision;
+ }
+
///
/// Checks if a number is equal to another.
///
@@ -267,11 +301,26 @@ public static double NormalizeAngle(double angle)
}
///
- /// Convert a value from radian to degree
+ /// Normalizes the value of an angle in radians between 0-2Ï€.
+ ///
+ /// Angle in radians.
+ /// The equivalent angle in the range 0-2Ï€.
+ public static double NormalizeAngleRadians(double angle)
+ {
+ if (angle < 0.0 || angle > TwoPI)
+ {
+ angle -= TwoPI * Math.Floor(angle / TwoPI);
+ }
+
+ return angle;
+ }
+
+ ///
+ /// Convert a value from radian to degree.
///
/// Value in radians
/// Calculates the negative values in a 0-360 range.
- /// The radian value
+ /// The degree value.
public static double RadToDeg(double value, bool absolute = true)
{
var result = value * RadToDegFactor;
@@ -282,7 +331,7 @@ public static double RadToDeg(double value, bool absolute = true)
/// Convert a value from radian to gradian.
///
/// Value in radians.
- /// The radian value.
+ /// The gradian value.
public static double RadToGrad(double value)
{
return value * RadToGradFactor;
@@ -303,8 +352,8 @@ public static double RoundToNearest(double number, double roundTo)
///
/// Returns the sine of specific angle in radians adjusting the value to 0 using as tolerance.
///
- ///
- ///
+ /// Angle in radians.
+ /// The sine of the angle.
public static double Sin(double value)
{
double result = Math.Sin(value);
diff --git a/CSMath/Matrix3.cs b/CSMath/Matrix3.cs
index 37e808e..c2862d6 100644
--- a/CSMath/Matrix3.cs
+++ b/CSMath/Matrix3.cs
@@ -6,14 +6,6 @@ namespace CSMath;
public partial struct Matrix3
{
- ///
- /// 4-dimensional zero matrix.
- ///
- public static readonly Matrix3 Zero = new Matrix3(
- 0.0, 0.0, 0.0,
- 0.0, 0.0, 0.0,
- 0.0, 0.0, 0.0);
-
///
/// 4-dimensional identity matrix.
///
@@ -22,16 +14,24 @@ public partial struct Matrix3
0.0, 1.0, 0.0,
0.0, 0.0, 1.0);
- #region Public Fields
+ ///
+ /// 4-dimensional zero matrix.
+ ///
+ public static readonly Matrix3 Zero = new Matrix3(
+ 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0);
///
/// Value at column 0, row 0 of the matrix.
///
public double M00;
+
///
/// Value at column 0, row 1 of the matrix.
///
public double M01;
+
///
/// Value at column 0, row 2 of the matrix.
///
@@ -41,10 +41,12 @@ public partial struct Matrix3
/// Value at column 1, row 0 of the matrix.
///
public double M10;
+
///
/// Value at column 1, row 1 of the matrix.
///
public double M11;
+
///
/// Value at column 1, row 2 of the matrix.
///
@@ -54,94 +56,17 @@ public partial struct Matrix3
/// Value at column 2, row 0 of the matrix.
///
public double M20;
+
///
/// Value at column 2, row 1 of the matrix.
///
public double M21;
+
///
/// Value at column 2, row 2 of the matrix.
///
public double M22;
- #endregion Public Fields
-
- public double this[int index]
- {
- get
- {
- switch (index)
- {
- case 0:
- return this.M00;
- case 1:
- return this.M01;
- case 2:
- return this.M02;
- case 3:
- return this.M10;
- case 4:
- return this.M11;
- case 5:
- return this.M12;
- case 6:
- return this.M20;
- case 7:
- return this.M21;
- case 8:
- return this.M22;
- default:
- throw new IndexOutOfRangeException();
- }
- }
- set
- {
- switch (index)
- {
- case 0:
- this.M00 = value;
- break;
- case 1:
- this.M01 = value;
- break;
- case 2:
- this.M02 = value;
- break;
- case 3:
- this.M10 = value;
- break;
- case 4:
- this.M11 = value;
- break;
- case 5:
- this.M12 = value;
- break;
- case 6:
- this.M20 = value;
- break;
- case 7:
- this.M21 = value;
- break;
- case 8:
- this.M22 = value;
- break;
- default:
- throw new IndexOutOfRangeException();
- }
- }
- }
-
- public double this[int column, int row]
- {
- get
- {
- return this[(column * 3) + row];
- }
- set
- {
- this[(column * 3) + row] = value;
- }
- }
-
public Matrix3(
double m00, double m10, double m20,
double m01, double m11, double m21,
@@ -176,17 +101,6 @@ public Matrix3(Matrix4 matrix)
this.M22 = matrix.M22;
}
- ///
- /// Obtains the transpose matrix.
- ///
- /// Transpose matrix.
- public Matrix3 Transpose()
- {
- return new Matrix3(this.M00, this.M10, this.M20,
- this.M01, this.M11, this.M21,
- this.M02, this.M12, this.M22);
- }
-
///
/// Gets the rotation matrix from the normal vector (extrusion direction) of an entity.
///
@@ -247,4 +161,92 @@ public override string ToString()
s.Append(string.Format("|{0}{3} {1}{3} {2}|", this.M20, this.M21, this.M22, separator));
return s.ToString();
}
+
+ ///
+ /// Obtains the transpose matrix.
+ ///
+ /// Transpose matrix.
+ public Matrix3 Transpose()
+ {
+ return new Matrix3(this.M00, this.M10, this.M20,
+ this.M01, this.M11, this.M21,
+ this.M02, this.M12, this.M22);
+ }
+
+ public double this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return this.M00;
+ case 1:
+ return this.M01;
+ case 2:
+ return this.M02;
+ case 3:
+ return this.M10;
+ case 4:
+ return this.M11;
+ case 5:
+ return this.M12;
+ case 6:
+ return this.M20;
+ case 7:
+ return this.M21;
+ case 8:
+ return this.M22;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ set
+ {
+ switch (index)
+ {
+ case 0:
+ this.M00 = value;
+ break;
+ case 1:
+ this.M01 = value;
+ break;
+ case 2:
+ this.M02 = value;
+ break;
+ case 3:
+ this.M10 = value;
+ break;
+ case 4:
+ this.M11 = value;
+ break;
+ case 5:
+ this.M12 = value;
+ break;
+ case 6:
+ this.M20 = value;
+ break;
+ case 7:
+ this.M21 = value;
+ break;
+ case 8:
+ this.M22 = value;
+ break;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+
+ public double this[int column, int row]
+ {
+ get
+ {
+ return this[(column * 3) + row];
+ }
+ set
+ {
+ this[(column * 3) + row] = value;
+ }
+ }
}
\ No newline at end of file
diff --git a/CSMath/Matrix3.operators.cs b/CSMath/Matrix3.operators.cs
index 525882b..a810f70 100644
--- a/CSMath/Matrix3.operators.cs
+++ b/CSMath/Matrix3.operators.cs
@@ -15,4 +15,4 @@ public partial struct Matrix3
a.M10 * b.M00 + a.M11 * b.M10 + a.M12 * b.M20, a.M10 * b.M01 + a.M11 * b.M11 + a.M12 * b.M21, a.M10 * b.M02 + a.M11 * b.M12 + a.M12 * b.M22,
a.M20 * b.M00 + a.M21 * b.M10 + a.M22 * b.M20, a.M20 * b.M01 + a.M21 * b.M11 + a.M22 * b.M21, a.M20 * b.M02 + a.M21 * b.M12 + a.M22 * b.M22);
}
-}
+}
\ No newline at end of file
diff --git a/CSMath/Matrix4.Operators.cs b/CSMath/Matrix4.Operators.cs
index 8f4e2ca..1658f85 100644
--- a/CSMath/Matrix4.Operators.cs
+++ b/CSMath/Matrix4.Operators.cs
@@ -76,4 +76,4 @@ public static Matrix4 Multiply(Matrix4 a, Matrix4 b)
matrix.M02 * v.X + matrix.M12 * v.Y + matrix.M22 * v.Z + matrix.M32 * v.M,
matrix.M03 * v.X + matrix.M13 * v.Y + matrix.M23 * v.Z + matrix.M33 * v.M);
}
-}
+}
\ No newline at end of file
diff --git a/CSMath/Matrix4.cs b/CSMath/Matrix4.cs
index c832c3d..acb2797 100644
--- a/CSMath/Matrix4.cs
+++ b/CSMath/Matrix4.cs
@@ -31,8 +31,6 @@ public partial struct Matrix4
///
public static readonly Matrix4 Zero = new Matrix4(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
- #region Public Fields
-
///
/// Value at column 0, row 0 of the matrix.
///
@@ -113,120 +111,6 @@ public partial struct Matrix4
///
public double M33;
- public double this[int index]
- {
- get
- {
- switch (index)
- {
- case 0:
- return this.M00;
- case 1:
- return this.M01;
- case 2:
- return this.M02;
- case 3:
- return this.M03;
- case 4:
- return this.M10;
- case 5:
- return this.M11;
- case 6:
- return this.M12;
- case 7:
- return this.M13;
- case 8:
- return this.M20;
- case 9:
- return this.M21;
- case 10:
- return this.M22;
- case 11:
- return this.M23;
- case 12:
- return this.M30;
- case 13:
- return this.M31;
- case 14:
- return this.M32;
- case 15:
- return this.M33;
- default:
- throw new IndexOutOfRangeException();
- }
- }
- set
- {
- switch (index)
- {
- case 0:
- this.M00 = value;
- break;
- case 1:
- this.M01 = value;
- break;
- case 2:
- this.M02 = value;
- break;
- case 3:
- this.M03 = value;
- break;
- case 4:
- this.M10 = value;
- break;
- case 5:
- this.M11 = value;
- break;
- case 6:
- this.M12 = value;
- break;
- case 7:
- this.M13 = value;
- break;
- case 8:
- this.M20 = value;
- break;
- case 9:
- this.M21 = value;
- break;
- case 10:
- this.M22 = value;
- break;
- case 11:
- this.M23 = value;
- break;
- case 12:
- this.M30 = value;
- break;
- case 13:
- this.M31 = value;
- break;
- case 14:
- this.M32 = value;
- break;
- case 15:
- this.M33 = value;
- break;
- default:
- throw new IndexOutOfRangeException();
- }
- }
- }
-
- public double this[int column, int row]
- {
- get
- {
- return this[(column * 4) + row];
- }
- set
- {
- this[(column * 4) + row] = value;
- }
- }
-
- #endregion Public Fields
-
public Matrix4(
double m00, double m10, double m20, double m30,
double m01, double m11, double m21, double m31,
@@ -883,4 +767,116 @@ public Matrix4 Transpose()
return result;
}
+
+ public double this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return this.M00;
+ case 1:
+ return this.M01;
+ case 2:
+ return this.M02;
+ case 3:
+ return this.M03;
+ case 4:
+ return this.M10;
+ case 5:
+ return this.M11;
+ case 6:
+ return this.M12;
+ case 7:
+ return this.M13;
+ case 8:
+ return this.M20;
+ case 9:
+ return this.M21;
+ case 10:
+ return this.M22;
+ case 11:
+ return this.M23;
+ case 12:
+ return this.M30;
+ case 13:
+ return this.M31;
+ case 14:
+ return this.M32;
+ case 15:
+ return this.M33;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ set
+ {
+ switch (index)
+ {
+ case 0:
+ this.M00 = value;
+ break;
+ case 1:
+ this.M01 = value;
+ break;
+ case 2:
+ this.M02 = value;
+ break;
+ case 3:
+ this.M03 = value;
+ break;
+ case 4:
+ this.M10 = value;
+ break;
+ case 5:
+ this.M11 = value;
+ break;
+ case 6:
+ this.M12 = value;
+ break;
+ case 7:
+ this.M13 = value;
+ break;
+ case 8:
+ this.M20 = value;
+ break;
+ case 9:
+ this.M21 = value;
+ break;
+ case 10:
+ this.M22 = value;
+ break;
+ case 11:
+ this.M23 = value;
+ break;
+ case 12:
+ this.M30 = value;
+ break;
+ case 13:
+ this.M31 = value;
+ break;
+ case 14:
+ this.M32 = value;
+ break;
+ case 15:
+ this.M33 = value;
+ break;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+
+ public double this[int column, int row]
+ {
+ get
+ {
+ return this[(column * 4) + row];
+ }
+ set
+ {
+ this[(column * 4) + row] = value;
+ }
+ }
}
\ No newline at end of file
diff --git a/CSMath/Quaternion.operators.cs b/CSMath/Quaternion.operators.cs
index 8ed5995..bb36008 100644
--- a/CSMath/Quaternion.operators.cs
+++ b/CSMath/Quaternion.operators.cs
@@ -9,4 +9,4 @@ public partial struct Quaternion
XYZ cr2 = XYZ.Cross(u, cr1);
return v + (cr1 * (float)q.W + cr2) * 2f;
}
-}
+}
\ No newline at end of file
diff --git a/CSMath/XY.operators.cs b/CSMath/XY.operators.cs
index 2067ef9..c41fc3c 100644
--- a/CSMath/XY.operators.cs
+++ b/CSMath/XY.operators.cs
@@ -4,15 +4,9 @@ namespace CSMath;
public partial struct XY : IVector, IEquatable
{
- ///
- /// Adds two vectors together.
- ///
- /// The first source vector.
- /// The second source vector.
- /// The summed vector.
- public static XY operator +(XY left, XY right)
+ public static explicit operator XY(XYZ xyz)
{
- return left.Add(right);
+ return new XY(xyz.X, xyz.Y);
}
///
@@ -26,6 +20,28 @@ public partial struct XY : IVector, IEquatable
return left.Subtract(right);
}
+ ///
+ /// Negates a given vector.
+ ///
+ /// The source vector.
+ /// The negated vector.
+ public static XY operator -(XY value)
+ {
+ return Zero.Subtract(value);
+ }
+
+ ///
+ /// Returns a boolean indicating whether the two given vectors are not equal.
+ ///
+ /// The first vector to compare.
+ /// The second vector to compare.
+ /// True if the vectors are not equal; False if they are equal.
+ public static bool operator !=(XY left, XY right)
+ {
+ return (left.X != right.X ||
+ left.Y != right.Y);
+ }
+
///
/// Multiplies two vectors together.
///
@@ -99,13 +115,14 @@ public partial struct XY : IVector, IEquatable
}
///
- /// Negates a given vector.
+ /// Adds two vectors together.
///
- /// The source vector.
- /// The negated vector.
- public static XY operator -(XY value)
+ /// The first source vector.
+ /// The second source vector.
+ /// The summed vector.
+ public static XY operator +(XY left, XY right)
{
- return Zero.Subtract(value);
+ return left.Add(right);
}
///
@@ -119,21 +136,4 @@ public partial struct XY : IVector, IEquatable
return (left.X == right.X &&
left.Y == right.Y);
}
-
- ///
- /// Returns a boolean indicating whether the two given vectors are not equal.
- ///
- /// The first vector to compare.
- /// The second vector to compare.
- /// True if the vectors are not equal; False if they are equal.
- public static bool operator !=(XY left, XY right)
- {
- return (left.X != right.X ||
- left.Y != right.Y);
- }
-
- public static explicit operator XY(XYZ xyz)
- {
- return new XY(xyz.X, xyz.Y);
- }
-}
+}
\ No newline at end of file
diff --git a/CSMath/XYZ.operators.cs b/CSMath/XYZ.operators.cs
index cd2d0c0..0a5eaa0 100644
--- a/CSMath/XYZ.operators.cs
+++ b/CSMath/XYZ.operators.cs
@@ -4,15 +4,9 @@ namespace CSMath;
public partial struct XYZ : IVector, IEquatable
{
- ///
- /// Adds two vectors together.
- ///
- /// The first source vector.
- /// The second source vector.
- /// The summed vector.
- public static XYZ operator +(XYZ left, XYZ right)
+ public static explicit operator XYZ(XY xy)
{
- return left.Add(right);
+ return new XYZ(xy.X, xy.Y, 0);
}
///
@@ -26,6 +20,29 @@ public partial struct XYZ : IVector, IEquatable
return left.Subtract(right);
}
+ ///
+ /// Negates a given vector.
+ ///
+ /// The source vector.
+ /// The negated vector.
+ public static XYZ operator -(XYZ value)
+ {
+ return Zero.Subtract(value);
+ }
+
+ ///
+ /// Returns a boolean indicating whether the two given vectors are not equal.
+ ///
+ /// The first vector to compare.
+ /// The second vector to compare.
+ /// True if the vectors are not equal; False if they are equal.
+ public static bool operator !=(XYZ left, XYZ right)
+ {
+ return (left.X != right.X ||
+ left.Y != right.Y ||
+ left.Z != right.Z);
+ }
+
///
/// Multiplies two vectors together.
///
@@ -101,13 +118,14 @@ public partial struct XYZ : IVector, IEquatable
}
///
- /// Negates a given vector.
+ /// Adds two vectors together.
///
- /// The source vector.
- /// The negated vector.
- public static XYZ operator -(XYZ value)
+ /// The first source vector.
+ /// The second source vector.
+ /// The summed vector.
+ public static XYZ operator +(XYZ left, XYZ right)
{
- return Zero.Subtract(value);
+ return left.Add(right);
}
///
@@ -122,22 +140,4 @@ public partial struct XYZ : IVector, IEquatable
left.Y == right.Y &&
left.Z == right.Z);
}
-
- ///
- /// Returns a boolean indicating whether the two given vectors are not equal.
- ///
- /// The first vector to compare.
- /// The second vector to compare.
- /// True if the vectors are not equal; False if they are equal.
- public static bool operator !=(XYZ left, XYZ right)
- {
- return (left.X != right.X ||
- left.Y != right.Y ||
- left.Z != right.Z);
- }
-
- public static explicit operator XYZ(XY xy)
- {
- return new XYZ(xy.X, xy.Y, 0);
- }
-}
+}
\ No newline at end of file
diff --git a/CSMath/XYZM.cs b/CSMath/XYZM.cs
index 7387882..8b95856 100644
--- a/CSMath/XYZM.cs
+++ b/CSMath/XYZM.cs
@@ -4,12 +4,13 @@ namespace CSMath;
public partial struct XYZM : IVector, IEquatable
{
- public readonly static XYZM NaN = new XYZM(double.NaN);
- public readonly static XYZM Zero = new XYZM(0, 0, 0, 0);
- public readonly static XYZM AxisX = new XYZM(1, 0, 0, 0);
- public readonly static XYZM AxisY = new XYZM(0, 1, 0, 0);
- public readonly static XYZM AxisZ = new XYZM(0, 0, 1, 0);
- public readonly static XYZM AxisM = new XYZM(0, 0, 0, 1);
+ ///
+ public uint Dimension { get { return 4; } }
+
+ ///
+ /// Specifies the M-value of the vector component
+ ///
+ public double M { get; set; }
///
/// Specifies the X-value of the vector component
@@ -26,54 +27,17 @@ public partial struct XYZM : IVector, IEquatable
///
public double Z { get; set; }
- ///
- /// Specifies the M-value of the vector component
- ///
- public double M { get; set; }
+ public static readonly XYZM AxisM = new XYZM(0, 0, 0, 1);
- ///
- public uint Dimension { get { return 4; } }
+ public static readonly XYZM AxisX = new XYZM(1, 0, 0, 0);
- ///
- public double this[int index]
- {
- get
- {
- switch (index)
- {
- case 0:
- return X;
- case 1:
- return Y;
- case 2:
- return Z;
- case 3:
- return M;
- default:
- throw new IndexOutOfRangeException($"The index must be between 0 and {this.Dimension}.");
- }
- }
- set
- {
- switch (index)
- {
- case 0:
- X = value;
- break;
- case 1:
- Y = value;
- break;
- case 2:
- Z = value;
- break;
- case 3:
- M = value;
- break;
- default:
- throw new IndexOutOfRangeException($"The index must be between 0 and {this.Dimension}.");
- }
- }
- }
+ public static readonly XYZM AxisY = new XYZM(0, 1, 0, 0);
+
+ public static readonly XYZM AxisZ = new XYZM(0, 0, 1, 0);
+
+ public static readonly XYZM NaN = new XYZM(double.NaN);
+
+ public static readonly XYZM Zero = new XYZM(0, 0, 0, 0);
///
/// Constructor with the coordinate components
@@ -144,4 +108,45 @@ public string ToString(IFormatProvider? cultureInfo)
{
return $"{X.ToString(cultureInfo)},{Y.ToString(cultureInfo)},{Z.ToString(cultureInfo)},{M.ToString(cultureInfo)}";
}
-}
+
+ ///
+ public double this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return X;
+ case 1:
+ return Y;
+ case 2:
+ return Z;
+ case 3:
+ return M;
+ default:
+ throw new IndexOutOfRangeException($"The index must be between 0 and {this.Dimension}.");
+ }
+ }
+ set
+ {
+ switch (index)
+ {
+ case 0:
+ X = value;
+ break;
+ case 1:
+ Y = value;
+ break;
+ case 2:
+ Z = value;
+ break;
+ case 3:
+ M = value;
+ break;
+ default:
+ throw new IndexOutOfRangeException($"The index must be between 0 and {this.Dimension}.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSMath/XYZM.operators.cs b/CSMath/XYZM.operators.cs
index 27e1a68..344a337 100644
--- a/CSMath/XYZM.operators.cs
+++ b/CSMath/XYZM.operators.cs
@@ -5,25 +5,35 @@ namespace CSMath;
public partial struct XYZM : IVector, IEquatable
{
///
- /// Adds two vectors together.
+ /// Subtracts the second vector from the first.
///
/// The first source vector.
/// The second source vector.
- /// The summed vector.
- public static XYZM operator +(XYZM left, XYZM right)
+ /// The difference vector.
+ public static XYZM operator -(XYZM left, XYZM right)
{
- return left.Add(right);
+ return left.Subtract(right);
}
///
- /// Subtracts the second vector from the first.
+ /// Negates a given vector.
///
- /// The first source vector.
- /// The second source vector.
- /// The difference vector.
- public static XYZM operator -(XYZM left, XYZM right)
+ /// The source vector.
+ /// The negated vector.
+ public static XYZM operator -(XYZM value)
{
- return left.Subtract(right);
+ return Zero.Subtract(value);
+ }
+
+ ///
+ /// Returns a boolean indicating whether the two given vectors are not equal.
+ ///
+ /// The first vector to compare.
+ /// The second vector to compare.
+ /// True if the vectors are not equal; False if they are equal.
+ public static bool operator !=(XYZM left, XYZM right)
+ {
+ return !left.IsEqual(right);
}
///
@@ -82,13 +92,14 @@ public partial struct XYZM : IVector, IEquatable
}
///
- /// Negates a given vector.
+ /// Adds two vectors together.
///
- /// The source vector.
- /// The negated vector.
- public static XYZM operator -(XYZM value)
+ /// The first source vector.
+ /// The second source vector.
+ /// The summed vector.
+ public static XYZM operator +(XYZM left, XYZM right)
{
- return Zero.Subtract(value);
+ return left.Add(right);
}
///
@@ -101,15 +112,4 @@ public partial struct XYZM : IVector, IEquatable
{
return left.IsEqual(right);
}
-
- ///
- /// Returns a boolean indicating whether the two given vectors are not equal.
- ///
- /// The first vector to compare.
- /// The second vector to compare.
- /// True if the vectors are not equal; False if they are equal.
- public static bool operator !=(XYZM left, XYZM right)
- {
- return !left.IsEqual(right);
- }
-}
+}
\ No newline at end of file