solidworks 二次开发 获取样条曲线bcurve的控制点

Get Curve Spline Points Example (VBA) - 2021 - SOLIDWORKS Design Help

varSplineParams = curve.GetBCurveParams(False)

后11位是控制点的3维坐标

用ezdxf画一下,可以看到控制点不是节点,要算obb就没办法直接用这个控制点

python 复制代码
import win32com.client
import pythoncom
import math
import numpy as np
from win32com.client import VARIANT
import ezdxf
import os
from uuid import uuid4

def get_selected_faces_edges_and_obb():
    """
    获取SolidWorks中选中面的所有边,并计算这些边的OBB包围盒
    """
    try:
        # 初始化COM库
        pythoncom.CoInitialize()
        
        # 连接到SolidWorks应用程序
        sw_app = win32com.client.Dispatch("SldWorks.Application")
        
        # 获取活动文档
        active_doc = sw_app.ActiveDoc
        if not active_doc:
            print("没有打开的文档")
            return False
            
        # 检查是否为零件文档 (1表示零件文档)
        if active_doc.GetType != 1:  
            print("当前文档不是零件文档")
            return False
        
        # 获取选中的对象
        selected_objects = active_doc.SelectionManager.GetSelectedObjectsComponent4(1, -1)
        
        # 获取选中的面
        selected_faces = []
        sel_mgr = active_doc.SelectionManager
        face_count = sel_mgr.GetSelectedObjectCount2(-1)
        
        for i in range(1, face_count + 1):
            face = sel_mgr.GetSelectedObject6(i, -1)
            if face:
                selected_faces.append(face)
                
        if not selected_faces:
            print("未选中任何面")
            return False
            
        # 提取所有选中面上的边
        all_points = []
        spline_control_points = []  # 专门存储样条线控制点用于绘制
        print(f"selected_faces数:{len(selected_faces)}")
        for face in selected_faces:
            # 获取面上的所有边
            edges = face.GetEdges
            print(f"面上的边数:{len(edges)}")
            if edges:
                for edge in edges:
                    if edge:
                        # 获取边的起点和终点
                        print("-------------------------")
                        curve = edge.GetCurve
                        if curve.IsCircle:
                            print("是圆")
               
                            
                        if curve.IsLine:
                            print("是线")
                            
                            start_pt = edge.GetStartVertex.GetPoint
                            end_pt = edge.GetEndVertex.GetPoint
                            print(f"线起点: {start_pt}")
                            print(f"线终点: {end_pt}")
                            
                            # 转换为毫米单位并添加到点列表
                            all_points.append((
                                start_pt[0] * 1000, 
                                start_pt[1] * 1000, 
                                start_pt[2] * 1000
                            ))
                            all_points.append((
                                end_pt[0] * 1000, 
                                end_pt[1] * 1000, 
                                end_pt[2] * 1000
                            ))
                            
                        if curve.IsBcurve:
                            print("是样条线")
                            varSplineParams = curve.GetBCurveParams(False)
                            print(f"样条线参数: {varSplineParams}")
                            
                            # 按3个一组的方式处理控制点坐标并加入all_points
                            if varSplineParams and len(varSplineParams) > 11:
                                control_points = varSplineParams[11:]  # 去掉前11位
                                
                                # 每3个元素为一组(x,y,z),转换为毫米单位并添加到all_points
                                for i in range(0, len(control_points) - 2, 3):
                                    if i + 2 < len(control_points):
                                        point_mm = (
                                            control_points[i] * 1000,     # X坐标
                                            control_points[i + 1] * 1000, # Y坐标
                                            control_points[i + 2] * 1000  # Z坐标
                                        )
                                        all_points.append(point_mm)
                                        spline_control_points.append(point_mm)  # 保存用于绘制
                                        print(f"添加控制点: {point_mm}")
                        if curve.IsEllipse:
                            print("是椭圆")
         
                            
        # 去重点坐标
        unique_points = list(set(all_points))
        print(f"唯一三维点数: {len(unique_points)}")
        
        if len(unique_points) < 2:
            print("点数不足,无法计算包围盒")
            return False
            
        # 投影到XY平面进行2D OBB计算(假设主要关注XY平面)
        points_2d = [(p[0], p[1]) for p in unique_points]
        points_2d = list(set(points_2d))  # 再次去重
        print(f"二维投影点数: {len(points_2d)}")
        
        # 绘制控制点到DXF文件
        if spline_control_points:
            draw_control_points_to_dxf(active_doc, spline_control_points)
        
        # 计算OBB包围盒
        obb_result = calculate_obb(points_2d)
        
        if obb_result:
            print("OBB包围盒计算结果:")
            print(f"  角度: {obb_result['angle']:.2f}度")
            print(f"  宽度: {obb_result['width']:.3f} mm")
            print(f"  高度: {obb_result['height']:.3f} mm")
            print(f"  面积: {obb_result['area']:.3f} mm²")
            print(f"  中心点: ({obb_result['center'][0]:.3f}, {obb_result['center'][1]:.3f}) mm")
            
            # 输出角点坐标
            print("  角点坐标:")
            for i, corner in enumerate(obb_result['corners']):
                print(f"    角点{i+1}: ({corner[0]:.3f}, {corner[1]:.3f}) mm")
                
        return obb_result
        
    except Exception as e:
        print(f"发生错误: {e}")
        return False
 
    finally:
        # 清理COM资源
        pythoncom.CoUninitialize()

