#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2009 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace Nuclex.Geometry.Lines.Collisions {
/// Detects intersections between infinite 2D lines and 2D triangles
public static class Line2Triangle2Collider {
/// Determines the contact location between a line and a triangle
///
/// Offset of the line from the coordinate system's center
///
/// Direction and length of the line
///
/// First corner point of triangle in counter-clockwise order
///
///
/// Second corner point of triangle in counter-clockwise order
///
///
/// Third corner point of triangle in counter-clockwise order
///
/// The point of intersection of the line with the triangle, if any
///
/// Everyone seems to know how to do 3D line / triangle intersections, but there
/// are no resources whatsoever on 2D line / triangle intersections. The code in here
/// is hand-written by myself. Instead of fancy math tricks, it simply tries to be
/// efficient using the existing code. It requires 4 line checks to find the accurate
/// intersection point with the triangle.
///
internal static LineContacts FindContacts(
Vector2 lineOffset, Vector2 lineDirection,
Vector2 triangleA, Vector2 triangleB, Vector2 triangleC
) {
Vector2 ab = triangleB - triangleA;
Vector2 bc = triangleC - triangleB;
Vector2 ca = triangleA - triangleC;
float abLength = ab.Length();
float bcLength = bc.Length();
float caLength = ca.Length();
Vector2 abNormalized = ab / abLength;
Vector2 bcNormalized = bc / bcLength;
Vector2 caNormalized = ca / caLength;
LineContacts abContacts = Line2Line2Collider.FindContacts(
triangleA, abNormalized, lineOffset, lineDirection
);
LineContacts bcContacts = Line2Line2Collider.FindContacts(
triangleB, bcNormalized, lineOffset, lineDirection
);
// Does the line cross the A-B line of the triangle?
if(isWithin(abContacts, abLength)) {
abContacts = Line2Line2Collider.FindContacts(
lineOffset, lineDirection, triangleA, ab / abLength
);
// Find out which other line it crosses: B-C or C-A?
if(isWithin(bcContacts, bcLength)) {
bcContacts = Line2Line2Collider.FindContacts(
lineOffset, lineDirection, triangleB, bc / bcLength
);
} else {
bcContacts = Line2Line2Collider.FindContacts(
lineOffset, lineDirection, triangleC, ca / caLength
);
}
// Report the contacts in the right order
if(abContacts.EntryTime < bcContacts.EntryTime) {
return new LineContacts(abContacts.EntryTime, bcContacts.EntryTime);
} else {
return new LineContacts(bcContacts.EntryTime, abContacts.EntryTime);
}
} else if(isWithin(bcContacts, bcLength)) { // Does is cross the B-C line?
bcContacts = Line2Line2Collider.FindContacts(
lineOffset, lineDirection, triangleB, bc / abLength
);
// We already checked A-B, so the other line it crosses must be C-A!
abContacts = Line2Line2Collider.FindContacts(
lineOffset, lineDirection, triangleC, ca / caLength
);
// Report the contacts in the right order
if(bcContacts.EntryTime < abContacts.EntryTime) {
return new LineContacts(bcContacts.EntryTime, abContacts.EntryTime);
} else {
return new LineContacts(abContacts.EntryTime, bcContacts.EntryTime);
}
} else { // No contact on A-B or B-C, contact is impossible
return LineContacts.None;
}
}
///
/// Finds out whether a reported contact point lies within a line segment
///
/// Reported contact point that will be checked
///
/// Length of the line segment against which the test that created the contact
/// point was made
///
/// True if the contact point is within the line segment
private static bool isWithin(LineContacts contacts, float length) {
return
(!float.IsNaN(contacts.EntryTime)) &&
(contacts.EntryTime >= 0.0f) &&
(contacts.EntryTime < length);
}
}
} // namespace Nuclex.Geometry.Lines.Collisions