日期:2025年10月14日标签:Computer Graphics

OCCT 中常用 Boolean 操作整理:Fuse、Cut、Common、Section、Splitter #

OCCT 的 Boolean Operations Component 不是只有传统的并、交、差。官方把它分成四类:General Fuse OperatorBoolean OperatorSection OperatorSplitter Operator。其中 General Fuse 是 Boolean、Section、Splitter 的基础算法。(Open CASCADE)

1. 先准备两个示例实体 #

下面统一用一个 box 和一个 cylinder 来说明结果:

TopoDS_Solid makeBox()
{
    gp_Pnt min(0, 0, 0);
    gp_Pnt max(100, 80, 30);

    BRepPrimAPI_MakeBox maker(min, max);
    return maker.Solid();
}

TopoDS_Solid makeCylinder()
{
    gp_Ax2 ax2(
        gp_Pnt(50, 40, 0),
        gp_Dir(gp_Dir::D::Z),
        gp_Dir(gp_Dir::D::X)
    );

    BRepPrimAPI_MakeCylinder maker(ax2, 10, 50);
    return maker.Solid();
}

这个例子中:

box:
    X: 0 ~ 100
    Y: 0 ~ 80
    Z: 0 ~ 30

cylinder:
    圆心轴线位置: X = 50, Y = 40
    方向: Z
    半径: 10
    高度: 50

所以圆柱穿过 box,并且在 box 顶部上方还多出一段。


2. OCCT 常用 Boolean 操作总览 #

操作含义常用 API结果类型
Fuse并集BRepAlgoAPI_Fuse通常是 TopoDS_Shape / Solid / Compound
Cut差集BRepAlgoAPI_Cut通常是 TopoDS_Shape / Solid
Common交集BRepAlgoAPI_Common两组对象的公共部分
Section求交线 / 截交边BRepAlgoAPI_Section通常是 Vertex / Edge / Compound of Edges
Splitter切分 Object,但不删除 Object 材料BRepAlgoAPI_Splitter被切开的 Object parts
General Fuse所有输入互相切分,保留全部 split partsBOPAlgo_Builder / BRepAlgoAPI_BuilderAlgoCompound
CellsBuilder在 General Fuse 的 cell 中按规则选择结果BOPAlgo_CellsBuilder自定义组合结果

OCCT 的 BRepAlgoAPI 包中直接列出了常见高级 API:BRepAlgoAPI_CommonBRepAlgoAPI_CutBRepAlgoAPI_FuseBRepAlgoAPI_SectionBRepAlgoAPI_Splitter 等。(Open CASCADE)


3. Boolean Operator:Fuse / Cut / Common #

OCCT 官方把传统 Boolean Operator 定义为两组对象之间的操作:

Objects = 第一组对象
Tools   = 第二组对象

传统 Boolean Operator 包含三种:FUSECOMMONCUT。它们分别表示两组对象的并集、交集和差集。OCCT 还提到一个 Cut21,可以理解成反向差集,也就是 Tools - Objects。(Open CASCADE)

3.1 Fuse:并集 #

Fuse(A, B) = A ∪ B

对于我们的例子:

Fuse(box, cylinder)

结果是 box 和 cylinder 的合并体。因为 cylinder 上方超出 box,所以最终结果会包含 box 以及圆柱伸出 box 顶部的部分。

常用代码:

TopoDS_Shape runFuse()
{
    TopoDS_Solid box = makeBox();
    TopoDS_Solid cylinder = makeCylinder();

    BRepAlgoAPI_Fuse fuse(box, cylinder);
    fuse.Build();

    if (fuse.HasErrors()) {
        fuse.DumpErrors(std::cerr);
        return {};
    }

    return fuse.Shape();
}

也可以使用 SetArguments()SetTools()

