Пересечение элементов

Автор Тема: Пересечение элементов  (Прочитано 13805 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Виктор Чекалин

  • Administrator
  • *****
  • Сообщений: 694
  • Карма: 111
  • Skype: chekalin-v
Re: Пересечение элементов
« Ответ #15 : 22-07-2015, 15:57:17 »
Нарисуйте линию
Код - C# [Выбрать]
  1. Line line = Line.CreateBound(point, new XYZ(10000, 0, 0));
как линию модели ModelLine.
Это визуально поможет определить, действительно ли линия пересекает геометрию элемента или нет.

Оффлайн ECity

  • ADN OPEN
  • Сообщений: 12
  • Карма: 0
Re: Пересечение элементов
« Ответ #16 : 19-05-2019, 13:34:33 »
Здравствуйте. Подниму темку немного :)

Вопрос не о пересечениях, как таковых, а о быстродействии.
Предположим, имеется 5000 стен и 5000 труб. Надо найти все пересечения. Встроенное средство "Проверка на пересечения", которое в пункте меню "Совместная работа", справляется с такой задачей секунды за 2, максимум 3.
Я попытался повторить этот алгоритм, получилось более минуты (!!!). Конечно, сначала было еще больше, но ряд оптимизаций помог. Но как ни кувыркаюсь, быстрее не работает. Что делаю не так?

Код - C# [Выбрать]
  1.    
  2.             // все трубы        
  3.             var pipesIds = new FilteredElementCollector(_mainDoc)
  4.                 .WherePasses(new ElementMulticategoryFilter(pipeCategories.Select(x => x.Type()).ToArray()))
  5.                 .WhereElementIsNotElementType()
  6.                 .ToElementIds();
  7.  
  8.             // если стены из присоединенного файла, получаем трансформ
  9.             var transform = _secondDoc.IsLink
  10.                 ? _secondDoc.LinkInstance.GetTotalTransform()
  11.                 : null;
  12.  
  13.             // все стены
  14.             var walls = new FilteredElementCollector(_secondDoc.Document)
  15.                 .WherePasses(new ElementMulticategoryFilter(wallCategories.Select(x => x.Type()).ToArray()))
  16.                 .WhereElementIsNotElementType()
  17.                 .ToElements();
  18.  
  19.             // ищем пересечения
  20.             var intersectedPipes = walls.SelectMany(wall =>
  21.             {
  22.                 var wallSolid = wall.GetSolid(transform);
  23.                 if (wallSolid == null) throw new Exception();
  24.  
  25.                 return new FilteredElementCollector(_mainDoc, pipesIds)
  26.                     .WherePasses(new ElementIntersectsSolidFilter(wallSolid))
  27.                     .ToElements();
  28.             });
  29.  
  30.  

Профилер говорит, что все время уходит  на ToElements(), то есть именно на вычисление пересечений. Я было подумал, что выборка элементов тормозит, но ToElementIds() работает тоже медленно.


Еще вопрос: прошу подтверждения своим мыслям:
1. Требуется найти пересечение стены с произвольным объектом и вырезать в стене прямоугольную дыру для этого объекта.
2. Для поиска такого пересечения подходит только поиск пересечения двух твердых тел, выдающее третье твердое тело.
3. Я должен спрецировать получившееся твердое тело на стену;
3а. Найти крайние точки проекции по вертикали и горизонтали;
3б. Вставить на поверхность стены отверстие по найденным крайним точкам.
4. Радоваться.
Единственный ли это алгоритм или есть что-то попроще и/или похитрее?

/* Форматируй код по правилам форума! Александр Ривилис */

И, извините, еще крошечный вопросик: как бы мне на свои длительные операции задействовать градусник, который в левом нижнем углу показывает проценты? Искал-искал, ничего похожего не нашел. И обработку кнопки "Отмена"?


Остальные 100500 вопросов боюсь вываливать :)

Спасибо!

