Пересечение линии и параллелепипеда

Автор Тема: Пересечение линии и параллелепипеда  (Прочитано 12583 раз)

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

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Еще раз посмотрел его код. Он возвращает не единичный вектор в смысле аналитической геометрии.
Да - можете назвать его (тип вектора) в честь этой темы сайта :) - он нам и нужен.

Оффлайн avcАвтор темы

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Весь день пытаюсь задействовать этот метод. Не работает. Изредка ловит некоторые объекты в некоторых точках.
Заметил что функция UnitVector разворачивает вектор с отрицательным максимумом. Взял по модулю. Не помогает.
Подумал, что тестирование идет только по лучу в направлении вектора. Попробовал добавить сравнение с обратным вектором. Не помогает.
Вот команда для тестирования (максимально упрощаю)
Код - C# [Выбрать]
  1.  [CommandMethod("Test")]
  2.     public static void TestCommand()
  3.     {
  4.       Document doc = AcadApp.DocumentManager.MdiActiveDocument;
  5.       Database db = doc.Database;
  6.       if (doc == null) return;
  7.       Editor ed = doc.Editor;
  8.       try
  9.       {
  10.         ObjectId spaceId = db.CurrentSpaceId;
  11.         Matrix3d ucs = ed.CurrentUserCoordinateSystem;
  12.         Vector3d dir = spaceId == SymbolUtilityServices.GetBlockModelSpaceId(db) ? // в модели
  13.           ((Point3d)AcadApp.GetSystemVariable("VIEWDIR")).GetAsVector().Negate().TransformBy(ucs)
  14.           : new Vector3d();
  15.         PromptPointResult ppr = ed.GetPoint("Click point");
  16.         if (ppr.Status != PromptStatus.OK) return;
  17.         Point3d picked = ppr.Value.TransformBy(ucs);
  18.         ed.WriteMessage("\r\n");
  19.         using (Transaction tr = db.TransactionManager.StartTransaction())
  20.         {
  21.           BlockTableRecord space = tr.GetObject(spaceId, OpenMode.ForRead) as BlockTableRecord;
  22.           foreach (ObjectId id in space)
  23.           {
  24.             if (!id.IsValid || id.IsErased || id.ObjectClass == dbAttrDef || id.ObjectClass == dbAttrRef || id.ObjectClass == dbProxy) // не работаем с объектами не имеющими GeometricExtents
  25.               continue;
  26.             Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
  27.             if (ent == null || !ent.Visible) continue;
  28.             Extents3d ext = ent.GeometricExtents;
  29.             if (picked.IsIntersect(dir, ext))
  30.               ed.WriteMessage($"Found by A>V>C>: {ent.GetRXClass().Name} #{ent.Id.OldIdPtr}\r\n");
  31.             if (IsIntersect(picked, dir, ext))
  32.               ed.WriteMessage($"Found by Дима_: {ent.GetRXClass().Name} #{ent.Id.OldIdPtr}\r\n");
  33.           }
  34.           tr.Commit();
  35.         }
  36.       }
  37.       catch (System.Exception ex) { ed.WriteMessage(ex.Message); }
  38.     }
Мой метод IsIntersect ловит все без проблем.
Замерил производительность - разницы нет. Хотя метод Димы выглядит куда как выигрышней. Значит все ресурсы пожирает GeometricExtents. Получается бороться нет смысла, буду просто тестировать пересечения с самим объектом, не проверяя пересечения с Extents.

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Весь день пытаюсь задействовать этот метод. Не работает.
Честно говоря мне и самому показалось, что Дима_ что-то не учитывает, но так как на тестирование у меня не было времени, то я промолчал.
Значит все ресурсы пожирает GeometricExtents. Получается бороться нет смысла
Имеет смысл бороться только если многократно ищешь пересечение с одним и тем же примитивом. Тогда выгодно однократно получить GeometricExtents и использовать его в дальнейшем.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
У любой задачи есть простое, изящное, быстрое, но не правильное решение. Если скорость не меняется (а точнее, та что есть устраивает), то конечно тратить время не нужно - в проверке меня смутило только применение к вектору TransformBy(ucs) - в моем алгоритме он не нужен (идёт сравнение с "правильными" векторами), если только я опять не забыл и GeometricExtens не возвращает в UCS (пишу с телефона, не проверить сейчас).

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
если только я опять не забыл и GeometricExtens не возвращает в UCS
Возвращает в WCS (или в OCS для примитивов в блоке).
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
в проверке меня смутило только применение к вектору TransformBy(ucs)
Тут как раз avc прав. VIEWDIR получается в UCS. Поэтому его следует преобразовать в WCS.



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

Оффлайн avcАвтор темы

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
И ViewDir, и GetPoint в UCS возвращают результат, к сожалению. Я на это не раз нарывался. Но собственно это все только для тестирования, можно тестить в WCS и убрать все эти мелкие танцы с бубном.
Попробую еще с GetNormal(). Но что-то не верится, что сработает.
Жаль, что не удалось найти простой способ. Даже если в моем случае нет смысла тестить на пересечение Extents, все равно пригодилось бы в других случаях.

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Ладно - становится интересно - приложи файл с объектами (которые не находятся).

Оффлайн avcАвтор темы

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
да любой бокс можно помучить

Оффлайн avcАвтор темы

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Замена UnitVector на GetNormal не помогает. Вообще никода ничего не ловится

Отмечено как Решение avc 20-08-2018, 17:39:55

