В данной небольшой заметке я не открою Америку и не расскажу что-то уникальное. Но заметку я решил написать, так как не сразу можно догадаться о причинах такого поведения.
Итак, у вас есть команда, работающая по сценарию: запуск команды, выбор примитива, какие-то действия с примитивом. И может случиться так: вы запустили команду, начался запрос на выбор примитива, вы покрутили колесиком, а затем нажали Esc и произошло зуммирование к тем границам экрана, которые были до вызова команды. Иногда это может сильно бесить пользователей.
Подробнее: AutoCAD. Как устранить обратный zoom при прерывании выбора примитивов
Стояла у меня задача, для выполнения которой требовалось создание собственного типа для системного семейства Текст (Текстовое примечание).
Немного поискав информацию на просторах интернета, я наткнулся на данный пример, объясняющий, что для создания типа требуется создавать дубликат существующего типа. Вот только в примере совсем не уделено внимание тому, как и какие параметры задавать. Особенно остро стоит вопрос задания значения для параметра «Цвет».
Почитав в теме комментарии, я понял, что вопрос волновал многих и, к моему сожалению, в комментариях нет правильного ответа. Я решил исправить эту маленькую несправедливость и сделал небольшой пример создания типа для системного семейства Текст. Все пояснения я оставил в виде комментариев к коду.
Вот и сам пример:
Подробнее: Создание типа для системного семейства Текст (TextNoteType)
В решении некоторых плагинов бывает возникает такая задача и мы начинаем искать решения в интернете, а там сложные формулы прямой на плоскости или в пространстве.
Но есть решение куда проще в реализации и понятнее. И поможет нам в этом скалярное произведение векторов (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. Данный код не учитывает таких различий