23. Revit API: 几何对象(四)- BrepBuilder

一、前言

上一篇写了Solid的创建、展示、变换、布尔操作,这一篇写另一种Solid的创建方法。

需要再次强调的是,BrepBuilder不适合用来创建Solid。


二、边界表示

Wiki】:边界表示(Boundary representation,简称B-Rep),是一种通过定义其体积限制来表示3D形状的方法。实体表示为连接的曲面元素的集合,这些曲面元素定义内部点和外部点之间的边界。

还有个叫 CSG(Constructive Solid Geometry)的,和一些其它表示方法。我不知道Revit用的是哪种,当然了,API调用仔调API就行了,也只能按开放的接口用。如果不爽调用这破API,可以去看“Open Cascade”,整会了就可以自个儿玩了。

Brep,简单来讲,就是定义每个面,定义面上的范围(边),再把它们严丝合缝的拼起来。

“Geometric Modeling”里面介绍了一些几何建模的方法,了解一下就行了。


三、BRepBuilder

3.1. 使用流程

以创建一个正方体为例。

Ⅰ 准备阶段:

  1. 创建BRepBuilder实例
  2. 创建Surface,即前后左右上下6个平面。
  3. 创建Curve,即正方体12条直线边。

Ⅱ 组装填充阶段:

  1. AddFace
  2. AddEdge
  3. AddLoop
  4. AddCoEdge
  5. FinishLoop,FinishFace
  6. Finish

3.2. 样例代码解释

正方体生成的代码和其它例子,可以查看SDK目录下的“SDKSample/BRepBuilderExample/CreateCube.cs”,或者查看GitHub

直接看代码可能有点迷糊,这里对组装填充的步骤进行解释。

请结合图来看。

在这里插入图片描述

