Всем привет! В этой статье я расскажу об 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.

Комментарии (0)