API BIM-системы Renga

    Всем привет! В этой статье я расскажу об API BIM-системы Renga. О самой системе можно почитать тут, здесь же можно запросить версию для некоммерческого использования. Если вкратце, то Renga это трехмерная система автоматизированного проектирования в архитектуре и строительстве. В ней проектировщик/архитектор/конструктор работает с информационной моделью здания, получает ассоциативные чертежи, спецификации, в общем, создает проект.



    Зачем нужно API CAD-системы


    Сначала, как водится, немного водички.

    Разработка расширений для CAD систем довольно распространена, поскольку в любом проектировании существуют различные направления, разделы и стандарты оформления проектной документации, которые требуют разной узкоспециализированной функциональности. Кроме того существуют задачи интеграции с программами расчета, визуализации, документооборота и многими другими. Выход — создание подключаемых модулей, расширяющих функциональность системы.

    Пример простого расширения


    Разберем основные шаги для создания расширения. Вам понадобится VisualStudio и уверенность, что вы знаете C++ (сейчас у Renga C++ API, но в перспективе у нас переход на COM, будем поддерживать .Net расширения).

    Итак, по шагам:

    1. Скачиваем SDK отсюда, распаковываем в удобное место.
    2. Создаем простую dll с настройками по умолчанию, назовем MyPlugin.
    3. В дополнительных папках включений и дополнительных папках lib добавляем путь к RengaSDK, во включаемые библиотеки добавляем RengaApi.lib и RengaBase.lib.
    4. Теперь напишем минимальную реализацию. По сути C++ расширение к Renga — обычная dll библиотека с экспортной функцией, которая возвращает указатель на интерфейс IPlugin. Поэтому все что нам нужно сделать:

      а)Создать класс, унаследованный от этого интерфейса. В классе реализуем оба метода интерфейса start и stop, эти методы будут вызваны после загрузки и перед выгрузкой соответственно. Реализацию пока оставим пустой.

      b) Реализовать экспортную функцию, возвращающую указатель на интерфейс IPlugin. Для этого можно воспользоваться макросом EXPORT_PLUGIN.
    5. Создадим файл описания расширения с именем MyPlugin.rnedesc с таким текстом:

      <RengaPlugin>
          <Name>MyPlugin</Name>
          <Version>1.0</Version>
          <Vendor>Vendor name</Vendor>
          <Copyright>Copyright text</Copyright>
          <RequiredAPIVersion>1.2</RequiredAPIVersion>
          <PluginFilename>MyPlugin.dll</PluginFilename>
      </RengaPlugin>
    6. Для того, чтобы расширение появилось в Renga в папке установки Renga в папке Plugins, создаем папку MyPlugin, в которую кладем нашу dll и файл описания.

    Если все сделано правильно, то при запуске Renga в диалоге настроек в разделе “Расширения” появится наше расширение, которое ничего не делает.

    Давайте заставим его вычислять количество кирпича, необходимого для возведения стен в здании. Норму расхода возьмем такую — 400 штук на кубический метр с учетом швов.

    1. Для начала создадим кнопку в основной панели приложения. Сделаем это прямо в методе initialize

    bool MyPlugin::initialize(const wchar_t * pluginPath)
    {
      auto countBricksAction = rengaapi::UIControls::createAction();
      countBricksAction.setToolTip(rengabase::String(L"Рассчитать количество кирпичей в стенах"));
    
      auto primaryPanelExtension = rengaapi::UIControls::createToolPanelExtension();
      primaryPanelExtension.addToolButton(countBricksAction);
      
      return true;
    }

    2. Дальше реализуем обработчик нажатия кнопки, создадим класс CountBricksHandler, наследника от интерфейса rengaapi::IInvokable. Создадим его в классе MyPlugin и зададим как обработчика кнопки.

    
    //CountBricksHandler.h
    #pragma once
    #include <RengaAPI/IInvokable.h>
    
    class CountBricksHandler : public rengaapi::IInvokable
    {
    public:
      void invoke();
    };
    
    //CountBricksHandler.cpp
    #include "stdafx.h"
    #include "CountBricksHandler.h"
    
    void CountBricksHandler::invoke()
    {}
    

    3. Объявим поле m_countBricksHandler в приватной секции класса MyPlugin, зададим в качестве обработчика для countBricksAction.

    
    auto countBricksAction = rengaapi::UIControls::createAction();
      countBricksAction.setToolTip(rengabase::String(L"Count bricks in walls"));
      countBricksAction.setTriggerHandler(&m_countBricksHandler);
    

    4. Теперь осталось реализовать расчет количества кирпичей в стенах. Создадим простой класс — калькулятор, который будет считать объем материала “Кирпич”, используемого в стенах, и выдавать количество в зависимости от переданной нормы расхода. Вызовем расчет в методе Invoke обработчика кнопки.

    
    //BricksCounter.h
    
    #pragma once
    
    namespace rengaapi
    {
      class Wall;
    }
    
    class BricksCounter
    {
    public:
      BricksCounter(int consumptionRatePerM3);
      int calculateBricks();
    
    private:
      double calculateBricksVolume();
      double calculateBricksVolumeInSingleWall(rengaapi::Wall* pWall);
      int calculateBricksCountInVolume(double bricksVolume);
    
    private:
      int m_consumptionRatePerM3;
    };
    

    
    //BricksCounter.cpp
    #include "stdafx.h"
    #include "BricksCounter.h"
    
    #include <RengaAPI/Project.h>
    #include <RengaAPI/ModelObjectTypes.h>
    #include <RengaAPI/Wall.h>
    #include <RengaAPI/Materials.h>
    
    const wchar_t* c_briksMaterialName = L"Кирпич";
    
    BricksCounter::BricksCounter(int consumptionRatePerM3)
      : m_consumptionRatePerM3(consumptionRatePerM3)
    {
    }
    
    int BricksCounter::calculateBricks()
    {
      double bricksVolume = calculateBricksVolume();
      int bricksNumber = calculateBricksInVolume(bricksVolume);
      return bricksNumber;
    }
    
    double BricksCounter::calculateBricksVolume()
    {
      double result = 0.0;
    
      assert(rengaapi::Project::hasProject());
      auto allObjects = rengaapi::Project::model().objects();
      for (auto objectIt = allObjects.begin(); objectIt != allObjects.end(); ++objectIt)
      {
        if ((*objectIt)->type() == rengaapi::ModelObjectTypes::WallType)
        {
          rengaapi::Wall* pWall = dynamic_cast<rengaapi::Wall*>(*objectIt);
          assert(pWall != nullptr);
          result += calculateBricksVolumeInSingleWall(pWall);
        }
      }
    
      return result;
    }
    
    double BricksCounter::calculateBricksVolumeInSingleWall(rengaapi::Wall * pWall)
    {
      auto materialId = pWall->material();
      rengaapi::LayeredMaterial wallMaterial;
      rengaapi::Materials::layeredMaterial(materialId, wallMaterial);
      auto materialLayers = wallMaterial.layers();
    
      auto layerQuantityCollection = pWall->quantities().materialQuantities();
      
      double bricksVolume = 0.0;
      for (size_t i = 0; i < materialLayers.size(); ++i)
      {
        if (materialLayers.get(i).material().name_() == rengabase::String(c_briksMaterialName))
        {
          auto oVolumeMeasure = layerQuantityCollection.get(i).netVolume();
          if (oVolumeMeasure.hasValue())
          {
            bricksVolume += oVolumeMeasure.getValue()->inMeters3();
          }
        }
      }
      return bricksVolume;
    }
    
    int BricksCounter::calculateBricksInVolume(double bricksVolume)
    {
      return static_cast<int>(bricksVolume * m_consumptionRatePerM3);
    }
    

    
    // CountBricksHandler.cpp
    
    #include "stdafx.h"
    #include "CountBricksHandler.h"
    #include "BricksCounter.h"
    
    #include <RengaAPI/Project.h>
    #include <RengaAPI/Message.h>
    
    const int c_bricksConsumptionRatePerM3 = 400;
    
    void CountBricksHandler::invoke()
    {
      if (!rengaapi::Project::hasProject())
      {
        rengaapi::Message::showMessageBox(rengaapi::Message::Info,
          rengabase::String(L"Сообщение MyPlugin"),
          rengabase::String(L"Проект отсутствует. Невозможно подсчитать поличество кирпича."));
      }
      else
      {
        BricksCounter counter(c_bricksConsumptionRatePerM3);
        int bricksCount = counter.calculateBricks();
        
        std::wstring message = std::wstring(L"Количество кирпича в стенах, шт: ") + std::to_wstring(bricksCount);
        rengaapi::Message::showMessageBox(rengaapi::Message::Info,
          rengabase::String(L"Сообщение MyPlugin"),
          rengabase::String(message.data()));
      }
    }
    

    Полный код расширения находится тут



    Еще про расширения Renga


    В принципе все для начала работы с API Renga есть в SDK (http://rengabim.com/sdk/), там же есть и примеры на все основные возможности.

    Также в качестве примеров мы разработали расширение для просмотра модели в виде иерархической структуры (дерева) и расширение для фильтрации объектов модели по различным критериям. Эти расширения доступны как в собранном виде, так и виде исходного кода, можно использовать как пример или дорабатывать по вкусу. Кроме того, сторонними разработчиками созданы расширения для 3D рендеринга и для экспорта в 3D PDF. Эти и новые расширения ищите здесь.

    Что позволяет API


    Сейчас API Renga позволяет:

    • Экспортировать 3D представление объектов в виде полигональной сетки (mesh-представление), разделенное на группы, чтобы можно было, к примеру, отличить фасад стены от торца.
    • Добавлять элементы управления в пользовательский интерфейс Renga (ну и реагировать на них, само собой).
    • Получать параметры и расчетные характеристики трехмерных объектов модели (объемы, площади, размеры и т.п.).
    • Создавать пользовательские свойства, назначать их объектам модели, менять значение.
    • Управлять видимостью и выделять объекты модели.

    На сегодня это все, будем рады ответить на ваши вопросы. Пишите под Renga, генерируйте идеи, развивайтесь вместе с нами!

    Евгений Тян, ведущий программист, Renga Software.
    АСКОН 89,25
    Крупнейший российский разработчик инженерного ПО
    Поделиться публикацией
    Комментарии 0

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

    Самое читаемое