Оффлайн Кирилл Захаров

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
Здравствуйте, решил тоже поразмять мозги.
Предлагаю такое решение:
Код - C# [Выбрать]
  1.         /// <summary>
  2.         /// Возвращает 2 точки пересечения луча с параллепипедом Extents3d или пустой список если если пересечений нет
  3.         /// </summary>
  4.         /// <param name="rayPt"></param>
  5.         /// <param name="rayVector"></param>
  6.         /// <param name="extents"></param>
  7.         /// <returns></returns>
  8.         private static List<Point3d> CalcRayExtendsIntersection
  9.             (Point3d rayPt, Vector3d rayVector, Extents3d extents)
  10.         {
  11.             List<Point3d> pts = new List<Point3d>();
  12.             //Проверить на пересечение со всеми 6 гранями
  13.             //Каждая из 6 плоскостей граней параллельна двум осям системы координат,
  14.             //поэтому пересечение находится подстановкой максимальных и минимальных координат extents
  15.             //в каноническое уравнение прямой - http://mathprofi.ru/uravnenija_pryamoi_v_prostranstve.html
  16.             //После нахождения точки пересечения с плоскостью, определить попадает ли она в габариты соответствующей грани
  17.             double minX = extents.MinPoint.X;
  18.             double minY = extents.MinPoint.Y;
  19.             double minZ = extents.MinPoint.Z;
  20.             double maxX = extents.MaxPoint.X;
  21.             double maxY = extents.MaxPoint.Y;
  22.             double maxZ = extents.MaxPoint.Z;
  23.  
  24.             if(rayVector.X!=0)
  25.             {
  26.                 double x = minX;
  27.                 double n = ((x - rayPt.X) / rayVector.X);
  28.                 double y = n * rayVector.Y + rayPt.Y;
  29.                 double z = n * rayVector.Z + rayPt.Z;
  30.                 if (y >= minY && y <= maxY && z >= minZ && z <= maxZ)//Здесь можно использовать какой-нибудь допуск
  31.                 {
  32.                     pts.Add(new Point3d(x, y, z));
  33.                 }
  34.                 x = maxX;
  35.                 n = ((x - rayPt.X) / rayVector.X);
  36.                 y = n * rayVector.Y + rayPt.Y;
  37.                 z = n * rayVector.Z + rayPt.Z;
  38.                 if (y >= minY && y <= maxY && z >= minZ && z <= maxZ)
  39.                 {
  40.                     pts.Add(new Point3d(x, y, z));
  41.                     if (pts.Count > 1) return pts;
  42.                 }
  43.             }
  44.  
  45.             if (rayVector.Y != 0)
  46.             {
  47.                 double y = minY;
  48.                 double n = ((y - rayPt.Y) / rayVector.Y);
  49.                 double x = n * rayVector.X + rayPt.X;
  50.                 double z = n * rayVector.Z + rayPt.Z;
  51.                 if (x >= minX && x <= maxX && z >= minZ && z <= maxZ)
  52.                 {
  53.                     pts.Add(new Point3d(x, y, z));
  54.                     if (pts.Count > 1) return pts;
  55.                 }
  56.                 y = maxY;
  57.                 n = ((y - rayPt.Y) / rayVector.Y);
  58.                 x = n * rayVector.X + rayPt.X;
  59.                 z = n * rayVector.Z + rayPt.Z;
  60.                 if (x >= minX && x <= maxX && z >= minZ && z <= maxZ)
  61.                 {
  62.                     pts.Add(new Point3d(x, y, z));
  63.                     if (pts.Count > 1) return pts;
  64.                 }
  65.             }
  66.  
  67.  
  68.             if (rayVector.Z != 0)
  69.             {
  70.                 double z = minZ;
  71.                 double n = ((z - rayPt.Z) / rayVector.Z);
  72.                 double x = n * rayVector.X + rayPt.X;
  73.                 double y = n * rayVector.Y + rayPt.Y;
  74.                 if (x >= minX && x <= maxX && y >= minY && y <= maxY)
  75.                 {
  76.                     pts.Add(new Point3d(x, y, z));
  77.                     if (pts.Count > 1) return pts;
  78.                 }
  79.                 z = maxZ;
  80.                 n = ((z - rayPt.Z) / rayVector.Z);
  81.                 x = n * rayVector.X + rayPt.X;
  82.                 y = n * rayVector.Y + rayPt.Y;
  83.                 if (x >= minX && x <= maxX && y >= minY && y <= maxY)
  84.                 {
  85.                     pts.Add(new Point3d(x, y, z));
  86.                     if (pts.Count > 1) return pts;
  87.                 }
  88.  
  89.             }
  90.             return pts;
  91.  
  92.         }
  93.  
Выглядит громоздко, но, я думаю, так даже понятнее.

Оффлайн avcАвтор темы

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Предлагаю такое решение
Гениально! Именно то, что я и пытался найти. Чистая незамутненная математика, без всяких DisposableWrapper и вызовов API. Я ожидал, что поиск пересечений со всеми плоскостями будет куда более громоздкий. И на практике ловит все объекты даже без введения допусков.
Спасибо!

Оффлайн avcАвтор темы

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
И кстати, это работает на обеих направлениях линии от заданной точки, так что речь все-таки о линии, а не о луче. То, что мне и надо. Слово Ray в названиях несколько смутило.

Оффлайн Кирилл Захаров

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
так что речь все-таки о линии, а не о луче
Да-да-да, линия, не луч.