本节主要基于第3章创建的CubeDemo
对象,实现在立方体上开洞的效果。CubeDemo
对象创建见3.1.5节示例代码3-5,3-6,3-7,3-8。
对象的创建主要分为三点:
- 一是创建一个洞口对象;
- 二是创建
CubeDemo
与洞口对象的关联关系; - 三是在原
CubeDemo
对象上开洞,通过CubeDemo
对象和洞口对象的布尔实现。
对象的布置中,需要在布置洞口时拾取CubeDemo
对象,并在洞口对象布置后刷新CubeDemo
对象。
9.2.1 创建洞口对象
3.1.5节中已经讲到,对象创建主要分为创建对象数据表、实现对象类、对象注册三个步骤,本节将不再赘述创建对象数据表和对象注册两部分,主要讲解洞口对象类的实现。
本节的洞口对象几何造型类似CubeDemo
的拉伸体造型,为了实现洞口效果,设置洞口几何图素的透明度为1。
#pragma once
namespace DemoObject
{
//定义智能指针、引用等
class OpenningDemo;
typedef OpenningDemo const& OpenningDemoCR;
typedef OpenningDemo& OpenningDemoR;
typedef OpenningDemo* OpenningDemoP;
typedef RefCountedPtr<OpenningDemo> OpenningDemoPtr;
class OpenningDemo : public BIMBase::Data::BPGraphicElement
{
DefineSuper(BPGraphicElement)
public:
OpenningDemo();
~OpenningDemo();
int getWidth() const;
void setWidth(int nWidth);
int getLength() const;
void setLength(int nLength);
int getHeight() const;
void setHeight(int nHeight);
protected:
virtual Utf8String _getSchemaName() const override { return PBM_SCHEMA_Demo; };
virtual Utf8String _getClassName() const override { return PBM_CLASS_OPENNING_Demo; };
virtual ::p3d::P3DStatus _copyToData (BPDataR data, BPProjectR project) const override;
virtual ::p3d::P3DStatus _initFromData(BPDataCR data) override;
virtual BPGraphicsPtr _createPhysicalGraphics(BPProjectR project, PModelIdCR modelId, bool isDynamics) override;
private:
int m_nWidth;
int m_nHeight;
int m_nLenght;
Demo_CREATE(OpenningDemo);
};
Demo_EXTENSION(OpenningDemo);
}
代码9-1:洞口OpenningDemo对象(头文件)
#include "pch.h"
#define Property_Lenght "Length"
#define Property_Width "Width"
#define Property_Height "Height"
using namespace DemoObject;
OpenningDemo::OpenningDemo()
{
m_nLenght = 500;
m_nWidth = 200;
m_nHeight = 500;
}
OpenningDemo::~OpenningDemo()
{}
int OpenningDemo::getWidth() const
{
return m_nWidth;
}
void OpenningDemo::setWidth(int nWidth)
{
m_nWidth = nWidth;
}
int OpenningDemo::getLength() const
{
return m_nLenght;
}
void OpenningDemo::setLength(int nLength)
{
m_nLenght = nLength;
}
int OpenningDemo::getHeight() const
{
return m_nHeight;
}
void OpenningDemo::setHeight(int nHeight)
{
m_nHeight = nHeight;
}
::p3d::P3DStatus OpenningDemo::_copyToData(::BIMBase::Core::BPDataR data, ::BIMBase::Core::BPProjectR project) const
{
if (T_Super::_copyToData(data, project) != P3DStatus::SUCCESS)
return ERROR;
P3DStatus status;
status = data.setValue(Property_Lenght, BPValue(this->getLength()));
if (P3DStatus::SUCCESS != status)
return ERROR;
status = data.setValue(Property_Width, BPValue(this->getWidth()));
if (P3DStatus::SUCCESS != status)
return ERROR;
status = data.setValue(Property_Height, BPValue(this->getHeight()));
if (P3DStatus::SUCCESS != status)
return ERROR;
return SUCCESS;
}
::p3d::P3DStatus OpenningDemo::_initFromData(::BIMBase::Core::BPDataCR data)
{
if (T_Super::_initFromData(data) != P3DStatus::SUCCESS)
return ERROR;
BPValue value;
P3DStatus status;
status = data.getValue(value, Property_Lenght);
if (P3DStatus::SUCCESS != status)
return ERROR;
setLength(value.getInteger());
status = data.getValue(value, Property_Width);
if (P3DStatus::SUCCESS != status)
return ERROR;
setWidth(value.getInteger());
status = data.getValue(value, Property_Height);
if (P3DStatus::SUCCESS != status)
return ERROR;
setHeight(value.getInteger());
return SUCCESS;
}
BIMBase::Core::BPGraphicsPtr OpenningDemo::_createPhysicalGraphics(::BIMBase::Core::BPProjectR project, ::BIMBase::PModelIdCR modelId, bool isDynamics)
{
BPModelPtr model = project.getModelById(modelId);
if (model.isNull())
return nullptr;
BPGraphicsPtr ptrGraphic = model->createPhysicalGraphics();
if (ptrGraphic.isNull())
return nullptr;
int nWidth = getWidth();
int nLength = getLength();
int nHeight = getHeight();
//绘制底部外轮廓
GeCurveArrayPtr outLines = GeCurveArray::create(GeCurveArray::BOUNDARY_TYPE_Outer);
pvector<GePoint3d> pts;
pts.push_back(GePoint3d::create(0, -nWidth / 2, 0));
pts.push_back(GePoint3d::create(nLength, -nWidth / 2, 0));
pts.push_back(GePoint3d::create(nLength, nWidth / 2, 0));
pts.push_back(GePoint3d::create(0, nWidth / 2, 0));
pts.push_back(GePoint3d::create(0, -nWidth / 2, 0));
IGeCurveBasePtr pLine = IGeCurveBase::createLineString(pts);
outLines->push_back(pLine);
//向Z方向拉伸
GeVec3d veccc = GeVec3d::create(0, 0, nHeight);
GeExtrusionInfo extrData(outLines, veccc, true);
IGeSolidBasePtr extrusion = IGeSolidBase::createGeExtrusion(extrData);
//设置洞口颜色为白色,透明度为1
BPSymbology symb;
symb.style = 0; //线型
symb.weight = 0; //线宽
symb.color = BPColorUtil::getEntityColor(RGB(255, 255, 255), project,
true);
ptrGraphic->addGeSolidBase(*extrusion, symb, 1);
return ptrGraphic;
}
代码9-2:洞口OpenningDemo对象(源文件)
从洞口对象源文件的_createPhysicalGraphics
接口实现中我们可以发现,洞口对象几何构造基本和CubeDemo
对象相同,只在调用addGeSolidBase
接口时将几何图素透明度设置为1。
9.2.2 创建立方体对象与洞口对象的关联关系
在实际应用场景中,存在许多构件联动的需求,比如墙与门窗的联动等,这些在schema中通过关联关系实现。
本章开洞范例中,需要将立方体与洞口对象进行关联,才能实现在洞口编辑时刷新立方体,或者立方体编辑时联动洞口。
首先,在3.1中设置立方体的schema文件的基础上,模仿CubeDemo类的创建方法,增加一个新的classes类并完成属性添加,将其命名为OpenningDemo;对Relationship Classes右键添加引用类,点击关联约束选项,即可查看Source与Target区块,其中source代表引用源类,本例为CubeDemo类,Target代表目标源,本例为OpenningDemo文件,保存schema文件后,即完成了相关对象的关联。
9.2.3 在立方体对象中实现关联洞口开洞
示例代码3-6和示例代码3-7已经详细讲解了CubeDemo
对象的创建,本节将在其基础上进行补充,实现CubeDemo
和洞口对象的布尔操作。
#pragma once
namespace DemoObject
{
//定义智能指针、引用等
class CubeDemo;
typedef CubeDemo const& CubeDemoCR;
typedef CubeDemo& CubeDemoR;
typedef CubeDemo* CubeDemoP;
typedef RefCountedPtr<CubeDemo> CubeDemoPtr;
class CubeDemo : public BIMBase::Data::BPGraphicElement
{
DefineSuper(BPGraphicElement)
public:
CubeDemo();
~CubeDemo();
int getWidth() const;
void setWidth(int nWidth);
int getLength() const;
void setLength(int nLength);
int getHeight() const;
void setHeight(int nHeight);
//获取起点
GePoint3d getStartPoint() const;
//获取中点
GePoint3d getMiddlePoint() const;
//获取终点
GePoint3d getEndPoint() const;
protected:
virtual Utf8String _getSchemaName() const override { return PBM_SCHEMA_Demo; };
virtual Utf8String _getClassName() const override { return PBM_CLASS_CUBE_Demo; };
//写数据
virtual ::p3d::P3DStatus _copyToData(BIMBase::Core::BPDataR instance, BIMBase::Core::BPProject& project) const override;
//读数据
virtual ::p3d::P3DStatus _initFromData(BIMBase::Core::BPDataCR instance) override;
//创建几何造型
virtual BIMBase::Core::BPGraphicsPtr _createPhysicalGraphics(BIMBase::Core::BPProjectR project, BIMBase::PModelIdCR modelId, bool bIsDynamics) override;
private:
int m_nWidth;
int m_nHeight;
int m_nLenght;
Demo_CREATE(CubeDemo);
};
Demo_EXTENSION(CubeDemo);
}
代码9-4:CubeDemo头文件
#include "pch.h"
#include "CubeDemo.h"
#define Property_Lenght "Length"
#define Property_Width "Width"
#define Property_Height "Height"
using namespace DemoObject;
CubeDemo::CubeDemo()
{
m_nLenght = 1000;
m_nWidth = 200;
m_nHeight = 3000;
}
CubeDemo::~CubeDemo()
{
}
int CubeDemo::getWidth() const
{
return m_nWidth;
}
void CubeDemo::setWidth(int nWidth)
{
m_nWidth = nWidth;
}
int CubeDemo::getLength() const
{
return m_nLenght;
}
void CubeDemo::setLength(int nLength)
{
m_nLenght = nLength;
}
int CubeDemo::getHeight() const
{
return m_nHeight;
}
void CubeDemo::setHeight(int nHeight)
{
m_nHeight = nHeight;
}
::p3d::P3DStatus CubeDemo::_copyToData(BIMBase::Core::BPDataR instance, BIMBase::Core::BPProject& project) const
{
if (T_Super::_copyToData(instance, project) != P3DStatus::SUCCESS)
return ERROR;
P3DStatus status;
status = instance.setValue(Property_Lenght, BPValue(this->getLength()));
if (P3DStatus::SUCCESS != status)
return ERROR;
status = instance.setValue(Property_Width, BPValue(this->getWidth()));
if (P3DStatus::SUCCESS != status)
return ERROR;
status = instance.setValue(Property_Height, BPValue(this->getHeight()));
if (P3DStatus::SUCCESS != status)
return ERROR;
return SUCCESS;
}
::p3d::P3DStatus CubeDemo::_initFromData(BIMBase::Core::BPDataCR instance)
{
if (T_Super::_initFromData(instance) != P3DStatus::SUCCESS)
return ERROR;
BPValue value;
P3DStatus status;
status = instance.getValue(value, Property_Lenght);
if (P3DStatus::SUCCESS != status)
return ERROR;
setLength(value.getInteger());
status = instance.getValue(value, Property_Width);
if (P3DStatus::SUCCESS != status)
return ERROR;
setWidth(value.getInteger());
status = instance.getValue(value, Property_Height);
if (P3DStatus::SUCCESS != status)
return ERROR;
setHeight(value.getInteger());
return SUCCESS;
}
BIMBase::Core::BPGraphicsPtr DemoObject::CubeDemo::_createPhysicalGraphics(BIMBase::Core::BPProjectR project, BIMBase::PModelIdCR modelId, bool isDynamics)
{
BPModelPtr ptrModel = project.getModelById(modelId);
if (ptrModel.isNull())
return nullptr;
BPGraphicsPtr ptrGraphic = ptrModel->createPhysicalGraphics();
if (ptrGraphic.isNull())
return nullptr;
int nWidth = getWidth();
int nLength = getLength();
int nHeight = getHeight();
//绘制底部外轮廓
GeCurveArrayPtr outLines = GeCurveArray::create(GeCurveArray::BOUNDARY_TYPE_Outer);
pvector<GePoint3d> pts;
pts.push_back(GePoint3d::create(0, -nWidth / 2, 0));
pts.push_back(GePoint3d::create(nLength, -nWidth / 2, 0));
pts.push_back(GePoint3d::create(nLength, nWidth / 2, 0));
pts.push_back(GePoint3d::create(0, nWidth / 2, 0));
pts.push_back(GePoint3d::create(0, -nWidth / 2, 0));
IGeCurveBasePtr pLine = IGeCurveBase::createLineString(pts);
outLines->push_back(pLine);
//向Z方向拉伸
GeVec3d veccc = GeVec3d::create(0, 0, nHeight);
GeExtrusionInfo extrData(outLines, veccc, true);
IGeSolidBasePtr extrusion = IGeSolidBase::createGeExtrusion(extrData);
ptrGraphic->addGeSolidBase(*extrusion);
//---------------以下为创建洞口代码-------------------
if (getData(project) == nullptr)
return ptrGraphic;
//获取cube关联的openning对象,用于布尔计算
BPDataKeyArray relDataIds;
BPRelationshipFinder::getRelatedDatasByRelationship(relDataIds, getDataKey(), project, PBM_SCHEMA_Demo, PBM_RELSHIP_CUBEWITHOPENNING);
if (relDataIds.size() == 0)
return ptrGraphic;
BPGraphicsPtr ptrGraphicResult = ptrModel->createPhysicalGraphics();
if (ptrGraphicResult.isNull())
return nullptr;
for each (auto dataKey in relDataIds)
{
BPDataPtr ptrData = BPDataUtil::getDataByKey(dataKey, project);
if (ptrData == nullptr)
continue;
OpenningDemoPtr ptrOpenning = OpenningDemo::create(*ptrData);
if (ptrOpenning == nullptr)
continue;
//如果当前宽度不一致,修改洞口宽度
if (abs(ptrOpenning->getWidth() - getWidth()) > 0.01)
{
ptrOpenning->setWidth(getWidth());
ptrOpenning->replaceInProject(project);
}
//获取洞口图素,返回局部坐标系下图素
BPGraphicsPtr ptrGraphicOpenning = ptrOpenning->createPhysicalGraphics(project, ptrModel->getModelId(), true);
GeTransform openningTrans = ptrOpenning->getTransform();
GeTransform cubeTrans = getTransform();
cubeTrans.setByInverse(cubeTrans);
BPGraphicsPtr ptrGraphicResultTemp = ptrModel->createPhysicalGraphics();
if (ptrGraphicResultTemp.isNull())
continue;
//当前cube为局部坐标系下图素,需要将openning的图素转到世界坐标系下,再转到cube的局部坐标系下进行
BPGraphicsUtils::transformPhysicalGraphics(*ptrGraphicOpenning, GeTransform::createByProduct(cubeTrans, openningTrans));
BPSolidBooleanUtil::doBoolean(ptrGraphicResultTemp, ptrGraphic, ptrGraphicOpenning, BPBooleanOp::Substract);
ptrGraphic = ptrModel->createPhysicalGraphics();
if (ptrGraphic.isNull())
continue;
BPGraphicsUtils::copyPhysicalGraphics(*ptrGraphic, *ptrGraphicResultTemp);
ptrGraphicResult = ptrGraphicResultTemp;
}
return ptrGraphicResult;
}
GePoint3d DemoObject::CubeDemo::getStartPoint() const
{
//通过转换矩阵获取原点
return getPlacement().getOrigin();
}
GePoint3d DemoObject::CubeDemo::getMiddlePoint() const
{
GePoint3d ptMiddle = GePoint3d::create(getLength() / 2.0, 0, 0);
//墙局部坐标系转到世界坐标系下
GeTransform trans = getPlacement().toTransform();
trans.multiply(ptMiddle);
return ptMiddle;
}
GePoint3d DemoObject::CubeDemo::getEndPoint() const
{
GePoint3d ptEnd = GePoint3d::create(getLength(), 0, 0);
//墙局部坐标系转到世界坐标系下
GeTransform trans = getPlacement().toTransform();
trans.multiply(ptEnd);
return ptEnd;
}
代码9-5:CubeDemo源文件
9.2.4 布置洞口对象
本节通过2.3.2中所讲的基本工具BPPrimitiveTool
实现洞口对象的布置。与普通布置工具不同的是,布置洞口时需要拾取CubeDemo
对象,在洞口对象布置后需添加CubeDemo
与洞口对象的关联关系并刷新CubeDemo
对象。
class ToolLayoutOpenningDemo :public BPPrimitiveTool
{
DefineSuper(BPPrimitiveTool)
public:
ToolLayoutOpenningTest();
~ToolLayoutOpenningTest();
protected:
virtual ::p3d::Utf8CP _getToolName() const { return "layoutOpenning"; }
virtual void _onPostInstall() override;
virtual void _onRestartTool() override;
virtual bool _onDataButton(BPBaseButtonEventCP) override;
virtual bool _onResetButton(BPBaseButtonEventCP) override;
virtual void _onDynamicFrame(BPBaseButtonEventCP) override;
virtual bool _onModelMotion(BPBaseButtonEventCP ev) override;
private:
GePoint3d m_ptC;
TestObject::OpenningTestPtr m_ptrOpenning;
TestObject::CubeTestPtr m_ptrCube;
};
代码9-5:洞口布置工具(头文件)
#pragma once
class ToolLayoutOpenningDemo :public BPPrimitiveTool
{
DefineSuper(BPPrimitiveTool)
public:
ToolLayoutOpenningDemo();
~ToolLayoutOpenningDemo();
protected:
virtual ::p3d::Utf8CP _getToolName() const { return "layoutOpenning"; }
virtual void _onPostInstall() override;
virtual void _onRestartTool() override;
virtual bool _onDataButton(BPBaseButtonEventCP) override;
virtual bool _onResetButton(BPBaseButtonEventCP) override;
virtual void _onDynamicFrame(BPBaseButtonEventCP) override;
virtual bool _onModelMotion(BPBaseButtonEventCP ev) override;
private:
GePoint3d m_ptC;
DemoObject::OpenningDemoPtr m_ptrOpenning;
DemoObject::CubeDemoPtr m_ptrCube;
};
代码9-5:洞口布置工具(头文件)
#include "pch.h"
#include "ToolLayoutOpenningDemo.h"
using namespace DemoObject;
ToolLayoutOpenningDemo::ToolLayoutOpenningDemo()
{
m_ptrCube = CubeDemo::create();
m_ptrOpenning = OpenningDemo::create();
}
ToolLayoutOpenningDemo::~ToolLayoutOpenningDemo()
{}
void ToolLayoutOpenningDemo::_onPostInstall()
{
T_Super::_onPostInstall();
BPSnap::getInstance().enableLocate(false);
BPSnap::getInstance().enableSnap(true);
}
void ToolLayoutOpenningDemo::_onRestartTool()
{
ToolLayoutOpenningDemo* newTool = new ToolLayoutOpenningDemo();
newTool->installTool();
}
bool ToolLayoutOpenningDemo::_onDataButton(BPBaseButtonEventCP ev)
{
BPViewportP viewPort = BPViewManager::getInstance().getActivedViewport();
if (viewPort == nullptr)
return false;
int nViewIndex = viewPort->getViewNumber();
BPViewportP vp = BPViewManager::getInstance().getViewport(nViewIndex);
if (NULL == vp)
return false;
BIMBase::Core::BPProjectP pProject = ev->getViewport()->getTargetModel()->getBPProject();
if (pProject == nullptr)
return false;
BPModelP pModel = ev->getViewport()->getTargetModel();
if (pModel == nullptr)
return false;
if (m_ptrCube.isNull())
return false;
if (!m_ptrCube->getDataKey().isValid())
return false;
if (m_ptrOpenning.isNull())
return false;
//增加构件到工程中
if (SUCCESS != m_ptrOpenning->addToProject(*pProject, pModel->getModelId()))
{
AfxMessageBox(L"Can not add to project!");
return false;
}
//与CubeDemo建立关联关系
BPDataId relationId;
BPRelationshipInserter::addRelationship(&relationId, *pProject, *m_ptrCube->getData(*pProject), *m_ptrOpenning->getData(*pProject), PBM_SCHEMA_Demo, PBM_RELSHIP_CUBEWITHOPENNING);
//刷新CubeDemo对象的图素
m_ptrCube->replaceGraphics(*pProject);
return true;
}
bool ToolLayoutOpenningDemo::_onResetButton(BPBaseButtonEventCP)
{
_exitTool();
return true;
}
void ToolLayoutOpenningDemo::_onDynamicFrame(BPBaseButtonEventCP ev)
{
m_ptrCube == nullptr;
m_ptrOpenning = OpenningDemo::create();
if (NULL == ev)
return;
//获取点击点所在的工程和模型ID
BPProjectP pProject = ev->getViewport()->getTargetModel()->getBPProject();
if (pProject == nullptr)
return;
BPModelP pModel = ev->getViewport()->getTargetModel();
if (pModel == nullptr)
return;
//获取鼠标当前拾取到的entity
int nErrorCode = 0;
p3d::PString reasonDesc = L"";
BIMBase::Core::BPPickDataPtr path = BPEntityLocateManager::getInstance().doLocatePickData(nErrorCode, reasonDesc, true, ev->getPoint(), ev->getViewport());
if (path == nullptr)
return;
BPEntityP element = path->getEntity();
if (element == nullptr)
return;
if (element->getClassId() != BIMBase::Core::BPSchemaManager::getClassIdByClassName(*pProject, PBM_SCHEMA_Demo, PBM_CLASS_CUBE_Demo))
return;
//将entity转为cube对象
BPDataPtr ptrCubeData = BPDataUtil::getDataOnEntity(*element);
if (ptrCubeData == nullptr)
return;
m_ptrCube = CubeDemo::create(*ptrCubeData);
if (m_ptrCube == nullptr)
return;
//获取Cube对象端点,将鼠标点投影到cube基线上
GeSegment3d segment = GeSegment3d::create(m_ptrCube->getStartPoint(), m_ptrCube->getEndPoint());
GePoint3d projectPoint = GePoint3d::createByZero();
double dPara = 0;
segment.projectPointToSegment(projectPoint, dPara, *ev->getPoint());
//获取cube对象的方向
BPPlacement placement = m_ptrCube->getPlacement();
placement.setOrigin(projectPoint);
//使洞口与cube宽度一致、延申方向一致
m_ptrOpenning->setWidth(m_ptrCube->getWidth());
m_ptrOpenning->setPlacement(placement);
//获取洞口图素用于动态展示,注意createPhysicalGraphics获取的为局部坐标系图素
BPGraphicsPtr ptrGraphic = pModel->createPhysicalGraphics();
ptrGraphic = m_ptrOpenning->createPhysicalGraphics(*pProject, pModel->getModelId(), true);
BPGraphicsUtils::transformPhysicalGraphics(*ptrGraphic, placement.toTransform());
BPRedrawEntitys redrawElems;
redrawElems.setDrawMode(BPDrawMode::enTempDraw);
redrawElems.setDrawPurpose(BPDrawPurpose::enFlash);
redrawElems.setDynamicsViews(ev->getViewport());
redrawElems.doRedraw(ptrGraphic->getEntityR());
}
bool ToolLayoutOpenningDemo::_onModelMotion(BPBaseButtonEventCP ev)
{
if (!getDynamicsStarted())
_beginDynamics();
return true;
}
//对工具进行注册
BPTool* CreateOpenningTool()
{
ToolLayoutOpenningDemo* tool = new ToolLayoutOpenningDemo();
return tool;
}
AutoDoRegisterFunctionsBegin
BPToolsManager::registerTool("layoutOpenning", &CreateOpenningTool);
AutoDoRegisterFunctionsEnd
代码9-6:洞口布置工具(源文件)
下图为立方体开洞效果。

图9-1 立方体开洞