Line intersections

Decided to revert to Vectors being structs after reconsidering the full implications.
This commit is contained in:
Zdenek Borovec 2024-04-19 00:42:56 +02:00
parent ad0599f481
commit a23ea94db4
11 changed files with 285 additions and 19 deletions

13
interfaces/IBounded2.cs Normal file
View file

@ -0,0 +1,13 @@
namespace GeometriCS
{
/// <summary>
/// Interface for finite (bounded) entities in two-dimensional space.
/// </summary>
public interface IBounded2d
{
/// <summary>
/// Extents of the bounded entity.
/// </summary>
public Extents2d Extents { get; }
}
}

View file

@ -18,6 +18,10 @@
/// The two lines are parallel.
/// </summary>
PARALLEL,
/// <summary>
/// The two lines are colinear and intersect on their whole length.
/// </summary>
COLINEAR,
}
/// <summary>
@ -47,11 +51,37 @@
/// <param name="line1">Second of the intersecting lines.</param>
public IntersectionLine2ToLine2d(Line2d line1, Line2d line2)
{
// Set the intersector references
Line1 = line1;
Line2 = line2;
// Process the intersection
throw new NotImplementedException();
// Check if lines are parallel
// TODO: Check what happens if coeff_B == 0
if(Utils.DoubleEquals(line1.Coeff_A / line1.Coeff_B, line2.Coeff_A / line2.Coeff_B))
{
// Null result
Result = null;
// Check if lines are coincidental
if (Utils.DoubleEquals(line1.Coeff_A / line2.Coeff_A, line1.Coeff_B / line2.Coeff_B) &&
Utils.DoubleEquals(line1.Coeff_A / line2.Coeff_A, line1.Coeff_C / line2.Coeff_C))
{
Status = Statuses.COLINEAR;
}
else
{
Status = Statuses.PARALLEL;
}
return;
}
// Perform the intersection calculation
double divisor = line1.Coeff_A * line2.Coeff_B - line2.Coeff_A * line1.Coeff_B;
double x0 = (line1.Coeff_B * line2.Coeff_C - line2.Coeff_B * line1.Coeff_C) / divisor;
double y0 = (line1.Coeff_C * line2.Coeff_A - line2.Coeff_C * line1.Coeff_A) / divisor;
Result = new Vector2d(x0, y0);
Status = Statuses.SUCCESS;
}
}
}

View file

@ -1,7 +1,7 @@
namespace GeometriCS
{
/// <summary>
/// Intersection between a line and a line segment
/// Intersection between a line and a line segment. Double precision.
/// </summary>
public class IntersectionLine2ToLineSegment2d
{
@ -19,6 +19,10 @@
/// </summary>
PARALLEL,
/// <summary>
/// The line segment is colinear with the line.
/// </summary>
COLINEAR,
/// <summary>
/// The intersection would happen if the intersectors were unbounded, but not inside the bounds.
/// </summary>
OUTOFBOUNDS,
@ -54,8 +58,48 @@
Line = line;
LineSegment = lineSegment;
// Process the intersection
throw new NotImplementedException();
// Construct line coefficients for the linesegment
(double segment_A, double segment_B, double segment_C) =
Utils.Line2CoefficientsFromTwoPoints(lineSegment.StartPoint, lineSegment.EndPoint);
// Check if lines are parallel
// TODO: Check what happens if coeff_B == 0
// TODO: Find a non-retarded way to put this into a function so
// that it can be reused by all the 2d linear intersections.
if (Utils.DoubleEquals(line.Coeff_A / line.Coeff_B, segment_A / segment_B))
{
// Null result
Result = null;
// Check if lines are coincidental
if (Utils.DoubleEquals(line.Coeff_A / segment_A, line.Coeff_B / segment_B) &&
Utils.DoubleEquals(line.Coeff_A / segment_A, line.Coeff_C / segment_C))
{
Status = Statuses.COLINEAR;
}
else
{
Status = Statuses.PARALLEL;
}
return;
}
// Perform the intersection calculation
double divisor = line.Coeff_A * segment_B - segment_A * line.Coeff_B;
double x0 = (line.Coeff_B * segment_C - segment_B * line.Coeff_C) / divisor;
double y0 = (line.Coeff_C * segment_A - segment_C * line.Coeff_A) / divisor;
Result = new Vector2d(x0, y0);
// Check if result is inside of the segment.
if (lineSegment.Extents.Contains((Vector2d)Result))
{
Status = Statuses.SUCCESS;
}
else
{
Status = Statuses.OUTOFBOUNDS;
}
}
}
}

View file

