среда, 25 ноября 2015 г.

[Revit API] - Граница подрезки вида

В примере справки показано создание области подрезки вида по помещению.

Опишу принцип работы на примере построения прямоугольной границы подрезки вида:


        public void SquareViewCropRegion()
        {
            _currentView = ActiveGraphicalView;
            
            try
            {
                using (var tr = new Transaction(Document))
                {
                    tr.Start("Изменение границы подрезки вида");
                    
                    ActivateCropBox();
                    if (!CanCropView())
                        return;
                    
                    ChangeViewCropRegion();
                    
                    tr.Commit();
                }
            }
            catch (Exception ex)
            {
                TaskDialog.Show("ChangeViewCropRegion",ex.ToString());
            }
        }
 
        private void ChangeViewCropRegion()
        {
            var ptMin = new UV(-50,-50);
            var ptMax = new UV(50,50);
            
            // Получение объекта для управления границами подрезки
            var manager = _currentView.GetCropRegionShapeManager();

            // Создание списка точек для построения контура
            var points = new List<XYZ>
            {
                ConvertUV2XYZ(ptMin),
                ConvertUV2XYZ(new UV(ptMax.U,ptMin.V)),
                ConvertUV2XYZ(ptMax),
                ConvertUV2XYZ(new UV(ptMin.U,ptMax.V)),
                ConvertUV2XYZ(ptMin)
            };

            // Создание объекта цепочки кривых
            var loop = new CurveLoop();

            // Добавление отрезков в цепочку кривых
            for (var i = 1; i < points.Count; i++)
                loop.Append(Line.CreateBound(points[i - 1], points[i]));
            
            // Назначение границ подрезки
            manager.SetCropShape(loop);
        }
 
        private void ActivateCropBox()
        {
            if (!_currentView.CropBoxActive)
                _currentView.CropBoxActive = true;

            if (!_currentView.CropBoxVisible)
                _currentView.CropBoxVisible = true;
        } 
 
        // Проверка на возможность произвольной подрезки вида
        private bool CanCropView()
        {
            if (!_currentView.GetCropRegionShapeManager().CanHaveShape)
            {
                TaskDialog.Show("ChangeViewCropRegion""Текущий вид не предназначен для произвольной подрезки");
                return false;
            }
            
            return true;
        }


 Небольшой метод для перевода координат из локальной системы в систему модели:

        private XYZ ConvertUV2XYZ(UV pt)
        {
            return _currentView.Origin + _currentView.UpDirection*pt.V + _currentView.RightDirection*pt.U;
        }

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

Удачи!

среда, 18 ноября 2015 г.

[Revit API] - Изменение имени типа семейства

Получаем тип элемента, задаём имя:
            //Получение Id типа семейства
            ElementId typeId = element.GetTypeId();            
            //Получение типа семейства
            Element type = doc.GetElement(typeId);
            // Назначение имени типа
            type.Name = "NewTypeName";

Если работаем с экземпляром семейства, а не с элементом, то имя типа поменять проще:
            //Получение типа семейства из его экземпляра
            FamilySymbol familySymbol = familyInstance.Symbol;
            // Назначение имени типа
            familySymbol.Name = "NewTypeName";

Можно сделать без вспомогательной переменной, в одну строку:

            familyInstance.Symbol.Name = "NewTypeName";

среда, 4 ноября 2015 г.

[Revit API] - Отладка в макросах

Для отладки в макросах служит кнопка "Войти в блок" в диспетчере макросов.
Подробнее см. справку 
Команда "Войти в блок" в справке неверно переведена как "ШагВ", что сбивало меня с толку.

воскресенье, 1 ноября 2015 г.

[Revit API] - Запись данных в ключевую спецификацию

Не так давно я писал о способе создания пользовательской таблицы.
Задумался о том, как же теперь наполнить её через API.
Оказалось что здесь всё прозрачно и методы работы описаны в документации:

//Code Region: Inserting a row
public void CreateSubtitle(ViewSchedule schedule)
{
    TableData colTableData = schedule.GetTableData();

    TableSectionData tsd = colTableData.GetSectionData(SectionType.Header);
    tsd.InsertRow(tsd.FirstRowNumber + 1);
    tsd.SetCellText(tsd.FirstRowNumber + 1, tsd.FirstColumnNumber, "Schedule of column top and base levels with offsets");
}

Получаю объект TableSectionData, далее создаются строчки, столбцы, пишутся/читаются данные
из ячеек, задаю форматирование.
Можно сделать таблицу с расширенными возможностями форматирования, записывать туда данные. То что возможности есть - хорошо, при необходимости применю, на некоторое время даже забыл
про эту тему.

До сегодняшнего дня был ещё один интересный нерешённый вопрос: как заполнять программным способом ключевую спецификацию? В первый раз он возник месяцев восемь назад,
как ни бился над ним - не нашёл подхода.

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

Обратите внимание на строчку:
TableSectionData tsd = colTableData.GetSectionData(SectionType.Header);

Тэкс, HeaderHeader. Если есть Header, может есть и сама таблица?

Набираю SectionType и Iintellisense предложил первой строчкой интересный вариант: 


Пишу код как в справке, заменил Header на Body.
Строки данных добавляются/удаляются методами InsertRow и RemoveRow. Уже хорошо.
А вот с записью данных возникла проблема. На методе  SetCellText возникает ошибка, записать текст не удаётся таким же образом как это делается в заголовке таблицы.

Всё, приехали? Как бы не так!

Выручил Revit Lookup: 
выбрал спецификацию, покопался в данных, но не нашёл ничего, что могло бы помочь в записи данных.


Делал несколько подходов, в последний из них забыл выбрать спецификацию в диспетчере проектов и получил совершенно другую картинку:


Что случилось? А случилось вот что: при снятом выделении вида в диспетчере проектов в Revit Lookup попадают все элементы текущего вида. То есть каждая строчка ключевой спецификации - это объект класса Element. Его нет в модели и у него нет категории. 
Зато есть параметры, соответствующие полям спецификации.

Через FilteredElementCollector получаем элементы ключевой спецификации, перебираем их и задаём нужные значения.

Пример кода:

    private void WriteToKeySchedule(View view)
    {
   // Получение элементов вида
   FilteredElementCollector collector = new FilteredElementCollector(view.Document, view.Id);
  
        // Для примера взят параметр "Комментарии", который есть у большинства элементов
        // перед запуском примера добавьте его в ключевую спецификацию
            string s = "test";
            foreach (Element element in collector)
            {
                element.LookupParameter("Комментарии").Set(s);
            }        
    }

Поля спецификации заполнены:

Далее дело техники - прикрутить модуль связи с Exel/CSV/XML... да с чем угодно, всё зависит от конкретных задач, и писать данные в ключевые спецификации

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

Удачи!