Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: avc от 20-07-2016, 13:36:31

Название: Как получить плоскость поверхности тела
Отправлено: avc от 20-07-2016, 13:36:31
Задачка на первый взгляд элементарная. Есть AcBr.Face, одна из поверхностей твёрдого тела. Надо всего-то получить ее плоскость Plane (при условии, что это плоская поверхность). Но приходится городить огромный огород: получать от Face ее петли BoundaryLoop, с петель получать ребра Edge, с них кривые Curve3d и искать нелинейную кривую, которая даст плоскость через IsPlanar. Но все ребра могут оказаться линейными, и надо будет еще перебирать вертексы, чтоб построить-таки плоскость по трем точкам. Да еще и проверять, чтоб эти вертексы были достаточно разнесены в пространстве, чтоб построить плоскость точно. Короче куча велосипедов на сотни строк кода. Мне почему-то кажется, что должен быть путь покороче. Может кто-нибудь знает?
Название: Re: Как получить плоскость поверхности тела
Отправлено: Александр Ривилис от 20-07-2016, 14:13:38
Мне почему-то кажется, что должен быть путь покороче.
Я посмотрел на все возможности и не вижу другого метода.
Название: Re: Как получить плоскость поверхности тела
Отправлено: Александр Ривилис от 20-07-2016, 14:28:22
Впрочем, можно пойти другим путём. Имея SubentityId для Face, можно воспользоваться методом Solid3d.CopyFace. Судя по документации если Face плоский, то мы получим Region, для которого получить плоскость раз плюнуть. Ну а если Face не плоский, то получим Body.
Но думаю, что если это работает (не проверял), то будет достаточно долгой операцией.
Название: Re: Как получить плоскость поверхности тела
Отправлено: avc от 20-07-2016, 14:45:58
Получается я хорошо искал :) - прямых путей нет. Через Solid3d.CopyFace звучит вроде по проще. По пробую. Спасибо.
Название: Re: Как получить плоскость поверхности тела
Отправлено: Александр Ривилис от 20-07-2016, 17:44:45
Нашел еще один вариант, правда без проверки на то, что грань плоская. Но это тоже можно сделать. Пробовал правда только на простейших 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.  
Название: Re: Как получить плоскость поверхности тела
Отправлено: avc от 20-07-2016, 18:11:29
Описание поверхности по двум параметрам? Дааа, красивый бубен, с таким я еще не плясал..
Получается таким методом можно построить касательную плоскость в любой точке любой(?) поверхности.
Любопытно, что за точки будут получаться если брать середину диапазона параметров, а там, например, дырка
Название: Re: Как получить плоскость поверхности тела
Отправлено: Александр Ривилис от 20-07-2016, 18:47:35
Получается таким методом можно построить касательную плоскость в любой точке любой(?) поверхности.
Да.
Любопытно, что за точки будут получаться если брать середину диапазона параметров, а там, например, дырка
Не думаю, что такое возможно. Параметры - это не координаты по X и Y.
Название: Re: Как получить плоскость поверхности тела
Отправлено: Александр Ривилис от 20-07-2016, 18:48:54
avc
Потестируй мой пример на свои Solid3d - интересно узнать есть ли какие-то ограничения.
Название: Re: Как получить плоскость поверхности тела
Отправлено: Александр Ривилис от 20-07-2016, 20:10:55
А вот ещё один вариант, который получает плоскости только для плоских граней. Тоже интересно потестировать:

Код - 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. }

Название: Re: Как получить плоскость поверхности тела
Отправлено: avc от 20-07-2016, 23:31:50
Тоже интересно потестировать
Попробовал последний вариант. Работает стабильно на любых солидах: сплайновых, дырявых, реальных мебельных деталях. Но чуть по медленней чем мой велосипед. Так что вариант хороший, код короче раз в 100, но для реальной программы оставлю свой. В любом случае ваши советы фантастически полезны в познавательном плане. Спасибо!
Название: Re: Как получить плоскость поверхности тела
Отправлено: Александр Ривилис от 20-07-2016, 23:53:26
код короче раз в 100
Я не люблю длинные коды. :)
Название: Re: Как получить плоскость поверхности тела
Отправлено: avc от 08-08-2016, 16:32:51
получим 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();
Название: Re: Как получить плоскость поверхности тела
Отправлено: Александр Ривилис от 08-08-2016, 17:16:02
. Я так понимаю, что никакого региона на самом деле в БД чертежа нет, а есть поверхность солида Face. Отсюда и глюк, да?
В БД чертежа нет ни того, ни другого. Есть лишь описание 3DSOLID, из которого вычисляются грани.
Название: Re: Как получить плоскость поверхности тела
Отправлено: avc от 08-08-2016, 17:26:34
Я собственно хотел уточнить, что получить плоскость региона напрямую нельзя? Надо все равно искать через BRep и его компоненты. Да?
Название: Re: Как получить плоскость поверхности тела
Отправлено: Александр Ривилис от 08-08-2016, 20:33:53
Я собственно хотел уточнить, что получить плоскость региона напрямую нельзя? Надо все равно искать через 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.
Название: Re: Как получить плоскость поверхности тела
Отправлено: avc от 09-08-2016, 15:19:07
Нормаль - это свойство Region
И оно работает даже когда IsPlanar = false! И похоже, что всегда указывает наружу солида. Отлично! То что надо. Спасибо!
Название: Re: Как получить плоскость поверхности тела
Отправлено: Александр Ривилис от 09-08-2016, 15:22:32
И оно работает даже когда IsPlanar = false!
Я обратил внимание на то, что для любого Region IsPlanar == false (и в ObjectARX тоже) даже если он сделан из плоской полилинии. Так что это из той же оперы, что и исключение в методе Region.GetPlane.