// 摘自 RevitSdkSamples 
private BRepBuilder CreateCubeImpl()
{
    // create a BRepBuilder; add faces to build a cube

    // BRepType.Solid,表示面都需要朝外;朝内就是Void
    BRepBuilder brepBuilder = new BRepBuilder(BRepType.Solid);
    
    // a cube 100x100x100, from (0,0,0) to (100, 100, 100)
    
    // 1. Planes.
    // 6个面,面的朝向如图
    Plane bottom = Plane.CreateByOriginAndBasis(new XYZ(50, 50, 0), new XYZ(1, 0, 0), new XYZ(0, 1, 0)); // bottom. XY plane, Z = 0, normal pointing inside the cube.
    Plane top = Plane.CreateByOriginAndBasis(new XYZ(50, 50, 100), new XYZ(1, 0, 0), new XYZ(0, 1, 0)); // top. XY plane, Z = 100, normal pointing outside the cube.
    Plane front = Plane.CreateByOriginAndBasis(new XYZ(100, 50, 50), new XYZ(0, 0, 1), new XYZ(0, 1, 0)); // front side. ZY plane, X = 0, normal pointing inside the cube.
    Plane back = Plane.CreateByOriginAndBasis(new XYZ(0, 50, 50), new XYZ(0, 0, 1), new XYZ(0, 1, 0)); // back side. ZY plane, X = 0, normal pointing outside the cube.
    Plane left = Plane.CreateByOriginAndBasis(new XYZ(50, 0, 50), new XYZ(0, 0, 1), new XYZ(1, 0, 0)); // left side. ZX plane, Y = 0, normal pointing inside the cube
    Plane right = Plane.CreateByOriginAndBasis(new XYZ(50, 100, 50), new XYZ(0, 0, 1), new XYZ(1, 0, 0)); // right side. ZX plane, Y = 100, normal pointing outside the cube
                                                   
    // 2. Faces.
    // 填充Face,注意参数True/False,表示面是否需要翻转
    // 面需要朝外,看图,bottom/front/left 都需要翻转面朝向
    BRepBuilderGeometryId faceId_Bottom = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(bottom, null), true);
    BRepBuilderGeometryId faceId_Top = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(top, null), false);
    BRepBuilderGeometryId faceId_Front = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(front, null), true);
    BRepBuilderGeometryId faceId_Back = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(back, null), false);
    BRepBuilderGeometryId faceId_Left = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(left, null), true);
    BRepBuilderGeometryId faceId_Right = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(right, null), false);
    
    // 3. Edges.
    
    // 3.a (define edge geometry)
    // 定义12条边,边的方向如图
    // walk around bottom face
    BRepBuilderEdgeGeometry edgeBottomFront = BRepBuilderEdgeGeometry.Create(new XYZ(100, 0, 0), new XYZ(100, 100, 0));
    BRepBuilderEdgeGeometry edgeBottomRight = BRepBuilderEdgeGeometry.Create(new XYZ(100, 100, 0), new XYZ(0, 100, 0));
    BRepBuilderEdgeGeometry edgeBottomBack = BRepBuilderEdgeGeometry.Create(new XYZ(0, 100, 0), new XYZ(0, 0, 0));
    BRepBuilderEdgeGeometry edgeBottomLeft = BRepBuilderEdgeGeometry.Create(new XYZ(0, 0, 0), new XYZ(100, 0, 0));
    
    // now walk around top face
    BRepBuilderEdgeGeometry edgeTopFront = BRepBuilderEdgeGeometry.Create(new XYZ(100, 0, 100), new XYZ(100, 100, 100));
    BRepBuilderEdgeGeometry edgeTopRight = BRepBuilderEdgeGeometry.Create(new XYZ(100, 100, 100), new XYZ(0, 100, 100));
    BRepBuilderEdgeGeometry edgeTopBack = BRepBuilderEdgeGeometry.Create(new XYZ(0, 100, 100), new XYZ(0, 0, 100));
    BRepBuilderEdgeGeometry edgeTopLeft = BRepBuilderEdgeGeometry.Create(new XYZ(0, 0, 100), new XYZ(100, 0, 100));
    
    // sides
    BRepBuilderEdgeGeometry edgeFrontRight = BRepBuilderEdgeGeometry.Create(new XYZ(100, 100, 0), new XYZ(100, 100, 100));
    BRepBuilderEdgeGeometry edgeRightBack = BRepBuilderEdgeGeometry.Create(new XYZ(0, 100, 0), new XYZ(0, 100, 100));
    BRepBuilderEdgeGeometry edgeBackLeft = BRepBuilderEdgeGeometry.Create(new XYZ(0, 0, 0), new XYZ(0, 0, 100));
    BRepBuilderEdgeGeometry edgeLeftFront = BRepBuilderEdgeGeometry.Create(new XYZ(100, 0, 0), new XYZ(100, 0, 100));

    // 3.b (define the edges themselves)
    // 填充边
    BRepBuilderGeometryId edgeId_BottomFront = brepBuilder.AddEdge(edgeBottomFront);
    BRepBuilderGeometryId edgeId_BottomRight = brepBuilder.AddEdge(edgeBottomRight);
    BRepBuilderGeometryId edgeId_BottomBack = brepBuilder.AddEdge(edgeBottomBack);
    BRepBuilderGeometryId edgeId_BottomLeft = brepBuilder.AddEdge(edgeBottomLeft);
    BRepBuilderGeometryId edgeId_TopFront = brepBuilder.AddEdge(edgeTopFront);
    BRepBuilderGeometryId edgeId_TopRight = brepBuilder.AddEdge(edgeTopRight);
    BRepBuilderGeometryId edgeId_TopBack = brepBuilder.AddEdge(edgeTopBack);
    BRepBuilderGeometryId edgeId_TopLeft = brepBuilder.AddEdge(edgeTopLeft);
    BRepBuilderGeometryId edgeId_FrontRight = brepBuilder.AddEdge(edgeFrontRight);
    BRepBuilderGeometryId edgeId_RightBack = brepBuilder.AddEdge(edgeRightBack);
    BRepBuilderGeometryId edgeId_BackLeft = brepBuilder.AddEdge(edgeBackLeft);
    BRepBuilderGeometryId edgeId_LeftFront = brepBuilder.AddEdge(edgeLeftFront);

    // 4. Loops.
    // 填充轮廓
    BRepBuilderGeometryId loopId_Bottom = brepBuilder.AddLoop(faceId_Bottom);
    BRepBuilderGeometryId loopId_Top = brepBuilder.AddLoop(faceId_Top);
    BRepBuilderGeometryId loopId_Front = brepBuilder.AddLoop(faceId_Front);
    BRepBuilderGeometryId loopId_Back = brepBuilder.AddLoop(faceId_Back);
    BRepBuilderGeometryId loopId_Right = brepBuilder.AddLoop(faceId_Right);
    BRepBuilderGeometryId loopId_Left = brepBuilder.AddLoop(faceId_Left);
    
    // 5. Co-edges. 
    // 定义轮廓,即 向loop中填充边,
    // ① 边需要首尾相连;② 边按右手螺旋定则,方向需要和面一致(即朝外)
    // Bottom face. All edges reversed
    brepBuilder.AddCoEdge(loopId_Bottom, edgeId_BottomFront, true); // other direction in front loop
    brepBuilder.AddCoEdge(loopId_Bottom, edgeId_BottomLeft, true);  // other direction in left loop
    brepBuilder.AddCoEdge(loopId_Bottom, edgeId_BottomBack, true);  // other direction in back loop
    brepBuilder.AddCoEdge(loopId_Bottom, edgeId_BottomRight, true); // other direction in right loop
    brepBuilder.FinishLoop(loopId_Bottom);  // 完成底面轮廓的定义
    brepBuilder.FinishFace(faceId_Bottom);  // 完成底面的定义
    
    // Top face. All edges NOT reversed.
    brepBuilder.AddCoEdge(loopId_Top, edgeId_TopFront, false);  // other direction in front loop.
    brepBuilder.AddCoEdge(loopId_Top, edgeId_TopRight, false);  // other direction in right loop
    brepBuilder.AddCoEdge(loopId_Top, edgeId_TopBack, false);   // other direction in back loop
    brepBuilder.AddCoEdge(loopId_Top, edgeId_TopLeft, false);   // other direction in left loop
    brepBuilder.FinishLoop(loopId_Top);
    brepBuilder.FinishFace(faceId_Top);
    
    // Front face.
    // 略,详情看源码
    // Back face
    // Right face
    // Left face
    
    brepBuilder.Finish();  // 完成构建
    return brepBuilder;
}
// 获取构件结果
Solid mySolid = CreateCubeImpl().GetResult();

