mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-22 19:28:48 +00:00
Add primitive 2D structs to complement System.Drawing types
This commit is contained in:
parent
aca68cce72
commit
e9b0ed08d4
@ -3,6 +3,7 @@
|
||||
## 3.2.0
|
||||
### Added
|
||||
- X10D: Added `MathUtility.InverseLerp(float, float, float)` and `MathUtility.InverseLerp(double, double, double)`
|
||||
- X10D: Added `Circle`, `CircleF`, `Line`, `LineF`, `Polygon`, and `PolygonF`, to complement System.Drawing structs such as `Point` and `Rectangle`
|
||||
- X10D: Added `Point.ToSize()`
|
||||
- X10D: Added `Point.ToSizeF()`
|
||||
- X10D: Added `Point.ToVector2()`
|
||||
|
93
X10D.Tests/src/Drawing/CircleFTests.cs
Normal file
93
X10D.Tests/src/Drawing/CircleFTests.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Drawing;
|
||||
|
||||
namespace X10D.Tests.Drawing;
|
||||
|
||||
[TestClass]
|
||||
public class CircleFTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Area_ShouldBePiRadiusRadius_GivenUnitCircle()
|
||||
{
|
||||
var unitCircle = CircleF.Unit;
|
||||
Assert.AreEqual(MathF.PI * unitCircle.Radius * unitCircle.Radius, unitCircle.Area);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Circumference_ShouldBe2PiRadius_GivenUnitCircle()
|
||||
{
|
||||
var unitCircle = CircleF.Unit;
|
||||
Assert.AreEqual(2.0f * MathF.PI * unitCircle.Radius, unitCircle.Circumference, 1e-6f);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeNegativeOne_GivenUnitCircleAndEmpty()
|
||||
{
|
||||
Assert.AreEqual(-1, CircleF.Empty.CompareTo(CircleF.Unit));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeOne_GivenUnitCircleAndEmpty()
|
||||
{
|
||||
Assert.AreEqual(1, CircleF.Unit.CompareTo(CircleF.Empty));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeZero_GivenUnitCircle()
|
||||
{
|
||||
var unitCircle = CircleF.Unit;
|
||||
Assert.AreEqual(0, unitCircle.CompareTo(unitCircle));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Diameter_ShouldBe2_GivenUnitCircle()
|
||||
{
|
||||
Assert.AreEqual(2.0f, CircleF.Unit.Diameter, 1e-6f);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ShouldBeTrue_GivenTwoUnitCircles()
|
||||
{
|
||||
var unitCircle1 = CircleF.Unit;
|
||||
var unitCircle2 = CircleF.Unit;
|
||||
Assert.AreEqual(unitCircle1, unitCircle2);
|
||||
Assert.IsTrue(unitCircle1 == unitCircle2);
|
||||
Assert.IsFalse(unitCircle1 != unitCircle2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ShouldBeFalse_GivenDifferentCircles()
|
||||
{
|
||||
Assert.AreNotEqual(CircleF.Unit, CircleF.Empty);
|
||||
Assert.IsFalse(CircleF.Unit == CircleF.Empty);
|
||||
Assert.IsTrue(CircleF.Unit != CircleF.Empty);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
|
||||
{
|
||||
// this test is pretty pointless, it exists only for code coverage purposes
|
||||
int hashCode = CircleF.Empty.GetHashCode();
|
||||
Assert.AreEqual(hashCode, CircleF.Empty.GetHashCode());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashCode_ShouldBeCorrect_GivenUnitCircle()
|
||||
{
|
||||
// this test is pretty pointless, it exists only for code coverage purposes
|
||||
int hashCode = CircleF.Unit.GetHashCode();
|
||||
Assert.AreEqual(hashCode, CircleF.Unit.GetHashCode());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Radius_ShouldBe0_GivenEmptyCircle()
|
||||
{
|
||||
Assert.AreEqual(0.0f, CircleF.Empty.Radius, 1e-6f);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Radius_ShouldBe1_GivenUnitCircle()
|
||||
{
|
||||
Assert.AreEqual(1.0f, CircleF.Unit.Radius, 1e-6f);
|
||||
}
|
||||
}
|
93
X10D.Tests/src/Drawing/CircleTests.cs
Normal file
93
X10D.Tests/src/Drawing/CircleTests.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Drawing;
|
||||
|
||||
namespace X10D.Tests.Drawing;
|
||||
|
||||
[TestClass]
|
||||
public class CircleTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Area_ShouldBePiRadiusRadius_GivenUnitCircle()
|
||||
{
|
||||
var unitCircle = Circle.Unit;
|
||||
Assert.AreEqual(MathF.PI * unitCircle.Radius * unitCircle.Radius, unitCircle.Area);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Circumference_ShouldBe2PiRadius_GivenUnitCircle()
|
||||
{
|
||||
var unitCircle = Circle.Unit;
|
||||
Assert.AreEqual(2.0f * MathF.PI * unitCircle.Radius, unitCircle.Circumference, 1e-6f);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeNegativeOne_GivenUnitCircleAndEmpty()
|
||||
{
|
||||
Assert.AreEqual(-1, Circle.Empty.CompareTo(Circle.Unit));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeOne_GivenUnitCircleAndEmpty()
|
||||
{
|
||||
Assert.AreEqual(1, Circle.Unit.CompareTo(Circle.Empty));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeZero_GivenUnitCircle()
|
||||
{
|
||||
var unitCircle = Circle.Unit;
|
||||
Assert.AreEqual(0, unitCircle.CompareTo(unitCircle));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Diameter_ShouldBe2_GivenUnitCircle()
|
||||
{
|
||||
Assert.AreEqual(2.0f, Circle.Unit.Diameter, 1e-6f);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ShouldBeTrue_GivenTwoUnitCircles()
|
||||
{
|
||||
var unitCircle1 = Circle.Unit;
|
||||
var unitCircle2 = Circle.Unit;
|
||||
Assert.AreEqual(unitCircle1, unitCircle2);
|
||||
Assert.IsTrue(unitCircle1 == unitCircle2);
|
||||
Assert.IsFalse(unitCircle1 != unitCircle2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ShouldBeFalse_GivenDifferentCircles()
|
||||
{
|
||||
Assert.AreNotEqual(Circle.Unit, Circle.Empty);
|
||||
Assert.IsFalse(Circle.Unit == Circle.Empty);
|
||||
Assert.IsTrue(Circle.Unit != Circle.Empty);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
|
||||
{
|
||||
// this test is pretty pointless, it exists only for code coverage purposes
|
||||
int hashCode = Circle.Empty.GetHashCode();
|
||||
Assert.AreEqual(hashCode, Circle.Empty.GetHashCode());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashCode_ShouldBeCorrect_GivenUnitCircle()
|
||||
{
|
||||
// this test is pretty pointless, it exists only for code coverage purposes
|
||||
int hashCode = Circle.Unit.GetHashCode();
|
||||
Assert.AreEqual(hashCode, Circle.Unit.GetHashCode());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Radius_ShouldBe0_GivenEmptyCircle()
|
||||
{
|
||||
Assert.AreEqual(0, Circle.Empty.Radius);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Radius_ShouldBe1_GivenUnitCircle()
|
||||
{
|
||||
Assert.AreEqual(1, Circle.Unit.Radius);
|
||||
}
|
||||
}
|
79
X10D.Tests/src/Drawing/LineFTests.cs
Normal file
79
X10D.Tests/src/Drawing/LineFTests.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Drawing;
|
||||
|
||||
namespace X10D.Tests.Drawing;
|
||||
|
||||
[TestClass]
|
||||
public class LineFTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeNegativeOne_GivenEmptyAndOne()
|
||||
{
|
||||
Assert.AreEqual(-1, LineF.Empty.CompareTo(LineF.One));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeOne_GivenOneAndEmpty()
|
||||
{
|
||||
Assert.AreEqual(1, LineF.One.CompareTo(LineF.Empty));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeZero_GivenUnitLine()
|
||||
{
|
||||
var unitLineF = LineF.One;
|
||||
Assert.AreEqual(0, unitLineF.CompareTo(unitLineF));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Length_ShouldBe0_GivenEmptyLine()
|
||||
{
|
||||
Assert.AreEqual(0.0f, LineF.Empty.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Length_ShouldBe1_GivenUnitXLine()
|
||||
{
|
||||
Assert.AreEqual(1.0f, LineF.UnitX.Length, 1e-6f);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Length_ShouldBe1_GivenUnitYLine()
|
||||
{
|
||||
Assert.AreEqual(1.0f, LineF.UnitY.Length, 1e-6f);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ShouldBeTrue_GivenTwoUnitLines()
|
||||
{
|
||||
var unitLineF1 = LineF.One;
|
||||
var unitLineF2 = LineF.One;
|
||||
Assert.AreEqual(unitLineF1, unitLineF2);
|
||||
Assert.IsTrue(unitLineF1 == unitLineF2);
|
||||
Assert.IsFalse(unitLineF1 != unitLineF2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ShouldBeFalse_GivenDifferentLines()
|
||||
{
|
||||
Assert.AreNotEqual(LineF.One, LineF.Empty);
|
||||
Assert.IsFalse(LineF.One == LineF.Empty);
|
||||
Assert.IsTrue(LineF.One != LineF.Empty);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashCode_ShouldBeCorrect_GivenEmptyLine()
|
||||
{
|
||||
// this test is pretty pointless, it exists only for code coverage purposes
|
||||
int hashCode = LineF.Empty.GetHashCode();
|
||||
Assert.AreEqual(hashCode, LineF.Empty.GetHashCode());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashCode_ShouldBeCorrect_GivenUnitLine()
|
||||
{
|
||||
// this test is pretty pointless, it exists only for code coverage purposes
|
||||
int hashCode = LineF.One.GetHashCode();
|
||||
Assert.AreEqual(hashCode, LineF.One.GetHashCode());
|
||||
}
|
||||
}
|
79
X10D.Tests/src/Drawing/LineTests.cs
Normal file
79
X10D.Tests/src/Drawing/LineTests.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Drawing;
|
||||
|
||||
namespace X10D.Tests.Drawing;
|
||||
|
||||
[TestClass]
|
||||
public class LineTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeNegativeOne_GivenEmptyAndOne()
|
||||
{
|
||||
Assert.AreEqual(-1, Line.Empty.CompareTo(Line.One));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeOne_GivenOneAndEmpty()
|
||||
{
|
||||
Assert.AreEqual(1, Line.One.CompareTo(Line.Empty));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompareTo_ShouldBeZero_GivenUnitLine()
|
||||
{
|
||||
var unitLine = Line.One;
|
||||
Assert.AreEqual(0, unitLine.CompareTo(unitLine));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Length_ShouldBe0_GivenEmptyLine()
|
||||
{
|
||||
Assert.AreEqual(0.0f, Line.Empty.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Length_ShouldBe1_GivenUnitXLine()
|
||||
{
|
||||
Assert.AreEqual(1.0f, Line.UnitX.Length, 1e-6f);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Length_ShouldBe1_GivenUnitYLine()
|
||||
{
|
||||
Assert.AreEqual(1.0f, Line.UnitY.Length, 1e-6f);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ShouldBeTrue_GivenTwoUnitLines()
|
||||
{
|
||||
var unitLine1 = Line.One;
|
||||
var unitLine2 = Line.One;
|
||||
Assert.AreEqual(unitLine1, unitLine2);
|
||||
Assert.IsTrue(unitLine1 == unitLine2);
|
||||
Assert.IsFalse(unitLine1 != unitLine2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ShouldBeFalse_GivenDifferentLines()
|
||||
{
|
||||
Assert.AreNotEqual(Line.One, Line.Empty);
|
||||
Assert.IsFalse(Line.One == Line.Empty);
|
||||
Assert.IsTrue(Line.One != Line.Empty);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashCode_ShouldBeCorrect_GivenEmptyLine()
|
||||
{
|
||||
// this test is pretty pointless, it exists only for code coverage purposes
|
||||
int hashCode = Line.Empty.GetHashCode();
|
||||
Assert.AreEqual(hashCode, Line.Empty.GetHashCode());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashCode_ShouldBeCorrect_GivenUnitLine()
|
||||
{
|
||||
// this test is pretty pointless, it exists only for code coverage purposes
|
||||
int hashCode = Line.One.GetHashCode();
|
||||
Assert.AreEqual(hashCode, Line.One.GetHashCode());
|
||||
}
|
||||
}
|
70
X10D.Tests/src/Drawing/PolygonFTests.cs
Normal file
70
X10D.Tests/src/Drawing/PolygonFTests.cs
Normal file
@ -0,0 +1,70 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Drawing;
|
||||
|
||||
namespace X10D.Tests.Drawing;
|
||||
|
||||
[TestClass]
|
||||
public class PolygonFTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void IsConvex_ShouldBeFalse_GivenEmptyPolygon()
|
||||
{
|
||||
Assert.IsFalse(PolygonF.Empty.IsConvex);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsConvex_ShouldBeTrue_GivenHexagon()
|
||||
{
|
||||
Assert.IsTrue(CreateHexagon().IsConvex);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PointCount_ShouldBe1_GivenPolygonFWith1Point()
|
||||
{
|
||||
var polygon = new PolygonF();
|
||||
polygon.AddPoint(new Point(1, 1));
|
||||
|
||||
Assert.AreEqual(1, polygon.PointCount);
|
||||
|
||||
// assert that the empty polygon was not modified
|
||||
Assert.AreEqual(0, PolygonF.Empty.PointCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PointCount_ShouldBe0_GivenEmptyPolygon()
|
||||
{
|
||||
Assert.AreEqual(0, PolygonF.Empty.PointCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ShouldBeTrue_GivenTwoUnitCircles()
|
||||
{
|
||||
var emptyPolygonF1 = PolygonF.Empty;
|
||||
var emptyPolygonF2 = PolygonF.Empty;
|
||||
Assert.AreEqual(emptyPolygonF1, emptyPolygonF2);
|
||||
Assert.IsTrue(emptyPolygonF1 == emptyPolygonF2);
|
||||
Assert.IsFalse(emptyPolygonF1 != emptyPolygonF2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
|
||||
{
|
||||
// this test is pretty pointless, it exists only for code coverage purposes
|
||||
int hashCode = PolygonF.Empty.GetHashCode();
|
||||
Assert.AreEqual(hashCode, PolygonF.Empty.GetHashCode());
|
||||
}
|
||||
|
||||
private static PolygonF CreateHexagon()
|
||||
{
|
||||
var hexagon = new PolygonF();
|
||||
hexagon.AddPoint(new Vector2(0, 0));
|
||||
hexagon.AddPoint(new Vector2(1, 0));
|
||||
hexagon.AddPoint(new Vector2(1, 1));
|
||||
hexagon.AddPoint(new Vector2(0, 1));
|
||||
hexagon.AddPoint(new Vector2(-1, 1));
|
||||
hexagon.AddPoint(new Vector2(-1, 0));
|
||||
return hexagon;
|
||||
}
|
||||
}
|
69
X10D.Tests/src/Drawing/PolygonTests.cs
Normal file
69
X10D.Tests/src/Drawing/PolygonTests.cs
Normal file
@ -0,0 +1,69 @@
|
||||
using System.Drawing;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Drawing;
|
||||
|
||||
namespace X10D.Tests.Drawing;
|
||||
|
||||
[TestClass]
|
||||
public class PolygonTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void IsConvex_ShouldBeFalse_GivenEmptyPolygon()
|
||||
{
|
||||
Assert.IsFalse(Polygon.Empty.IsConvex);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsConvex_ShouldBeTrue_GivenHexagon()
|
||||
{
|
||||
Assert.IsTrue(CreateHexagon().IsConvex);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PointCount_ShouldBe1_GivenPolygonWith1Point()
|
||||
{
|
||||
var polygon = new Polygon();
|
||||
polygon.AddPoint(new Point(1, 1));
|
||||
|
||||
Assert.AreEqual(1, polygon.PointCount);
|
||||
|
||||
// assert that the empty polygon was not modified
|
||||
Assert.AreEqual(0, Polygon.Empty.PointCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PointCount_ShouldBe0_GivenEmptyPolygon()
|
||||
{
|
||||
Assert.AreEqual(0, Polygon.Empty.PointCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ShouldBeTrue_GivenTwoUnitCircles()
|
||||
{
|
||||
var emptyPolygon1 = Polygon.Empty;
|
||||
var emptyPolygon2 = Polygon.Empty;
|
||||
Assert.AreEqual(emptyPolygon1, emptyPolygon2);
|
||||
Assert.IsTrue(emptyPolygon1 == emptyPolygon2);
|
||||
Assert.IsFalse(emptyPolygon1 != emptyPolygon2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
|
||||
{
|
||||
// this test is pretty pointless, it exists only for code coverage purposes
|
||||
int hashCode = Polygon.Empty.GetHashCode();
|
||||
Assert.AreEqual(hashCode, Polygon.Empty.GetHashCode());
|
||||
}
|
||||
|
||||
private static Polygon CreateHexagon()
|
||||
{
|
||||
var hexagon = new Polygon();
|
||||
hexagon.AddPoint(new Point(0, 0));
|
||||
hexagon.AddPoint(new Point(1, 0));
|
||||
hexagon.AddPoint(new Point(1, 1));
|
||||
hexagon.AddPoint(new Point(0, 1));
|
||||
hexagon.AddPoint(new Point(-1, 1));
|
||||
hexagon.AddPoint(new Point(-1, 0));
|
||||
return hexagon;
|
||||
}
|
||||
}
|
272
X10D/src/Drawing/Circle.cs
Normal file
272
X10D/src/Drawing/Circle.cs
Normal file
@ -0,0 +1,272 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace X10D.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a circle that is composed of a 32-bit signed integer center point and radius.
|
||||
/// </summary>
|
||||
public readonly struct Circle : IEquatable<Circle>, IComparable<Circle>, IComparable
|
||||
{
|
||||
/// <summary>
|
||||
/// The empty circle. That is, a circle whose center point is (0, 0) and whose radius is 0.
|
||||
/// </summary>
|
||||
public static readonly Circle Empty = new();
|
||||
|
||||
/// <summary>
|
||||
/// The unit circle. That is, a circle whose center point is (0, 0) and whose radius is 1.
|
||||
/// </summary>
|
||||
public static readonly Circle Unit = new(Point.Empty, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Circle" /> struct.
|
||||
/// </summary>
|
||||
/// <param name="center">The center point of the circle.</param>
|
||||
/// <param name="radius">The radius of the circle.</param>
|
||||
public Circle(Point center, int radius)
|
||||
{
|
||||
Center = center;
|
||||
Radius = radius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the area of the circle.
|
||||
/// </summary>
|
||||
/// <value>The area of the circle, calculated as <c>πr²</c>.</value>
|
||||
public float Area
|
||||
{
|
||||
get => MathF.PI * Radius * Radius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the center point of the circle.
|
||||
/// </summary>
|
||||
/// <value>The center point.</value>
|
||||
public Point Center { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the circumference of the circle.
|
||||
/// </summary>
|
||||
/// <value>The circumference of the circle, calculated as <c>2πr</c>.</value>
|
||||
public float Circumference
|
||||
{
|
||||
get => 2 * MathF.PI * Radius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the diameter of the circle.
|
||||
/// </summary>
|
||||
/// <value>The diameter. This is always twice the <see cref="Radius" />.</value>
|
||||
public int Diameter
|
||||
{
|
||||
get => Radius * 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the radius of the circle.
|
||||
/// </summary>
|
||||
/// <value>The radius.</value>
|
||||
public int Radius { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="Circle" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator ==(Circle left, Circle right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="Circle" /> are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered not equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator !=(Circle left, Circle right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the radius of one circle is less than that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Radius" /> of <paramref name="left" /> is less than that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator <(Circle left, Circle right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the radius of one circle is greater than to that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Radius" /> of <paramref name="left" /> is greater than that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator >(Circle left, Circle right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the radius of one circle is less than or equal to that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Radius" /> of <paramref name="left" /> is less than or equal to that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator <=(Circle left, Circle right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the radius of one circle is greater than or equal to that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Radius" /> of <paramref name="left" /> is greater than or equal to that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator >=(Circle left, Circle right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance to another <see cref="Circle" />.
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object.</param>
|
||||
/// <returns>
|
||||
/// A signed number indicating the relative values of this instance and <paramref name="obj" />.
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Return value</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
///
|
||||
/// <item>
|
||||
/// <term>Less than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Radius" /> of this instance is less than that of <paramref name="obj" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Zero</term>
|
||||
/// <description>
|
||||
/// This instance is equal to <paramref name="obj" />, or the <see cref="Radius" /> of both this instance
|
||||
/// and <paramref name="obj" /> are not a number (<see cref="float.NaN" />),
|
||||
/// <see cref="float.PositiveInfinity" />, or <see cref="float.NegativeInfinity" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Greater than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Radius" /> of this instance is greater than that of <paramref name="obj" />, or
|
||||
/// <paramref name="obj" /> is <see langword="null" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
/// <remarks>Comparison only takes into consideration the <see cref="Radius" />.</remarks>
|
||||
/// <exception cref="ArgumentException"><paramref name="obj" /> is not an instance of <see cref="Circle" />.</exception>
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (obj is not Circle other)
|
||||
{
|
||||
throw new ArgumentException($"Object must be of type {GetType()}");
|
||||
}
|
||||
|
||||
return CompareTo(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance to another <see cref="Circle" />.
|
||||
/// </summary>
|
||||
/// <param name="other">The other circle.</param>
|
||||
/// <returns>
|
||||
/// A signed number indicating the relative values of this instance and <paramref name="other" />.
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Return value</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
///
|
||||
/// <item>
|
||||
/// <term>Less than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Radius" /> of this instance is less than that of <paramref name="other" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Zero</term>
|
||||
/// <description>
|
||||
/// This instance is equal to <paramref name="other" />, or the <see cref="Radius" /> of both this instance
|
||||
/// and <paramref name="other" /> are not a number (<see cref="float.NaN" />),
|
||||
/// <see cref="float.PositiveInfinity" />, or <see cref="float.NegativeInfinity" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Greater than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Radius" /> of this instance is greater than that of <paramref name="other" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
/// <remarks>Comparison only takes into consideration the <see cref="Radius" />.</remarks>
|
||||
public int CompareTo(Circle other)
|
||||
{
|
||||
return Radius.CompareTo(other.Radius);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Circle circle && Equals(circle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this instance and another instance are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The instance with which to compare.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if this instance and <paramref name="other" /> are considered equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public bool Equals(Circle other)
|
||||
{
|
||||
return Center.Equals(other.Center) && Radius.Equals(other.Radius);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Center, Radius);
|
||||
}
|
||||
}
|
284
X10D/src/Drawing/CircleF.cs
Normal file
284
X10D/src/Drawing/CircleF.cs
Normal file
@ -0,0 +1,284 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using X10D.Numerics;
|
||||
|
||||
namespace X10D.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a circle that is composed of a single-precision floating-point center point and radius.
|
||||
/// </summary>
|
||||
public readonly struct CircleF : IEquatable<CircleF>, IComparable<CircleF>, IComparable
|
||||
{
|
||||
/// <summary>
|
||||
/// The empty circle. That is, a circle whose center point is (0, 0) and whose radius is 0.
|
||||
/// </summary>
|
||||
public static readonly CircleF Empty = new();
|
||||
|
||||
/// <summary>
|
||||
/// The unit circle. That is, a circle whose center point is (0, 0) and whose radius is 1.
|
||||
/// </summary>
|
||||
public static readonly CircleF Unit = new(Vector2.Zero, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CircleF" /> struct.
|
||||
/// </summary>
|
||||
/// <param name="center">The center point of the circle.</param>
|
||||
/// <param name="radius">The radius of the circle.</param>
|
||||
public CircleF(Vector2 center, float radius)
|
||||
: this(center.ToPointF(), radius)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CircleF" /> struct.
|
||||
/// </summary>
|
||||
/// <param name="center">The center point of the circle.</param>
|
||||
/// <param name="radius">The radius of the circle.</param>
|
||||
public CircleF(PointF center, float radius)
|
||||
{
|
||||
Center = center;
|
||||
Radius = radius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the area of the circle.
|
||||
/// </summary>
|
||||
/// <value>The area of the circle, calculated as <c>πr²</c>.</value>
|
||||
public float Area
|
||||
{
|
||||
get => MathF.PI * Radius * Radius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the center point of the circle.
|
||||
/// </summary>
|
||||
/// <value>The center point.</value>
|
||||
public PointF Center { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the circumference of the circle.
|
||||
/// </summary>
|
||||
/// <value>The circumference of the circle, calculated as <c>2πr</c>.</value>
|
||||
public float Circumference
|
||||
{
|
||||
get => 2 * MathF.PI * Radius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the diameter of the circle.
|
||||
/// </summary>
|
||||
/// <value>The diameter. This is always twice the <see cref="Radius" />.</value>
|
||||
public float Diameter
|
||||
{
|
||||
get => Radius * 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the radius of the circle.
|
||||
/// </summary>
|
||||
/// <value>The radius.</value>
|
||||
public float Radius { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="CircleF" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator ==(CircleF left, CircleF right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="CircleF" /> are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered not equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator !=(CircleF left, CircleF right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the radius of one circle is less than that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Radius" /> of <paramref name="left" /> is less than that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator <(CircleF left, CircleF right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the radius of one circle is greater than to that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Radius" /> of <paramref name="left" /> is greater than that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator >(CircleF left, CircleF right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the radius of one circle is less than or equal to that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Radius" /> of <paramref name="left" /> is less than or equal to that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator <=(CircleF left, CircleF right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the radius of one circle is greater than or equal to that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Radius" /> of <paramref name="left" /> is greater than or equal to that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator >=(CircleF left, CircleF right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance to another <see cref="Circle" />.
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object.</param>
|
||||
/// <returns>
|
||||
/// A signed number indicating the relative values of this instance and <paramref name="obj" />.
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Return value</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
///
|
||||
/// <item>
|
||||
/// <term>Less than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Radius" /> of this instance is less than that of <paramref name="obj" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Zero</term>
|
||||
/// <description>
|
||||
/// This instance is equal to <paramref name="obj" />, or the <see cref="Radius" /> of both this instance
|
||||
/// and <paramref name="obj" /> are not a number (<see cref="float.NaN" />),
|
||||
/// <see cref="float.PositiveInfinity" />, or <see cref="float.NegativeInfinity" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Greater than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Radius" /> of this instance is greater than that of <paramref name="obj" />, or
|
||||
/// <paramref name="obj" /> is <see langword="null" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
/// <remarks>Comparison only takes into consideration the <see cref="Radius" />.</remarks>
|
||||
/// <exception cref="ArgumentException"><paramref name="obj" /> is not an instance of <see cref="CircleF" />.</exception>
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (obj is not Circle other)
|
||||
{
|
||||
throw new ArgumentException($"Object must be of type {GetType()}");
|
||||
}
|
||||
|
||||
return CompareTo(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance to another <see cref="CircleF" />.
|
||||
/// </summary>
|
||||
/// <param name="other">The other circle.</param>
|
||||
/// <returns>
|
||||
/// A signed number indicating the relative values of this instance and <paramref name="other" />.
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Return value</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
///
|
||||
/// <item>
|
||||
/// <term>Less than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Radius" /> of this instance is less than that of <paramref name="other" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Zero</term>
|
||||
/// <description>
|
||||
/// This instance is equal to <paramref name="other" />, or the <see cref="Radius" /> of both this instance
|
||||
/// and <paramref name="other" /> are not a number (<see cref="float.NaN" />),
|
||||
/// <see cref="float.PositiveInfinity" />, or <see cref="float.NegativeInfinity" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Greater than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Radius" /> of this instance is greater than that of <paramref name="other" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
/// <remarks>Comparison only takes into consideration the <see cref="Radius" />.</remarks>
|
||||
public int CompareTo(CircleF other)
|
||||
{
|
||||
return Radius.CompareTo(other.Radius);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is CircleF circle && Equals(circle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this instance and another instance are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The instance with which to compare.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if this instance and <paramref name="other" /> are considered equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public bool Equals(CircleF other)
|
||||
{
|
||||
return Center.Equals(other.Center) && Radius.Equals(other.Radius);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Center, Radius);
|
||||
}
|
||||
}
|
276
X10D/src/Drawing/Line.cs
Normal file
276
X10D/src/Drawing/Line.cs
Normal file
@ -0,0 +1,276 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace X10D.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a line that is composed of 32-bit signed integer X and Y coordinates.
|
||||
/// </summary>
|
||||
public readonly struct Line : IEquatable<Line>, IComparable<Line>, IComparable
|
||||
{
|
||||
/// <summary>
|
||||
/// The empty line. That is, a line whose start and end points are at (0, 0).
|
||||
/// </summary>
|
||||
public static readonly Line Empty = new();
|
||||
|
||||
/// <summary>
|
||||
/// The line whose start point is at (0, 0) and end point is at (1, 1).
|
||||
/// </summary>
|
||||
public static readonly Line One = new(Point.Empty, new Point(1, 1));
|
||||
|
||||
/// <summary>
|
||||
/// The line whose start point is at (0, 0) and end point is at (1, 0).
|
||||
/// </summary>
|
||||
public static readonly Line UnitX = new(Point.Empty, new Point(1, 0));
|
||||
|
||||
/// <summary>
|
||||
/// The line whose start point is at (0, 0) and end point is at (0, 1).
|
||||
/// </summary>
|
||||
public static readonly Line UnitY = new(Point.Empty, new Point(0, 1));
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Line" /> struct by taking the start and end points.
|
||||
/// </summary>
|
||||
/// <param name="start">The start point.</param>
|
||||
/// <param name="end">The end point.</param>
|
||||
public Line(Point start, Point end)
|
||||
{
|
||||
End = end;
|
||||
Start = start;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the end point of the line.
|
||||
/// </summary>
|
||||
/// <value>The end point.</value>
|
||||
public Point End { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of this line.
|
||||
/// </summary>
|
||||
/// <value>The length.</value>
|
||||
public float Length
|
||||
{
|
||||
get => MathF.Sqrt(LengthSquared);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of this line, squared.
|
||||
/// </summary>
|
||||
/// <value>The squared length.</value>
|
||||
public float LengthSquared
|
||||
{
|
||||
get => MathF.Pow(End.X - Start.X, 2.0f) + MathF.Pow(End.Y - Start.Y, 2.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start point of the line.
|
||||
/// </summary>
|
||||
/// <value>The start point.</value>
|
||||
public Point Start { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="Line" /> are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered not equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator ==(Line left, Line right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="Line" /> are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered not equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator !=(Line left, Line right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the length of one line is less than that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Length" /> of <paramref name="left" /> is less than that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator <(Line left, Line right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the length of one line is greater than that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Length" /> of <paramref name="left" /> is greater than that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator >(Line left, Line right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the length of one line is less than or equal to that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Length" /> of <paramref name="left" /> is less than or equal to that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator <=(Line left, Line right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the length of one line is greater than or equal to that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Length" /> of <paramref name="left" /> is greater than or equal to that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator >=(Line left, Line right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance to another object.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object with with which to compare</param>
|
||||
/// <returns>
|
||||
/// A signed number indicating the relative values of this instance and <paramref name="obj"/>.
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Return value</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
///
|
||||
/// <item>
|
||||
/// <term>Less than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Length" /> of this instance is less than that of <paramref name="obj" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Zero</term>
|
||||
/// <description>
|
||||
/// This instance is equal to <paramref name="obj" />, or the <see cref="Length" /> of both this instance
|
||||
/// and <paramref name="obj" /> are not a number (<see cref="float.NaN" />),
|
||||
/// <see cref="float.PositiveInfinity" />, or <see cref="float.NegativeInfinity" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Greater than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Length" /> of this instance is greater than that of <paramref name="obj" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Comparison internally measures the <see cref="LengthSquared" /> property to avoid calls to <see cref="MathF.Sqrt" />.
|
||||
/// <exception cref="ArgumentException"><paramref name="obj" /> is not an instance of <see cref="Line" />.</exception>
|
||||
/// </remarks>
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (obj is not Line other)
|
||||
{
|
||||
throw new ArgumentException($"Object must be of type {GetType()}");
|
||||
}
|
||||
|
||||
return CompareTo(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance to another <see cref="Line" />.
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns>
|
||||
/// A signed number indicating the relative values of this instance and <paramref name="other" />.
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Return value</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
///
|
||||
/// <item>
|
||||
/// <term>Less than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Length" /> of this instance is less than that of <paramref name="other" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Zero</term>
|
||||
/// <description>
|
||||
/// This instance is equal to <paramref name="other" />, or the <see cref="Length" /> of both this instance
|
||||
/// and <paramref name="other" /> are not a number (<see cref="float.NaN" />),
|
||||
/// <see cref="float.PositiveInfinity" />, or <see cref="float.NegativeInfinity" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Greater than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Length" /> of this instance is greater than that of <paramref name="other" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Comparison internally measures the <see cref="LengthSquared" /> property to avoid calls to <see cref="MathF.Sqrt" />.
|
||||
/// </remarks>
|
||||
public int CompareTo(Line other)
|
||||
{
|
||||
return LengthSquared.CompareTo(other.LengthSquared);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Line other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this instance and another instance are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The instance with which to compare.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if this instance and <paramref name="other" /> are considered equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public bool Equals(Line other)
|
||||
{
|
||||
return End.Equals(other.End) && Start.Equals(other.Start);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(End, Start);
|
||||
}
|
||||
}
|
310
X10D/src/Drawing/LineF.cs
Normal file
310
X10D/src/Drawing/LineF.cs
Normal file
@ -0,0 +1,310 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using X10D.Numerics;
|
||||
|
||||
namespace X10D.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a line that is composed of single-precision floating-point X and Y coordinates.
|
||||
/// </summary>
|
||||
public readonly struct LineF : IEquatable<LineF>, IComparable<LineF>, IComparable
|
||||
{
|
||||
/// <summary>
|
||||
/// The empty line. That is, a line whose start and end points are at (0, 0).
|
||||
/// </summary>
|
||||
public static readonly LineF Empty = new();
|
||||
|
||||
/// <summary>
|
||||
/// The line whose start point is at (0, 0) and end point is at (1, 1).
|
||||
/// </summary>
|
||||
public static readonly LineF One = new(Vector2.Zero, new Vector2(1, 1));
|
||||
|
||||
/// <summary>
|
||||
/// The line whose start point is at (0, 0) and end point is at (1, 0).
|
||||
/// </summary>
|
||||
public static readonly LineF UnitX = new(Vector2.Zero, new Vector2(1, 0));
|
||||
|
||||
/// <summary>
|
||||
/// The line whose start point is at (0, 0) and end point is at (0, 1).
|
||||
/// </summary>
|
||||
public static readonly LineF UnitY = new(Vector2.Zero, new Vector2(0, 1));
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LineF" /> struct by taking the start and end points.
|
||||
/// </summary>
|
||||
/// <param name="start">The start point.</param>
|
||||
/// <param name="end">The end point.</param>
|
||||
public LineF(Vector2 start, Vector2 end)
|
||||
: this(start.ToPointF(), end.ToPointF())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LineF" /> struct by taking the start and end points.
|
||||
/// </summary>
|
||||
/// <param name="start">The start point.</param>
|
||||
/// <param name="end">The end point.</param>
|
||||
public LineF(PointF start, PointF end)
|
||||
{
|
||||
End = end;
|
||||
Start = start;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the end point of the line.
|
||||
/// </summary>
|
||||
/// <value>The end point.</value>
|
||||
public PointF End { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of this line.
|
||||
/// </summary>
|
||||
/// <value>The length.</value>
|
||||
public float Length
|
||||
{
|
||||
get => MathF.Sqrt(LengthSquared);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of this line, squared.
|
||||
/// </summary>
|
||||
/// <value>The squared length.</value>
|
||||
public float LengthSquared
|
||||
{
|
||||
get => MathF.Pow(End.X - Start.X, 2.0f) + MathF.Pow(End.Y - Start.Y, 2.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start point of the line.
|
||||
/// </summary>
|
||||
/// <value>The start point.</value>
|
||||
public PointF Start { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="LineF" /> are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered not equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator ==(LineF left, LineF right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="LineF" /> are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered not equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator !=(LineF left, LineF right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the length of one line is less than that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Length" /> of <paramref name="left" /> is less than that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator <(LineF left, LineF right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the length of one line is greater than that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Length" /> of <paramref name="left" /> is greater than that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator >(LineF left, LineF right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the length of one line is less than or equal to that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Length" /> of <paramref name="left" /> is less than or equal to that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator <=(LineF left, LineF right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the length of one line is greater than or equal to that of another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the <see cref="Length" /> of <paramref name="left" /> is greater than or equal to that of
|
||||
/// <paramref name="right" />; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator >=(LineF left, LineF right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts a <see cref="Line" /> to a <see cref="LineF" />.
|
||||
/// </summary>
|
||||
/// <param name="line">The line to convert.</param>
|
||||
/// <returns>The converted line.</returns>
|
||||
public static explicit operator Line(LineF line)
|
||||
{
|
||||
PointF start = line.Start;
|
||||
PointF end = line.End;
|
||||
return new Line(new Point((int)start.X, (int)start.Y), new Point((int)end.X, (int)end.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts a <see cref="Line" /> to a <see cref="LineF" />.
|
||||
/// </summary>
|
||||
/// <param name="line">The line to convert.</param>
|
||||
/// <returns>The line polygon.</returns>
|
||||
public static implicit operator LineF(Line line)
|
||||
{
|
||||
return new LineF(line.Start, line.End);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance to another object.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object with with which to compare</param>
|
||||
/// <returns>
|
||||
/// A signed number indicating the relative values of this instance and <paramref name="obj"/>.
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Return value</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
///
|
||||
/// <item>
|
||||
/// <term>Less than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Length" /> of this instance is less than that of <paramref name="obj" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Zero</term>
|
||||
/// <description>
|
||||
/// This instance is equal to <paramref name="obj" />, or the <see cref="Length" /> of both this instance
|
||||
/// and <paramref name="obj" /> are not a number (<see cref="float.NaN" />),
|
||||
/// <see cref="float.PositiveInfinity" />, or <see cref="float.NegativeInfinity" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Greater than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Length" /> of this instance is greater than that of <paramref name="obj" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Comparison internally measures the <see cref="LengthSquared" /> property to avoid calls to <see cref="MathF.Sqrt" />.
|
||||
/// <exception cref="ArgumentException"><paramref name="obj" /> is not an instance of <see cref="Line" />.</exception>
|
||||
/// </remarks>
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (obj is not LineF other)
|
||||
{
|
||||
throw new ArgumentException($"Object must be of type {GetType()}");
|
||||
}
|
||||
|
||||
return CompareTo(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance to another <see cref="Line" />.
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns>
|
||||
/// A signed number indicating the relative values of this instance and <paramref name="other" />.
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Return value</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
///
|
||||
/// <item>
|
||||
/// <term>Less than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Length" /> of this instance is less than that of <paramref name="other" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Zero</term>
|
||||
/// <description>
|
||||
/// This instance is equal to <paramref name="other" />, or the <see cref="Length" /> of both this instance
|
||||
/// and <paramref name="other" /> are not a number (<see cref="float.NaN" />),
|
||||
/// <see cref="float.PositiveInfinity" />, or <see cref="float.NegativeInfinity" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Greater than zero</term>
|
||||
/// <description>
|
||||
/// The <see cref="Length" /> of this instance is greater than that of <paramref name="other" />.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Comparison internally measures the <see cref="LengthSquared" /> property to avoid calls to <see cref="MathF.Sqrt" />.
|
||||
/// </remarks>
|
||||
public int CompareTo(LineF other)
|
||||
{
|
||||
return LengthSquared.CompareTo(other.LengthSquared);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is LineF other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this instance and another instance are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The instance with which to compare.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if this instance and <paramref name="other" /> are considered equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public bool Equals(LineF other)
|
||||
{
|
||||
return End.Equals(other.End) && Start.Equals(other.Start);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(End, Start);
|
||||
}
|
||||
}
|
197
X10D/src/Drawing/Polygon.cs
Normal file
197
X10D/src/Drawing/Polygon.cs
Normal file
@ -0,0 +1,197 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace X10D.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D polygon composed of 32-bit signed integer points.
|
||||
/// </summary>
|
||||
public struct Polygon : IEquatable<Polygon>
|
||||
{
|
||||
/// <summary>
|
||||
/// The empty polygon. That is, a polygon with no points.
|
||||
/// </summary>
|
||||
public static readonly Polygon Empty = new(ArraySegment<Point>.Empty);
|
||||
|
||||
private Point[]? _points;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Polygon" /> struct by copying the specified polygon.
|
||||
/// </summary>
|
||||
public Polygon(Polygon polygon)
|
||||
: this(polygon._points ?? ArraySegment<Point>.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Polygon" /> struct by constructing it from the specified points.
|
||||
/// </summary>
|
||||
/// <param name="points">An enumerable collection of points from which the polygon should be constructed.</param>
|
||||
public Polygon(IEnumerable<Point> points)
|
||||
{
|
||||
_points = points.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this polygon is convex.
|
||||
/// </summary>
|
||||
/// <value><see langword="true" /> if this polygon is convex; otherwise, <see langword="false" />.</value>
|
||||
public bool IsConvex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_points is null || _points.Length < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var positive = false;
|
||||
var negative = false;
|
||||
Point p0 = _points[0];
|
||||
|
||||
for (var index = 1; index < _points.Length; index++)
|
||||
{
|
||||
Point p1 = _points[index];
|
||||
int d = (p1.X - p0.X) * (p1.Y + p0.Y);
|
||||
|
||||
if (d > 0)
|
||||
{
|
||||
positive = true;
|
||||
}
|
||||
else if (d < 0)
|
||||
{
|
||||
negative = true;
|
||||
}
|
||||
|
||||
if (positive && negative)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of points in this polygon.
|
||||
/// </summary>
|
||||
/// <value>An <see cref="int" /> value, representing the number of points in this polygon.</value>
|
||||
public int PointCount
|
||||
{
|
||||
get => _points?.Length ?? 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only view of the points in this polygon.
|
||||
/// </summary>
|
||||
/// <value>A <see cref="IReadOnlyList{T}" /> of <see cref="Point" /> values, representing the points of this polygon.</value>
|
||||
public IReadOnlyList<Point> Points
|
||||
{
|
||||
get => _points?.ToArray() ?? ArraySegment<Point>.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="Polygon" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator ==(Polygon left, Polygon right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="Polygon" /> are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered not equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator !=(Polygon left, Polygon right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a point to this polygon.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to add.</param>
|
||||
public void AddPoint(Point point)
|
||||
{
|
||||
_points ??= Array.Empty<Point>();
|
||||
Span<Point> span = stackalloc Point[_points.Length + 1];
|
||||
_points.CopyTo(span);
|
||||
span[^1] = point;
|
||||
_points = span.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a collection of points to this polygon.
|
||||
/// </summary>
|
||||
/// <param name="points">An enumerable collection of points to add.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="points" /> is <see langword="null" />.</exception>
|
||||
public void AddPoints(IEnumerable<Point> points)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(points);
|
||||
#else
|
||||
if (points is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(points));
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (Point point in points)
|
||||
{
|
||||
AddPoint(point);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all points from this polygon.
|
||||
/// </summary>
|
||||
public void ClearPoints()
|
||||
{
|
||||
_points = Array.Empty<Point>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Polygon polygon && Equals(polygon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this instance and another instance are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The instance with which to compare.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if this instance and <paramref name="other" /> are considered equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public bool Equals(Polygon other)
|
||||
{
|
||||
return _points switch
|
||||
{
|
||||
null when other._points is null => true,
|
||||
null => false,
|
||||
not null when other._points is null => false,
|
||||
_ => _points.SequenceEqual(other._points)
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
Point[] points = _points ?? Array.Empty<Point>();
|
||||
return points.Aggregate(0, HashCode.Combine);
|
||||
}
|
||||
}
|
291
X10D/src/Drawing/PolygonF.cs
Normal file
291
X10D/src/Drawing/PolygonF.cs
Normal file
@ -0,0 +1,291 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using X10D.Numerics;
|
||||
|
||||
namespace X10D.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D polygon composed of single-precision floating-point points.
|
||||
/// </summary>
|
||||
public struct PolygonF
|
||||
{
|
||||
/// <summary>
|
||||
/// The empty polygon. That is, a polygon with no points.
|
||||
/// </summary>
|
||||
public static readonly PolygonF Empty = new(ArraySegment<PointF>.Empty);
|
||||
|
||||
private PointF[]? _points;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PolygonF" /> struct by copying the specified polygon.
|
||||
/// </summary>
|
||||
public PolygonF(PolygonF polygon)
|
||||
: this(polygon._points ?? Array.Empty<PointF>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PolygonF" /> struct by constructing it from the specified points.
|
||||
/// </summary>
|
||||
/// <param name="points">An enumerable collection of points from which the polygon should be constructed.</param>
|
||||
public PolygonF(IEnumerable<Vector2> points)
|
||||
: this(points.Select(p => p.ToPointF()))
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(points);
|
||||
#else
|
||||
if (points is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(points));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PolygonF" /> struct by constructing it from the specified points.
|
||||
/// </summary>
|
||||
/// <param name="points">An enumerable collection of points from which the polygon should be constructed.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="points" /> is <see langword="null" />.</exception>
|
||||
public PolygonF(IEnumerable<PointF> points)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(points);
|
||||
#else
|
||||
if (points is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(points));
|
||||
}
|
||||
#endif
|
||||
|
||||
_points = points.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this polygon is convex.
|
||||
/// </summary>
|
||||
/// <value><see langword="true" /> if this polygon is convex; otherwise, <see langword="false" />.</value>
|
||||
public bool IsConvex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_points is null || _points.Length < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var positive = false;
|
||||
var negative = false;
|
||||
PointF p0 = _points[0];
|
||||
|
||||
for (var index = 1; index < _points.Length; index++)
|
||||
{
|
||||
PointF p1 = _points[index];
|
||||
float d = (p1.X - p0.X) * (p1.Y + p0.Y);
|
||||
|
||||
if (d > 0)
|
||||
{
|
||||
positive = true;
|
||||
}
|
||||
else if (d < 0)
|
||||
{
|
||||
negative = true;
|
||||
}
|
||||
|
||||
if (positive && negative)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of points in this polygon.
|
||||
/// </summary>
|
||||
/// <value>An <see cref="int" /> value, representing the number of points in this polygon.</value>
|
||||
public int PointCount
|
||||
{
|
||||
get => _points?.Length ?? 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only view of the points in this polygon.
|
||||
/// </summary>
|
||||
/// <value>A <see cref="IReadOnlyList{T}" /> of <see cref="PointF" /> values, representing the points of this polygon.</value>
|
||||
public IReadOnlyList<PointF> Points
|
||||
{
|
||||
get => _points?.ToArray() ?? ArraySegment<PointF>.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="PolygonF" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator ==(PolygonF left, PolygonF right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="PolygonF" /> are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are considered not equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator !=(PolygonF left, PolygonF right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts a <see cref="Polygon" /> to a <see cref="PolygonF" />.
|
||||
/// </summary>
|
||||
/// <param name="polygon">The polygon to convert.</param>
|
||||
/// <returns>The converted polygon.</returns>
|
||||
public static explicit operator Polygon(PolygonF polygon)
|
||||
{
|
||||
var points = new List<Point>();
|
||||
|
||||
foreach (PointF point in polygon.Points)
|
||||
{
|
||||
points.Add(new Point((int)point.X, (int)point.Y));
|
||||
}
|
||||
|
||||
return new Polygon(points);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts a <see cref="Polygon" /> to a <see cref="PolygonF" />.
|
||||
/// </summary>
|
||||
/// <param name="polygon">The polygon to convert.</param>
|
||||
/// <returns>The converted polygon.</returns>
|
||||
public static implicit operator PolygonF(Polygon polygon)
|
||||
{
|
||||
var points = new List<PointF>();
|
||||
|
||||
foreach (Point point in polygon.Points)
|
||||
{
|
||||
points.Add(point);
|
||||
}
|
||||
|
||||
return new PolygonF(points);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a point to this polygon.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to add.</param>
|
||||
public void AddPoint(PointF point)
|
||||
{
|
||||
_points ??= Array.Empty<PointF>();
|
||||
Span<PointF> span = stackalloc PointF[_points.Length + 1];
|
||||
_points.CopyTo(span);
|
||||
span[^1] = point;
|
||||
_points = span.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a point to this polygon.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to add.</param>
|
||||
public void AddPoint(Vector2 point)
|
||||
{
|
||||
AddPoint(point.ToPointF());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a collection of points to this polygon.
|
||||
/// </summary>
|
||||
/// <param name="points">An enumerable collection of points to add.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="points" /> is <see langword="null" />.</exception>
|
||||
public void AddPoints(IEnumerable<PointF> points)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(points);
|
||||
#else
|
||||
if (points is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(points));
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (PointF point in points)
|
||||
{
|
||||
AddPoint(point);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a collection of points to this polygon.
|
||||
/// </summary>
|
||||
/// <param name="points">An enumerable collection of points to add.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="points" /> is <see langword="null" />.</exception>
|
||||
public void AddPoints(IEnumerable<Vector2> points)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(points);
|
||||
#else
|
||||
if (points is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(points));
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (Vector2 point in points)
|
||||
{
|
||||
AddPoint(point);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all points from this polygon.
|
||||
/// </summary>
|
||||
public void ClearPoints()
|
||||
{
|
||||
_points = Array.Empty<PointF>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is PolygonF polygon && Equals(polygon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this instance and another instance are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The instance with which to compare.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if this instance and <paramref name="other" /> are considered equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public bool Equals(PolygonF other)
|
||||
{
|
||||
return _points switch
|
||||
{
|
||||
null when other._points is null => true,
|
||||
null => false,
|
||||
not null when other._points is null => false,
|
||||
_ => _points.SequenceEqual(other._points)
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
PointF[] points = _points ?? Array.Empty<PointF>();
|
||||
return points.Aggregate(0, HashCode.Combine);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user