Как получить плоскость поверхности тела

Автор Тема: Как получить плоскость поверхности тела  (Прочитано 12991 раз)

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

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

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Задачка на первый взгляд элементарная. Есть AcBr.Face, одна из поверхностей твёрдого тела. Надо всего-то получить ее плоскость Plane (при условии, что это плоская поверхность). Но приходится городить огромный огород: получать от Face ее петли BoundaryLoop, с петель получать ребра Edge, с них кривые Curve3d и искать нелинейную кривую, которая даст плоскость через IsPlanar. Но все ребра могут оказаться линейными, и надо будет еще перебирать вертексы, чтоб построить-таки плоскость по трем точкам. Да еще и проверять, чтоб эти вертексы были достаточно разнесены в пространстве, чтоб построить плоскость точно. Короче куча велосипедов на сотни строк кода. Мне почему-то кажется, что должен быть путь покороче. Может кто-нибудь знает?

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

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

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Впрочем, можно пойти другим путём. Имея SubentityId для Face, можно воспользоваться методом Solid3d.CopyFace. Судя по документации если Face плоский, то мы получим Region, для которого получить плоскость раз плюнуть. Ну а если Face не плоский, то получим Body.
Но думаю, что если это работает (не проверял), то будет достаточно долгой операцией.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Получается я хорошо искал :) - прямых путей нет. Через Solid3d.CopyFace звучит вроде по проще. По пробую. Спасибо.

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Нашел еще один вариант, правда без проверки на то, что грань плоская. Но это тоже можно сделать. Пробовал правда только на простейших Solid3d:

Код - C# [Выбрать]
  1. [CommandMethod("SEB", CommandFlags.Modal)]
  2. public void SebCommand()
  3. {
  4.   Document doc = Application.DocumentManager.MdiActiveDocument;
  5.   if (doc == null) return;
  6.   Editor ed = doc.Editor;
  7.   ObjectId curSpaceId = doc.Database.CurrentSpaceId;
  8.   PromptEntityOptions prEnt = new PromptEntityOptions("\nВыберите твердое тело: ");
  9.   prEnt.SetRejectMessage("Это не твердое тело");
  10.   prEnt.AddAllowedClass(typeof(Solid3d), false);
  11.   PromptEntityResult rsEnt = ed.GetEntity(prEnt);
  12.   if (rsEnt.Status != PromptStatus.OK) return;
  13.   using (Solid3d sol = rsEnt.ObjectId.Open(OpenMode.ForRead) as Solid3d)
  14.   {
  15.     AcBr.Brep brep = new AcBr.Brep(sol);
  16.     int iFaces = 0;
  17.     foreach (AcBr.Face face in brep.Faces)
  18.     {
  19.       ed.WriteMessage("\nГрань №{0}.", iFaces++);
  20.       AcGe.NurbSurface ebs = face.GetSurfaceAsNurb();
  21.       if (ebs != null)
  22.       {
  23.  
  24.         Interval[] iv = ebs.GetEnvelope();
  25.         double d1 = iv[0].LowerBound;
  26.         double d2 = iv[1].LowerBound;
  27.         Point2d ptParams = new Point2d(d1, d2);
  28.         PointOnSurface pos = new PointOnSurface(ebs, ptParams);
  29.         Vector3d normal = pos.GetNormal(ptParams);
  30.         Point3d pt = pos.GetPoint(ptParams);
  31.         // Можем получить плоскость
  32.         using (Plane plane = new Plane(pt, normal))
  33.         {
  34.           ed.WriteMessage("\nТочка = {0}, Нормаль={1}", pt, normal);
  35.         }
  36.       }
  37.     }
  38.   }
  39. }
  40.  
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Описание поверхности по двум параметрам? Дааа, красивый бубен, с таким я еще не плясал..
Получается таким методом можно построить касательную плоскость в любой точке любой(?) поверхности.
Любопытно, что за точки будут получаться если брать середину диапазона параметров, а там, например, дырка

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Получается таким методом можно построить касательную плоскость в любой точке любой(?) поверхности.
Да.
Любопытно, что за точки будут получаться если брать середину диапазона параметров, а там, например, дырка
Не думаю, что такое возможно. Параметры - это не координаты по X и Y.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

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

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
А вот ещё один вариант, который получает плоскости только для плоских граней. Тоже интересно потестировать:

Код - C# [Выбрать]
  1. [CommandMethod("SEB", CommandFlags.Modal)]
  2. public void SebCommand()
  3. {
  4.   Document doc = Application.DocumentManager.MdiActiveDocument;
  5.   if (doc == null) return;
  6.   Editor ed = doc.Editor;
  7.   ObjectId curSpaceId = doc.Database.CurrentSpaceId;
  8.   PromptEntityOptions prEnt = new PromptEntityOptions("\nВыберите твердое тело: ");
  9.   prEnt.SetRejectMessage("Это не твердое тело");
  10.   prEnt.AddAllowedClass(typeof(Solid3d), false);
  11.   PromptEntityResult rsEnt = ed.GetEntity(prEnt);
  12.   if (rsEnt.Status != PromptStatus.OK) return;
  13.   using (Solid3d sol = rsEnt.ObjectId.Open(OpenMode.ForRead) as Solid3d)
  14.   {
  15.     AcBr.Brep brep = new AcBr.Brep(sol);
  16.     int iFaces = 0;
  17.     foreach (AcBr.Face face in brep.Faces)
  18.     {
  19.       ed.WriteMessage("\nГрань №{0}.", iFaces++);
  20.       AcGe.Surface ebs = face.Surface;
  21.       if (ebs != null && ebs is ExternalBoundedSurface)
  22.       {
  23.         ExternalBoundedSurface eebs = ebs as ExternalBoundedSurface;
  24.         if (eebs.IsPlane)
  25.         {
  26.           Interval[] iv = ebs.GetEnvelope();
  27.           double d1 = iv[0].LowerBound;
  28.           double d2 = iv[1].LowerBound;
  29.           Point2d ptParams = new Point2d(d1, d2);
  30.           PointOnSurface pos = new PointOnSurface(ebs, ptParams);
  31.           Vector3d normal = pos.GetNormal(ptParams);
  32.           Point3d pt = pos.GetPoint(ptParams);
  33.           using (Plane plane = new Plane(pt, normal))
  34.           {
  35.             ed.WriteMessage(" Точка = {0}, Нормаль={1}", pt, normal);
  36.           }
  37.         }
  38.         else
  39.         {
  40.           ed.WriteMessage(" Грань не плоская.");
  41.         }
  42.       }
  43.     }
  44.   }
  45. }

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

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

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Тоже интересно потестировать
Попробовал последний вариант. Работает стабильно на любых солидах: сплайновых, дырявых, реальных мебельных деталях. Но чуть по медленней чем мой велосипед. Так что вариант хороший, код короче раз в 100, но для реальной программы оставлю свой. В любом случае ваши советы фантастически полезны в познавательном плане. Спасибо!

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

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

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

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
получим Region, для которого получить плоскость раз плюнуть
В совершенно другом контексте наткнулся на то, что раз плюнуть не получается. GetPlane всегда выдает eNotApplicable. И IsPlanar всегда false. Неплоский регион - это нонсенс. Я так понимаю, что никакого региона на самом деле в БД чертежа нет, а есть поверхность солида Face. Отсюда и глюк, да?
Код для теста такой:
Код - C# [Выбрать]
  1. PromptSelectionResult psrFace = ed.GetSelection(pso);
  2. if (psrFace.Status != PromptStatus.OK) return false;
  3. SelectedObject so = psrFace.Value[0];
  4. SelectedSubObject[] sso = so.GetSubentities();
  5. subEnt = solid.GetSubentity(sso[0].FullSubentityPath);
  6. if (subEnt is Region)
  7.   return (subEnt as Region).GetPlane();

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
. Я так понимаю, что никакого региона на самом деле в БД чертежа нет, а есть поверхность солида Face. Отсюда и глюк, да?
В БД чертежа нет ни того, ни другого. Есть лишь описание 3DSOLID, из которого вычисляются грани.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Я собственно хотел уточнить, что получить плоскость региона напрямую нельзя? Надо все равно искать через BRep и его компоненты. Да?

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Я собственно хотел уточнить, что получить плоскость региона напрямую нельзя? Надо все равно искать через BRep и его компоненты. Да?
Можно. Хотя и Region.GetPlane() не работает - явная недоработка. Альтернативный способ:
Код - C# [Выбрать]
  1. using System;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.ApplicationServices;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.EditorInput;
  7.  
  8. // This line is not mandatory, but improves loading performances
  9. [assembly: CommandClass(typeof(Rivilis.RegionPlane))]
  10.  
  11. namespace Rivilis
  12. {
  13.   public class RegionPlane
  14.   {
  15.     [CommandMethod("GetRegionPlane")]
  16.     public void GetRegionPlane()
  17.     {
  18.       Document doc = Application.DocumentManager.MdiActiveDocument;
  19.       if (doc == null) return;
  20.       Editor ed = doc.Editor;
  21.       PromptEntityOptions prEntOpt =
  22.         new PromptEntityOptions("\nВыберите область (Region) :");
  23.       prEntOpt.SetRejectMessage("\nЭто не область!");
  24.       prEntOpt.AddAllowedClass(typeof(Region), true);
  25.       PromptEntityResult prEntRes = ed.GetEntity(prEntOpt);
  26.       if (prEntRes.Status != PromptStatus.OK) return;
  27.       using (Region reg = prEntRes.ObjectId.Open(OpenMode.ForRead) as Region)
  28.       {
  29.         Point3dCollection pts = new Point3dCollection();
  30.         IntegerCollection i1 = new IntegerCollection();
  31.         IntegerCollection i2  = new IntegerCollection();
  32.         reg.GetGripPoints(pts, i1, i2);
  33.         Plane plane = new Plane(pts[0], reg.Normal);
  34.         ed.WriteMessage("\nOrigin = {0} Normal = {1}",
  35.           plane.PointOnPlane, plane.Normal);
  36.       }
  37.     }
  38.   }
  39. }

Нормаль - это свойство Region. Осталось вычислить какую-то точку на плоскости, так как плоскость можно определить нормалью и точкой на плоскости. Для этого годятся методы GetGripPoints и GetStretchPoints.
« Последнее редактирование: 09-08-2016, 08:58:47 от Александр Ривилис »
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение