Проблема при использовании ElementIntersectsSolidFilter
Вопрос: У меня проблема с использованием ElementIntersectsSolidFilter. Результат получается совсем не тот, который я ожидаю увидеть.
Мне нужно определить, лежит ли заданная точка на любой части стены и получить список таких стен. Допустимая погрешность – 100 мм. Т.е. если точка находится на расстоянии 100 мм от стены, считаем, что точка лежит на стене.
Возможность фильтрации элементов по пересечению геометрии с помощью класса ElementIntersectsSolidFilter выглядит как идеальное решение для этой задачи:
- Я создаю фильтр ElementIntersectsSolidFilter filterSphere = new ElementIntersectsSolidFilter(tolSphere), где tolSphere – твердотельный объект-сфера с радиусом равным погрешности
- Затем, я получаю список стен, удовлетворяющих этому фильтру - new FilteredElementCollector( doc ) .OfClass( typeof( Wall ) ) .WherePasses( filterSphere );
К сожалению, протестировав этот код с заданной точкой, которая лежит прямо на поверхности стены, в результате я получил, что НЕТ таких стен, которые пересекают эту точку с погрешностью 100 мм. Результат я получил лишь тогда, когда задал погрешность в 1000 мм.
Я подумал, что возможно я что-то сделал не так, когда делал трансформацию координат, в связи со сложной логикой.
Однако, добавив в код отображение твердотельного объекта, как описано в статье Отображение твердотельного объекта с помощью Фреймворка визуализации (на англ.), я получил доказательство того, что точка действительно лежит на стене:
В конце концов, опытным путем я установил, что этот метод работает, если погрешность больше, чем толщина стены.
Выглядит так, что внутренняя реализация этого фильтра работает так, что пересекаемые объекты должны полностью пересекаться.
Вопросы:
- Это баг или так задумано?
- Если так и должно быть, то как же мне реализовать мою задачу? Есть ли что-то лучше, чем булевые операции?
Ответ: действительно, это ограничение булевых операций, которые иногда проявляются в Revit.
Если объекты пересекаются вдоль поверхности, то такие пересечения не определяются. Это отмечено у нас как проблема Revit-32243.
В вашем случае, возможно имеет смысл изменить сферу на куб. Куб должен пересекать поверхность по некоторым ее краям и пересечение определится верно.
Ответ: Спасибо за подтверждение того, что я был на верном пути.
Я попробую реализовать с кубом.
Вот что у меня в итоге получилось:
- /// <summary>
- /// Создание куба со стороной длиной d
- /// в нулевой точке
- /// </summary>
- static Solid CreateCube( double d )
- {
- return CreateRectangularPrism(
- XYZ.Zero, d, d, d );
- }
- /// <summary>
- /// Создание прямоугольной призмы
- /// </summary>
- static Solid CreateRectangularPrism(
- XYZ center,
- double d1,
- double d2,
- double d3 )
- {
- List<Curve> profile = new List<Curve>();
- XYZ profile00 = new XYZ( -d1 / 2, -d2 / 2, -d3 / 2 );
- XYZ profile01 = new XYZ( -d1 / 2, d2 / 2, -d3 / 2 );
- XYZ profile11 = new XYZ( d1 / 2, d2 / 2, -d3 / 2 );
- XYZ profile10 = new XYZ( d1 / 2, -d2 / 2, -d3 / 2 );
- profile.Add( Line.CreateBound( profile00, profile01 ) );
- profile.Add( Line.CreateBound( profile01, profile11 ) );
- profile.Add( Line.CreateBound( profile11, profile10 ) );
- profile.Add( Line.CreateBound( profile10, profile00 ) );
- CurveLoop curveLoop = CurveLoop.Create( profile );
- SolidOptions options = new SolidOptions(
- ElementId.InvalidElementId,
- ElementId.InvalidElementId );
- return GeometryCreationUtilities
- .CreateExtrusionGeometry(
- new CurveLoop[] { curveLoop },
- XYZ.BasisZ, d3, options );
- }
Вам очевидно нужно будет задать свою точку месторасположения объектов.
Ответ: Спасибо. Подтверждаю, что этот метод работает отлично:
Чтобы протестировать это самостоятельно, создайте модель с одной стеной. Код создаст 3 твердотельных объекта: сфера с радиусом меньше чем толщина стены, сфера с радиусом больше, чем толщина стены и куб со стороной меньше толщины стены. Центр каждого объекта должен лежать точно на поверхности стены.
Для каждого объекта создайте фильтр для определения пересечения со стеной. Первый не вернет результата, два последних – вернут.
Для визуализации используйте фреймворк визуализации.
Помимо кода выше, по созданию прямоугольной призмы, вот метод по созданию сферы:
- /// <summary>
- /// Создает сферы заданного радиуса с центром в заданной точке
- /// </summary>
- static public Solid CreateSphereAt(
- XYZ centre,
- double radius )
- {
- Frame frame = new Frame( centre, XYZ.BasisX,
- XYZ.BasisY, XYZ.BasisZ );
- Arc arc = Arc.Create(
- centre - radius * XYZ.BasisZ,
- centre + radius * XYZ.BasisZ,
- centre + radius * XYZ.BasisX );
- Line line = Line.CreateBound(
- arc.GetEndPoint( 1 ),
- arc.GetEndPoint( 0 ) );
- CurveLoop halfCircle = new CurveLoop();
- halfCircle.Append( arc );
- halfCircle.Append( line );
- List<CurveLoop> loops = new List<CurveLoop>( 1 );
- loops.Add( halfCircle );
- return GeometryCreationUtilities
- .CreateRevolvedGeometry( frame, loops,
- 0, 2 * Math.PI );
- }
Для визуализации я использовал следующий код:
- private static int schemaId = -1;
- public static void PaintSolid(
- Document doc,
- Solid s,
- double value )
- {
- Application app = doc.Application;
- View view = doc.ActiveView;
- if( view.AnalysisDisplayStyleId
- == ElementId.InvalidElementId )
- {
- CreateAVFDisplayStyle( doc, view );
- }
- SpatialFieldManager sfm = SpatialFieldManager
- .GetSpatialFieldManager( view );
- if( null == sfm )
- {
- sfm = SpatialFieldManager
- .CreateSpatialFieldManager( view, 1 );
- }
- if( -1 != schemaId )
- {
- IList<int> results = sfm.GetRegisteredResults();
- if( !results.Contains( schemaId ) )
- {
- schemaId = -1;
- }
- }
- if( -1 == schemaId )
- {
- AnalysisResultSchema resultSchema1
- = new AnalysisResultSchema( "PaintedSolid",
- "Description" );
- schemaId = sfm.RegisterResult( resultSchema1 );
- }
- FaceArray faces = s.Faces;
- Transform trf = Transform.Identity;
- foreach( Face face in faces )
- {
- int idx = sfm.AddSpatialFieldPrimitive( face, trf );
- IList<UV> uvPts = new List<UV>();
- List<double> doubleList = new List<double>();
- IList<ValueAtPoint> valList = new List<ValueAtPoint>();
- BoundingBoxUV bb = face.GetBoundingBox();
- uvPts.Add( bb.Min );
- doubleList.Add( value );
- valList.Add( new ValueAtPoint( doubleList ) );
- FieldDomainPointsByUV pnts
- = new FieldDomainPointsByUV( uvPts );
- FieldValues vals = new FieldValues( valList );
- sfm.UpdateSpatialFieldPrimitive( idx, pnts,
- vals, schemaId );
- }
- }
- static void CreateAVFDisplayStyle(
- Document doc,
- View view )
- {
- using( Transaction t = new Transaction( doc ) )
- {
- t.Start( "Create AVF Style" );
- AnalysisDisplayColoredSurfaceSettings
- coloredSurfaceSettings = new
- AnalysisDisplayColoredSurfaceSettings();
- coloredSurfaceSettings.ShowGridLines = true;
- AnalysisDisplayColorSettings colorSettings
- = new AnalysisDisplayColorSettings();
- AnalysisDisplayLegendSettings legendSettings
- = new AnalysisDisplayLegendSettings();
- legendSettings.ShowLegend = false;
- AnalysisDisplayStyle analysisDisplayStyle
- = AnalysisDisplayStyle.CreateAnalysisDisplayStyle(
- doc, "Paint Solid", coloredSurfaceSettings,
- colorSettings, legendSettings );
- view.AnalysisDisplayStyleId = analysisDisplayStyle.Id;
- t.Commit();
- }
- }
Ответ: Спасибо за подтверждение и код.
Один момент. На данный момент возможно использование DirectShapeвместо фреймворка визуализации. Фреймворк визуализации можно продолжать использовать, но DirectShape гораздо проще в использовании.
Обсуждение: http://adn-cis.org/forum/index.php?topic=2959
Опубликовано 28.08.2015