Revit: Проверка что два отрезка лежат на одной прямой

Рейтинг:  2 / 5

Звезда активнаЗвезда активнаЗвезда не активнаЗвезда не активнаЗвезда не активна
 

В решении некоторых плагинов бывает возникает такая задача и мы начинаем искать решения в интернете, а там сложные формулы прямой на плоскости или в пространстве.

Но есть решение куда проще в реализации и понятнее. И поможет нам в этом скалярное произведение векторов (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. Данный код не учитывает таких различий

© 2018-2023 modplus.org