布尔运算¶
约 136 个字 295 行代码 5 张图片 预计阅读时间 4 分钟
Cut(差集)¶
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut
from OCC.Display.SimpleGui import init_display
display, start_display, *_ = init_display()
# 几何体 A:立方体
box = BRepPrimAPI_MakeBox(20, 20, 20).Shape()
# 几何体 B:圆柱
cyl = BRepPrimAPI_MakeCylinder(10, 20).Shape()
# 布尔差集:A - B
cut = BRepAlgoAPI_Cut(box, cyl).Shape()
display.DisplayShape(cut, color="BLUE1", transparency=0.5 ,update=True)
display.FitAll()
start_display()
改进版¶
当遇到多次Cut的时候,会遇到小缝隙或重叠,以及计算成本增加,为此,可以在原来的基础上增加一个更为稳健的版本:
def fuzzy_cut(shape_A, shape_B, tol=5e-5, parallel=False):
"""returns shape_A - shape_B"""
cut = BRepAlgoAPI_Cut()
L1 = TopTools_ListOfShape()
L1.Append(shape_A)
L2 = TopTools_ListOfShape()
L2.Append(shape_B)
cut.SetArguments(L1)
cut.SetTools(L2)
cut.SetFuzzyValue(tol)
cut.SetRunParallel(parallel)
cut.Build()
return cut.Shape()
允许A、B为列表的形式,内部可开启并行开关parallel
,现在再来试一下,多次布尔的效果:
import random
import time
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder
from OCC.Core.gp import gp_Pnt, gp_Vec, gp_Ax2, gp_Dir
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut
from OCC.Core.TopTools import TopTools_ListOfShape
import OCC.Core.Quantity as Q
from OCC.Display.SimpleGui import init_display
def random_vec():
x, y, z = [random.uniform(-1.0, 1.0) for _ in range(3)]
return gp_Vec(x, y, z)
def fuzzy_cut(shape_a, shape_b, tol=1e-4, parallel=True):
"""
返回 shape_a - shape_b 的布尔差集。
关键:SetFuzzyValue(tol) 允许“模糊”匹配,缓解共面/微小间隙导致失败的问题。
"""
op = BRepAlgoAPI_Cut()
lst_a = TopTools_ListOfShape()
lst_a.Append(shape_a)
lst_b = TopTools_ListOfShape()
lst_b.Append(shape_b)
op.SetArguments(lst_a)
op.SetTools(lst_b)
op.SetFuzzyValue(tol) # 模糊容差(根据模型尺度适当调)
op.SetRunParallel(parallel) # 可选:多线程
op.Build()
return op.Shape()
def main():
display, start_display, _, _ = init_display()
scope = 200.0
n_cyl = 40
box = BRepPrimAPI_MakeBox(scope, scope, scope).Shape()
def make_random_cylinder():
ax2 = gp_Ax2()
# 随机中心位置
ax2.SetLocation(gp_Pnt((random_vec() * scope).XYZ()))
# 随机轴向
ax2.SetDirection(gp_Dir(random_vec()))
cyl = BRepPrimAPI_MakeCylinder(ax2, random.uniform(8.0, 36.0), 5000.0)
return cyl.Shape()
t0 = time.time()
shp = box
for i in range(n_cyl):
cyl = make_random_cylinder()
ti = time.time()
shp = fuzzy_cut(shp, cyl, tol=1e-4, parallel=False)
print(f"Cut #{i:02d} took {time.time() - ti:.3f}s")
print(f"Total time: {time.time() - t0:.3f}s")
display.DisplayShape(shp, color = Q.Quantity_NOC_SKYBLUE, transparency=0.3,update=True)
display.FitAll()
start_display()
if __name__ == "__main__":
main()
Fuse(并集)¶
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse
from OCC.Display.SimpleGui import init_display
display, start_display, *_ = init_display()
# 几何体 A:立方体
box = BRepPrimAPI_MakeBox(20, 20, 20).Shape()
# 几何体 B:圆柱
cyl = BRepPrimAPI_MakeCylinder(10, 20).Shape()
# 布尔并集:A ∪ B
result = BRepAlgoAPI_Fuse(box, cyl).Shape()
display.DisplayShape(result, color="BLUE1", transparency=0.5 ,update=True)
display.FitAll()
start_display()
相同的道理可以做一个更为稳健的并集操作:
def fuzzy_fuse(shape_a, shape_b, tol=1e-4, parallel=False):
"""
返回 shape_a ∪ shape_b 的模糊并集结果
"""
op = BRepAlgoAPI_Fuse()
lst_a = TopTools_ListOfShape()
lst_a.Append(shape_a)
lst_b = TopTools_ListOfShape()
lst_b.Append(shape_b)
op.SetArguments(lst_a)
op.SetTools(lst_b)
op.SetFuzzyValue(tol)
op.SetRunParallel(parallel)
op.Build()
return op.Shape()
Common(交集)¶
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Common
from OCC.Display.SimpleGui import init_display
display, start_display, *_ = init_display()
# 几何体 A:立方体
box = BRepPrimAPI_MakeBox(20, 20, 20).Shape()
# 几何体 B:圆柱
cyl = BRepPrimAPI_MakeCylinder(10, 20).Shape()
# 布尔交集:A ∩ B
result = BRepAlgoAPI_Common(box, cyl).Shape()
display.DisplayShape(result, color="BLUE1", transparency=0.5 ,update=True)
display.FitAll()
start_display()
相同的道理,提供一个更稳健的版本:
def fuzzy_common(shape_a, shape_b, tol=1e-4, parallel=False):
"""
返回交集 shape_a ∩ shape_b
"""
op = BRepAlgoAPI_Common()
lst_a = TopTools_ListOfShape(); lst_a.Append(shape_a)
lst_b = TopTools_ListOfShape(); lst_b.Append(shape_b)
op.SetArguments(lst_a)
op.SetTools(lst_b)
op.SetFuzzyValue(tol)
op.SetRunParallel(parallel)
op.Build()
return op.Shape()
综合应用¶
from OCC.Core.gp import gp_Pnt
from OCC.Core.BRepBuilderAPI import (
BRepBuilderAPI_MakeFace,
BRepBuilderAPI_MakePolygon,
BRepBuilderAPI_Sewing,
BRepBuilderAPI_MakeSolid,
)
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse, BRepAlgoAPI_Common, BRepAlgoAPI_Cut
from OCC.Core.TopoDS import topods
from OCC.Display.SimpleGui import init_display
def MakeSolidFromShell(shell):
ms = BRepBuilderAPI_MakeSolid()
ms.Add(topods.Shell(shell))
solid = ms.Solid()
return solid
def make_face_from_4_points(pnt1, pnt2, pnt3, pnt4):
# 用 4 点闭合多段线 Wire
poly = BRepBuilderAPI_MakePolygon(pnt1, pnt2, pnt3, pnt4, True).Wire()
# 做Face
return BRepBuilderAPI_MakeFace(poly).Face()
def get_faceted_L_shape(x, y, z):
# 手工拼 8 个面(其中 2 个是 6 边形面)
pnt_A = gp_Pnt(x + 0, y + 0, z + 0)
pnt_B = gp_Pnt(x + 20, y + 0, z + 0)
pnt_C = gp_Pnt(x + 20, y + 10, z + 0)
pnt_D = gp_Pnt(x + 0, y + 10, z + 0)
pnt_E = gp_Pnt(x + 0, y + 0, z + 20)
pnt_F = gp_Pnt(x + 10, y + 0, z + 20)
pnt_G = gp_Pnt(x + 10, y + 10, z + 20)
pnt_H = gp_Pnt(x + 0, y + 10, z + 20)
pnt_I = gp_Pnt(x + 10, y + 0, z + 10)
pnt_J = gp_Pnt(x + 10, y + 10, z + 10)
pnt_K = gp_Pnt(x + 20, y + 0, z + 10)
pnt_L = gp_Pnt(x + 20, y + 10, z + 10)
face_1 = make_face_from_4_points(pnt_A, pnt_B, pnt_C, pnt_D)
face_2 = make_face_from_4_points(pnt_B, pnt_C, pnt_L, pnt_K)
face_3 = make_face_from_4_points(pnt_E, pnt_F, pnt_G, pnt_H)
face_4 = make_face_from_4_points(pnt_A, pnt_E, pnt_H, pnt_D)
face_5 = make_face_from_4_points(pnt_G, pnt_F, pnt_I, pnt_J)
face_6 = make_face_from_4_points(pnt_I, pnt_K, pnt_L, pnt_J)
polygon_1 = BRepBuilderAPI_MakePolygon()
polygon_1.Add(pnt_A)
polygon_1.Add(pnt_B)
polygon_1.Add(pnt_K)
polygon_1.Add(pnt_I)
polygon_1.Add(pnt_F)
polygon_1.Add(pnt_E)
polygon_1.Close()
face_7 = BRepBuilderAPI_MakeFace(polygon_1.Wire()).Face()
polygon_2 = BRepBuilderAPI_MakePolygon()
polygon_2.Add(pnt_D)
polygon_2.Add(pnt_H)
polygon_2.Add(pnt_G)
polygon_2.Add(pnt_J)
polygon_2.Add(pnt_L)
polygon_2.Add(pnt_C)
polygon_2.Close()
face_8 = BRepBuilderAPI_MakeFace(polygon_2.Wire()).Face()
# 缝合,合并共边,
sew = BRepBuilderAPI_Sewing()
for face in [face_1, face_2, face_3, face_4, face_5, face_6, face_7, face_8]:
sew.Add(face)
sew.Perform()
# 得到封闭壳
return sew.SewedShape()
spacing = 30
# cut (box - box)
yshift = 0
myBox11 = BRepPrimAPI_MakeBox(gp_Pnt(0, yshift, 0), 10, 10, 10).Shape()
myBox12 = BRepPrimAPI_MakeBox(gp_Pnt(5, yshift + 5, 5), 10, 10, 10).Shape()
myCut1 = BRepAlgoAPI_Cut(myBox11, myBox12).Shape()
# cut (L-Shape - box)
yshift += spacing
myBox21 = get_faceted_L_shape(0, yshift, 0)
myBox22 = BRepPrimAPI_MakeBox(gp_Pnt(15, yshift + 5, 5), 10, 10, 10).Shape()
myCut2 = BRepAlgoAPI_Cut(myBox21, myBox22).Shape()
# intersection (L-Shape - box)
yshift += spacing
myBox23 = get_faceted_L_shape(0, yshift, 0)
myBox24 = BRepPrimAPI_MakeBox(gp_Pnt(15, yshift + 5, 5), 10, 10, 10).Shape()
myCut3 = BRepAlgoAPI_Common(myBox23, myBox24).Shape()
# Simple box CUT from LShape using MakeSolidFromShell
yshift += spacing
myBox31 = MakeSolidFromShell(get_faceted_L_shape(0, yshift, 0))
myBox32 = BRepPrimAPI_MakeBox(gp_Pnt(15, yshift + 5, 5), 10, 10, 10).Shape()
myCut4 = BRepAlgoAPI_Cut(myBox31, myBox32).Shape()
# Simple box FUSE from LShape using MakeSolidFromShell
yshift += spacing
myBox33 = MakeSolidFromShell(get_faceted_L_shape(0, yshift, 0))
myBox34 = BRepPrimAPI_MakeBox(gp_Pnt(15, yshift + 5, 5), 10, 10, 10).Shape()
myCut5 = BRepAlgoAPI_Fuse(myBox33, myBox34).Shape()
# Simple box COMMON with LShape using MakeSolidFromShell
yshift += spacing
myBox35 = MakeSolidFromShell(get_faceted_L_shape(0, yshift, 0))
myBox36 = BRepPrimAPI_MakeBox(gp_Pnt(15, yshift + 5, 5), 10, 10, 10).Shape()
myCut6 = BRepAlgoAPI_Common(myBox35, myBox36).Shape()
# display stage
display, start_display, add_menu, add_function_to_menu = init_display()
display.DisplayShape(myCut1, color="RED", transparency=0.7)
display.DisplayShape(myCut2, color="BLUE", transparency=0.7)
display.DisplayShape(myCut3, color="WHITE", transparency=0.7)
display.DisplayShape(myCut4, color="GREEN", transparency=0.7)
display.DisplayShape(myCut5, color="YELLOW", transparency=0.7)
display.DisplayShape(myCut6, color="CYAN", transparency=0.7)
display.FitAll()
start_display()