第07章:高级建模技巧
7.1 参数化设计
7.1.1 参数化设计概述
参数化设计是CadQuery的核心优势之一。通过将尺寸和几何关系定义为参数,可以轻松生成不同规格的模型。
参数化设计的优点:
- 快速生成变体模型
- 易于修改和维护
- 支持批量生产
- 便于设计优化
7.1.2 基本参数化
import cadquery as cq
# 定义参数
length = 80.0
width = 60.0
height = 20.0
hole_diameter = 15.0
fillet_radius = 3.0
wall_thickness = 3.0
# 使用参数创建模型
result = (
cq.Workplane("XY")
.box(length, width, height)
.faces(">Z")
.shell(-wall_thickness)
.faces(">Z")
.workplane()
.hole(hole_diameter)
.edges("|Z")
.fillet(fillet_radius)
)
cq.exporters.export(result, "parametric_box.step")
7.1.3 参数化函数
import cadquery as cq
def make_enclosure(length, width, height, wall=2.0, corner_radius=3.0):
"""
创建参数化外壳
Args:
length: 长度
width: 宽度
height: 高度
wall: 壁厚
corner_radius: 圆角半径
"""
result = (
cq.Workplane("XY")
.box(length, width, height)
.edges("|Z")
.fillet(corner_radius)
.faces(">Z")
.shell(-wall)
)
return result
# 生成不同尺寸的外壳
small = make_enclosure(50, 30, 20)
medium = make_enclosure(80, 50, 30, wall=3.0)
large = make_enclosure(120, 80, 40, wall=4.0, corner_radius=5.0)
cq.exporters.export(small, "enclosure_small.step")
cq.exporters.export(medium, "enclosure_medium.step")
cq.exporters.export(large, "enclosure_large.step")
7.1.4 参数关联
import cadquery as cq
# 参数定义
bolt_diameter = 8
bolt_spacing = 30
num_bolts_x = 4
num_bolts_y = 3
# 派生参数(自动计算)
plate_length = bolt_spacing * (num_bolts_x - 1) + bolt_diameter * 4
plate_width = bolt_spacing * (num_bolts_y - 1) + bolt_diameter * 4
plate_thickness = bolt_diameter * 0.5
edge_distance = bolt_diameter * 2
# 创建带关联参数的安装板
result = (
cq.Workplane("XY")
.box(plate_length, plate_width, plate_thickness)
.faces(">Z")
.workplane()
.rarray(bolt_spacing, bolt_spacing, num_bolts_x, num_bolts_y)
.cboreHole(bolt_diameter, bolt_diameter * 1.8, plate_thickness * 0.3)
.edges()
.fillet(plate_thickness * 0.2)
)
cq.exporters.export(result, "mounting_plate.step")
7.1.5 配置驱动设计
import cadquery as cq
# 配置字典
configs = {
"M6": {
"thread_diameter": 6,
"head_diameter": 10,
"head_height": 4,
"length": 20
},
"M8": {
"thread_diameter": 8,
"head_diameter": 13,
"head_height": 5,
"length": 25
},
"M10": {
"thread_diameter": 10,
"head_diameter": 16,
"head_height": 6.4,
"length": 30
}
}
def make_hex_bolt(config):
"""根据配置创建六角螺栓"""
td = config["thread_diameter"]
hd = config["head_diameter"]
hh = config["head_height"]
length = config["length"]
return (
cq.Workplane("XY")
.polygon(6, hd)
.extrude(hh)
.faces("<Z")
.workplane()
.circle(td / 2)
.extrude(-length)
.edges(">Z")
.chamfer(hh * 0.1)
)
# 生成不同规格的螺栓
for name, config in configs.items():
bolt = make_hex_bolt(config)
cq.exporters.export(bolt, f"bolt_{name}.step")
7.2 复杂曲面建模
7.2.1 样条曲面
import cadquery as cq
import math
# 创建样条曲线并放样
points1 = [(0, 0), (20, 10), (40, 5), (60, 15), (80, 0)]
points2 = [(0, 0), (20, -10), (40, -5), (60, -15), (80, 0)]
result = (
cq.Workplane("XZ")
.spline(points1)
.close()
.workplane(offset=30)
.spline(points2)
.close()
.loft()
)
cq.exporters.export(result, "spline_surface.step")
7.2.2 旋转曲面
import cadquery as cq
import math
def vase_profile(height, base_radius, neck_radius, belly_radius):
"""创建花瓶轮廓"""
points = []
segments = 50
for i in range(segments + 1):
t = i / segments
h = t * height
# 使用正弦函数创建曲线轮廓
if t < 0.3:
r = base_radius + (belly_radius - base_radius) * math.sin(t / 0.3 * math.pi / 2)
elif t < 0.7:
r = belly_radius * (1 + 0.1 * math.sin((t - 0.3) / 0.4 * 2 * math.pi))
else:
r = belly_radius + (neck_radius - belly_radius) * math.sin((t - 0.7) / 0.3 * math.pi / 2)
points.append((r, h))
return points
# 创建花瓶
profile = vase_profile(100, 15, 10, 30)
vase = (
cq.Workplane("XZ")
.moveTo(0, 0)
.spline(profile)
.lineTo(0, 100)
.close()
.revolve(360, (0, 0, 0), (0, 1, 0))
.shell(-2) # 创建空心
)
cq.exporters.export(vase, "vase.step")
7.2.3 扫略曲面
import cadquery as cq
import math
# 创建螺旋路径
def helix_path(radius, pitch, turns):
points = []
segments = int(turns * 36)
for i in range(segments + 1):
angle = 2 * math.pi * i / 36
x = radius * math.cos(angle)
y = radius * math.sin(angle)
z = pitch * i / 36
points.append((x, y, z))
return points
# 使用内置的螺旋线
helix = cq.Wire.makeHelix(pitch=10, height=80, radius=25)
# 创建截面
# 异形截面
profile = (
cq.Workplane("XY")
.ellipse(5, 3)
)
# 沿螺旋线扫略
result = profile.sweep(helix, isFrenet=True)
cq.exporters.export(result, "helix_sweep.step")
7.2.4 放样曲面进阶
import cadquery as cq
from cadquery import Sketch
# 多截面放样
s1 = Sketch().rect(40, 40).vertices().fillet(5)
s2 = Sketch().circle(15)
s3 = Sketch().polygon(8, 20).vertices().fillet(2)
s4 = Sketch().rect(30, 30).vertices().fillet(8)
result = (
cq.Workplane("XY")
.placeSketch(s1)
.workplane(offset=20)
.placeSketch(s2)
.workplane(offset=20)
.placeSketch(s3)
.workplane(offset=20)
.placeSketch(s4)
.loft()
)
cq.exporters.export(result, "multi_loft.step")
7.3 图案和阵列
7.3.1 矩形阵列
import cadquery as cq
# 使用rarray创建矩形阵列
result = (
cq.Workplane("XY")
.box(100, 80, 10)
.faces(">Z")
.workplane()
.rarray(15, 15, 5, 4) # x间距, y间距, x数量, y数量
.hole(5)
)
cq.exporters.export(result, "rect_array.step")
7.3.2 极坐标阵列
import cadquery as cq
# 圆周阵列
result = (
cq.Workplane("XY")
.circle(50)
.extrude(10)
.faces(">Z")
.workplane()
.polarArray(35, 0, 360, 8) # 半径, 起始角, 结束角, 数量
.hole(8)
.faces(">Z")
.workplane()
.hole(20) # 中心孔
)
cq.exporters.export(result, "polar_array.step")
7.3.3 自定义点阵列
import cadquery as cq
import math
# 自定义点位置
custom_points = [
(0, 0),
(30, 0),
(15, 26), # 三角形布局
(-30, 15),
(30, 30),
]
result = (
cq.Workplane("XY")
.box(100, 80, 10)
.faces(">Z")
.workplane()
.pushPoints(custom_points)
.hole(8)
)
cq.exporters.export(result, "custom_array.step")
7.3.4 沿路径阵列
import cadquery as cq
import math
# 沿曲线分布
def points_along_curve(num_points):
points = []
for i in range(num_points):
t = i / (num_points - 1)
x = 80 * t - 40
y = 20 * math.sin(t * 2 * math.pi)
points.append((x, y))
return points
# 创建基板
base = cq.Workplane("XY").box(100, 60, 5)
# 沿曲线放置凸台
curve_points = points_along_curve(10)
result = (
base
.faces(">Z")
.workplane()
.pushPoints(curve_points)
.circle(3)
.extrude(8)
)
cq.exporters.export(result, "curve_array.step")
7.4 镜像与对称
7.4.1 平面镜像
import cadquery as cq
# 创建半边模型
half = (
cq.Workplane("XY")
.moveTo(0, 0)
.lineTo(30, 0)
.lineTo(30, 20)
.lineTo(20, 20)
.lineTo(20, 10)
.lineTo(0, 10)
.close()
.extrude(10)
)
# 镜像得到完整模型
result = half.mirror("XZ")
# 或者使用union合并原始和镜像
full = half.union(half.mirror("XZ"))
cq.exporters.export(full, "mirrored.step")
7.4.2 多轴对称
import cadquery as cq
# 创建四分之一模型
quarter = (
cq.Workplane("XY")
.moveTo(0, 0)
.lineTo(20, 0)
.threePointArc((28, 8), (20, 20))
.lineTo(0, 20)
.close()
.extrude(10)
)
# X和Y方向都镜像
half = quarter.union(quarter.mirror("YZ"))
full = half.union(half.mirror("XZ"))
cq.exporters.export(full, "quad_symmetric.step")
7.4.3 旋转对称
import cadquery as cq
import math
# 创建一个扇区
sector = (
cq.Workplane("XY")
.moveTo(0, 0)
.lineTo(30, 0)
.threePointArc((30 * math.cos(math.pi/3), 30 * math.sin(math.pi/3)),
(30 * math.cos(math.pi/6), 30 * math.sin(math.pi/6)))
.lineTo(0, 0)
.close()
.extrude(5)
)
# 旋转复制
result = sector
for i in range(1, 6):
angle = i * 60
rotated = sector.rotate((0, 0, 0), (0, 0, 1), angle)
result = result.union(rotated)
cq.exporters.export(result, "rotational_symmetric.step")
7.5 布尔运算进阶
7.5.1 复杂布尔组合
import cadquery as cq
# 创建基础形状
box = cq.Workplane("XY").box(50, 50, 30)
cylinder = cq.Workplane("XY").cylinder(40, 15)
sphere = cq.Workplane("XY").sphere(20)
# 复杂布尔运算
# box和cylinder的并集,减去sphere
result = box.union(cylinder).cut(sphere)
cq.exporters.export(result, "complex_boolean.step")
7.5.2 交集应用
import cadquery as cq
# 使用交集创建特殊形状
# 两个圆柱的交集(双曲抛物面)
cyl1 = cq.Workplane("XY").cylinder(60, 25)
cyl2 = cq.Workplane("XZ").cylinder(60, 25)
result = cyl1.intersect(cyl2)
cq.exporters.export(result, "cylinder_intersect.step")
7.5.3 条件布尔运算
import cadquery as cq
def make_part_with_holes(add_holes=True, num_holes=4):
"""根据条件添加特征"""
result = (
cq.Workplane("XY")
.box(80, 60, 10)
.edges("|Z")
.fillet(5)
)
if add_holes and num_holes > 0:
result = (
result
.faces(">Z")
.workplane()
.rarray(60 / (num_holes - 1), 40, num_holes, 2)
.hole(5)
)
return result
# 生成不同版本
part_no_holes = make_part_with_holes(add_holes=False)
part_4_holes = make_part_with_holes(num_holes=4)
part_6_holes = make_part_with_holes(num_holes=6)
7.6 特征识别与操作
7.6.1 面积筛选
import cadquery as cq
# 创建模型
model = (
cq.Workplane("XY")
.box(50, 30, 20)
.faces(">Z")
.workplane()
.circle(10)
.extrude(10)
)
# 获取所有面并按面积排序
faces = model.faces().vals()
face_areas = [(f, f.Area()) for f in faces]
face_areas.sort(key=lambda x: x[1], reverse=True)
print("面积从大到小:")
for f, area in face_areas[:5]:
print(f" {area:.2f} mm²")
7.6.2 边长筛选
import cadquery as cq
# 创建模型
model = cq.Workplane("XY").box(50, 30, 20)
# 获取所有边
edges = model.edges().vals()
# 按长度筛选
long_edges = [e for e in edges if e.Length() > 25]
print(f"长度>25mm的边数: {len(long_edges)}")
7.6.3 几何中心定位
import cadquery as cq
# 创建模型
model = (
cq.Workplane("XY")
.box(50, 30, 20)
.faces(">Z")
.workplane()
.rect(30, 15)
.cutBlind(-10)
)
# 获取边界框信息
bb = model.val().BoundingBox()
center = ((bb.xmin + bb.xmax) / 2,
(bb.ymin + bb.ymax) / 2,
(bb.zmin + bb.zmax) / 2)
print(f"模型中心: {center}")
print(f"尺寸: {bb.xmax - bb.xmin} x {bb.ymax - bb.ymin} x {bb.zmax - bb.zmin}")
7.7 性能优化
7.7.1 减少布尔运算
import cadquery as cq
# 低效:多次布尔运算
# def inefficient():
# result = cq.Workplane("XY").box(100, 100, 10)
# for i in range(10):
# for j in range(10):
# hole = cq.Workplane("XY").center(i*10, j*10).circle(3).extrude(15)
# result = result.cut(hole)
# return result
# 高效:使用阵列一次性操作
def efficient():
result = (
cq.Workplane("XY")
.box(100, 100, 10)
.faces(">Z")
.workplane()
.rarray(10, 10, 10, 10)
.hole(6)
)
return result
model = efficient()
cq.exporters.export(model, "optimized_holes.step")
7.7.2 简化几何
import cadquery as cq
# 使用合理的精度
# 对于大型模型,可以降低某些特征的精度
def make_model(fillet_segments=20):
"""控制圆角分段数"""
result = (
cq.Workplane("XY")
.box(50, 50, 20)
.edges()
.fillet(3)
)
return result
model = make_model()
7.7.3 延迟求值
import cadquery as cq
# 构建操作链而不立即求值
operations = [
lambda wp: wp.box(50, 50, 20),
lambda wp: wp.faces(">Z").workplane().hole(10),
lambda wp: wp.edges("|Z").fillet(2),
]
# 按需执行
def build_model(ops):
result = cq.Workplane("XY")
for op in ops:
result = op(result)
return result
model = build_model(operations)
7.8 调试技巧
7.8.1 中间结果可视化
import cadquery as cq
# 在CQ-editor中分步显示
# step1 = cq.Workplane("XY").box(50, 50, 20)
# show_object(step1, name="step1_box", options={"color": "blue"})
# step2 = step1.faces(">Z").workplane().hole(10)
# show_object(step2, name="step2_hole", options={"color": "green"})
# step3 = step2.edges("|Z").fillet(3)
# show_object(step3, name="step3_fillet", options={"color": "red"})
7.8.2 几何信息打印
import cadquery as cq
def debug_model(model, name="model"):
"""打印模型的详细信息"""
print(f"\n=== {name} 信息 ===")
# 边界框
bb = model.val().BoundingBox()
print(f"边界框: ({bb.xmin:.2f}, {bb.ymin:.2f}, {bb.zmin:.2f}) - "
f"({bb.xmax:.2f}, {bb.ymax:.2f}, {bb.zmax:.2f})")
# 体积
vol = model.val().Volume()
print(f"体积: {vol:.2f} mm³")
# 面数和边数
num_faces = len(model.faces().vals())
num_edges = len(model.edges().vals())
print(f"面数: {num_faces}")
print(f"边数: {num_edges}")
# 使用
model = cq.Workplane("XY").box(30, 20, 10)
debug_model(model, "测试盒子")
7.8.3 选择器验证
import cadquery as cq
model = cq.Workplane("XY").box(30, 20, 10)
# 验证选择器
def test_selector(model, selector_str, element_type="faces"):
"""测试选择器是否正确选择元素"""
try:
if element_type == "faces":
selected = model.faces(selector_str)
elif element_type == "edges":
selected = model.edges(selector_str)
elif element_type == "vertices":
selected = model.vertices(selector_str)
count = len(selected.vals())
print(f"选择器 '{selector_str}' ({element_type}): 选中 {count} 个元素")
return True
except Exception as e:
print(f"选择器 '{selector_str}' 错误: {e}")
return False
# 测试各种选择器
test_selector(model, ">Z")
test_selector(model, "<Z")
test_selector(model, "|Z", "edges")
test_selector(model, "#Z", "edges")
7.9 自由函数API形状操作
第1章介绍了自由函数(Free Function)API 的基本概念和 cq.cut、cq.fuse、cq.intersect 等基础操作。本节进一步介绍 v2.6+ 引入的形状级操作方法,以及更丰富的自由函数应用场景。
7.9.1 自由函数布尔运算
与 Workplane 方法链不同,自由函数直接操作 Shape 对象,适合在非 Workplane 上下文中组合已有实体:
import cadquery as cq
# 创建两个独立形状
box = cq.Workplane("XY").box(30, 30, 20).val()
cyl = cq.Workplane("XY").cylinder(12, 25).val()
# 自由函数版布尔运算
union = cq.fuse(box, cyl) # 并集
diff = cq.cut(box, cyl) # 差集(box减去cyl)
inter = cq.intersect(box, cyl) # 交集
# 将结果包装回 Workplane 以便导出
cq.exporters.export(cq.Workplane("XY").newObject([union]), "ff_union.step")
cq.exporters.export(cq.Workplane("XY").newObject([diff]), "ff_diff.step")
| 方法链 | 自由函数 | 说明 |
|---|---|---|
wp1.union(wp2) |
cq.fuse(shape1, shape2) |
布尔并集 |
wp1.cut(wp2) |
cq.cut(shape1, shape2) |
布尔差集 |
wp1.intersect(wp2) |
cq.intersect(shape1, shape2) |
布尔交集 |
7.9.2 形状级移除:Shape.remove()
v2.6+ 引入的 Shape.remove() 方法直接在形状层面执行切除操作,无需 Workplane 上下文:
import cadquery as cq
# 创建底板
plate = cq.Workplane("XY").box(50, 40, 10).val()
# 在形状级别切除孔洞
hole = cq.Workplane("XY").workplane(offset=15).cylinder(4, 15).val()
result = plate.remove(hole)
cq.exporters.export(cq.Workplane("XY").newObject([result]), "shape_remove.step")
7.9.3 面沿法向拉伸:Face.extend()
Face.extend(amount) 将单个面沿其法线方向拉伸指定距离,返回拉伸后的实体。这一操作在需要对面进行局部增厚时非常有用:
import cadquery as cq
# 创建基础块并提取顶面
block = cq.Workplane("XY").box(30, 30, 10)
top_face = block.faces(">Z").val()
# 将顶面沿法向(Z轴正方向)拉伸5mm
extended = top_face.extend(5)
# 与原块合并
result = block.union(cq.Workplane("XY").newObject([extended]))
cq.exporters.export(result, "face_extend.step")
7.9.4 实体内部挖腔:Solid.addCavity()
Solid.addCavity(cavity) 从一个实体内挖去另一个实体,形成空腔结构:
import cadquery as cq
# 创建外壳实体
outer = cq.Workplane("XY").box(40, 40, 30).val()
# 创建内部空腔形状
inner = cq.Workplane("XY").box(30, 30, 25).val()
# 挖腔
hollow = outer.addCavity(inner)
cq.exporters.export(cq.Workplane("XY").newObject([hollow]), "cavity.step")
7.9.5 缝合操作:cq.stitch()
cq.stitch() 将一组共边的面缝合为壳体(Shell)或实体(Solid)。适用于从零散面片构建模型或修复非流形几何:
import cadquery as cq
# 创建盒子的六个面
box = cq.Workplane("XY").box(30, 30, 20)
faces = box.faces().vals()
# 将所有面重新缝合为壳
shell = cq.stitch(faces)
# 从壳生成实体
solid = cq.Workplane("XY").newObject([cq.Solid.makeSolid(shell)])
cq.exporters.export(solid, "stitched.step")
7.10 选择器与形状集合操作
v2.5+ 版本增强了选择器返回的形状集合,使其支持类似 Python 列表的函数式遍历操作。这些操作让筛选和批量处理形状变得简洁高效。
7.10.1 按索引访问:[]
形状集合支持下标索引访问,可以直接获取第N个形状:
import cadquery as cq
model = cq.Workplane("XY").box(50, 30, 20)
faces = model.faces().vals()
# 直接访问第0个面
first_face = faces[0]
print(f"第0个面面积: {first_face.Area():.2f} mm²")
# 访问前三个面
first_three = faces[:3]
print(f"前三个面总面积: {sum(f.Area() for f in first_three):.2f} mm²")
7.10.2 按条件筛选:filter()
使用列表推导式按自定义条件筛选形状,返回满足条件的新集合:
import cadquery as cq
model = cq.Workplane("XY").box(50, 30, 20)
# 筛选面积大于500的面
large_faces = [f for f in model.faces().vals() if f.Area() > 500]
print("大面积面:")
for f in large_faces:
print(f" 面积: {f.Area():.2f} mm²")
# 筛选长度大于30的边
long_edges = [e for e in model.edges().vals() if e.Length() > 30]
print(f"长度>30mm的边: {len(long_edges)} 条")
7.10.3 映射操作:map()
对形状集合中的每个元素应用操作,返回结果集合:
import cadquery as cq
model = cq.Workplane("XY").box(50, 30, 20)
# 获取所有面的中心点坐标
centers = [f.Center() for f in model.faces().vals()]
for i, c in enumerate(centers):
print(f"面{i} 中心: ({c.x:.1f}, {c.y:.1f}, {c.z:.1f})")
7.10.4 排序操作:sort()
按指定属性对形状集合排序:
import cadquery as cq
model = cq.Workplane("XY").box(50, 30, 20)
# 按面积从小到大排序
faces = model.faces().vals()
sorted_faces = sorted(faces, key=lambda f: f.Area())
print("面按面积升序:")
for f in sorted_faces:
print(f" {f.Area():.2f} mm²")
7.10.5 批量操作:apply()
对集合中的每个形状应用选择器操作,实现批量特征提取:
import cadquery as cq
model = (
cq.Workplane("XY")
.box(50, 30, 20)
.faces(">Z")
.workplane()
.rect(30, 15)
.cutBlind(-10)
)
# 获取每个面中的所有边
all_faces = model.faces().vals()
for i, face in enumerate(all_faces):
edges = face.Edges()
print(f"面{i} 包含 {len(edges)} 条边")
7.10.6 方法调用:invoke()
在形状集合的每个元素上直接调用方法:
import cadquery as cq
model = cq.Workplane("XY").box(50, 30, 20)
# 获取所有面的类型信息
for face in model.faces().vals():
surf_type = type(face).__name__
print(f"面类型: {surf_type}, 面积: {face.Area():.2f} mm²")
7.11 本章小结
本章介绍了CadQuery的高级建模技巧:
- 参数化设计
- 参数定义和关联
- 参数化函数
- 配置驱动设计
- 复杂曲面
- 样条曲面
- 旋转曲面
- 扫略和放样
- 图案和阵列
- 矩形阵列
- 极坐标阵列
- 自定义阵列
- 镜像与对称
- 平面镜像
- 多轴对称
- 旋转对称
- 布尔运算进阶
- 复杂组合
- 条件布尔
- 特征识别与操作
- 面积与边长筛选
- 几何中心定位
- 性能优化
- 减少布尔运算
- 简化几何
- 调试技巧
- 中间结果可视化
- 几何信息打印
- 自由函数API形状操作
- 自由函数布尔运算
- 形状级移除与拉伸
- 内部挖腔与缝合
- 选择器与形状集合操作
- 按条件筛选与映射
- 排序与索引访问
- 批量操作
通过本章学习,您应该能够:
- 创建参数化的复杂模型
- 使用高级曲面建模技术
- 高效地创建图案和阵列
- 使用自由函数API进行形状级操作
- 灵活运用选择器与形状集合操作
- 优化模型性能
- 有效调试CadQuery脚本
下一章我们将进入二次开发部分,学习如何扩展CadQuery功能。