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

28/08/2015

Проблема при использовании ElementIntersectsSolidFilter

Вопрос: У меня проблема с использованием ElementIntersectsSolidFilter. Результат получается совсем не тот, который я ожидаю увидеть.

Мне нужно определить, лежит ли заданная точка на любой части стены и получить список таких стен. Допустимая погрешность – 100 мм. Т.е. если точка находится на расстоянии 100 мм от стены, считаем, что точка лежит на стене.

Возможность фильтрации элементов по пересечению геометрии с помощью класса ElementIntersectsSolidFilter выглядит как идеальное решение для этой задачи:

  • Я создаю фильтр ElementIntersectsSolidFilter filterSphere = new ElementIntersectsSolidFilter(tolSphere), где tolSphere – твердотельный объект-сфера с радиусом равным погрешности
  • Затем, я получаю список стен, удовлетворяющих этому фильтру - new FilteredElementCollector( doc ) .OfClass( typeof( Wall ) ) .WherePasses( filterSphere );

К сожалению, протестировав этот код с заданной точкой, которая лежит прямо на поверхности стены, в результате я получил, что НЕТ таких стен, которые пересекают эту точку с погрешностью 100 мм. Результат я получил лишь тогда, когда задал погрешность в 1000 мм.

Я подумал, что возможно я что-то сделал не так, когда делал трансформацию координат, в связи со сложной логикой.

Однако, добавив в код отображение твердотельного объекта, как описано в статье Отображение твердотельного объекта с помощью Фреймворка визуализации (на англ.), я получил доказательство того, что точка действительно лежит на стене:

 

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

Выглядит так, что внутренняя реализация этого фильтра работает так, что пересекаемые объекты должны полностью пересекаться.

Вопросы:

  • Это баг или так задумано?
  • Если так и должно быть, то как же мне реализовать мою задачу? Есть ли что-то лучше, чем булевые операции?

Ответ: действительно, это ограничение булевых операций, которые иногда проявляются в Revit.

Если объекты пересекаются вдоль поверхности, то такие пересечения не определяются. Это отмечено у нас как проблема Revit-32243.

В вашем случае, возможно имеет смысл изменить сферу на куб. Куб должен пересекать поверхность по некоторым ее краям и пересечение определится верно.

Ответ: Спасибо за подтверждение того, что я был на верном пути.

Я попробую реализовать с кубом.

Вот что у меня в итоге получилось:

Код - C#: [Выделить]
  1.   /// <summary>
  2.   /// Создание куба со стороной длиной d
  3.   /// в нулевой точке
  4.   /// </summary>
  5.   static Solid CreateCube( double d )
  6.   {
  7.     return CreateRectangularPrism(
  8.       XYZ.Zero, d, d, d );
  9.   }
  10.  
  11.   /// <summary>
  12.   /// Создание прямоугольной призмы
  13.   /// </summary>
  14.   static Solid CreateRectangularPrism(
  15.     XYZ center,
  16.     double d1,
  17.     double d2,
  18.     double d3 )
  19.   {
  20.     List<Curve> profile = new List<Curve>();
  21.     XYZ profile00 = new XYZ( -d1 / 2, -d2 / 2, -d3 / 2 );
  22.     XYZ profile01 = new XYZ( -d1 / 2, d2 / 2, -d3 / 2 );
  23.     XYZ profile11 = new XYZ( d1 / 2, d2 / 2, -d3 / 2 );
  24.     XYZ profile10 = new XYZ( d1 / 2, -d2 / 2, -d3 / 2 );
  25.  
  26.     profile.Add( Line.CreateBound( profile00, profile01 ) );
  27.     profile.Add( Line.CreateBound( profile01, profile11 ) );
  28.     profile.Add( Line.CreateBound( profile11, profile10 ) );
  29.     profile.Add( Line.CreateBound( profile10, profile00 ) );
  30.  
  31.     CurveLoop curveLoop = CurveLoop.Create( profile );
  32.  
  33.     SolidOptions options = new SolidOptions(
  34.       ElementId.InvalidElementId,
  35.       ElementId.InvalidElementId );
  36.  
  37.     return GeometryCreationUtilities
  38.       .CreateExtrusionGeometry(
  39.         new CurveLoop[] { curveLoop },
  40.         XYZ.BasisZ, d3, options );
  41.   }

Вам очевидно нужно будет задать свою точку месторасположения объектов.

Ответ: Спасибо. Подтверждаю, что этот метод работает отлично:

 

Чтобы протестировать это самостоятельно, создайте модель с одной стеной. Код создаст 3 твердотельных объекта: сфера с радиусом меньше чем толщина стены, сфера с радиусом больше, чем толщина стены и куб со стороной меньше толщины стены. Центр каждого объекта должен лежать точно на поверхности стены.

Для каждого объекта создайте фильтр для определения пересечения со стеной. Первый не вернет результата, два последних – вернут.

Для визуализации используйте фреймворк визуализации.

Помимо кода выше, по созданию прямоугольной призмы, вот метод по созданию сферы:

