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

28/10/2014

Создание наклонной стены

В последнее время мне часто задают вопросы по созданию наклонных стен. Поэтому я хотел бы рассмотреть основные ключевые моменты по созданию и привести несколько примеров.

Как программно создать наклонную стену

В пользовательском интерфейсе наклонную стену можно создать по поверхности концептуального формообразующего элемента.

Эта функциональность доступна начиная с Revit 2011 API с помощью метода FaceWall.Create().

Ранее я уже это рассказывал об использовании этого метода в статьях Класс FaceWall – наклонные стены и создание наклонной стены на поверхности формообразующего элемента (на англ.).

Наклонная стена или наклонное перекрытие

Вопрос: Метод создания стены в Revit API не позволяет создать наклонную стену:

Код - C#: [Выделить]
  1.   Wall.Create( revitDoc, curves, wallType.Id,
  2.     level.Id, isStructural );

Тем не менее, я могу создать наклонное перекрытие:

Код - C#: [Выделить]
  1.   doc.Create.NewSlab( profile, level, slopedArrow,
  2.     slope, isStructural );

Мы часто проектируем здания с не совсем вертикальными стенами, и когда мы их переносим в Revit они в итоге становятся перекрытиями, чего я хотел бы избежать.

Есть ли способ создать наклонные стены с помощью Revit API?

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

Чтобы проделать эти шаги в API, нужно создать семейство типа «Концептуальный формообразующий элемент» с наклонной под нужным углом поверхностью. Вставить экземпляр этого объекта в проект и создать стену на поверхности с помощью метода FaceWall.Create.

Ниже рассмотрим несколько примеров по созданию.

Создание наклонной стены из концептуального формообразующего элемента

Создание наклонной стены из формообразующего элемента мы уже рассматривали раннее в статье Класс FaceWall – наклонные стены.

Статья была написана Adam Nagy для Revit 2012 и код в той статье уже немного устарел.

Я обновил код в и добавил его в примеры The Building Coder в команду CmdFaceWall.

Вопрос: Я бы хотел создать наклонные стены с помощью API. В интерфейсе это можно сделать, создав формообразующий элемент и создать стену на его поверхности.

Вроде бы невозможно создать формообразующий элемент прямо в проекте через API. Поэтому я создаю программно семейство формообразующего элемента и вставляю его в проект. Однако, по каким-то причинам геометрия этого элемента всегда равна null.

Вот мой код:

