原文地址:https://blog.youkuaiyun.com/Mechanicoder/article/details/139218495
简介
本文参考Open Geometry Group (OGG)组织的一次建模公开赛比赛要求,选择电风扇为建模对象,使用OCC接口完成所选模型的建模。在电风扇题目做题过程中,涉及拉伸、旋转、阵列、倒圆角等常见的CAD建模功能,以及曲线、曲面的一些基础操作,对OCC初学者可能会有一些帮助,因此整理、总结成这篇文章。
建模
落地扇模型包括底座、立柱、外壳、前罩、后罩、扇叶等零件,建模过程中需要计算确定各零件的几何尺寸信息、彼此之间的相对位置关系,例如立柱高度参数、扇叶相对于立柱的位置参数、前后网罩尺寸参数等。由于这些参数并非直观易得,直接使用OCC建模可能得到尺寸不协调的结果,需要反复调参,增大开发时间。因此,可首先借助于CAD建模软件预先完成设计,然后再通过OCC实现编程建模。
这里采用CrownCAD在线协同建模软件,个人可免费使用建模基础功能,对于落地风扇的设计已足够,设计模型如下图所示,可通过链接在线浏览:落地风扇。
以下将根据建模思路,完成各个零件的编程实现。自底向上依次为基座、立柱、外壳、电机支架、电机、风扇、风扇轮毂、扇叶、前网罩和后网罩。各小节仅描述了建模步骤以及使用到的OCC关键接口,代码实现可直接跳转至代码实现总结。建模过程采用CSG方式。
基座
由上、下两个圆台合并而成,用于支撑整个风扇。下半黑色部分是一个下小上大的圆台、上半灰色部分是下大上小的圆台。两个圆台均采用截面旋转360°得到,并将得到的两个实体求并。
建模步骤:
- 根据下部分轮廓点,依次连点成线,构造一个封闭的多边形,
BRepBuilderAPI_MakePolygon
- 将多边形构造成面,
BRepBuilderAPI_MakeFace
- 由面构造实体,
BRepPrimAPI_MakeRevol
上一部分同理可得,最终效果如下:
立柱
立柱包括下部分柱体、衔接圆锥、按钮圆柱、按钮槽、按钮、数字、衔接球槽特征。
建模步骤:
- 下部分立柱,构造一个标准圆柱,
BRepPrimAPI_MakeCylinder
- 衔接圆锥,构造一个标准圆锥,
BRepPrimAPI_MakeCone
- 按钮圆柱,按钮圆柱上特征较多,采用实体建模的方式逐步获得
- 构造标准圆柱,
BRepPrimAPI_MakeCylinder
- 构造顶部裁剪圆锥,即构造一个拉伸体,
BRepPrimAPI_MakePrism
- 构造顶部衔接球槽,即构造一个标准球,
BRepPrimAPI_MakeSphere
- 构造按钮槽,同样头早一个拉伸体,并线性阵列按钮槽,
BRepBuilderAPI_Transform
- 按钮立柱依次裁剪裁剪圆锥、顶部标准球、按钮槽拉伸体,
BRepAlgoAPI_Cut, BRepAlgoAPI_Fuse
- 构造按钮,是一个截面为椭圆形的拉伸体,
GC_MakeEllipse
- 对所有棱边位置倒圆角过渡,
BRepFilletAPI_MakeFillet
按钮
按钮单独建模,使之可以在3D视图中进行交互操作。例如,鼠标点击按钮①、②、③,实现按钮下沉、风扇按对应级数开始旋转,点击按钮〇实现风扇停转。这些动画交互在之后的动画及交互一节详细说明。
按钮建模步骤:
- 构造以椭圆为截面的拉伸体,按钮的截面应比前一步中的按钮槽截面略小,
GC_MakeEllipse
- 按钮线性阵列,得到四个按钮,
BRepBuilderAPI_Transform
- 构造按钮表面的文字,分别为0、1、2、3:
- 构造文字截面,
Font_BRepTextBuilder
- 文字截面拉伸,
BRepPrimAPI_MakePrism
- 每个按钮减去对应文字的拉伸体,可得到带凹陷文字的按钮,
BRepAlgoAPI_Cut
外壳
外壳用于支撑风扇电机、衔接前后网罩。
建模步骤:
- 构造衔接球
1.1. 首先构造一个标准球,同前,BRepPrimAPI_MakeSphere
1.2. 构造外壳内腔,该内腔将容纳风扇,是一个标准的圆柱,BRepPrimAPI_MakeCylinder
1.3. 球减去外壳内腔,即可削去球的顶部,用于承接风扇外壳- 构造电机立柱,标准圆柱,用于承接电机,
BRepPrimAPI_MakeSphere
- 使用一个标准圆柱,代表电机
- 构造前后衔接环,即一个标准的圆环,用于连接前端风扇外壳、后端网罩。由于风扇罩子横截面外形将是一条样条曲线,为方便建模、衔接,特地构造一小段圆环。使用两个圆柱相减即可得到
- 风扇罩子横截面外形是一条样条曲线,是风扇建模中首次出现样条曲线。
5.1. 构造样条外形,Geom2d_BezierCurve
5.2. 样条偏置,Geom2d_OffsetCurve
5.3. 将两段样条首尾相连,即可得到封闭轮廓、横截面,BRepBuilderAPI_MakeWire, BRepBuilderAPI_MakeFace
5.4. 横截面旋转即得到外壳,BRepPrimAPI_MakeRevol
- 对以上所得 1~5 结果求并即可得到完整的风扇外壳
需要特殊说明的是,上述步骤 5.1. 中的样条曲线建模及偏置均在 XOY 平面上进行,这是根据作者对OCC的使用经验,2D偏置比3D更稳定一些。当然直接进行3D样条曲线,或许也是可以的。
另外由于OCC底层几何算法一般使用3D曲线 Geom_Curve
,对2D曲线 Geom2d_Curve
所作的适配有限,因此,对于2D即 XOY 平面上所得的轮廓,在进行构造面进而旋转之前,需要额外构造几何3D曲线,即确保执行 BRep_Tool::Curve(Edge)
可以得到正确的结果。
BRepLib::BuildCurves3d(Shape)
风扇
风扇包括连接扇叶的轮毂、叶片两大部分,其中叶片为样条曲面,是整个落地风扇建模过程中最为复杂的部分。
建模步骤:
- 电机轴,构造一个标准圆柱,
BRepPrimAPI_MakeCylinder
- 风扇轮毂,同样是一个标准圆柱,
BRepPrimAPI_MakeCone
- 扇叶,每个扇叶是自由曲面,可由二维点集构造获得,因此第一步是构造点集。在风扇轮毂、外壳内壁之间构造若干个曲面,并在曲面上构造出风扇的截面线,分别对截面线采样,即可得到二维点集:
3.1. 构造第一个面上曲线,BRep_CurveOnSurface
3.1.1. 构造一个分层B样条曲面,Geom_BSplineSurface
3.1.2. 构造一条参数曲线,Geom2d_Curve
3.1. 构造第二个面上曲线,
…
共构造得到 N 个面上曲线- 二维点集拟合,即可得到二维曲面,``
- 按一周分布 M 个叶片进行旋转阵列,即可得到 M 个叶片
- 另外,为了增强展示效果,为每个叶片分配了不同颜色。
前网罩
前网罩包括网条和中心logo环两大部分,其中网条使用Bezier构造。
建模步骤:
- 前网罩网条通过Bezier曲线及其偏置曲线封闭所得;
- 风扇前网罩成弧形而非平面,因此再构造一个外形修剪体用来裁剪网条。外形修剪体由两条Bezier曲线封闭得到;
- 中心圆环以及logo字体通过圆柱体减去字体的拉伸体所得,通用使用外形修剪体进行修剪
后网罩
后网罩与前网罩采用相似的方式构造,区别在于后网罩除了外形是弧面之外,内腔亦是弧面,因此进行了两次裁剪。
动画及交互
在建模之外,增加了一点交互功能,增加这次实践的趣味。即通过鼠标或数字键实现对风扇转速的控制,同时实现按钮被按下的效果。
风扇有1、2、3、0四档,每档转速不同,经测试效果,分别设置转速(RPM)为30、60、90、0。
进一步地,可以在衔接球位置实现风扇的左右、上下摇头动画。
代码实现总结
以上各步骤的基础建模功能包括拉伸、旋转、阵列、裁剪、合并、倒圆角等。
拉伸
TopoDS_Shape FanMaker::Extrude(const std::vector<TopoDS_Edge>& edges, const gp_Vec& vec) const
{
TopoDS_Face face = MakeFace(edges);
if (face.IsNull())
{
return TopoDS_Shape();
}
TopoDS_Shape extrude_shape = BRepPrimAPI_MakePrism(face, vec);
return extrude_shape;
}
旋转
TopoDS_Shape FanMaker::Revolve(const std::vector<TopoDS_Edge>& edges, const gp_Ax1& axis) const
{
TopoDS_Face face = MakeFace(edges);
if (face.IsNull())
{
return TopoDS_Shape();
}
TopoDS_Shape revol = BRepPrimAPI_MakeRevol(face, axis);
return revol;
}
阵列
TopoDS_Shape FanMaker::CircularPattern(const TopoDS_Shape& shape, const gp_Ax1& axis, int num) const
{
// 网条阵列
gp_Trsf trsf;
trsf.SetRotation(axis, M_PI * 2.0 / double(num));
std::vector<TopoDS_Shape> pattern(num);
pattern[0] = shape;
for (size_t i = 1; i < num; i++)
{
pattern[i] = BRepBuilderAPI_Transform(pattern[i - 1], trsf);
}
return Compound(pattern);
}
字体拉伸
TopoDS_Shape FanMaker::PrismText(const NCollection_String& text,
const gp_Ax2& loc, const gp_Vec& prism_vec, int pt_size/* = 12*/) const
{
Font_BRepFont font("consola", Font_FontAspect::Font_FontAspect_Regular, (double)pt_size);
Graphic3d_HorizontalTextAlignment halign = Graphic3d_HTA_CENTER;
Graphic3d_VerticalTextAlignment valign = Graphic3d_VTA_CENTER;
Font_BRepTextBuilder font_brep;
TopoDS_Shape text_face = font_brep.Perform(font, text, loc, halign, valign);
TopoDS_Shape text_solid = BRepPrimAPI_MakePrism(text_face, prism_vec);
return text_solid;
}
布尔运算
合并:
TopoDS_Shape FanMaker::Fuse(const std::vector<TopoDS_Shape>& shapes) const
{
if (shapes.empty())
{
return {};
}
else if (shapes.size() == 1)
{
return shapes[0];
}
else
{
TopoDS_Shape result = BRepAlgoAPI_Fuse(shapes[0], shapes[1]);
for (size_t i = 2; i < shapes.size(); i++)
{
if (!shapes[i].IsNull())
{
result = BRepAlgoAPI_Fuse(result, shapes[i]);
}
}
return result;
}
}
裁剪:
TopoDS_Shape FanMaker::Cut(const TopoDS_Shape& s,
const std::vector<TopoDS_Shape>& cutters) const
{
BRepAlgoAPI_Cut cut;
TopTools_ListOfShape args, tools;
args.Append(s);
for (const auto& cutter : cutters)
{
tools.Append(cutter);
}
cut.SetArguments(args);
cut.SetTools(tools);
cut.Build();
if (!cut.IsDone())
{
return TopoDS_Shape();
}
return cut.Shape();
}
总结
建模过程思路简单,难点在于合理确定各部分零件建模的基准、衔接位置。先通过CAD软件完成草稿建模,基本确定各几何参数,然后再由OCC实现建模,从而可专注于逻辑实现而非参数调试。
完整源代码链接:https://gitee.com/mechanicoder/occt_-upgrade-test/tree/master/others/fan_modeling。
原文地址:https://blog.youkuaiyun.com/Mechanicoder/article/details/139218495