В решении некоторых плагинов бывает возникает такая задача и мы начинаем искать решения в интернете, а там сложные формулы прямой на плоскости или в пространстве.
Но есть решение куда проще в реализации и понятнее. И поможет нам в этом скалярное произведение векторов (DotProduct):
Если скалярное произведение двух единичных векторов по модулю равно 1, то эти два вектора коллинеарны (параллельны друг другу).
Если произведение будет 1 – вектора направлены в одну сторону, а если -1 – в противоположные. Но в нашей задаче это не важно и нас интересует только модуль произведения. Из первого утверждения получаем метод проверки параллельности:
/// <summary>
/// Проверка параллельности двух векторов
/// </summary>
/// <param name="vector">Первый вектор</param>
/// <param name="checkedVector">Второй вектор</param>
/// <param name="tolerance">Допуск расстояния при проверке</param>
public static bool IsParallelTo(this XYZ vector, XYZ checkedVector, double tolerance = 0.0001)
{
return Math.Abs(Math.Abs(vector.DotProduct(checkedVector)) - 1.0) < tolerance;
}
/// <summary>
/// Проверка параллельности двух отрезков
/// </summary>
/// <param name="line">Первый отрезок</param>
/// <param name="checkedLine">Второй отрезок</param>
/// <param name="tolerance">Допуск расстояния при проверке</param>
public static bool IsParallelTo(this Line line, Line checkedLine, double tolerance = 0.0001)
{
return line.Direction.IsParallelTo(checkedLine.Direction, tolerance);
}
Мы уже решили половину задачи. А полное решение искомой задачи звучит так:
Два отрезка лежат на одной прямой если каждый единичный вектор, построенный через любую пару концевых точек двух отрезков, коллинеарен единичному вектору направления одного из отрезков.
Причем это утверждение будет работать и в 3D пространстве, а не только на плоскости.
Ну и конечно код этого решения в виде метода расширения для отрезка:
/// <summary>
/// Лежат ли текущий и проверяемый отрезок на одной прямой.
/// </summary>
/// <param name="firstLine">Текущий отрезок</param>
/// <param name="secondLine">Проверяемый отрезок</param>
/// <param name="tolerance">Допуск на сравнение чисел</param>
public static bool IsLieOnSameStraightLine(this Line firstLine, Line secondLine, double tolerance = 0.0001)
{
// Если два отрезка не параллельны, то и дальнейшая проверка не требуется
if (!firstLine.IsParallelTo(secondLine))
return false;
// Можно получить все концевые точки методом GetEndPoint(int), но это приведет к раздутию
// кода и его некрасивости. Поэтому используем метод Tessellate(), который для отрезков вернет
// те же самые две концевые точки
var firstLinePoints = firstLine.Tessellate();
var secondLinePoints = secondLine.Tessellate();
// Вектор первого отрезка будем использовать как эталон проверки.
// Свойство Direction всегда содержит единичный вектор
var fv = firstLine.Direction;
// Нам требуется проверять попарно концевые точки первого отрезка с концевыми точками второго.
// Это удобно сделать двумя итерациями
foreach (var firstLinePoint in firstLinePoints)
{
foreach (var secondLinePoint in secondLinePoints)
{
// Если два отрезка будут будут иметь общую концевую точку, то проверка сработает не верно,
// так как произведение векторов даст 0.0. Такие пары просто пропускаем
if (Math.Abs(firstLinePoint.DistanceTo(secondLinePoint)) < tolerance)
continue;
// Не важно из какой какую точку отнимать. Главное, привести к единичному вектору
var v = (secondLinePoint - firstLinePoint).Normalize();
// Если вектора не параллельны, то и отрезки не лежат на одной прямой
if (!fv.IsParallelTo(v))
return false;
}
}
// Если в предыдущих итерациях мы не вышли из метода, значит два отрезка лежат на одной прямой
return true;
}
Не забывайте, что в Revit есть отрезки, а есть лучи, и оба они представлены типом Line. Данный код не учитывает таких различий