patterncsharpMinor
Lines, intersections and terrible unit tests
Viewed 0 times
terribleintersectionstestsunitandlines
Problem
I needed
So, I built
The intersection code is my favourite part, essentially it builds both lines in slope-intercept form (
For this method, I defined coincident lines as not intersecting. (Since they don't intersect at a single point, but instead an infinite number of points.)
Once the point is found, determining if line-segments intersect is simple: just check if the point falls within the bounds of the rectangle of the line. (Why? We already know the point is on some location of the line, which means we only need to determine if it's
So, code for
```
public struct Line
{
public const float EqualThreshold = float.Epsilon * 2;
public Point Start { get; }
public Point End { get; }
public Vector2F Vector { get; }
public Rectangle Bounds => new Rectangle(Start.X, Start.Y, End.X - Start.X, End.Y - Start.X);
public Line(Point start, Point end)
{
Start = start;
End = end;
Vector = new Vector2F(End.X - Start.X, End.Y - Start.Y);
}
///
/// Gets the at which the current intersects the target .
///
/// The target to test.
/// The intersection point. Returns null if the lines are parallel, or do not intersect with each line's segment.
public PointF? Intersect(Line l) => Intersect(this, l);
public static PointF? Intersect(Line l1, Line l2)
{
var intPoint = IntersectForever(l1, l2);
if (intPoint == null)
{
return null;
}
// So we know
Line and LineF for the next stage of a project I'm working on, so I developed them. I also needed to determine if two lines intersected, preferably as cheaply as possible. (Lots of physics calculations.)So, I built
Line. Then I built LineTests, then LineF.The intersection code is my favourite part, essentially it builds both lines in slope-intercept form (
y = mx + b), then solves for x, then plugs x into the first line to get y, and returns that point.For this method, I defined coincident lines as not intersecting. (Since they don't intersect at a single point, but instead an infinite number of points.)
Once the point is found, determining if line-segments intersect is simple: just check if the point falls within the bounds of the rectangle of the line. (Why? We already know the point is on some location of the line, which means we only need to determine if it's
X falls in the range of our segment X. I built the method to also check the Y since I named it RectContainsPoint.)So, code for
Line.cs:```
public struct Line
{
public const float EqualThreshold = float.Epsilon * 2;
public Point Start { get; }
public Point End { get; }
public Vector2F Vector { get; }
public Rectangle Bounds => new Rectangle(Start.X, Start.Y, End.X - Start.X, End.Y - Start.X);
public Line(Point start, Point end)
{
Start = start;
End = end;
Vector = new Vector2F(End.X - Start.X, End.Y - Start.Y);
}
///
/// Gets the at which the current intersects the target .
///
/// The target to test.
/// The intersection point. Returns null if the lines are parallel, or do not intersect with each line's segment.
public PointF? Intersect(Line l) => Intersect(this, l);
public static PointF? Intersect(Line l1, Line l2)
{
var intPoint = IntersectForever(l1, l2);
if (intPoint == null)
{
return null;
}
// So we know
Solution
This is more of a long comment.
essentially it builds both lines in slope-intercept form
This doesn't work for vertical lines, because a vertical line cannot be a function (in the mathematical sense).
You detect this edge case in your code. I would prefer using some other mathematical model for a line to perform the calculations that doesn't have this shortcoming in the first place. That would mean to have less code dealing with special cases to distract the reader. Thus is subjective of course.
You have identical code in your classes except for the type of point:
I'd refactor that into an abstract generic superclass
I'm not sure what the point is (pun intended) in having those two versions of what's essentially the same class. The result is a
essentially it builds both lines in slope-intercept form
(y = mx + b)This doesn't work for vertical lines, because a vertical line cannot be a function (in the mathematical sense).
You detect this edge case in your code. I would prefer using some other mathematical model for a line to perform the calculations that doesn't have this shortcoming in the first place. That would mean to have less code dealing with special cases to distract the reader. Thus is subjective of course.
You have identical code in your classes except for the type of point:
public PointF Start { get; }
public PointF End { get; }
public Vector2F Vector { get; }
public RectangleF Bounds => new RectangleF(Start.X, Start.Y, End.X - Start.X, End.Y - Start.X);I'd refactor that into an abstract generic superclass
AbstractLine and let the two line classes extend that. Going generic has the advantage that you work with the exact type in each sub class. Using a common superclass of both point objects would make casting necessary. The downside is that you'd have to first create a common super type in the form of an interface for both point types anyway in order to restrict the generic type.I'm not sure what the point is (pun intended) in having those two versions of what's essentially the same class. The result is a
PointF either way.Code Snippets
public PointF Start { get; }
public PointF End { get; }
public Vector2F Vector { get; }
public RectangleF Bounds => new RectangleF(Start.X, Start.Y, End.X - Start.X, End.Y - Start.X);Context
StackExchange Code Review Q#141169, answer score: 6
Revisions (0)
No revisions yet.