« Последнее редактирование: 19-05-2019, 20:09:17 от Александр Ривилис »

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Пересечение элементов
« Ответ #17 : 19-05-2019, 20:49:46 »
И, извините, еще крошечный вопросик: как бы мне на свои длительные операции задействовать градусник, который в левом нижнем углу показывает проценты? Искал-искал, ничего похожего не нашел. И обработку кнопки "Отмена"?


Остальные 100500 вопросов боюсь вываливать :)

Спасибо!
Один вопрос - одна тема.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Игнатович

  • Administrator
  • *****
  • Сообщений: 1152
  • Карма: 338
  • Skype: alexandr.ignatovich.itc
Re: Пересечение элементов
« Ответ #18 : 20-05-2019, 12:00:12 »
Тут дело в том, ElementIntersectsSolidFilter - это медленный фильтр, а вы его вызываете очень много раз.

Попробуйте пойти другим путём. Трубы (кроме FlexPipe) прямые, есть механизм ReferenceIntersector, который пускает луч из заданной точки и может найти пересечения этого луча с элементами, поверхностями, кривыми (и т.д.) в том числе и из связанных файлов. Будет работать сильно быстрее. Ну а для гибких труб (которых вряд ли будет в проекте особенно много) используйте Ваш механизм c ElementIntersectsSolidFilter

Оффлайн ECity

  • ADN OPEN
  • Сообщений: 12
  • Карма: 0
Re: Пересечение элементов
« Ответ #19 : 26-05-2019, 11:42:03 »
Цитировать
/* Форматируй код по правилам форума! Александр Ривилис */
Извините, исправлюсь :)

