воскресенье, 20 декабря 2015 г.

[Revit API] - Особенности работы с макросами.

Виктор Чекалин разместил ряд статей на adn-cis.org на тему работы с макросами:

Работа с макросами в Revit - Введение
Работа с макросами в Revit - Первый макрос
Работа с макросами в Revit - Выбор языка программирования
Работа с макросами - Изучение Revit API
Работа с макросами. Отладка
Работа с макросами Пошаговая отладка кода
Работа с макросами. Использование контрольных точек
Работа с макросами Комментирование кода
Работа с макросами. Исключения
Работа с макросами. Пример макроса
Работа с макросами. Дальнейшие действия и заключение

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

Для начала опишу плюсы работы с макросами:

1. Прекрасно подходят для новичков. Не нужно заморачиваться с созданием проекта, писать файл манифеста, можно быстро написать короткий код.

2. Для проверки работы макроса не требуется каждый раз перезагружать Revit. Если единожды запустил плагин, созданный в Visual Studio, то Revit занимает файл сборки dll и её не удаётся перезаписать, пока не перезагрузишь Revit.
С макросами такой проблемы не существует. Собрал решение, запустил из диспетчера макросов.

3. Очень хорошо подходят для тестовых проработок. Если нужно быстро что-то проверить, посмотреть как работает пример кода.

4. Возможно сделать отладку макроса, не перезагружая каждый раз Revit. Как выполнить отладку макроса я уже писал ранее, повторюсь:
Для отладки в макросах служит кнопка "Войти в блок" в диспетчере макросов.Подробнее см. справку                                                                                                                    Команда "Войти в блок" в справке неверно переведена как "ШагВ", что сбило меня с толку.
5. Мне очень нравится, что SharpDevelop не подсвечивает ошибки в коде сразу же после набора. Ошибки подчёркиваются после компиляции решения. В Visual Studio меня очень раздражает, когда происходит подчёркивание незавершённого кода.

Теперь о том, почему я отказался от регулярной работы с макросами и перешёл на Visual Studio:

1. Макрос хранится в файле Revit со всеми вытекающими последствиями. Если будет сбой с вылетом из Revit, то не закрывайте SharpDevelop.

Варианты действий после аварийного завершения Revit:
- скопируйте куда-нибудь код из окна редактора
- сохраните файл с кодом

- найдите решение с макросом во временной папке пользователя. 
Вот как выглядит путь к папке с решением на моём компьютере:
%username%\AppData\Local\Temp\{6586EA70-C4F5-4461-8CEB-FD58406E0247}\Revit\DocHookups9148\890337088\test\Source

После открытия проекта Revit вставьте сохранённый код в редакторе макроса. 

2. Возможны проблемы при совместной работе. Могут занять рабочий набор с макросом (да, да, макрос тоже имеет свой рабочий набор!), не удастся выполнить синхронизацию. Был противоположный случай - я редактировал макрос, занял его рабочий набор, синхронизировался, закрыл файл и другие участники проекта не смогли синхронизироваться.

3. Распространение и запуск макроса для пользователей. Макрос живёт в проекте, а следовательно чтобы подгрузить его в другой проект, то нужно открыть файл с макросом, скопировать его текст в свой файл. Конечно можно сделать отдельный проект Revit с макросами, держать его открытым, макросы из него будут запускаться в другом проекте, но это о-о-о-чень напряжно для пользователей. Гораздо проще запускать плагины кнопкой с панели.

4. Обновление макроса. Допустим, я получил сообщение об ошибке в моём макросе. Мои действия: отладил, поправил. Теперь нужно обновить макрос в файле пользователя. Я не вижу другого пути, кроме как через копирование кода из окна редактора в моём файле и вставку кода в файле пользователя. А если проект не один? Может есть какой-то другой подход, не знаю, но мне проще обновить dll-ки на сервере, чем морочить голову пользователям макросами.

5. Разрастание кода. При тестовых проработках нужно внимательно следить за тем, чтобы маленький тест не разросся в полноценный проект. Появление нового класса или появление более пяти методов является для меня сигналом к переходу на Visual Studio.

6. InteliSense не подхватывает некоторые методы. Например, для объекта Element не отображается метод get_Parameter.

Не стоит совсем отказываться от работы с макросами. 
Это идеальный инструмент для начала работы с Revit API. Когда макрос перерастает в полноценный плагин, то я рекомендую переносить проект в Visual Studio.
Сейчас я применяю макросы в тестовых проработках и при изысканиях. Быстренько набрал небольшой код, проверил, отладил, скопировал методы в проект Visual Studio.
 
Удачи!
 
UPD:
Статья  Вадима Муратова: Revit: макросы и как их копировать://zen.yandex.ru/media/muratovbim/revit-makrosy-i-kak-ih-kopirovat-6020d15586f4e22208fd0c37Revit: макросы и как их копироватьhttps://zen.yandex.ru/media/muratovbim/revit-makrosy-i-kak-ih-kopirovat-6020d15586f4e22208fd0c37