TopoDS_Shape runFuse2()
{
    TopoDS_Solid box = makeBox();
    TopoDS_Solid cylinder = makeCylinder();

    TopTools_ListOfShape objects;
    objects.Append(box);

    TopTools_ListOfShape tools;
    tools.Append(cylinder);

    BRepAlgoAPI_Fuse fuse;
    fuse.SetArguments(objects);
    fuse.SetTools(tools);
    fuse.Build();

    if (fuse.HasErrors()) {
        fuse.DumpErrors(std::cerr);
        return {};
    }

    return fuse.Shape();
}

BRepAlgoAPI_Fuse 是 Boolean Union,也就是并集操作。(Open CASCADE)


3.2 Cut:差集 #

Cut(A, B) = A - B

对于我们的例子:

Cut(box, cylinder)

结果是 box 被 cylinder 挖掉。因为圆柱穿过 box,所以结果类似一个带圆柱孔的长方体。

代码:

TopoDS_Shape runCut()
{
    TopoDS_Solid box = makeBox();
    TopoDS_Solid cylinder = makeCylinder();

    BRepAlgoAPI_Cut cut(box, cylinder);
    cut.Build();

    if (cut.HasErrors()) {
        cut.DumpErrors(std::cerr);
        return {};
    }

    return cut.Shape();
}

BRepAlgoAPI_Cut 是 Boolean Subtraction,也就是从 arguments 中减去 tools。(Open CASCADE)

注意:Cut(box, cylinder)Cut(cylinder, box) 完全不同。

Cut(box, cylinder)      = box - cylinder
Cut(cylinder, box)      = cylinder - box

OCCT 中的 Cut21 可以理解为反向 Cut,即 Cut(G2, G1)。(Open CASCADE)


3.3 Common:交集 #

Common(A, B) = A ∩ B

对于我们的例子:

Common(box, cylinder)

结果是 box 和 cylinder 重叠的部分,也就是位于 box 内部的那段圆柱体:

半径: 10
高度: 30
位置: box 内部

代码:

TopoDS_Shape runCommon()
{
    TopoDS_Solid box = makeBox();
    TopoDS_Solid cylinder = makeCylinder();

    BRepAlgoAPI_Common common(box, cylinder);
    common.Build();

    if (common.HasErrors()) {
        common.DumpErrors(std::cerr);
        return {};
    }

    return common.Shape();
}

BRepAlgoAPI_Common 是 Boolean Intersection,即两组对象的交集。(Open CASCADE)


4. Section:求交线 / 截交边 #

Section 不是求公共体积,而是求相交产生的边和点。

Section(A, B) = A 和 B 的交线 / 交点

对于我们的例子:

Section(box, cylinder)

因为 cylinder 完全在 box 的 XY 范围内,并沿 Z 方向穿过 box,所以主要交线出现在:

box 底面 z = 0 与 cylinder 侧面的交线:一个圆
box 顶面 z = 30 与 cylinder 侧面的交线:一个圆

所以结果通常是两个圆形 edge / wire 的 compound,而不是 solid。

代码:

TopoDS_Shape runSection()
{
    TopoDS_Solid box = makeBox();
    TopoDS_Solid cylinder = makeCylinder();

    BRepAlgoAPI_Section section(box, cylinder);
    section.Build();

    if (section.HasErrors()) {
        section.DumpErrors(std::cerr);
        return {};
    }

    return section.Shape();
}

更推荐显式写成 arguments 和 tools:

TopoDS_Shape runSection2()
{
    TopoDS_Solid box = makeBox();
    TopoDS_Solid cylinder = makeCylinder();

    TopTools_ListOfShape objects;
    objects.Append(box);

    TopTools_ListOfShape tools;
    tools.Append(cylinder);

    BRepAlgoAPI_Section section;
    section.SetArguments(objects);
    section.SetTools(tools);
    section.Build();

    if (section.HasErrors()) {
        section.DumpErrors(std::cerr);
        return {};
    }

    return section.Shape();
}

BRepAlgoAPI_Section 的结果由 vertices 和 edges 组成,文档中也说明它用于构造 arguments 和 tools 之间的 Section 操作。(Open CASCADE)

SectionCommon 很容易混淆:

Common(box, cylinder)
    得到公共体积,是一个实体。