Цитировать
есть механизм ReferenceIntersector
Спасибо за ответ. Действительно, работает сильно быстрее. Только не выдает пересечений  ;D
Делаю в точности так, как описано в справке. В тестовом файле одна труба и две стены, одна из которых из связанного файла. Труба пересекает обе стены.
Выдает единственное пересечение со стеной из собственного файла только если использую конструктор с одним параметром. Во всех остальных случаях не выдает ничего (перепробовал все возможные варианты). Что ж за напасть?  :(

Да и в случае выдачи единственного пересечения возвращает идентификатор пересекаемого элемента равный -1. И как мне получить стену?

Код - C# [Выбрать]
  1. // трубы
  2. var pipes = new FilteredElementCollector(_mainDoc)
  3.     .WherePasses(new ElementMulticategoryFilter(pipeCategories.Select(x => x.Type()).ToArray()))
  4.     .WhereElementIsNotElementType()
  5.     .ToElements();
  6.  
  7. // стены из линка
  8. var walls = new FilteredElementCollector(_secondDoc.Document)
  9.     .WherePasses(new ElementMulticategoryFilter(wallCategories.Select(x => x.Type()).ToArray()))
  10.     .WhereElementIsNotElementType()
  11.     .ToElementIds();
  12.  
  13. var filter = new ElementClassFilter(typeof(Wall));
  14. var view = _mainDoc.ActiveView as View3D;
  15.  
  16. var refIntersector = new ReferenceIntersector(filter, FindReferenceTarget.All, view); -- не работает
  17. var refIntersector = new ReferenceIntersector(walls, FindReferenceTarget.All, view); -- не работает
  18. var refIntersector = new ReferenceIntersector(view) -- выдает одно пересечение
  19.  
  20. refIntersector.FindReferencesInRevitLinks = true;
  21.  
  22. pipes.ForEach(pipe =>
  23. {
  24.     var line = (pipe.Location as LocationCurve).Curve as Line;
  25.     var intrs = refIntersector.Find(line.Origin, line.Direction);
  26.     foreach (var referenceWithContext in intrs)
  27.     {
  28.         Reference reference = referenceWithContext.GetReference(); // сюда попадаем только один раз
  29.     }
  30. });
  31.  
  32.  

Оффлайн Александр Игнатович

  • Administrator
  • *****
  • Сообщений: 1152
  • Карма: 338
  • Skype: alexandr.ignatovich.itc
Re: Пересечение элементов
« Ответ #20 : 26-05-2019, 12:33:24 »
В немножко упрощенном виде (беру первую найденную трубу) получилось вот что:
Код - C# [Выбрать]
  1. public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
  2. {
  3.         var uiapp = commandData.Application;
  4.         var uidoc = uiapp.ActiveUIDocument;
  5.         var doc = uidoc.Document;
  6.  
  7.         var collector = new FilteredElementCollector(doc);
  8.  
  9.         var pipe = (Pipe)collector
  10.                 .OfClass(typeof(Pipe))
  11.                 .FirstElement();
  12.  
  13.         var pipeLocation = (LocationCurve)pipe.Location;
  14.  
  15.         var pipeLine = (Line)pipeLocation.Curve;
  16.  
  17.         var referenceIntersector = new ReferenceIntersector(new ElementClassFilter(typeof (Wall)), FindReferenceTarget.Element, (View3D) uidoc.ActiveGraphicalView)
  18.                 {
  19.                         FindReferencesInRevitLinks = true
  20.                 };
  21.  
  22.         var origin = pipeLine.GetEndPoint(0);
  23.  
  24.         var intersections = referenceIntersector.Find(origin, pipeLine.Direction)
  25.                 .Where(x => x.Proximity <= pipeLine.Length)
  26.                 .Distinct(new ReferenceWithContextElementEqualityComparer())
  27.                 .ToList();
  28.  
  29.         TaskDialog.Show("dev", $"Found {intersections.Count} intersections");
  30.        
  31.         return Result.Succeeded;
  32. }

Код - C# [Выбрать]
  1. public class ReferenceWithContextElementEqualityComparer : IEqualityComparer<ReferenceWithContext>
  2. {
  3.         public bool Equals(ReferenceWithContext x, ReferenceWithContext y)
  4.         {
  5.                 if (ReferenceEquals(x, y)) return true;
  6.                 if (ReferenceEquals(null, x)) return false;
  7.                 if (ReferenceEquals(null, y)) return false;
  8.  
  9.                 var xReference = x.GetReference();
  10.  
  11.                 var yReference = y.GetReference();
  12.  
  13.                 return xReference.LinkedElementId == yReference.LinkedElementId
  14.                            && xReference.ElementId == yReference.ElementId;
  15.         }
  16.  
  17.         public int GetHashCode(ReferenceWithContext obj)
  18.         {
  19.                 var reference = obj.GetReference();
  20.  
  21.                 unchecked
  22.                 {
  23.                         return (reference.LinkedElementId.GetHashCode()*397) ^ reference.ElementId.GetHashCode();
  24.                 }
  25.         }
  26. }

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

Добавил EqualityComparer, т.к. ReferenceIntersector находит больше одного пересечения с каждым элементом - т.е. обе поверхности стены. И да, проверку .Where(x => x.Proximity <= pipeLine.Length) тоже надо сделать с учетом некого эпсилона (вспоминаем, как хранятся вещественные числа)

Оффлайн ECity

  • ADN OPEN
  • Сообщений: 12
  • Карма: 0
Re: Пересечение элементов
« Ответ #21 : 26-05-2019, 13:42:44 »
Спасибо!
В общем-то, ваш код от моего отличается только одним: я беру Line.Origin, а вы Line.GetEndPoint.
Сказать, что я офигел - ничего не сказать  ;D
Я как дурак читаю справку - там написано, что Line.Origin - это оригин. И больше ничего. Я и подумал, что это именно то, о чем я подумал. Оказалось, это что-то другое.
Расследование показало, что моя труба имеет концы по координатам -21.76 и 34,34 при длине 56,1. А оригин имеет координату -16,51. Что вообще происходит?!  :o

Оффлайн Александр Игнатович

  • Administrator
  • *****
  • Сообщений: 1152
  • Карма: 338
  • Skype: alexandr.ignatovich.itc
Re: Пересечение элементов
« Ответ #22 : 27-05-2019, 09:54:08 »
А что тут удивительного? Origin и Direction определяют уравнение в пространстве P = Ax + B, где P - точка на линии, A - Direction, B - Origin, x - скаляр - параметр. Origin никоим образом не обязан быть каким-либо концом прямой, к тому же кривая может быть как ограниченная, так и нет, см. свойство IsBound

Оффлайн ECity

  • ADN OPEN
  • Сообщений: 12
  • Карма: 0
Re: Пересечение элементов
« Ответ #23 : 01-06-2019, 07:34:29 »
Ну происходящее вы мне объяснили, стало понятнее. Спасибо.

Но не могу согласиться с вами, что нет ничего удивительного. Наоборот, все это чрезвычайно удивительно.
Во-первых, в уравнении прямой А - это не Direction, а Gradient. Если уж мы оперируем математическими терминами, то надо их и придерживаться. В - не Origin, а intercept. Это если по-английски. По-русски это угловой коэффициент и смещение. Никаких direction и origin. Мне кажется, это выдумки разработчиков Ревита, которые не потрудились составить нормальную справку. Ибо Origin - это Начало. Если у меня есть луч, который я подсовываю в ReferenceIntersector, то там у них все в порядке - origin и direction. Все понятно и прозрачно. А вот для отрезка почему-то начинаются проблемы. Если у меня есть отрезок (кусок трубы), то я вправе предположить, что НАЧАЛО отрезка есть одна из его крайних точек. И не могу согласиться с тем, что у них НАЧАЛО отрезка лежит на его середине. Это какой-то сюр. Один из краеугольных камней программирования - наименование переменной должно соответствовать ее назначению. Если это начало - будь добр положить в переменную начало. Если же это середина отрезка (линии, луча и т.п.), ты не имеешь права называть переменную origin. Это твой прямой путь в программистский ад :)