def draw_control_points_to_dxf(sw_document, control_points):
    """
    将控制点绘制到DXF文件并保存在SolidWorks文档同目录下
    """
    try:
        # 创建新的DXF文档
        doc = ezdxf.new(dxfversion='R2010')
        msp = doc.modelspace()
        
        # 绘制点
        for point in control_points:
            msp.add_point((point[0], point[1]))
        
        # 获取SolidWorks文档路径
        sw_file_path = sw_document.GetPathName
        if sw_file_path:
            # 获取目录路径
            directory = os.path.dirname(sw_file_path)
            # 生成文件名
            filename = f"控制点{uuid4().hex}.dxf"
            full_path = os.path.join(directory, filename)
            
            # 保存DXF文件
            doc.saveas(full_path)
            print(f"控制点已保存到: {full_path}")
        else:
            # 如果文档未保存,则保存到当前工作目录
            filename = f"控制点{uuid4().hex}.dxf"
            doc.saveas(filename)
            print(f"控制点已保存到: {os.path.abspath(filename)}")
            
    except Exception as e:
        print(f"绘制控制点时出错: {e}")

def rotate_point(point, angle, center=(0, 0)):
    """绕指定中心点旋转点"""
    x, y = point
    cx, cy = center
    
    x -= cx
    y -= cy
    
    rad = math.radians(angle)
    cos_rad, sin_rad = math.cos(rad), math.sin(rad)
    new_x = x * cos_rad - y * sin_rad
    new_y = x * sin_rad + y * cos_rad
    
    new_x += cx
    new_y += cy
    
    return (new_x, new_y)

def get_bounding_box_area(points, angle):
    """计算给定角度下的包围盒面积和边界信息"""
    rotated_points = [rotate_point(p, -angle) for p in points]
    
    x_coords = [p[0] for p in rotated_points]
    y_coords = [p[1] for p in rotated_points]
    
    min_x, max_x = min(x_coords), max(x_coords)
    min_y, max_y = min(y_coords), max(y_coords)
    
    width = max_x - min_x
    height = max_y - min_y
    area = width * height
    
    return area, (min_x, max_x, min_y, max_y)

def ternary_search_min_area(points, left, right, eps=1e-6):
    """使用三分法搜索最小面积角度"""
    while right - left > eps:
        mid1 = left + (right - left) / 3
        mid2 = right - (right - left) / 3
        
        area1, _ = get_bounding_box_area(points, mid1)
        area2, _ = get_bounding_box_area(points, mid2)
        
        if area1 < area2:
            right = mid2
        else:
            left = mid1
    
    optimal_angle = (left + right) / 2
    min_area, bounds = get_bounding_box_area(points, optimal_angle)
    return optimal_angle, min_area, bounds

