Всем привет! В этой статье я расскажу об API BIM-системы Renga. О самой системе можно почитать тут, здесь же можно запросить версию для некоммерческого использования. Если вкратце, то Renga это трехмерная система автоматизированного проектирования в архитектуре и строительстве. В ней проектировщик/архитектор/конструктор работает с информационной моделью здания, получает ассоциативные чертежи, спецификации, в общем, создает проект.
Зачем нужно API CAD-системы
Сначала, как водится, немного водички.
Разработка расширений для CAD систем довольно распространена, поскольку в любом проектировании существуют различные направления, разделы и стандарты оформления проектной документации, которые требуют разной узкоспециализированной функциональности. Кроме того существуют задачи интеграции с программами расчета, визуализации, документооборота и многими другими. Выход — создание подключаемых модулей, расширяющих функциональность системы.
Пример простого расширения
Разберем основные шаги для создания расширения. Вам понадобится VisualStudio и уверенность, что вы знаете C++ (сейчас у Renga C++ API, но в перспективе у нас переход на COM, будем поддерживать .Net расширения).
Итак, по шагам:
- Скачиваем SDK отсюда, распаковываем в удобное место.
- Создаем простую dll с настройками по умолчанию, назовем MyPlugin.
- В дополнительных папках включений и дополнительных папках lib добавляем путь к RengaSDK, во включаемые библиотеки добавляем RengaApi.lib и RengaBase.lib.
- Теперь напишем минимальную реализацию. По сути C++ расширение к Renga — обычная dll библиотека с экспортной функцией, которая возвращает указатель на интерфейс IPlugin. Поэтому все что нам нужно сделать:
а)Создать класс, унаследованный от этого интерфейса. В классе реализуем оба метода интерфейса start и stop, эти методы будут вызваны после загрузки и перед выгрузкой соответственно. Реализацию пока оставим пустой.
b) Реализовать экспортную функцию, возвращающую указатель на интерфейс IPlugin. Для этого можно воспользоваться макросом EXPORT_PLUGIN.
- Создадим файл описания расширения с именем 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>
- Для того, чтобы расширение появилось в 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.
Автор: Rengabim