пятница, 11 декабря 2015 г.

[Revit API] - Как получить линии из условного обозначения отверстия (продолжение)

Всё-таки нашёл решение с получением линий из семейства категории "Типовые аннотации". Как и обещал, пишу продолжение.

Google не помог, в руководстве пользователя тоже ничего не смог обнаружить.
На этот раз выручил сайт revitapisearch.com. Этот сайт - последний рубеж в моих изысканиях.
Если что-то совсем не выходит или нужно окончательно убедиться в том, что невозможно сделать через Revit API - захожу туда, нажимаю на кнопку "Members" и забиваю в поиске на страничке слова по смыслу.
Довольно-таки муторное занятие:


И далее F3, F3, F3 по всей простыне.
На 118-м найденном слове наткнулся на метод:


Ага, на этот раз охота оказалась удачной.

Строку из предыдущего сообщения

GeometryElement geometryElement = element.get_Geometry(geomOption);

заменяю на

FamilyInstance fi = (FamilyInstance)element;
GeometryElement geometryElement = fi.GetOriginalGeometry(geomOption);

Код скомпилировался и даже сработал, но в итоге я получил какую-то козявку в базовой точке проекта:


Видимо OriginalGeometry - это описание геометрии внутри семейства.

В объекте типа FamilyInstance есть метод GetTransform()
Смотрю что есть интересного в линии:

Добавляю строчку с трансформацией линии:

if (l != null)
{
    l = (Line)l.CreateTransformed(fi.GetTransform());
    lines.Add(l);
}

В итоге получаю список линий, с изменённым положением и отмасштабированных таким же образом, как и семейства аннотаций:
Удачи! 

четверг, 3 декабря 2015 г.

[Revit API] - Как получить линии из условного обозначения отверстия

На прошлой неделе Хасан Мамаев задал вопрос:
Я пытаюсь получить геометрию нескольких экземпляров одного плоского семейства таким кодом:
i.GetInstanceGeometry()
но выдаёт ошибку: AttributeError: 'FamilyInstance' object has no attribute 'GetInstanceGeometry'
Объекты, из которых нужно вытянуть линии - условное отображение отверстий:

Сегодня появилось время, пытаюсь разобраться.

Первым делом обращаюсь к документации.
Есть пример с извлечением геометрии из стены, но применить один в один не удалось.

После изысканий разработал такой код:

private List<Line> GetLines(Element element)
{

    List<Line> lines = new List<Line>();

    Autodesk.Revit.DB.Options geomOption = element.Document.Application.Create.NewGeometryOptions();

            if (null != geomOption)
            {
                geomOption.View = element.Document.ActiveView;               
            }

    GeometryElement geometryElement = element.get_Geometry(geomOption);

    foreach (GeometryInstance gi in geometryElement)
    {
        foreach (GeometryObject go in gi.GetInstanceGeometry())
        {
            Line l = go as Line;

            if (l != null)
            {
                lines.Add(l);
            }
        }
    }

    return lines;

}

Этот код заработал для элементов типа Элементы узлов
Для Типовых аннотаций не работает. Если найду решение, то сделаю продолжение.

Удачи!

среда, 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... да с чем угодно, всё зависит от конкретных задач, и писать данные в ключевые спецификации

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

Удачи!

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

[Revit] - Разделение грани криволинейной стены

По ходу опытов с окраской стеновых панелей нашёл вопрос по теме на форуме Autodesk

Да, криволинейные грани не делятся инструментом "Разделение грани", но это не повод, чтобы отступать.
Так как в это время искал информацию по поводу разделения деталей стен, то решение родилось быстро:




Добавил отделочный слой стены толщиной 5 мм, создал детали и разделил этот слой на отдельные детали через редактирование эскиза.
Для деталей слоя снял галку "Исходный материал" и задал требуемые материалы.

Если требуется просто нанести картинку, то всё гораздо проще. Можно воспользоваться инструментом
"Разместить деколь".

UPD: отмечу, что практической пользы пока не придумал. Был чисто спортивный интерес - получить разделение криволинейной поверхности стены. 
Развёртку этой поверхности в Revit не построишь, размеры не проставить. 
В той же теме на форуме пользователь kami.ukr предложил такой подход: выполнить развёртку на чертёжном виде с получением всех размеров и площадей, экспортировать её и подгрузить как текстуру для отображения на 3D виде.

четверг, 22 октября 2015 г.

[Revit] - Маленький трюк с размерами

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

Бывает, ничего страшного. Перемещаю стену налево и задаю размер или перемещаю на 1000 мм влево, можно ещё отзеркалить.

Есть ещё вариант: поставить знак "минус" перед значением размера:
Способ универсален, работает и с другими объектами Revit.



вторник, 20 октября 2015 г.