3.3. API实用性分析

第一点就是使用繁杂,容易出错。我们看到了,即使是生成一个最简单的正方体,都要大费周章。

而若是使用上一篇写的“GeometryCreationUtilities”呢?

public Solid CreateBox()
{
    XYZ point1 = XYZ.Zero;
    XYZ point2 = new XYZ(100, 0, 0);
    XYZ point3 = new XYZ(100, 100, 0);
    XYZ point4 = new XYZ(0, 100, 0);

    CurveLoop curveLoop = new CurveLoop();
    curveLoop.Append(Line.CreateBound(point1, point2));
    curveLoop.Append(Line.CreateBound(point2, point3));
    curveLoop.Append(Line.CreateBound(point3, point4));
    curveLoop.Append(Line.CreateBound(point4, point1));

    Solid solid = GeometryCreationUtilities.CreateExtrusionGeometry(new List<CurveLoop>() { curveLoop }, XYZ.BasisZ, 100);

    return solid;
}

第二点就是使用难度高。正方体是简单,但若复杂一点呢?曲面、锥面、直纹面、赫尔米特曲面的组合呢,这些面都有现成的API,但问题在于“Edge”,如何去计算多个曲面的交界边是BrepBuilder使用难度高的主要原因。

我这小卡拉米,是算不出曲面与曲面的交线的,就算是最简单的一个平面斜切圆柱,都得写写画画好一会儿。

虽然 Revit Face类提供了计算面交线的方法,但是Surface可没有。Face没有构造方法,只能从Solid上获取。而要使用Brep生成Solid,就需要知道面的交线。这样就死循环了,所以还是得算,这也就是使用难度高。

当然了,该方法有时还是挺好用的,比如说,对一个现有的曲面进行拉伸。

四、总结

这一篇简单写了BrepBilder的使用流程,结合示例进行了解释。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值