@ -3,7 +3,7 @@
/// <summary>
/// Intersection between two planes, double precision.
/// </summary>
public struct IntersectionPlaneToPlaned
public class IntersectionPlaneToPlaned
{
/// <summary>
/// Possible statuses the intersection can be.

90
structs/Extents2.cs Normal file
View file

@ -0,0 +1,90 @@
namespace GeometriCS
{
/// <summary>
/// Geometric extents of a finitely large entity. Double precision.
/// </summary>
public struct Extents2d
{
/// <summary>
/// The point with the lowest Y and X.
/// </summary>
public Vector2d MinPoint { get; }
/// <summary>
/// The point with the highest Y and X.
/// </summary>
public Vector2d MaxPoint { get; }
/// <summary>
/// Take two points and create Extents from their bounding box.
/// </summary>
/// <param name="pt1">First point.</param>
/// <param name="pt2">Second point.</param>
/// <returns>Extents of the bounding box of the two points.</returns>
public static Extents2d FromTwoPoints(Vector2d pt1, Vector2d pt2)
{
// Find the smallest and largest x and y values.
double minx = pt1.X < pt2.X ? pt1.X : pt2.X;
double miny = pt1.Y < pt2.Y ? pt1.Y : pt2.Y;
double maxx = pt1.X > pt2.X ? pt1.X : pt2.X;
double maxy = pt1.Y > pt2.Y ? pt1.Y : pt2.Y;
// Create new points from them and create the extents.
return new Extents2d(new Vector2d(minx, miny),
new Vector2d(maxx, maxy));
}
/// <summary>
/// Is there an overlap between the bounding boxes of these two extents?
/// </summary>
/// <param name="other"></param>
/// <returns><c>true</c> if there is an overlap, otherwise <c>false</c>.</returns>
public bool Overlaps(Extents2d other)
{
// If at least one of my points are in others X band, or at least one of others points are in my X band
// check whether at least one of my points are in others Y band, or at least one of the others points
// are in my Y band. If both conditions are met, there is an overlap.
return (MinPoint.X.IsInRange(other.MinPoint.X, other.MaxPoint.X) ||
MaxPoint.X.IsInRange(other.MinPoint.X, other.MaxPoint.X) ||
other.MinPoint.X.IsInRange(MinPoint.X, MaxPoint.X) ||
other.MaxPoint.X.IsInRange(MinPoint.X, MaxPoint.X) ) && (
MinPoint.Y.IsInRange(other.MinPoint.Y, other.MaxPoint.Y) ||
MaxPoint.Y.IsInRange(other.MinPoint.Y, other.MaxPoint.Y) ||
other.MinPoint.Y.IsInRange(MinPoint.Y, MaxPoint.Y) ||
other.MaxPoint.Y.IsInRange(MinPoint.Y, MaxPoint.Y));
}
/// <summary>
/// Does the bounding box contain the point.
/// </summary>
/// <param name="point">Point to test.</param>
/// <returns><c>true</c> if the point is inside of the bounding box, otherwise <c>false</c>.</returns>
public bool Contains(Vector2d point)
{
return point.X.IsInRange(MinPoint.X, MaxPoint.X) && point.Y.IsInRange(MinPoint.Y, MaxPoint.Y);
}
/// <summary>
/// Constructor for the Extents2d.
/// </summary>
/// <param name="minPoint">The point with the lowest X and Y.</param>
/// <param name="maxPoint">The point with the greatest X and Y.</param>
/// <exception cref="ArgumentException">
/// The maxpoint is not greater in both dimensions than the minpoint.
/// </exception>
public Extents2d(Vector2d minPoint, Vector2d maxPoint)
{
/// Check validity
if((minPoint.X > maxPoint.X && !Utils.DoubleEquals(minPoint.X, maxPoint.X)) ||
(minPoint.Y > maxPoint.Y && !Utils.DoubleEquals(minPoint.Y, maxPoint.Y)))
{
throw new ArgumentException($"The minpoint {minPoint} has to " +
$"have lesser both X and Y than the maxpoint {maxPoint}.");
}
// Assign the properties.
MinPoint = minPoint;
MaxPoint = maxPoint;
}
}
}

View file

@ -1,6 +1,4 @@
using System.Runtime.CompilerServices;
namespace GeometriCS
namespace GeometriCS
{
/// <summary>
/// A line through a two-dimensional vector space.
@ -53,7 +51,8 @@ namespace GeometriCS
/// <returns>Line passing through the two points.</returns>
public static Line2d FromTwoPoints(Vector2d firstPoint, Vector2d secondPoint)
{
throw new NotImplementedException();
(double a, double b, double c) = Utils.Line2CoefficientsFromTwoPoints(firstPoint, secondPoint);
return new Line2d(a, b, c);
}
/// <summary>

View file

@ -3,7 +3,7 @@
/// <summary>
/// A segment of a two-dimensional line with double precision.
/// </summary>
public class LineSegment2d
public class LineSegment2d : IBounded2d
{
/// <summary>
/// Start point of the line.
@ -15,6 +15,10 @@
/// </summary>
public Vector2d EndPoint { get; set;}
// Consider pre-calculating this in point setters later on,
// once we have group intersection support and can gauge performance effects.
public Extents2d Extents => Extents2d.FromTwoPoints(StartPoint, EndPoint);
/// <summary>
/// A constructor for a 2d line segment.
/// </summary>
@ -22,8 +26,8 @@
/// <param name="endPoint">Point at which the line ends.</param>
public LineSegment2d(Vector2d startPoint, Vector2d endPoint)
{
StartPoint = (Vector2d) startPoint.Clone();
EndPoint = (Vector2d) endPoint.Clone();
StartPoint = startPoint;
EndPoint = endPoint;
}
/// <summary>

View file

@ -22,8 +22,8 @@
/// <param name="endPoint">End point of the line.</param>
public LineSegment3d(Vector3d startPoint, Vector3d endPoint)
{
StartPoint = (Vector3d) startPoint.Clone();
EndPoint = (Vector3d) endPoint.Clone();
StartPoint = startPoint;
EndPoint = endPoint;
}
/// <summary>

View file

@ -3,7 +3,7 @@
/// <summary>
/// Two-dimensional vector with double precision.
/// </summary>
public class Vector2d : ICloneable
public struct Vector2d : ICloneable
{
/// <summary>
/// Vector with 0 length.
@ -30,6 +30,34 @@
/// </summary>
public double Y { get; set; }
/// <summary>
/// Indexer for the vector.
/// </summary>
/// <param name="i">index</param>
/// <returns>[0] is X; [1] is Y</returns>
/// <exception cref="IndexOutOfRangeException">Index was out of range {0, 1}.</exception>
public double this [int i]
{
get
{
switch (i)
{
case 0: return X;
case 1: return Y;
default: throw new IndexOutOfRangeException();
}
}
set
{
switch (i)
{
case 0: X = value; break;
case 1: Y = value; break;
default: throw new IndexOutOfRangeException();
}
}
}
/// <summary>
/// Length of the vector.
/// </summary>

View file

@ -3,7 +3,7 @@
/// <summary>
/// Three-dimensional vector with double precision.
/// </summary>
public class Vector3d : ICloneable
public struct Vector3d : ICloneable
{
/// <summary>
/// Vector with 0 length.
@ -40,6 +40,36 @@
/// </summary>
public double Z { get; set; }
/// <summary>
/// Indexer for the vector.
/// </summary>
/// <param name="i">index</param>
/// <returns>[0] is X; [1] is Y; [2] is Z</returns>
/// <exception cref="IndexOutOfRangeException">Index was out of range {0, 1, 3}.</exception>
public double this[int i]
{
get
{
switch (i)
{
case 0: return X;
case 1: return Y;
case 2: return Z;
default: throw new IndexOutOfRangeException();
}
}
set
{
switch (i)
{
case 0: X = value; break;
case 1: Y = value; break;
case 2: Z = value; break;
default: throw new IndexOutOfRangeException();
}
}
}
/// <summary>
/// Length of the vector.
/// </summary>

View file

@ -3,13 +3,25 @@
/// <summary>
/// Common utilities throughout the library.
/// </summary>
internal static class Utils
public static class Utils
{
/// <summary>
/// Default precision for double equality comparison.
/// </summary>
private const double DOUBLEPRECISION = 1E-5;
/// <summary>
/// Is this number in the range <lower; higher> (inclusive).
/// </summary>
/// <param name="num">Number to compare</param>
/// <param name="lower">Lower end of the range.</param>
/// <param name="higher">Higher end of the range.</param>
/// <returns><c>true</c>if number is in the range <lower; higher>, <c>false</c> if it is outside.</returns>
public static bool IsInRange(this double num, double lower, double higher)
{
return num >= lower && num <= higher;
}
/// <summary>
/// Tests whether the difference between two doubles is within a given limit.
/// </summary>
@ -17,6 +29,22 @@
/// <param name="b">Second value to test.</param>
/// <param name="prec">Maximum allowed difference.</param>
/// <returns>Do the values almost equal each other.</returns>
public static bool DoubleEquals(double a, double b, double prec = DOUBLEPRECISION) => Math.Abs(a - b) <= prec;
public static bool DoubleEquals(double a, double b, double prec = DOUBLEPRECISION) => Math.Abs(a - b) <= prec;
/// <summary>
/// Returns the coefficients of a line defined by equation Ax + By + C = 0,
/// given two points that lie on the line.
/// </summary>
/// <param name="pt1">First point on the line.</param>
/// <param name="pt2">Second point on the line.</param>
/// <returns>Touple (Coeff_A, Coeff_B, Coeff_C).</returns>
public static (double A, double B, double C) Line2CoefficientsFromTwoPoints(Vector2d pt1, Vector2d pt2)
{
double coeff_A = pt2.Y - pt1.Y;
double coeff_B = pt1.X - pt2.X;
double coeff_C = (pt1.Y * (pt2.X - pt1.X)) -
(pt1.X * (pt2.Y - pt1.Y));
return (coeff_A, coeff_B, coeff_C);
}
}
}