[Revit] - Видимость разделителя помещений

Столкнулся с тем, что разделители помещений куда-то пропадают сразу же после нанесения.
Тэкс. В переопределении видимости/графики они включены. Текущий рабочий набор включён, открыт. Скрыть лампочкой ещё не успел. Сделал новый вид, нанёс, ага, тут видны.
Значит дело в настройках вида. Когда уже отчаялся найти их, обратился к каноническим
33-м причинам. Остановился на пункте 5. Оказалось, что нижняя граница основного секущего диапазона и глубина проецирования были на 100 мм выше отметки связанного уровня, а разделители помещений лежат как раз в связанном уровне.

Да, и по ходу нашёл ещё новую для меня вещь. Всегда думал, что базовая точка проекта и точка съёмки - это какие-то особые объекты, которые всегда спрятаны и увидеть можно только включив лампочку на нижней панельке. Оказалось, что их видимостью можно управлять, сидят они тут: Переопределение видимости/графики -> Генплан.

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

[Revit] - Панели в сборных зданиях

По следам моего предыдущего сообщения собралась информация, которая тянет на отдельный пост.

от Александра Канивца:

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

2. Модифицировать семейство стен из предыдущего поста.
    - сделать семейства панелей общими
    - сделать семейство категории "Стены"
    - вставить в него семейство панели
    - сделать выбор панели через параметр с типом данных "Типоразмер в семействе"

Я сделал так, пришлось немного повозиться с семейством категории "Стены" для удобства использования, но зато не требуется перелопачивать всю базу семейств, стало проще подбирать панели в проекте, а также вставлять архитектурные окна. Окна правда глючат, не ловят изменения стены, иногда их вообще вышибает из стены и новые после этого не ставятся. Инструкцией такой метод не предусмотрен, стабильную работу окон в таких стенах не гарантирую.

от Алексея Лобанова:

панель сделать на основе шаблона "Несущие колонны", тогда границы помещения тоже ловятся. Чтобы учесть зазоры выполнить объём в низкой детализации без учёта зазоров, в средней и высокой - с учётом зазоров:


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

от Александра Попова:

ссылка на видео по системе IDAT, на мой взгляд - идеальное решение:


 от Максима Коцаря:

моделирование аналитической модели для сборных плит при помощи Dynamo:


от Алексея Борисова:

Способ нанесения краски в семействах:




[Revit] - Семейства категории "Каркас несущий" и границы помещений.

На днях мне задали вопрос: можно ли сделать так, чтобы семейства категории "Каркас несущий" (далее Балка.rfa) имели границу помещения?

Первым делом пошёл в гуглить. Тишина. В том числе и на зарубежных форумах. Ну или я не спец по поиску. Нашёл только аналогичный вопрос в группе ВКонтакте.

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

После некоторых изысканий вывел такой алгоритм:

1. Создаём семейство категории "Перекрытия".  Как это обычно делается в Revit - создаётся оно не очевидным путём: 
     - создаём модель в контексте категории "Перекрытия"
     - создаём произвольное выдавливание
     - создаём из него группу
     - не выходя из режима редактирования модели в контексте 
        R -> Сохранить как... -> Библиотека -> Группа 
      
После сохранения получаем загружаемое семейство категории "Перекрытия" (далее Граница помещения - перекрытия.rfa). 

2. Редактируем семейство , задаём в нём опорные плоскости и параметры для управления длиной/шириной/высотой, задаём параметры L, B, H как параметры экземпляра.

UPD: Семейство нужно сделать общим


3. Вставляем семейство Граница помещения - перекрытия.rfa в Балка.rfa, задаём нужные параметры L, B, H. (я связал их с параметрами семейства Балка.rfa - Длина, b, h)



4. Вставляем семейство Балка.rfa в проект и любуемся результатом:

Если семейство Балка.rfa уже было загружено в проект, то границы помещения могут не обновиться сразу же. Для этого достаточно переместить балку в любом направлении и вернуть обратно. Также не забываем включить расчёт объёмов помещений.

Ещё я прокинул параметр "Граница помещения" семейства Граница помещения - перекрытия.rfa в семейство Балка.rfa, чтобы можно было управлять границей помещения для каждого элемента в проекте таким же образом, как это делается для стандартных элементов - таких как стены, перекрытия или крыши.

На следующий день выяснилось, что проектировщики выполнили с помощью семейств на основе "Каркас несущий" железобетонное сборное здание и архитекторы не могут размещать помещения в своём проекте.

Для стен принцип такой же. Только чтобы всё было по фен-шую сделал новое семейство границы помещения на основе "Стены" и вставил его в семейства сборных стеновых панелей.


 В результате получил возможность размещения помещений в сборных зданиях.

По идее можно выполнить семейство произвольной формы для вырезания из объёма помещения.

UPD: здесь можно посмотреть ещё приёмы работы с панелями в сборных зданиях