Section(box, cylinder)
    得到相交边线,是 edge / wire / compound。

5. Splitter:切分 Object,但不删除 Object #

Splitter 的语义是:

用 Tools 去切分 Objects
结果只包含 Objects 被切分后的 parts
不包含只属于 Tools 的 parts

OCCT 文档对 BRepAlgoAPI_Splitter 的描述是:Objects 是将被切分的形状,Tools 是用来切分 Objects 的形状;结果只包含 Objects 组的 split parts,不包含 Tools 组的 split parts。(Open CASCADE)

对于我们的例子:

Splitter(box, cylinder)

结果不是 box - cylinder,而是:

box 被 cylinder 的面切开
box 内部圆柱区域仍然属于结果
box 外部剩余区域也属于结果

可以理解成 box 被分成了两个部分:

1. box 中 cylinder 占据的圆柱区域
2. box 中 cylinder 外面的剩余区域

代码:

TopoDS_Shape runSplitter()
{
    TopoDS_Solid box = makeBox();
    TopoDS_Solid cylinder = makeCylinder();

    TopTools_ListOfShape objects;
    objects.Append(box);

    TopTools_ListOfShape tools;
    tools.Append(cylinder);

    BRepAlgoAPI_Splitter splitter;
    splitter.SetArguments(objects);
    splitter.SetTools(tools);
    splitter.Build();

    if (splitter.HasErrors()) {
        splitter.DumpErrors(std::cerr);
        return {};
    }

    return splitter.Shape();
}

对比一下:

Cut(box, cylinder)
    删除 box 中被 cylinder 占据的区域。

Splitter(box, cylinder)
    不删除,只是把 box 按 cylinder 分割成多个 part。

6. General Fuse:所有输入互相切分 #

General Fuse 可以理解成 Boolean 操作的“原材料”。它不直接表达并、交、差,而是把所有输入 shape 互相求交并切分,生成全部 split parts。OCCT 文档中给出的两对象关系可以理解为:General Fuse 结果包含 A-only、B-only、A∩B 三类 split parts;Common、Cut、Fuse 都可以从这些 split parts 中组合出来。(Open CASCADE)

对于我们的例子:

GeneralFuse(box, cylinder)

会得到类似这些部分:

1. box 中不属于 cylinder 的部分
2. box 和 cylinder 重叠的圆柱段
3. cylinder 超出 box 顶部的部分

常用低层 API 是:

BOPAlgo_Builder

示意代码:

TopoDS_Shape runGeneralFuse()
{
    TopoDS_Solid box = makeBox();
    TopoDS_Solid cylinder = makeCylinder();

    TopTools_ListOfShape args;
    args.Append(box);
    args.Append(cylinder);

    BOPAlgo_Builder builder;
    builder.SetArguments(args);
    builder.Perform();

    if (builder.HasErrors()) {
        builder.DumpErrors(std::cerr);
        return {};
    }

    return builder.Shape();
}

BRepAlgoAPI_BuilderAlgo 是 General Fuse algorithm 的 API 层基类;BRepAlgoAPI_Splitter 也是基于 General Fuse 的算法。(Open CASCADE)


7. CellsBuilder:复杂布尔表达式 #

CellsBuilder 适合处理更复杂的布尔表达式,比如:

(A ∪ B) - C
A ∩ B ∩ C
A - (B ∪ C)

它的思路是:先通过 General Fuse 把输入 shape 切分成很多 cell,然后你自己选择哪些 cell 进入结果。OCCT 官方介绍中说,Cells Builder 可用于在同一批 shape 上执行多个标准 Boolean 操作,或者构造自定义布尔表达式;它通过一次 intersection 获得 split parts,然后按需要选择 cell。(Open CASCADE)

概念上:

General Fuse:
    给你所有切分后的 cell

CellsBuilder:
    让你挑选需要的 cell,并组成最终结果

8. Arguments、Objects、Tools 的关系 #

在 OCCT Boolean API 里容易混淆的是:

SetArguments(...)
SetTools(...)

对于 CutFuseCommonSplitter 来说,可以这样理解:

Arguments ≈ Objects
Tools     = Tools

即:

algo.SetArguments(objects);
algo.SetTools(tools);

对于 Cut

Result = Objects - Tools

对于 Fuse

Result = Objects ∪ Tools

对于 Common

Result = Objects ∩ Tools

对于 Splitter

Result = Objects 被 Tools 切分后的 parts

OCCT 的 BRepAlgoAPI_BooleanOperation 文档说明,Boolean operation 的参数被分成 Objects 和 Tools 两组,并且支持 FUSECOMMONCUTSECTION 等操作类型。(Open CASCADE)


9. 推荐的通用调用模板 #

日常写 Boolean 操作时,可以统一写成下面这种风格:

TopoDS_Shape runBoolean()
{
    TopoDS_Shape object = makeBox();
    TopoDS_Shape tool = makeCylinder();

    TopTools_ListOfShape objects;
    objects.Append(object);

    TopTools_ListOfShape tools;
    tools.Append(tool);

    BRepAlgoAPI_Cut algo; // 可换成 Fuse / Common / Section / Splitter
    algo.SetArguments(objects);
    algo.SetTools(tools);
    algo.Build();

    if (algo.HasErrors()) {
        algo.DumpErrors(std::cerr);
        return {};
    }

    algo.DumpWarnings(std::cerr);

    return algo.Shape();
}

这里的:

BRepAlgoAPI_Cut algo;

可以替换为:

BRepAlgoAPI_Fuse algo;
BRepAlgoAPI_Common algo;
BRepAlgoAPI_Section algo;
BRepAlgoAPI_Splitter algo;

10. 实战注意事项 #

10.1 返回类型尽量用 TopoDS_Shape #

除了你明确知道结果一定是 solid,否则 Boolean 结果建议先用:

TopoDS_Shape

例如:

TopoDS_Shape result = section.Shape();

不要直接假设:

TopoDS_Solid result = section.Shape(); // 不推荐

因为 Section 的结果通常是 edges,不是 solid。


10.2 Section 结果需要遍历 edge #

Section 返回的通常是一个 compound,里面包含若干 edge。可以这样取出:

TopoDS_Shape sectionShape = runSection();

for (TopExp_Explorer exp(sectionShape, TopAbs_EDGE); exp.More(); exp.Next()) {
    TopoDS_Edge edge = TopoDS::Edge(exp.Current());

    Standard_Real first = 0.0;
    Standard_Real last = 0.0;
    Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, first, last);

    if (!curve.IsNull()) {
        std::cout << "Edge curve range: "
                  << first << " ~ " << last << std::endl;
    }
}

10.3 不要返回局部对象引用 #

错误写法:

const TopoDS_Solid& makeBox()
{
    TopoDS_Solid box = BRepPrimAPI_MakeBox(100, 80, 30).Solid();
    return box; // 错误:返回局部变量引用
}

正确写法:

TopoDS_Solid makeBox()
{
    TopoDS_Solid box = BRepPrimAPI_MakeBox(100, 80, 30).Solid();
    return box;
}

调用处也建议按值接收:

TopoDS_Solid box = makeBox();
TopoDS_Solid cylinder = makeCylinder();

11. 一句话记忆 #

Fuse:
    合并,A ∪ B

Cut:
    减去,A - B

Common:
    只保留重叠体积,A ∩ B

Section:
    只要相交线 / 相交点,不要体积

Splitter:
    用 Tool 切分 Object,但 Object 的材料不删除

General Fuse:
    所有输入互相切分,保留所有 split parts

CellsBuilder:
    从 split cells 中自己挑选要保留的部分

最常用的五个 API 是:

BRepAlgoAPI_Fuse
BRepAlgoAPI_Cut
BRepAlgoAPI_Common
BRepAlgoAPI_Section
BRepAlgoAPI_Splitter

更底层或更灵活的算法是:

BOPAlgo_Builder        // General Fuse
BOPAlgo_CellsBuilder   // 自定义 cell 组合 / 复杂布尔表达式

(完)

目录