Код - C#: [Выделить]
  1.   public static void SlopedWallTest(
  2.     ExternalCommandData revit )
  3.   {
  4.     Document massDoc = revit.Application.Application.NewFamilyDocument(
  5.         @"C:\ProgramData\Autodesk\RAC 2012\Family Templates\English_I\Conceptual Mass\Mass.rft" );
  6.  
  7.     Transaction transaction = new Transaction( massDoc );
  8.     transaction.SetName( "Тест" );
  9.     transaction.Start();
  10.  
  11.     ExternalCommandData cdata = revit;
  12.     Autodesk.Revit.ApplicationServices.Application app = revit.Application.Application;
  13.     app = revit.Application.Application;
  14.  
  15.     ReferenceArray ref_ar = new ReferenceArray();
  16.  
  17.     Autodesk.Revit.DB.XYZ ptA = new XYZ( 0, 0, 0 );
  18.     XYZ ptB = new XYZ( 0, 30, 0 );
  19.     ModelCurve modelcurve = MakeLine( revit.Application, ptA, ptB, massDoc );
  20.     ref_ar.Append( modelcurve.GeometryCurve.Reference );
  21.  
  22.     ptA = new XYZ( 0, 30, 0 );
  23.     ptB = new XYZ( 2, 30, 0 );
  24.     modelcurve = MakeLine( revit.Application, ptA, ptB, massDoc );
  25.     ref_ar.Append( modelcurve.GeometryCurve.Reference );
  26.  
  27.     ptA = new XYZ( 2, 30, 0 );
  28.     ptB = new XYZ( 2, 0, 0 );
  29.     modelcurve = MakeLine( revit.Application, ptA, ptB, massDoc );
  30.     ref_ar.Append( modelcurve.GeometryCurve.Reference );
  31.  
  32.     ptA = new XYZ( 2, 0, 0 );
  33.     ptB = new XYZ( 0, 0, 0 );
  34.     modelcurve = MakeLine( revit.Application, ptA, ptB, massDoc );
  35.     ref_ar.Append( modelcurve.GeometryCurve.Reference );
  36.  
  37.     // Выдавливание в заданном направлении
  38.     XYZ direction = new XYZ( -6, 0, 50 );
  39.     Form form = massDoc.FamilyCreate.NewExtrusionForm( true, ref_ar, direction );
  40.     transaction.Commit();
  41.  
  42.     if( File.Exists( @"C:\TestFamily.rfa" ) )
  43.       File.Delete( @"C:\TestFamily.rfa" );
  44.  
  45.     massDoc.SaveAs( @"C:\TestFamily.rfa" );
  46.  
  47.     if( !revit.Application.ActiveUIDocument.Document.LoadFamily( @"C:\TestFamily.rfa" ) )
  48.       throw new Exception( "Не удалось загрузить семейство" );
  49.  
  50.     Family family = null;
  51.     foreach( Element el in new FilteredElementCollector(
  52.         revit.Application.ActiveUIDocument.Document ).WhereElementIsNotElementType().ToElements() )
  53.     {
  54.       if( el is Family )
  55.       {
  56.         if( ( (Family) el ).Name.ToUpper().Trim().StartsWith( "Тест" ) )
  57.           family = (Family) el;
  58.       }
  59.     }
  60.  
  61.     FamilySymbol fs = null;
  62.     foreach( FamilySymbol sym in family.Symbols )
  63.       fs = sym;
  64.  
  65.     // создаем экземпляр семейства.
  66.     revit.Application.ActiveUIDocument.Document.Create.NewFamilyInstance(
  67.         new XYZ( 0, 0, 0 ), fs, revit.Application.ActiveUIDocument.Document.ActiveView.Level,
  68.         StructuralType.NonStructural );
  69.  
  70.     WallType wallType = null;
  71.     foreach( WallType wt in revit.Application.ActiveUIDocument.Document.WallTypes )
  72.     {
  73.       if( FaceWall.IsWallTypeValidForFaceWall( revit.Application.ActiveUIDocument.Document, wt.Id ) )
  74.       {
  75.         wallType = wt;
  76.         break;
  77.       }
  78.     }
  79.  
  80.     foreach( Element el in new FilteredElementCollector(
  81.         revit.Application.ActiveUIDocument.Document ).WhereElementIsNotElementType().ToElements() )
  82.     {
  83.       if( el is FamilyInstance )
  84.       {
  85.         if( ( (FamilyInstance) el ).Symbol.Family.Name.ToUpper().StartsWith( "Тест" ) )
  86.         {
  87.           Options options = revit.Application.Application.Create.NewGeometryOptions();
  88.           options.ComputeReferences = true;
  89.           options.View = revit.Application.ActiveUIDocument.Document.ActiveView;
  90.           GeometryElement geoel = el.get_Geometry( options );
  91.  
  92.           // Попытка создать наклонную стены из геометрии.
  93.           for( int i = 0; i < geoel.Objects.Size; i++ )
  94.           {
  95.             if( geoel.Objects.get_Item( i ) is Solid )
  96.             {
  97.               Solid solid = (Solid) geoel.Objects.get_Item( i );
  98.               for( int j = 0; j < solid.Faces.Size; j++ )
  99.               {
  100.                 try
  101.                 {
  102.                   if( solid.Faces.get_Item( i ).Reference != null )
  103.                   {
  104.                     FaceWall.Create( revit.Application.ActiveUIDocument.Document,
  105.                         wallType.Id, WallLocationLine.CoreCenterline,
  106.                         solid.Faces.get_Item( i ).Reference );
  107.                   }
  108.                 }
  109.                 catch( System.Exception e )
  110.                 {
  111.                   System.Windows.Forms.MessageBox.Show( e.Message );
  112.                 }
  113.               }
  114.             }
  115.           }
  116.         }
  117.       }
  118.     }
  119.   }
  120.  
  121.   public static ModelCurve MakeLine( UIApplication app, XYZ ptA, XYZ ptB, Document doc )
  122.   {
  123.     // создание рабочей поверхности по заданным точкам
  124.     Line line = app.Application.Create.NewLine( ptA, ptB, true );
  125.     XYZ norm = ptA.CrossProduct( ptB );
  126.     if( norm.GetLength() == 0 ) norm = XYZ.BasisZ;
  127.     Plane plane = app.Application.Create.NewPlane( norm, ptB );
  128.     SketchPlane skplane = doc.FamilyCreate.NewSketchPlane( plane );
  129.     // Создаем линию
  130.     ModelCurve modelcurve = doc.FamilyCreate.NewModelCurve( line, skplane );
  131.     return modelcurve;
  132.   }

