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

01/10/2015

Создание семейства в памяти

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

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

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

Ответ: Как вы знаете и уже это делаете, можно создавать семейства в памяти и загружать их в проект без сохранения на диске.

Я уже пытался это реализовать в далеком 2011 году, для Revit 2012, и рассказывал об этом статье создание и вставка семейства структурного ребра жесткости (на англ.).

На тот момент, загрузка семейства из памяти не работала как положено.

С тех пор это поведение было исправлено, возможно в 2013 версии.

Для полного ответа на ваш вопрос, я создал репозиторий на GitHub – Stiffener и последовательно мигрировал тот пример на Revit 2014.

Первым делом, я просто запустил тот пример на Revit 2014 точно в таком же виде, как он был реализован для Revit 2012, т.е. сохраняя на диск перед загрузкой:

 

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

Этот способ заработал и работает примерно так как вы описали – название семейства и типоразмера являются автоматически сгенерированными.

Дальше, я задал название семейства. Это часть той задачи, которую вы хотите решить:


Ну и наконец, я мигрировал на Revit 2015 и Revit 2016, и добавил одну строчку кода для изменения названия типоразмера.

 

Вроде бы это то что вам нужно.

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

  • 2012.0.0.0 Первый коммит для версии 2012 из оригинального поста от 13 июня 2011 года.
  •  2014.0.0.0 миграция на Revit 2014, включая обновления кода, версии .NET Framework, удаление устаревших методов.
  • 2014.0.0.1 обновил до возможности вставки экземпляра без сохранения на диск. Пересмотрел логику, добавил выражения using для транзакций.
  • 2014.0.0.2 задал название семейства после загрузки в проект.
  • 2014.0.0.3 задал не копировать сборки Revit
  • 2015.0.0.0 миграция на Revit 2015
  • 2016.0.0.0 миграция на Revit 2016, активация типоразмера, изменение название типоразмера
  • 2016.0.0.1 убрал использование устаревших методов.

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

Конечная реализация выглядит теперь вот так:

