Files
cadSet/SimpleCADAPI/src/simplecadapi/evolve.py
T
2026-07-22 19:38:36 +08:00

592 lines
23 KiB
Python

from simplecadapi import *
import math
from .errors import raise_harness_error
from .math import fit_cubic_bspline_control_points
def make_n_hole_flange_rsolid(
flange_outer_diameter=120.0,
flange_inner_diameter=60.0,
flange_thickness=15.0,
boss_outer_diameter=80.0,
boss_height=5.0,
hole_diameter=8.0,
hole_circle_diameter=100.0,
hole_count=8,
chamfer_size=1.0,
) -> Solid:
"""Create an n-hole flange with a raised boss ring and optional chamfers. The center of the bottom face is placed at the origin."""
print(f"开始创建{hole_count}孔法兰...")
from typing import cast
import math
try:
# 步骤1: 创建法兰主体圆盘
print(" 步骤1: 创建法兰主体圆盘...")
# 创建外圆柱体
outer_cylinder = make_cylinder_rsolid(
radius=flange_outer_diameter / 2,
height=flange_thickness,
bottom_face_center=(0, 0, 0),
)
print(
f" 法兰外圆柱创建成功,直径: {flange_outer_diameter}mm, 厚度: {flange_thickness}mm"
)
# 创建内孔圆柱体
inner_cylinder = make_cylinder_rsolid(
radius=flange_inner_diameter / 2,
height=flange_thickness + 2, # 确保完全切穿
bottom_face_center=(0, 0, -1),
)
print(f" 法兰内孔圆柱创建成功,直径: {flange_inner_diameter}mm")
# 从外圆柱中减去内孔形成法兰主体
flange_body = cut_rsolid(outer_cylinder, inner_cylinder)
body_volume = flange_body.get_volume()
print(f" 法兰主体创建完成,体积: {body_volume:.2f} mm³")
except Exception as e:
print(f" 错误: 步骤1创建法兰主体失败 - {e}")
raise_harness_error(
operation="make_n_hole_flange_rsolid",
what_happened="Failed while creating the flange body.",
possible_causes=[
"The flange diameter or thickness parameters are invalid.",
"The inner cut did not produce a single flange solid.",
],
how_to_fix=[
"Check flange_outer_diameter, flange_inner_diameter, and flange_thickness.",
"Make sure the inner diameter is smaller than the outer diameter.",
],
error=e,
)
try:
# 步骤2: 创建中心凸起圆环(保持中心通孔)
print(" 步骤2: 创建中心凸起圆环...")
# 创建凸起圆环的实心部分(位于法兰顶部)
boss_outer_solid = make_cylinder_rsolid(
radius=boss_outer_diameter / 2,
height=boss_height,
bottom_face_center=(0, 0, flange_thickness),
)
print(
f" 凸起圆环外圆柱创建成功,直径: {boss_outer_diameter}mm,高度: {boss_height}mm"
)
# 创建凸起圆环的内孔(与法兰内孔保持一致)
boss_inner_hole = make_cylinder_rsolid(
radius=flange_inner_diameter / 2,
height=boss_height + 2, # 确保完全切穿凸起圆环
bottom_face_center=(0, 0, flange_thickness - 1),
)
print(f" 凸起圆环内孔创建成功,直径: {flange_inner_diameter}mm")
# 从凸起圆环中减去内孔,形成环形凸起
boss_ring = cut_rsolid(boss_outer_solid, boss_inner_hole)
print(" 凸起圆环内孔切割完成,形成环形凸起")
# 合并法兰主体和凸起圆环
flange_with_boss = union_rsolid([flange_body, boss_ring])
boss_volume = flange_with_boss.get_volume()
print(f" 法兰主体与凸起圆环合并完成,体积: {boss_volume:.2f} mm³")
except Exception as e:
print(f" 错误: 步骤2创建中心凸起圆环失败 - {e}")
raise_harness_error(
operation="make_n_hole_flange_rsolid",
what_happened="Failed while creating or merging the center boss ring.",
possible_causes=[
"The boss ring parameters are incompatible with the flange body.",
"The boss cut or union result was not a single solid.",
],
how_to_fix=[
"Check boss_outer_diameter, boss_height, and flange_inner_diameter.",
"Make sure the boss overlaps the flange body as intended before union.",
],
error=e,
)
try:
# 步骤3: 创建连接孔
print(" 步骤3: 创建连接孔...")
hole_radius = hole_diameter / 2
hole_circle_radius = hole_circle_diameter / 2
total_height = flange_thickness + boss_height + 2 # 确保完全穿透
print(
f" 连接孔参数:孔径{hole_diameter}mm,分布圆直径{hole_circle_diameter}mm,孔数{hole_count}"
)
# 逐个创建每个孔并切割
current_flange = flange_with_boss
angle_step = 360.0 / hole_count
for i in range(hole_count):
angle = i * angle_step
x = hole_circle_radius * math.cos(math.radians(angle))
y = hole_circle_radius * math.sin(math.radians(angle))
# 创建单个孔
hole = make_cylinder_rsolid(
radius=hole_radius,
height=total_height,
bottom_face_center=(x, y, -1), # 从底部向上切割
)
# 切割孔
current_flange = cut_rsolid(current_flange, hole)
print(f" 第{i + 1}个孔位置: ({x:.2f}, {y:.2f}), 角度: {angle:.1f}°")
flange_with_holes = current_flange
final_volume_holes = flange_with_holes.get_volume()
print(f" 所有连接孔切割完成,最终体积: {final_volume_holes:.2f} mm³")
print(f" 总体积减少: {boss_volume - final_volume_holes:.2f} mm³")
except Exception as e:
print(f" 错误: 步骤3创建连接孔失败 - {e}")
raise_harness_error(
operation="make_n_hole_flange_rsolid",
what_happened="Failed while cutting the bolt holes.",
possible_causes=[
"The hole pattern parameters place holes outside the flange body.",
"One of the hole cuts did not produce a valid single solid.",
],
how_to_fix=[
"Check hole_diameter, hole_circle_diameter, and hole_count.",
"Verify the bolt circle lies inside the flange body.",
],
error=e,
)
try:
# 步骤4: 添加倒角处理
print(" 步骤4: 添加倒角处理...")
# 获取所有边
all_edges = flange_with_holes.get_edges()
print(f" 找到{len(all_edges)}条边")
# 尝试对主要边进行倒角
try:
chamfered_flange = chamfer_rsolid(
flange_with_holes, all_edges, chamfer_size
)
print(f" 倒角处理完成,倒角尺寸: {chamfer_size}mm")
except:
print(" 倒角处理失败,使用无倒角版本")
chamfered_flange = flange_with_holes
except Exception as e:
print(f" 警告: 步骤4倒角处理失败,使用无倒角版本 - {e}")
chamfered_flange = flange_with_holes
try:
# 步骤5: 验证最终结果
print(" 步骤5: 验证最终结果...")
if not isinstance(chamfered_flange, Solid):
raise ValueError("最终结果不是有效的Solid对象")
volume = chamfered_flange.get_volume()
print(f" 最终法兰体积: {volume:.2f} mm³")
print(f" 法兰外径: {flange_outer_diameter}mm")
print(f" 法兰内径: {flange_inner_diameter}mm")
print(f" 法兰厚度: {flange_thickness}mm")
print(f" 凸起圆环外径: {boss_outer_diameter}mm")
print(f" 凸起圆环高度: {boss_height}mm")
print(f" 连接孔: {hole_count}个,直径{hole_diameter}mm")
except Exception as e:
print(f" 错误: 步骤5验证失败 - {e}")
raise_harness_error(
operation="make_n_hole_flange_rsolid",
what_happened="The final flange result failed validation.",
possible_causes=[
"An earlier modeling step returned an invalid solid.",
"The final solid lost validity during later feature operations.",
],
how_to_fix=[
"Inspect the intermediate flange body after each step.",
"Temporarily skip chamfering to isolate the first failing stage.",
],
error=e,
)
print(f"{hole_count}孔法兰创建完成!")
return chamfered_flange
def make_naca_propeller_blade_rsolid(
blade_length=5.0,
root_chord=1.5,
tip_chord=0.3,
total_twist_angle=45.0,
num_sections=7,
t_c=0.16,
) -> Solid:
"""Create a single propeller blade solid from a twisted NACA 0016 profile. The blade root starts at the origin and extends along +Z."""
import math
print(f" 参数: 长度={blade_length}, 根部弦长={root_chord}, 叶尖弦长={tip_chord}")
print(f" 扭转角度={total_twist_angle}°, 截面数={num_sections}")
def generate_naca_0016_points(chord_length=1.0, num_points=50):
"""Generate sample points for a NACA 0016 airfoil profile."""
print(f" 生成NACA 0016翼型点,弦长={chord_length:.3f}")
# NACA 0016翼型厚度分布函数
# y/c = 0.16 * (0.2969*sqrt(x/c) - 0.1260*(x/c) - 0.3516*(x/c)^2 + 0.2843*(x/c)^3 - 0.1015*(x/c)^4)
def naca_0016_thickness(x_c):
"""Return the NACA 0016 half-thickness at a given x/c position."""
if x_c < 0 or x_c > 1:
return 0.0
return t_c * (
0.2969 * math.sqrt(x_c)
- 0.1260 * x_c
- 0.3516 * x_c**2
+ 0.2843 * x_c**3
- 0.1015 * x_c**4
)
points = []
# 生成上表面点(从前缘到后缘)
for i in range(num_points + 1):
x_c = i / num_points # x/c从0到1
y_c = naca_0016_thickness(x_c) # 上表面半厚度
x = x_c * chord_length
y = y_c * chord_length
points.append((x, y, 0.0))
# 生成下表面点(从后缘到前缘,排除重复的后缘点)
for i in range(num_points - 1, -1, -1):
x_c = i / num_points
y_c = -naca_0016_thickness(x_c) # 下表面负半厚度
x = x_c * chord_length
y = y_c * chord_length
points.append((x, y, 0.0))
print(f" 生成了 {len(points)} 个翼型点")
return points
try:
# 步骤1: 生成各个径向位置的翼型截面
print(" 步骤1: 生成各个径向位置的翼型截面...")
section_wires = []
for i in range(num_sections):
print(f" 创建第 {i + 1}/{num_sections} 个截面...")
# 计算径向位置 (0到blade_length)
r_i = (i / (num_sections - 1)) * blade_length
print(f" 径向位置: {r_i:.3f}")
# 计算当前弦长(线性插值)
chord_ratio = r_i / blade_length
chord_i = root_chord - chord_ratio * (root_chord - tip_chord)
print(f" 弦长: {chord_i:.3f}")
# 计算当前扭转角度(线性插值)
twist_angle_i = chord_ratio * total_twist_angle
print(f" 扭转角度: {twist_angle_i:.1f}°")
# 生成当前弦长的NACA 0016翼型点
airfoil_points = generate_naca_0016_points(chord_i, num_points=30)
# 创建基础翼型线。BSpline 构造函数接收 exact 控制点;这里先把采样翼型
# 拟合成 cubic 控制多边形,再显式传入解析后的 knot 数据。
if airfoil_points[0] != airfoil_points[-1]:
airfoil_points.append(airfoil_points[0])
airfoil_fit = fit_cubic_bspline_control_points(airfoil_points, tolerance=0.01)
airfoil_wire = make_spline_rwire(
control_points=airfoil_fit.control_points,
knots=airfoil_fit.unique_knots,
multiplicities=airfoil_fit.multiplicities,
)
print(f" 基础翼型线创建成功")
# 先绕Z轴旋转(扭转角度)
if abs(twist_angle_i) > 1e-6: # 如果扭转角度不为零
airfoil_wire = rotate_shape(
airfoil_wire, twist_angle_i, (0, 0, 1), (0, 0, 0)
) # type: ignore
print(f" 翼型扭转完成: {twist_angle_i:.1f}°")
# 然后平移到径向位置
airfoil_wire = translate_shape(airfoil_wire, (0, 0, r_i)) # type: ignore
print(f" 翼型平移到径向位置: Z={r_i:.3f}")
section_wires.append(airfoil_wire)
except Exception as e:
print(f" 错误: 步骤1生成翼型截面失败 - {e}")
raise_harness_error(
operation="make_naca_propeller_blade_rsolid",
what_happened="Failed while generating blade section profiles.",
possible_causes=[
"The airfoil generation produced invalid points.",
"The section transform or twist step produced invalid geometry.",
],
how_to_fix=[
"Check blade_length, root_chord, tip_chord, total_twist_angle, and num_sections.",
"Inspect the generated section wires before lofting.",
],
error=e,
)
try:
# 步骤2: 通过放样创建桨叶实体
print(" 步骤2: 通过放样创建桨叶实体...")
# 验证截面数量
if len(section_wires) < 2:
raise ValueError(
f"截面数量不足:需要至少2个截面,当前有{len(section_wires)}个"
)
print(f" 准备放样 {len(section_wires)} 个截面...")
# 使用放样创建实体
blade_solid = loft_rsolid(section_wires, ruled=False)
print(f" 放样完成")
# 验证结果
if not isinstance(blade_solid, Solid):
raise ValueError("放样结果不是有效的Solid对象")
print(f" 桨叶体积: {blade_solid.get_volume():.6f}")
except Exception as e:
print(f" 错误: 步骤2放样创建实体失败 - {e}")
raise_harness_error(
operation="make_naca_propeller_blade_rsolid",
what_happened="Failed while lofting the blade sections.",
possible_causes=[
"The section wires are not mutually compatible for lofting.",
"One or more section profiles are invalid or self-intersecting.",
],
how_to_fix=[
"Validate every section wire individually.",
"Try reducing twist or simplifying the airfoil section count to isolate the incompatibility.",
],
error=e,
)
print("螺旋桨叶片创建完成!")
print(f"最终参数总结:")
print(f" - 叶片长度: {blade_length}")
print(f" - 根部弦长: {root_chord} -> 叶尖弦长: {tip_chord}")
print(f" - 总扭转: {total_twist_angle}°")
print(f" - 截面数: {num_sections}")
print(f" - 厚度比: {t_c}")
return blade_solid
def make_threaded_rod_rsolid(
thread_diameter=8.0,
thread_length=20.0,
total_length=30.0,
thread_pitch=1.25,
thread_start_position=0.0,
chamfer_size=0.5,
) -> Solid:
"""Create a threaded rod with configurable rod length, thread span, and pitch. The top center is placed at the origin and the rod extends in -Z."""
print(f"开始创建螺杆...")
print(f" 参数: 直径={thread_diameter}mm, 总长度={total_length}mm")
print(
f" 螺纹: 长度={thread_length}mm, 螺距={thread_pitch}mm, 起始位置={thread_start_position}mm"
)
from typing import cast
import math
# 参数验证
if thread_diameter <= 0:
raise ValueError("螺纹直径必须大于0")
if total_length <= 0:
raise ValueError("螺杆总长度必须大于0")
if thread_length <= 0:
raise ValueError("螺纹长度必须大于0")
if thread_pitch <= 0:
raise ValueError("螺纹螺距必须大于0")
if thread_length > total_length:
raise ValueError("螺纹长度不能大于螺杆总长度")
if (
thread_start_position < 0
or thread_start_position + thread_length > total_length
):
raise ValueError("螺纹起始位置或结束位置超出螺杆范围")
try:
# 步骤1: 创建螺杆主体
print(" 步骤1: 创建螺杆主体...")
thread_radius = thread_diameter / 2.0
thread_face = make_circle_rface((0, 0, 0), thread_radius)
thread_solid = extrude_rsolid(thread_face, (0, 0, 1), total_length)
print(
f" 螺杆主体创建成功,直径: {thread_diameter}mm,长度: {total_length}mm"
)
except Exception as e:
print(f" 错误: 步骤1创建螺杆主体失败 - {e}")
raise_harness_error(
operation="make_threaded_rod_rsolid",
what_happened="Failed while creating the rod body.",
possible_causes=[
"The rod diameter or length parameters are invalid.",
"The base rod extrusion failed.",
],
how_to_fix=[
"Check thread_diameter and total_length.",
"Make sure all rod dimensions are positive and finite.",
],
error=e,
)
try:
# 步骤2: 创建螺纹切割体
print(" 步骤2: 创建螺纹切割体...")
# 计算螺纹深度(根据标准螺纹深度约为螺距的0.613倍)
thread_depth = thread_pitch * 0.613 # 约0.766mm for M8
# 定义V型螺纹切割轮廓的顶点
thread_points = [
(float(thread_radius), 0.0, 0.0), # 螺纹大径处,轴向位置0
(
float(thread_radius - thread_depth),
float(thread_pitch * 0.5),
0.0,
), # 螺纹根部,轴向偏移pitch/2
(
float(thread_radius),
float(thread_pitch),
0.0,
), # 螺纹大径处,轴向偏移pitch
]
# 创建V型螺纹切割轮廓
thread_cut_wire = make_polyline_rwire(thread_points, closed=True)
print(f" V型螺纹切割轮廓创建成功,螺纹深度: {thread_depth:.3f}mm")
# 使用螺旋扫掠创建螺纹切割体
helical_cut_solid = helical_sweep_rsolid(
thread_cut_wire, thread_pitch, thread_length, thread_radius
)
# 调整螺旋切割体位置到螺纹起始位置
translated_helical_cut = translate_shape(
helical_cut_solid, (0, 0, -thread_length - thread_start_position)
)
helical_cut_solid = cast(Solid, translated_helical_cut)
print(
f" 螺旋切割体创建成功,螺距: {thread_pitch}mm,起始位置: {thread_start_position}mm"
)
except Exception as e:
print(f" 错误: 步骤2创建螺纹切割体失败 - {e}")
raise_harness_error(
operation="make_threaded_rod_rsolid",
what_happened="Failed while creating the helical thread cutter.",
possible_causes=[
"The thread pitch or thread length is invalid.",
"The helical sweep failed for the chosen thread profile.",
],
how_to_fix=[
"Check thread_pitch, thread_length, and thread_start_position.",
"Try a simpler thread profile or shorter thread span to isolate the sweep failure.",
],
error=e,
)
try:
# 步骤3: 从螺杆中减去螺纹切割体形成螺纹
print(" 步骤3: 切割螺纹...")
thread_solid = cut_rsolid(thread_solid, helical_cut_solid)
print(" 螺纹切割完成")
except Exception as e:
print(f" 错误: 步骤3切割螺纹失败 - {e}")
raise_harness_error(
operation="make_threaded_rod_rsolid",
what_happened="Failed while cutting the thread geometry into the rod body.",
possible_causes=[
"The thread cutter does not intersect the rod body as expected.",
"The boolean cut failed on the current geometry.",
],
how_to_fix=[
"Inspect the rod body and the translated helical cutter before the cut step.",
"Reduce thread depth or simplify the cutter geometry to isolate the boolean failure.",
],
error=e,
)
try:
# 步骤4: 添加螺杆末端倒角
print(" 步骤4: 添加螺杆末端倒角...")
# 使用QL语义选择器选择螺杆底端的圆形边,避免依赖拓扑顺序。
bottom_edge_selector = (
ql.edges()
.where(ql.curve_type("circle"))
.order_by(ql.center_axis("z"))
.take(1)
.exactly(1)
)
# 对螺杆末端进行倒角处理
thread_solid = chamfer_rsolid(thread_solid, bottom_edge_selector, chamfer_size)
print(f" 螺杆末端倒角完成,倒角尺寸: {chamfer_size}mm")
except Exception as e:
print(f" 错误: 步骤4添加螺杆末端倒角失败 - {e}")
print(f" 跳过倒角处理,继续使用原始螺杆")
# 倒角失败时继续使用原始螺杆
try:
# 步骤5: 验证最终结果
print(" 步骤5: 验证最终结果...")
if not isinstance(thread_solid, Solid):
raise ValueError("最终结果不是有效的Solid对象")
volume = thread_solid.get_volume()
print(f" 最终螺杆体积: {volume:.2f} mm³")
print(f" 螺杆直径: {thread_diameter}mm")
print(f" 螺杆总长度: {total_length}mm")
print(f" 螺纹长度: {thread_length}mm")
print(f" 螺纹螺距: {thread_pitch}mm")
print(f" 螺纹起始位置: {thread_start_position}mm")
except Exception as e:
print(f" 错误: 步骤5验证失败 - {e}")
raise_harness_error(
operation="make_threaded_rod_rsolid",
what_happened="The final threaded rod result failed validation.",
possible_causes=[
"An earlier rod or thread step returned an invalid solid.",
"The final result is no longer a valid Solid after feature operations.",
],
how_to_fix=[
"Inspect the intermediate rod body, thread cutter, and final cut result.",
"Temporarily skip chamfering to isolate the first failing stage.",
],
error=e,
)
print("螺杆创建完成!")
return thread_solid