Код - C#: [Выделить]
  1.   /// <summary>
  2.   /// Создает сферы заданного радиуса с центром в заданной точке
  3.   /// </summary>
  4.   static public Solid CreateSphereAt(
  5.     XYZ centre,
  6.     double radius )
  7.   {
  8.     Frame frame = new Frame( centre, XYZ.BasisX,
  9.       XYZ.BasisY, XYZ.BasisZ );
  10.  
  11.     Arc arc = Arc.Create(
  12.       centre - radius * XYZ.BasisZ,
  13.       centre + radius * XYZ.BasisZ,
  14.       centre + radius * XYZ.BasisX );
  15.  
  16.     Line line = Line.CreateBound(
  17.       arc.GetEndPoint( 1 ),
  18.       arc.GetEndPoint( 0 ) );
  19.  
  20.     CurveLoop halfCircle = new CurveLoop();
  21.     halfCircle.Append( arc );
  22.     halfCircle.Append( line );
  23.  
  24.     List<CurveLoop> loops = new List<CurveLoop>( 1 );
  25.     loops.Add( halfCircle );
  26.  
  27.     return GeometryCreationUtilities
  28.       .CreateRevolvedGeometry( frame, loops,
  29.         0, 2 * Math.PI );
  30.   }
  31.  

Для визуализации я использовал следующий код:

Код - C#: [Выделить]
  1. private static int schemaId = -1;
  2.  
  3.   public static void PaintSolid(
  4.     Document doc,
  5.     Solid s,
  6.     double value )
  7.   {
  8.     Application app = doc.Application;
  9.     View view = doc.ActiveView;
  10.  
  11.     if( view.AnalysisDisplayStyleId
  12.       == ElementId.InvalidElementId )
  13.     {
  14.       CreateAVFDisplayStyle( doc, view );
  15.     }
  16.  
  17.     SpatialFieldManager sfm = SpatialFieldManager
  18.       .GetSpatialFieldManager( view );
  19.  
  20.     if( null == sfm )
  21.     {
  22.       sfm = SpatialFieldManager
  23.         .CreateSpatialFieldManager( view, 1 );
  24.     }
  25.  
  26.     if( -1 != schemaId )
  27.     {
  28.       IList<int> results = sfm.GetRegisteredResults();
  29.       if( !results.Contains( schemaId ) )
  30.       {
  31.         schemaId = -1;
  32.       }
  33.     }
  34.  
  35.     if( -1 == schemaId )
  36.     {
  37.       AnalysisResultSchema resultSchema1
  38.         = new AnalysisResultSchema( "PaintedSolid",
  39.           "Description" );
  40.  
  41.       schemaId = sfm.RegisterResult( resultSchema1 );
  42.     }
  43.  
  44.     FaceArray faces = s.Faces;
  45.     Transform trf = Transform.Identity;
  46.     foreach( Face face in faces )
  47.     {
  48.       int idx = sfm.AddSpatialFieldPrimitive( face, trf );
  49.       IList<UV> uvPts = new List<UV>();
  50.       List<double> doubleList = new List<double>();
  51.       IList<ValueAtPoint> valList = new List<ValueAtPoint>();
  52.       BoundingBoxUV bb = face.GetBoundingBox();
  53.       uvPts.Add( bb.Min );
  54.       doubleList.Add( value );
  55.       valList.Add( new ValueAtPoint( doubleList ) );
  56.  
  57.       FieldDomainPointsByUV pnts
  58.         = new FieldDomainPointsByUV( uvPts );
  59.  
  60.       FieldValues vals = new FieldValues( valList );
  61.       sfm.UpdateSpatialFieldPrimitive( idx, pnts,
  62.         vals, schemaId );
  63.     }
  64.   }
  65.  
  66.   static void CreateAVFDisplayStyle(
  67.     Document doc,
  68.     View view )
  69.   {
  70.     using( Transaction t = new Transaction( doc ) )
  71.     {
  72.       t.Start( "Create AVF Style" );
  73.  
  74.       AnalysisDisplayColoredSurfaceSettings
  75.         coloredSurfaceSettings = new
  76.           AnalysisDisplayColoredSurfaceSettings();
  77.  
  78.       coloredSurfaceSettings.ShowGridLines = true;
  79.  
  80.       AnalysisDisplayColorSettings colorSettings
  81.         = new AnalysisDisplayColorSettings();
  82.  
  83.       AnalysisDisplayLegendSettings legendSettings
  84.         = new AnalysisDisplayLegendSettings();
  85.  
  86.       legendSettings.ShowLegend = false;
  87.  
  88.       AnalysisDisplayStyle analysisDisplayStyle
  89.         = AnalysisDisplayStyle.CreateAnalysisDisplayStyle(
  90.           doc, "Paint Solid", coloredSurfaceSettings,
  91.           colorSettings, legendSettings );
  92.  
  93.       view.AnalysisDisplayStyleId = analysisDisplayStyle.Id;
  94.  
  95.       t.Commit();
  96.     }
  97.   }

Ответ: Спасибо за подтверждение и код.

Один момент. На данный момент возможно использование DirectShapeвместо фреймворка визуализации. Фреймворк визуализации можно продолжать использовать, но DirectShape гораздо проще в использовании.

Источник: http://thebuildingcoder.typepad.com/blog/2015/07/intersect-solid-filter-avf-and-directshape-for-debugging.html

Автор перевода: Виктор Чекалин

Обсуждение: http://adn-cis.org/forum/index.php?topic=2959

Опубликовано 28.08.2015