Ответ: Всегда нужно вызывать метод Document.Regenerate если вам нужно получить доступ к информации из базы данных Revit о созданном элементе внутри той же самой транзакции.

Если этого не сделать, то информация будет устаревшей.

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

Также я немного отредактировал ваш код и сделал его более оптимальным в части фильтрации элементов и работе с циклами.

Вместо использования свойства ActiveView.Level в качестве уровня для формообразующего элемента, следует использовать свойство ActiveView.GenLevel.

Если формообразующему элементу не будет задан уровень, то создать стену на его поверхности будет невозможно.

Фактически же, если вы будете использовать ActiveView.Level, то при вызове метода FaceWall.Create будет выброшено исключение «Невозможно создать стену на поверхности».

Вот исправленный код и адаптированный под Revit 2015:

Код - C#: [Выделить]
  1. const string _conceptual_mass_template_path
  2.     = "C:/ProgramData/Autodesk/RVT 2015"
  3.     + "/Family Templates/English/Conceptual Mass"
  4.     + "/Metric Mass.rft";
  5.  
  6.   const string _family_name = "TestFamily";
  7.  
  8.   const string _family_path = "C:/" + _family_name + ".rfa";
  9.  
  10.   static ModelCurve MakeLine(
  11.     Document doc,
  12.     XYZ p,
  13.     XYZ q )
  14.   {
  15.     // Создаем поверхность по точкам
  16.  
  17.     Line line = Line.CreateBound( p, q );
  18.     XYZ norm = p.CrossProduct( q );
  19.     if( norm.GetLength() == 0 ) { norm = XYZ.BasisZ; }
  20.     Plane plane = new Plane( norm, q );
  21.  
  22.     SketchPlane skplane = SketchPlane.Create(
  23.       doc, plane );
  24.  
  25.     // Создаем линию
  26.  
  27.     return doc.FamilyCreate.NewModelCurve(
  28.       line, skplane );
  29.   }
  30.  
  31.   /// <summary>
  32.   /// Создаем выдавливание в редакторе концептуального формообразующего элемента
  33.   /// </summary>
  34.   static void CreateMassExtrusion(
  35.     Document doc )
  36.   {
  37.     using( Transaction tx = new Transaction( doc ) )
  38.     {
  39.       tx.Start( "Создание формообразующего элемента" );
  40.  
  41.       ReferenceArray refar = new ReferenceArray();
  42.  
  43.       XYZ[] pts = new XYZ[] {
  44.         new XYZ( -10, -10, 0 ),
  45.         new XYZ( +10, -10, 0 ),
  46.         new XYZ( +10, +10, 0 ),
  47.         new XYZ( -10, +10, 0 ) };
  48.  
  49.       int j, n = pts.Length;
  50.  
  51.       for( int i = 0; i < n; ++i )
  52.       {
  53.         j = i + 1;
  54.  
  55.         if( j >= n ) { j = 0; }
  56.  
  57.         // Метод Creator.CreateModelLine создает
  58.         // поверхность, при которой
  59.         // метод NewExtrusionForm падает с ошибкой
  60.         // “Невозможно создать выдавливание"
  61.  
  62.         //ModelCurve c = Creator.CreateModelLine( doc, pts[i], pts[j] );
  63.  
  64.         ModelCurve c = MakeLine( doc, pts[i], pts[j] );
  65.  
  66.         refar.Append( c.GeometryCurve.Reference );
  67.       }
  68.  
  69.       //doc.Regenerate();
  70.  
  71.       // Выдавливание имеет длину и направление.
  72.       // Направление должно быть перпендикулярно поверхности
  73.       // Длина должна быть больше 0
  74.  
  75.       XYZ direction = new XYZ( /*-6*/ 0, 0, 20 );
  76.  
  77.       Form form = doc.FamilyCreate.NewExtrusionForm(.
  78.         true, refar, direction );
  79.  
  80.       tx.Commit();
  81.     }
  82.   }
  83.  
  84.   static void CreateFaceWalls(
  85.     Document doc )
  86.   {
  87.     Application app = doc.Application;
  88.  
  89.     Document massDoc = app.NewFamilyDocument(
  90.       _conceptual_mass_template_path );
  91.  
  92.     CreateMassExtrusion( massDoc );
  93.  
  94.     //if( File.Exists( _family_path ) )
  95.     //  File.Delete( _family_path );
  96.  
  97.     SaveAsOptions opt = new SaveAsOptions();
  98.     opt.OverwriteExistingFile = true;
  99.  
  100.     massDoc.SaveAs( _family_path, opt );
  101.  
  102.     using( Transaction tx = new Transaction( doc ) )
  103.     {
  104.       tx.Start( "Создание наклонной стены" );
  105.  
  106.       if( !doc.LoadFamily( _family_path ) )
  107.         throw new Exception( "Не удалось загрузить семейство." );
  108.  
  109.       Family family = new FilteredElementCollector( doc )
  110.         .OfClass( typeof( Family ) )
  111.         .Where<Element>( x => x.Name.Equals( _family_name ) )
  112.         .Cast<Family>()
  113.         .FirstOrDefault();
  114.  
  115.       FamilySymbol fs = doc.GetElement(
  116.         family.GetFamilySymbolIds().First<ElementId>() )
  117.           as FamilySymbol;
  118.  
  119.       // Создаем экземпляр семейства.
  120.  
  121.       Level level = doc.ActiveView.GenLevel;
  122.  
  123.       FamilyInstance fi = doc.Create.NewFamilyInstance(
  124.         XYZ.Zero, fs, level, StructuralType.NonStructural );
  125.  
  126.       doc.Regenerate(); // нужно для того чтобы получить геометрию!
  127.  
  128.       // Определяем тип стены.
  129.  
  130.       WallType wallType = new FilteredElementCollector( doc )
  131.         .OfClass( typeof( WallType ) )
  132.         .Cast<WallType>()
  133.         .Where<WallType>( x => FaceWall.IsWallTypeValidForFaceWall( doc, x.Id ) )
  134.         .FirstOrDefault();
  135.  
  136.       // Получаем геометрию формообразующего элемента.
  137.  
  138.       Options options = app.Create.NewGeometryOptions();
  139.       options.ComputeReferences = true;
  140.  
  141.       //options.View = doc.ActiveView; // формообразующий элемент не виден на текущем виде
  142.  
  143.       GeometryElement geo = fi.get_Geometry( options );
  144.  
  145.       // Создаем наклонную стену на поверхности по геометрии.
  146.  
  147.       foreach( GeometryObject obj in geo )
  148.       {
  149.         Solid solid = obj as Solid;
  150.  
  151.         if( null != solid )
  152.         {
  153.           foreach( Face f in solid.Faces )
  154.           {
  155.             PlanarFace pf = f as PlanarFace;
  156.  
  157.             if( null != pf )
  158.             {
  159.               XYZ v = pf.Normal;
  160.  
  161.               // Ошибки:
  162.               //
  163.               // Не удалось создать стену на наклонной поверхности.
  164.               //
  165.               // так как использовался ActiveView.Level
  166.               // вместо ActiveView.GenLevel.
  167.               //
  168.               // Поверхность не подходит для создания стены.
  169.               //
  170.               // Так как поверхность горизонтальная.
  171.  
  172.               if( !Util.IsVertical( v ) )
  173.               {
  174.                 FaceWall.Create(
  175.                   doc, wallType.Id,
  176.                   WallLocationLine.CoreCenterline,
  177.                   f.Reference );
  178.               }
  179.             }
  180.           }
  181.         }
  182.       }
  183.       tx.Commit();
  184.     }
  185.   }

Спасибо Адаму за предоставленное решение.

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

Источник: http://thebuildingcoder.typepad.com/blog/2014/10/creating-a-sloped-wall.html

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

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