Еще один камень в огород ревитовских разработчиков - упомянутое вами IsBound. Читаю: "Describes whether the parameter of the curve is restricted to a particular interval". Перевожу: "Ограничен ли параметр кривой определенным интервалом". Я, конечно, все понимаю. Большой проект, трудности в личной жизни, мировой экономический кризис. Но справка на то и справка, что должна отвечать на вопросы, а не порождать новые. Что за параметр?! Каким еще определенным?! Конечно, я потрачу некоторое время и понимание из интуитивного перейдет в точное. Но почему сразу не писать нормальные справки? Вот это и есть удивительно.

Извините за ворчание :)

Оффлайн ECity

  • ADN OPEN
  • Сообщений: 12
  • Карма: 0
Re: Пересечение элементов
« Ответ #24 : 01-06-2019, 09:47:25 »
Вдруг подумалось: я ведь могу сразу получить точки пересечения луча с поверхностями стены? Мне в итоге нужны две точки: вход трубы в стену и выход. Сейчас я их ищу так: WallSolid.IntersectWithCurve(pipeCurve, new SolidCurveIntersectionOptions()). Для этого надо получить геометрию стены и так далее. А если луч уже прошел сквозь стену, геометрия луча известна, proximity есть. Осталось только выяснить - есть ли функция поиска точки по началу луча, направлению и расстоянию? Или самому городить?

Оффлайн Александр Игнатович

  • Administrator
  • *****
  • Сообщений: 1152
  • Карма: 338
  • Skype: alexandr.ignatovich.itc
Re: Пересечение элементов
« Ответ #25 : 04-06-2019, 10:56:49 »
Во-первых, в уравнении прямой А - это не Direction, а Gradient. Если уж мы оперируем математическими терминами, то надо их и придерживаться. В - не Origin, а intercept.

А вот и нет. Slope и intercept определяют в уравнении прямой на плоскости. В пространстве называют direction или tangent vector. Точку обычно обозначают буквой P, так что Direction и Origin неплохое такое именование. Да и насчёт IsBound тоже неплохо, параметр прямой в пространстве x принадлежит R, если он ограничен некоторым интервалом, то это отрезок, так что зря ругаетесь.

я ведь могу сразу получить точки пересечения луча с поверхностями стены?
Да, reference intersector возвращает все пересечения с поверхностями. убрать ReferenceWithContextElementEqualityComparer