def calculate_obb(points):
    """计算最小面积包围矩形"""
    if len(points) < 2:
        return None
    
    # 首先找到一个较好的初始角度范围
    angles_to_check = []
    
    # 使用较小步长进行初步搜索
    for i in range(0, 180, 2):
        angles_to_check.append(i)
    
    # 找到距离最远的点对
    max_dist = 0
    farthest_pair = None
    for i in range(len(points)):
        for j in range(i+1, len(points)):
            dist = math.sqrt((points[i][0]-points[j][0])**2 + (points[i][1]-points[j][1])**2)
            if dist > max_dist:
                max_dist = dist
                farthest_pair = (points[i], points[j])
    
    if farthest_pair:
        p1, p2 = farthest_pair
        angle = math.degrees(math.atan2(p2[1] - p1[1], p2[0] - p1[0]))
        angles_to_check.extend([angle, angle + 90])
    
    angles_to_check = list(set([a % 180 for a in angles_to_check]))
    
    # 找到初步的最小面积角度
    min_area = float('inf')
    best_angle = 0
    best_bounds = None
    
    for angle in angles_to_check:
        area, bounds = get_bounding_box_area(points, angle)
        if area < min_area:
            min_area = area
            best_angle = angle
            best_bounds = bounds
    
    # 在最优角度附近使用三分法进行精细搜索
    search_range = 5  # 搜索范围±5度
    left_angle = (best_angle - search_range) % 180
    right_angle = (best_angle + search_range) % 180
    
    # 处理跨越0度的情况
    if left_angle > right_angle:
        # 在[0, right_angle]和[left_angle, 180]两个区间分别搜索
        optimal_angle1, min_area1, bounds1 = ternary_search_min_area(points, 0, right_angle)
        optimal_angle2, min_area2, bounds2 = ternary_search_min_area(points, left_angle, 180)
        
        if min_area1 < min_area2:
            optimal_angle = optimal_angle1
            min_area = min_area1
            best_bounds = bounds1
        else:
            optimal_angle = optimal_angle2
            min_area = min_area2
            best_bounds = bounds2
    else:
        optimal_angle, min_area, best_bounds = ternary_search_min_area(points, left_angle, right_angle)
    
    # 构造最终的包围盒
    min_x, max_x, min_y, max_y = best_bounds
    width = max_x - min_x
    height = max_y - min_y
    
    # 计算旋转后的四个角点
    rotated_corners = [
        (min_x, min_y),
        (max_x, min_y),
        (max_x, max_y),
        (min_x, max_y)
    ]
    
    # 将角点旋转回原始坐标系
    corners = [rotate_point(p, optimal_angle) for p in rotated_corners]
    
    center_x = (min_x + max_x) / 2
    center_y = (min_y + max_y) / 2
    center_original = rotate_point((center_x, center_y), optimal_angle)
    
    return {
        'type': 'OBB',
        'corners': corners,
        'width': width,
        'height': height,
        'area': min_area,
        'angle': optimal_angle,
        'center': center_original
    }

# 主函数调用示例
if __name__ == "__main__":
    get_selected_faces_edges_and_obb()
相关推荐
一 乐4 小时前
智慧养老|基于springboot+小程序社区养老保障系统设计与实现(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端·小程序
罗光记4 小时前
Solon AI 开发学习 7-chat - 四种消息类型及提示语增强
数据库·其他·百度·facebook·新浪微博
煎蛋学姐4 小时前
SSM社区志愿者服务系统d6d36(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·社区志愿者服务系统
语落心生5 小时前
解读广告数仓(二)数据架构与关键系统设计
数据库
冉冰学姐5 小时前
SSM实验室安全管理系统c03w5(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架应用·实验室安全管理·数字化管理系统
松☆5 小时前
OpenHarmony + Flutter 混合开发实战:构建高性能离线优先的行业应用(含 SQLite 与数据同步策略)
数据库·flutter·sqlite
语落心生5 小时前
解读广告数仓(四) - 指标计算与应用实现
数据库
语落心生5 小时前
解读广告数仓(一) - 广告业务模型与指标体系深化分析
数据库
老华带你飞5 小时前
旅游|基于Java旅游信息推荐系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端·旅游