Создание семейства в памяти
Вопрос: Когда создается новое семейство, то ему автоматически присваивается имя, которое можно увидеть в заголовке. Возможно ли изменить это имя и заголовок без сохранения файла? Я не хочу сохранять фал. Я просто хочу загрузить его сразу в другую модель.
Мне нужно создать тысячу мелких семейств, и я не хочу при это сохранять на жесткий диск.
В теории, жесткий диск в 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 для определения различий между коммитами.
Конечная реализация выглядит теперь вот так:
- [Transaction( TransactionMode.Manual )]
- public class Command : IExternalCommand
- {
- #region Constants
- /// <summary>
- /// Расширение файла шаблона семейств
- /// </summary>
- const string _family_template_ext = ".rft";
- /// <summary>
- /// Расширение файла семейств
- /// </summary>
- const string _rfa_ext = ".rfa";
- /// <summary>
- /// Путь к библиотеке шаблонов семейств
- /// </summary>
- //const string _path = "C:/ProgramData/Autodesk/RST 2012/Family Templates/English";
- const string _path = "C:/Users/All Users/Autodesk/RVT 2014/Family Templates/English";
- /// <summary>
- /// Название файла шаблона семейства
- /// </summary>
- const string _family_template_name = "Metric Structural Stiffener";
- // Путь к шаблону проекта
- //const string _path = "C:/ProgramData/Autodesk/RST 2012/Family Templates/English_I";
- //const string _family_name = "Structural Stiffener";
- /// <summary>
- /// Название нового семейства
- /// </summary>
- const string _family_name = "Stiffener";
- /// <summary>
- /// коеффициент перевода из миллиметров в футы
- /// </summary>
- const double _mm_to_foot = 1 / 304.8;
- #endregion // Constants
- /// <summary>
- /// Конвертация длины в миллиметрах в футы
- /// </summary>
- double MmToFoot( double length_in_mm )
- {
- return _mm_to_foot * length_in_mm;
- }
- /// <summary>
- /// Конвертация координат точки, заданной в миллиметрах, в футы
- /// </summary>
- XYZ MmToFootPoint( XYZ p )
- {
- return p.Multiply( _mm_to_foot );
- }
- static int n = 4;
- /// <summary>
- /// Точки профиля выдавливания в миллиметрах
- /// в данном случае это простой прямоугольник
- /// </summary>
- static List<XYZ> _countour = new List<XYZ>( n )
- {
- new XYZ( 0 , -75 , 0 ),
- new XYZ( 508, -75 , 0 ),
- new XYZ( 508, 75 , 0 ),
- new XYZ( 0, 75 , 0 )
- };
- /// <summary>
- /// толщина ребра жесткости в миллиметрах
- /// </summary>
- const double _thicknessMm = 20.0;
- /// <summary>
- /// возвращает первый элемент заданного типа
- /// с заданным именем
- /// </summary>
- Element FindElement(
- Document doc,
- Type targetType,
- string targetName )
- {
- return new FilteredElementCollector( doc )
- .OfClass( targetType )
- .First<Element>( e => e.Name.Equals( targetName ) );
- // Устаревший код использования
- // LINQ
- //var targetElems
- // = from element in collector
- // where element.Name.Equals( targetName )
- // select element;
- //return targetElems.First<El
- //List<Element> elems = targetElems.ToList<Element>();
- //if( elems.Count > 0 )
- //{ // we should have only one with the given name.
- // return elems[0];
- //}
- // cannot find it.
- //return null;
- /*
- // most efficient way to find a named
- // family symbol: use a parameter filter.
- ParameterValueProvider provider
- = new ParameterValueProvider(
- new ElementId( BuiltInParameter.DATUM_TEXT ) ); // VIEW_NAME for a view
- FilterStringRuleEvaluator evaluator
- = new FilterStringEquals();
- FilterRule rule = new FilterStringRule(
- provider, evaluator, targetName, true );
- ElementParameterFilter filter
- = new ElementParameterFilter( rule );
- return new FilteredElementCollector( doc )
- .OfClass( targetType )
- .WherePasses( filter )
- .FirstElement();
- */
- }
- /// <summary>
- /// Конвертация списка трехмерных точек
- /// в массив кривых
- /// Координаты точек заданы в миллиметрах,
- /// возвращаемый массив кривых - в футах
- /// </summary>
- CurveArray CreateProfile( List<XYZ> pts )
- {
- CurveArray profile = new CurveArray();
- int n = _countour.Count;
- for( int i = 0; i < n; ++i )
- {
- int j = ( 0 == i ) ? n - 1 : i - 1;
- profile.Append( Line.CreateBound(
- MmToFootPoint( pts[j] ),
- MmToFootPoint( pts[i] ) ) );
- }
- return profile;
- }
- /// <summary>
- /// Создаем выдавливание заданной толщины
- /// и списка точек, заданных в миллиметрах
- /// в заданном редакторе семейств, в котором
- /// должна быть рабочаяя плоскость "Ref. Level".
- /// </summary>
- Extrusion CreateExtrusion(
- Document doc,
- List<XYZ> pts,
- double thickness )
- {
- Autodesk.Revit.Creation.FamilyItemFactory factory
- = doc.FamilyCreate;
- SketchPlane sketch = FindElement( doc,
- typeof( SketchPlane ), "Ref. Level" )
- as SketchPlane;
- CurveArrArray curveArrArray = new CurveArrArray();
- curveArrArray.Append( CreateProfile( pts ) );
- double extrusionHeight = MmToFoot( thickness );
- return factory.NewExtrusion( true,
- curveArrArray, sketch, extrusionHeight );
- }
- public Result Execute(
- ExternalCommandData commandData,
- ref string message,
- ElementSet elements )
- {
- UIApplication uiapp = commandData.Application;
- UIDocument uidoc = uiapp.ActiveUIDocument;
- Application app = uiapp.Application;
- Document doc = uidoc.Document;
- Document fdoc = null;
- Transaction t = null;
- if( null == doc )
- {
- message = "Запустите команду в открытом проекте";
- return Result.Failed;
- }
- #region Создание нового структурного ребра жесткости
- // Проверяем было ли уже семейство создано или
- // загружено ранее
- Family family
- = new FilteredElementCollector( doc )
- .OfClass( typeof( Family ) )
- .Cast<Family>()
- .FirstOrDefault<Family>( e
- => e.Name.Equals( _family_name ) );
- if( null != family )
- {
- fdoc = family.Document;
- }
- else
- {
- string templateFileName = Path.Combine( _path,
- _family_template_name + _family_template_ext );
- fdoc = app.NewFamilyDocument(
- templateFileName );
- if( null == fdoc )
- {
- message = "Не могу создать редактор семейств";
- return Result.Failed;
- }
- using( t = new Transaction( fdoc ) )
- {
- t.Start( "Создание ребра жесткости" );
- CreateExtrusion( fdoc, _countour, _thicknessMm );
- t.Commit();
- }
- //fdoc.Title = _family_name; // Это свойство только для чтения
- bool needToSaveBeforeLoad = false;
- if( needToSaveBeforeLoad )
- {
- // Сохраняем наше семейство в файл
- // и открываем его в заново
- string filename = Path.Combine(
- Path.GetTempPath(), _family_name + _rfa_ext );
- SaveAsOptions opt = new SaveAsOptions();
- opt.OverwriteExistingFile = true;
- fdoc.SaveAs( filename, opt );
- bool closeAndOpen = true;
- if( closeAndOpen )
- {
- // Нельзя закрыть новый документ, если он является единственным
- // Иначе будет исключение "The active document may
- // not be closed from the API."
- fdoc.Close( false );
- }
- }
- }
- #endregion // Создание нового структурного ребра жесткости
- #region Загрузка структурного ребра жесткости
- // Нужно загружать при отсутствующих транзакциях; иначе Revit
- // выбросит исключение InvalidOperationException: The document
- // must not be modifiable before calling LoadFamily.
- // Any open transaction must be closed prior the call.
- // Вызывая этот метод без сохранения семейтва в файл
- // в Revit 2012 падает с ошибкой:
- family = fdoc.LoadFamily( doc );
- // Костыль для Revit 2012,
- // больше не нужен в Revit 2014:
- //doc.LoadFamily( filename, out family );
- // Менять название нужно конечно же
- // в открытой транзакции
- //family.Name = _family_name;
- FamilySymbol symbol = null;
- foreach( ElementId id
- in family.GetFamilySymbolIds() )
- {
- // В нашем семейсте только один типоразмер
- // поэтому его и возьмем
- symbol = doc.GetElement( id ) as FamilySymbol;
- break;
- }
- #endregion // Загрузка структурного ребра жесткости
- #region Вставка структурного ребра жесткости
- using( t = new Transaction( doc ) )
- {
- t.Start( "Вставка структурного ребра жесткости" );
- // Меняем название
- family.Name = _family_name;
- symbol.Name = _family_name;
- // В Revit 2016 нужно сделать типоразмер активным
- symbol.Activate();
- bool useSimpleInsertionPoint = true;
- if( useSimpleInsertionPoint )
- {
- XYZ p = uidoc.Selection.PickPoint(
- "выберите точку для вставки" );
- StructuralType st = StructuralType.UnknownFraming;
- doc.Create.NewFamilyInstance( p, symbol, st );
- }
- bool useFaceReference = false;
- if( useFaceReference )
- {
- Reference r = uidoc.Selection.PickObject(
- ObjectType.Face,
- "Выберите грань для вставки семейства" );
- Element e = doc.GetElement( r.ElementId );
- GeometryObject obj = e.GetGeometryObjectFromReference( r );
- PlanarFace face = obj as PlanarFace;
- if( null == face )
- {
- message = "Выберите плоскую грань для вставки семейства";
- t.RollBack();
- return Result.Failed;
- }
- else
- {
- XYZ p = r.GlobalPoint;
- //XYZ v = face.Normal.CrossProduct( XYZ.BasisZ ); // 2015
- XYZ v = face.FaceNormal.CrossProduct( XYZ.BasisZ ); // 2016
- if( v.IsZeroLength() )
- {
- v = face.FaceNormal.CrossProduct( XYZ.BasisX );
- }
- doc.Create.NewFamilyInstance( r, p, v, symbol );
- // Данный код выбросит исключение
- //doc.Create.NewFamilyInstance( face, p, v, symbol );
- }
- }
- t.Commit();
- }
- #endregion // Вставка структурного ребра жесткости
- return Result.Succeeded;
- }
- }
Обсуждение: http://adn-cis.org/forum/index.php?topic=3070
Опубликовано 01.10.2015