Код - C#: [Выделить]
  1. [Transaction( TransactionMode.Manual )]
  2. public class Command : IExternalCommand
  3. {
  4.   #region Constants
  5.   /// <summary>
  6.   /// Расширение файла шаблона семейств
  7.   /// </summary>
  8.   const string _family_template_ext = ".rft";
  9.  
  10.   /// <summary>
  11.   /// Расширение файла семейств
  12.   /// </summary>
  13.   const string _rfa_ext = ".rfa";
  14.  
  15.   /// <summary>
  16.   /// Путь к библиотеке шаблонов семейств
  17.   /// </summary>
  18.   //const string _path = "C:/ProgramData/Autodesk/RST 2012/Family Templates/English";
  19.   const string _path = "C:/Users/All Users/Autodesk/RVT 2014/Family Templates/English";
  20.  
  21.   /// <summary>
  22.   /// Название файла шаблона семейства
  23.   /// </summary>
  24.   const string _family_template_name = "Metric Structural Stiffener";
  25.  
  26.   // Путь к шаблону проекта
  27.  
  28.   //const string _path = "C:/ProgramData/Autodesk/RST 2012/Family Templates/English_I";
  29.   //const string _family_name = "Structural Stiffener";
  30.  
  31.   /// <summary>
  32.   /// Название нового семейства
  33.   /// </summary>
  34.   const string _family_name = "Stiffener";
  35.  
  36.   /// <summary>
  37.   /// коеффициент перевода из миллиметров в футы
  38.   /// </summary>
  39.   const double _mm_to_foot = 1 / 304.8;
  40.   #endregion // Constants
  41.  
  42.   /// <summary>
  43.   /// Конвертация длины в миллиметрах в футы
  44.   /// </summary>
  45.   double MmToFoot( double length_in_mm )
  46.   {
  47.     return _mm_to_foot * length_in_mm;
  48.   }
  49.  
  50.   /// <summary>
  51.   /// Конвертация координат точки, заданной в миллиметрах, в футы
  52.   /// </summary>
  53.   XYZ MmToFootPoint( XYZ p )
  54.   {
  55.     return p.Multiply( _mm_to_foot );
  56.   }
  57.  
  58.   static int n = 4;
  59.  
  60.   /// <summary>
  61.   /// Точки профиля выдавливания в миллиметрах
  62.   /// в данном случае это простой прямоугольник
  63.   /// </summary>
  64.   static List<XYZ> _countour = new List<XYZ>( n )
  65.   {
  66.     new XYZ( 0 , -75 , 0 ),
  67.     new XYZ( 508, -75 , 0 ),
  68.     new XYZ( 508, 75 , 0 ),
  69.     new XYZ( 0, 75 , 0 )
  70.   };
  71.  
  72.   /// <summary>
  73.   /// толщина ребра жесткости в миллиметрах
  74.   /// </summary>
  75.   const double _thicknessMm = 20.0;
  76.  
  77.   /// <summary>
  78.   /// возвращает первый элемент заданного типа
  79.   /// с заданным именем
  80.   /// </summary>
  81.   Element FindElement(
  82.     Document doc,
  83.     Type targetType,
  84.     string targetName )
  85.   {
  86.     return new FilteredElementCollector( doc )
  87.       .OfClass( targetType )
  88.       .First<Element>( e => e.Name.Equals( targetName ) );
  89.  
  90.     // Устаревший код использования
  91.     // LINQ 
  92.  
  93.     //var targetElems
  94.     //  = from element in collector
  95.     //    where element.Name.Equals( targetName )
  96.     //    select element;
  97.  
  98.     //return targetElems.First<El
  99.     //List<Element> elems = targetElems.ToList<Element>();
  100.  
  101.     //if( elems.Count > 0 )
  102.     //{  // we should have only one with the given name.
  103.     //  return elems[0];
  104.     //}
  105.  
  106.     // cannot find it.
  107.     //return null;
  108.  
  109.     /*
  110.     // most efficient way to find a named
  111.     // family symbol: use a parameter filter.
  112.  
  113.     ParameterValueProvider provider
  114.       = new ParameterValueProvider(
  115.         new ElementId( BuiltInParameter.DATUM_TEXT ) ); // VIEW_NAME for a view
  116.  
  117.     FilterStringRuleEvaluator evaluator
  118.       = new FilterStringEquals();
  119.  
  120.     FilterRule rule = new FilterStringRule(
  121.       provider, evaluator, targetName, true );
  122.  
  123.     ElementParameterFilter filter
  124.       = new ElementParameterFilter( rule );
  125.  
  126.     return new FilteredElementCollector( doc )
  127.       .OfClass( targetType )
  128.       .WherePasses( filter )
  129.       .FirstElement();
  130.     */
  131.   }
  132.  
  133.   /// <summary>
  134.   /// Конвертация списка трехмерных точек
  135.   /// в массив кривых
  136.   /// Координаты точек заданы в миллиметрах,
  137.   /// возвращаемый массив кривых - в футах
  138.   /// </summary>
  139.   CurveArray CreateProfile( List<XYZ> pts )
  140.   {
  141.     CurveArray profile = new CurveArray();
  142.  
  143.     int n = _countour.Count;
  144.  
  145.     for( int i = 0; i < n; ++i )
  146.     {
  147.       int j = ( 0 == i ) ? n - 1 : i - 1;
  148.  
  149.       profile.Append( Line.CreateBound(
  150.         MmToFootPoint( pts[j] ),
  151.         MmToFootPoint( pts[i] ) ) );
  152.     }
  153.     return profile;
  154.   }
  155.  
  156.   /// <summary>
  157.   /// Создаем выдавливание заданной толщины
  158.   /// и списка точек, заданных в миллиметрах
  159.   /// в заданном редакторе семейств, в котором
  160.   /// должна быть рабочаяя плоскость "Ref. Level".
  161.   /// </summary>
  162.   Extrusion CreateExtrusion(
  163.     Document doc,
  164.     List<XYZ> pts,
  165.     double thickness )
  166.   {
  167.     Autodesk.Revit.Creation.FamilyItemFactory factory
  168.       = doc.FamilyCreate;
  169.  
  170.     SketchPlane sketch = FindElement( doc,
  171.       typeof( SketchPlane ), "Ref. Level" )
  172.         as SketchPlane;
  173.  
  174.     CurveArrArray curveArrArray = new CurveArrArray();
  175.  
  176.     curveArrArray.Append( CreateProfile( pts ) );
  177.  
  178.     double extrusionHeight = MmToFoot( thickness );
  179.  
  180.     return factory.NewExtrusion( true,
  181.       curveArrArray, sketch, extrusionHeight );
  182.   }
  183.  
  184.   public Result Execute(
  185.     ExternalCommandData commandData,
  186.     ref string message,
  187.     ElementSet elements )
  188.   {
  189.     UIApplication uiapp = commandData.Application;
  190.     UIDocument uidoc = uiapp.ActiveUIDocument;
  191.     Application app = uiapp.Application;
  192.     Document doc = uidoc.Document;
  193.     Document fdoc = null;
  194.     Transaction t = null;
  195.  
  196.     if( null == doc )
  197.     {
  198.       message = "Запустите команду в открытом проекте";
  199.       return Result.Failed;
  200.     }
  201.  
  202.     #region Создание нового структурного ребра жесткости
  203.  
  204.     // Проверяем было ли уже семейство создано или
  205.     // загружено ранее
  206.  
  207.     Family family
  208.       = new FilteredElementCollector( doc )
  209.         .OfClass( typeof( Family ) )
  210.         .Cast<Family>()
  211.         .FirstOrDefault<Family>( e
  212.           => e.Name.Equals( _family_name ) );
  213.  
  214.     if( null != family )
  215.     {
  216.       fdoc = family.Document;
  217.     }
  218.     else
  219.     {
  220.       string templateFileName = Path.Combine( _path,
  221.         _family_template_name + _family_template_ext );
  222.  
  223.       fdoc = app.NewFamilyDocument(
  224.         templateFileName );
  225.  
  226.       if( null == fdoc )
  227.       {
  228.         message = "Не могу создать редактор семейств";
  229.         return Result.Failed;
  230.       }
  231.  
  232.       using( t = new Transaction( fdoc ) )
  233.       {
  234.         t.Start( "Создание ребра жесткости" );
  235.  
  236.         CreateExtrusion( fdoc, _countour, _thicknessMm );
  237.  
  238.         t.Commit();
  239.       }
  240.  
  241.       //fdoc.Title = _family_name; // Это свойство только для чтения
  242.  
  243.       bool needToSaveBeforeLoad = false;
  244.  
  245.       if( needToSaveBeforeLoad )
  246.       {
  247.         // Сохраняем наше семейство в файл
  248.         // и открываем его в заново
  249.  
  250.         string filename = Path.Combine(
  251.           Path.GetTempPath(), _family_name + _rfa_ext );
  252.  
  253.         SaveAsOptions opt = new SaveAsOptions();
  254.         opt.OverwriteExistingFile = true;
  255.  
  256.         fdoc.SaveAs( filename, opt );
  257.  
  258.         bool closeAndOpen = true;
  259.  
  260.         if( closeAndOpen )
  261.         {
  262.           // Нельзя закрыть новый документ, если он является единственным
  263.           // Иначе будет исключение "The active document may
  264.           // not be closed from the API."
  265.  
  266.           fdoc.Close( false );
  267.           
  268.         }
  269.       }
  270.     }
  271.     #endregion // Создание нового структурного ребра жесткости
  272.  
  273.     #region Загрузка структурного ребра жесткости
  274.  
  275.     // Нужно загружать при отсутствующих транзакциях; иначе Revit
  276.     // выбросит исключение InvalidOperationException: The document
  277.     // must not be modifiable before calling LoadFamily.
  278.     // Any open transaction must be closed prior the call.
  279.  
  280.     // Вызывая этот метод без сохранения семейтва в файл
  281.     // в Revit 2012 падает с ошибкой:
  282.  
  283.     family = fdoc.LoadFamily( doc );
  284.  
  285.     // Костыль для Revit 2012,
  286.     // больше не нужен в Revit 2014:
  287.  
  288.     //doc.LoadFamily( filename, out family );
  289.  
  290.     // Менять название нужно конечно же
  291.     // в открытой транзакции
  292.  
  293.     //family.Name = _family_name;
  294.  
  295.     FamilySymbol symbol = null;
  296.  
  297.     foreach( ElementId id
  298.       in family.GetFamilySymbolIds() )
  299.     {
  300.       // В нашем семейсте только один типоразмер
  301.       // поэтому его и возьмем
  302.  
  303.       symbol = doc.GetElement( id ) as FamilySymbol;
  304.  
  305.       break;
  306.     }
  307.     #endregion // Загрузка структурного ребра жесткости
  308.  
  309.     #region Вставка структурного ребра жесткости
  310.  
  311.     using( t = new Transaction( doc ) )
  312.     {
  313.       t.Start( "Вставка структурного ребра жесткости" );
  314.  
  315.       // Меняем название
  316.  
  317.       family.Name = _family_name;
  318.       symbol.Name = _family_name;
  319.  
  320.       // В Revit 2016 нужно сделать типоразмер активным
  321.  
  322.       symbol.Activate();
  323.  
  324.       bool useSimpleInsertionPoint = true;
  325.  
  326.       if( useSimpleInsertionPoint )
  327.       {
  328.         XYZ p = uidoc.Selection.PickPoint(
  329.           "выберите точку для вставки" );
  330.  
  331.         StructuralType st = StructuralType.UnknownFraming;
  332.  
  333.         doc.Create.NewFamilyInstance( p, symbol, st );
  334.       }
  335.  
  336.       bool useFaceReference = false;
  337.  
  338.       if( useFaceReference )
  339.       {
  340.         Reference r = uidoc.Selection.PickObject(
  341.           ObjectType.Face,
  342.           "Выберите грань для вставки семейства" );
  343.  
  344.         Element e = doc.GetElement( r.ElementId );
  345.         GeometryObject obj = e.GetGeometryObjectFromReference( r );
  346.         PlanarFace face = obj as PlanarFace;
  347.  
  348.         if( null == face )
  349.         {
  350.           message = "Выберите плоскую грань для вставки семейства";
  351.           t.RollBack();
  352.           return Result.Failed;
  353.         }
  354.         else
  355.         {
  356.           XYZ p = r.GlobalPoint;
  357.           //XYZ v = face.Normal.CrossProduct( XYZ.BasisZ ); // 2015
  358.           XYZ v = face.FaceNormal.CrossProduct( XYZ.BasisZ ); // 2016
  359.           if( v.IsZeroLength() )
  360.           {
  361.             v = face.FaceNormal.CrossProduct( XYZ.BasisX );
  362.           }
  363.           doc.Create.NewFamilyInstance( r, p, v, symbol );
  364.  
  365.           // Данный код выбросит исключение
  366.           //doc.Create.NewFamilyInstance( face, p, v, symbol );
  367.         }
  368.       }
  369.       t.Commit();
  370.     }
  371.     #endregion // Вставка структурного ребра жесткости
  372.  
  373.     return Result.Succeeded;
  374.   }
  375. }

Источник: http://thebuildingcoder.typepad.com/blog/2015/09/svg-in-memory-family-creation-and-revitlookup-bounding-box-1.html#5

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

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

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