首页
/ 解锁FreeCAD Python API:从自动化建模到工程仿真的全流程实践指南

解锁FreeCAD Python API:从自动化建模到工程仿真的全流程实践指南

2026-03-12 03:17:04作者:管翌锬

在现代工程设计领域,3D建模软件已成为不可或缺的工具。然而,传统的手动建模方式往往面临效率低下、重复性高、参数调整困难等挑战。FreeCAD作为一款开源的参数化3D建模软件,其强大的Python API为解决这些痛点提供了全新可能。本文将通过"问题导向-核心价值-分层实践-场景拓展"的四象限框架,帮助你系统掌握FreeCAD Python API的应用,实现从手动操作到自动化设计的效率跃迁。

一、问题导向:工程设计中的效率瓶颈与解决方案

1.1 重复建模的时间消耗问题

痛点描述:在机械设计中,经常需要创建多个结构相似但尺寸不同的零部件,手动重复建模不仅耗时,还容易引入人为错误。

解决方案:利用FreeCAD Python API实现参数化建模,通过调整参数自动生成系列化零件。

代码示例:参数化创建螺栓系列

import FreeCAD as App
import Part

def create_bolt_assembly(bolt_params):
    """
    创建参数化螺栓组件
    
    参数:
        bolt_params: 包含螺栓参数的字典,格式如下:
        {
            "diameter": 10.0,      # 螺栓直径(mm)
            "length": 50.0,        # 螺栓长度(mm)
            "head_diameter": 16.0, # 螺栓头直径(mm)
            "head_height": 6.0,    # 螺栓头高度(mm)
            "thread_pitch": 1.5    # 螺纹螺距(mm)
        }
    """
    # 创建新文档
    doc = App.newDocument("BoltGenerator")
    
    # 创建螺栓杆
    bolt_shaft = Part.makeCylinder(
        bolt_params["diameter"] / 2, 
        bolt_params["length"] - bolt_params["head_height"]
    )
    
    # 创建螺栓头(六边形)
    bolt_head = Part.makePolygon([
        App.Vector(bolt_params["head_diameter"]/2, 0, bolt_params["length"] - bolt_params["head_height"]),
        App.Vector(bolt_params["head_diameter"]/2 * 0.866, bolt_params["head_diameter"]/2 * 0.5, bolt_params["length"] - bolt_params["head_height"]),
        App.Vector(-bolt_params["head_diameter"]/2 * 0.866, bolt_params["head_diameter"]/2 * 0.5, bolt_params["length"] - bolt_params["head_height"]),
        App.Vector(-bolt_params["head_diameter"]/2, 0, bolt_params["length"] - bolt_params["head_height"]),
        App.Vector(-bolt_params["head_diameter"]/2 * 0.866, -bolt_params["head_diameter"]/2 * 0.5, bolt_params["length"] - bolt_params["head_height"]),
        App.Vector(bolt_params["head_diameter"]/2 * 0.866, -bolt_params["head_diameter"]/2 * 0.5, bolt_params["length"] - bolt_params["head_height"]),
        App.Vector(bolt_params["head_diameter"]/2, 0, bolt_params["length"] - bolt_params["head_height"])
    ])
    bolt_head_face = Part.Face(bolt_head)
    bolt_head_solid = bolt_head_face.extrude(App.Vector(0, 0, bolt_params["head_height"]))
    
    # 合并螺栓头和螺栓杆
    bolt = bolt_shaft.fuse(bolt_head_solid)
    
    # 添加到文档
    obj = doc.addObject("Part::Feature", "Bolt")
    obj.Shape = bolt
    
    doc.recompute()
    return doc

# 创建M10和M12两种规格的螺栓
m10_bolt = create_bolt_assembly({
    "diameter": 10.0, "length": 50.0, 
    "head_diameter": 16.0, "head_height": 6.0, 
    "thread_pitch": 1.5
})

m12_bolt = create_bolt_assembly({
    "diameter": 12.0, "length": 60.0, 
    "head_diameter": 19.0, "head_height": 7.5, 
    "thread_pitch": 1.75
})

应用场景:标准件库创建、系列化产品设计、快速原型迭代。

适用场景:当需要创建多个尺寸不同但结构相似的零件时,参数化建模能显著减少重复劳动。

常见误区:过度参数化导致模型复杂度过高,应只对关键尺寸进行参数化控制。

优化建议:将常用参数保存到配置文件或数据库,实现参数的集中管理和快速调用。

1.2 复杂装配体的定位与约束挑战

痛点描述:手动装配多个零件时,精确调整零件位置和添加约束关系耗时且困难,尤其在包含数十个零件的复杂装配体中。

解决方案:使用Python API自动化装配过程,通过代码精确控制零件位置和添加约束关系。

代码示例:自动化装配机械臂组件

import FreeCAD as App
import FreeCADGui as Gui
import Assembly

def assemble_mechanical_arm():
    """自动化装配机械臂组件"""
    # 创建新文档
    doc = App.newDocument("MechanicalArmAssembly")
    
    # 创建基础部件
    base = Part.makeBox(100, 80, 20)
    base_obj = doc.addObject("Part::Feature", "Base")
    base_obj.Shape = base
    
    # 创建大臂
    boom = Part.makeBox(150, 30, 25)
    boom_obj = doc.addObject("Part::Feature", "Boom")
    boom_obj.Shape = boom
    # 设置大臂位置
    boom_placement = App.Placement()
    boom_placement.Base = App.Vector(40, 25, 15)  # 定位到大臂连接点
    boom_obj.Placement = boom_placement
    
    # 创建小臂
    stick = Part.makeBox(120, 25, 20)
    stick_obj = doc.addObject("Part::Feature", "Stick")
    stick_obj.Shape = stick
    # 设置小臂位置
    stick_placement = App.Placement()
    stick_placement.Base = App.Vector(190, 30, 15)  # 定位到小臂连接点
    stick_obj.Placement = stick_placement
    
    # 创建铲斗
    bucket = Part.makeBox(80, 40, 30)
    bucket_obj = doc.addObject("Part::Feature", "Bucket")
    bucket_obj.Shape = bucket
    # 设置铲斗位置
    bucket_placement = App.Placement()
    bucket_placement.Base = App.Vector(310, 35, 15)  # 定位到铲斗连接点
    bucket_obj.Placement = bucket_placement
    
    # 创建旋转关节
    def create_joint(name, position):
        joint = Part.makeCylinder(10, 30)
        joint_obj = doc.addObject("Part::Feature", name)
        joint_obj.Shape = joint
        joint_placement = App.Placement()
        joint_placement.Base = position
        joint_obj.Placement = joint_placement
        return joint_obj
    
    # 添加关节
    base_boom_joint = create_joint("BaseBoomJoint", App.Vector(40, 40, 15))
    boom_stick_joint = create_joint("BoomStickJoint", App.Vector(190, 40, 15))
    stick_bucket_joint = create_joint("StickBucketJoint", App.Vector(310, 40, 15))
    
    # 添加装配约束
    Assembly.addConstraint(doc, base, base_boom_joint, "Coincident", 
                          {"firstElement": (base, ["Face1"]), "secondElement": (base_boom_joint, ["Face1"])})
    
    doc.recompute()
    return doc

# 执行装配
arm_assembly = assemble_mechanical_arm()
# 切换到装配工作台
Gui.activateWorkbench("AssemblyWorkbench")

机械臂装配示意图

应用场景:机械装置装配、机器人结构搭建、复杂机电产品设计。

适用场景:当装配体包含多个零件且需要精确相对位置关系时,API装配能提高效率和准确性。

常见误区:忽略零件之间的干涉检查,应在装配过程中加入碰撞检测。

优化建议:使用坐标系变换和矩阵运算实现复杂运动关系的精确控制。

二、核心价值:FreeCAD Python API的技术优势

2.1 设计流程的自动化与标准化

FreeCAD Python API的核心价值在于将设计流程从手动操作转变为程序化控制,实现了以下关键优势:

  1. 自动化重复任务:通过脚本自动执行重复性高的建模操作,如阵列特征、标准化零件创建等。
  2. 设计过程标准化:确保团队成员使用统一的设计标准和参数设置,减少设计差异。
  3. 版本控制集成:将设计参数和脚本纳入版本控制系统,实现设计过程的可追溯性。
  4. 跨平台一致性:在不同操作系统上保持设计流程和结果的一致性。

2.2 与同类工具的技术对比

特性 FreeCAD Python API 商业CAD软件API 开源CAD工具
成本 完全免费 高昂许可费用 免费但功能有限
定制自由度 极高,可访问源代码 有限,受厂商限制 中等,扩展能力有限
社区支持 活跃的开源社区 厂商提供的技术支持 小型社区,支持有限
学习曲线 中等,需Python基础 陡峭,专有API学习成本高 平缓但功能简单
集成能力 高,可与各类Python库集成 中等,主要与厂商生态集成 低,集成选项有限

三、分层实践:从基础应用到创新实践

3.1 基础应用:API核心模块与基础操作

痛点描述:初学者面对FreeCAD复杂的API体系往往不知从何入手,难以掌握核心功能模块的使用方法。

解决方案:系统学习FreeCAD核心API模块,通过基础示例掌握关键操作。

代码示例:FreeCAD核心模块基础操作

import FreeCAD as App
import Part
import Draft
import Sketcher

def api_fundamentals_demo():
    """FreeCAD API核心功能演示"""
    # 1. 文档管理
    doc = App.newDocument("APIDemo")
    doc.Label = "FreeCAD API基础演示"
    
    # 2. 基础几何体创建 (Part模块)
    # 创建立方体
    cube = Part.makeBox(50, 30, 20)
    cube_obj = doc.addObject("Part::Feature", "Cube")
    cube_obj.Shape = cube
    
    # 创建圆柱体
    cylinder = Part.makeCylinder(15, 40)
    cylinder_obj = doc.addObject("Part::Feature", "Cylinder")
    cylinder_obj.Shape = cylinder
    cylinder_obj.Placement = App.Placement(App.Vector(70, 0, 0), App.Rotation())
    
    # 3. 草图绘制 (Sketcher模块)
    sketch = doc.addObject('Sketcher::SketchObject', 'Sketch')
    sketch.Placement = App.Placement(App.Vector(0, 70, 0), App.Rotation())
    # 选择草图平面
    sketch.Support = (doc.getObject("XY_Plane"), [""])
    sketch.MapMode = "FlatFace"
    # 添加几何元素
    sketch.addGeometry(Sketcher.GeometryOfType('Circle', False), False)
    sketch.setGeometry(0, Sketcher.Circle(App.Vector(0, 0, 0), App.Vector(0, 0, 1), 25))
    # 添加约束
    sketch.addConstraint(Sketcher.Constraint('Radius', 0, 20))  # 半径约束
    sketch.addConstraint(Sketcher.Constraint('DistanceX', 0, 3, 0, 1, 0))  # X坐标约束
    sketch.addConstraint(Sketcher.Constraint('DistanceY', 0, 3, 0, 1, 0))  # Y坐标约束
    
    # 4. 2D到3D转换 (Draft模块)
    # 创建多边形
    polygon_points = [
        App.Vector(0, 120, 0), App.Vector(30, 150, 0), 
        App.Vector(60, 120, 0), App.Vector(30, 90, 0), App.Vector(0, 120, 0)
    ]
    polygon = Draft.make_polygon(polygon_points, closed=True)
    # 拉伸为3D形状
    extruded = Draft.make_extrusion(polygon, 15)
    extruded.Label = "ExtrudedPolygon"
    
    doc.recompute()
    return doc

# 执行演示
demo_doc = api_fundamentals_demo()

应用场景:快速原型设计、基础零件创建、API入门学习。

适用场景:适合FreeCAD API初学者了解核心模块功能和基本操作方法。

常见误区:忽略文档重组(recompute)操作,导致模型无法正确显示。

优化建议:使用try-except结构捕获API调用可能出现的异常,提高脚本健壮性。

3.2 进阶技巧:参数化设计与批量处理

痛点描述:随着模型复杂度增加,手动修改多个关联参数变得困难,且难以实现设计方案的快速迭代。

解决方案:实现全参数化模型,通过外部数据驱动设计变更,实现批量处理和快速迭代。

代码示例:基于CSV数据的参数化零件批量生成

import FreeCAD as App
import Part
import csv
from pathlib import Path

class ParametricPartGenerator:
    """参数化零件生成器"""
    
    def __init__(self, template_file):
        """
        初始化生成器
        
        参数:
            template_file: CSV参数模板文件路径
        """
        self.template_file = template_file
        self.parameters = []
        self.load_parameters()
        
    def load_parameters(self):
        """从CSV文件加载参数"""
        try:
            with open(self.template_file, 'r', encoding='utf-8') as f:
                reader = csv.DictReader(f)
                self.parameters = list(reader)
                print(f"成功加载 {len(self.parameters)} 组参数")
        except Exception as e:
            print(f"加载参数失败: {str(e)}")
    
    def create_part_from_params(self, params):
        """根据参数创建单个零件"""
        # 创建文档
        doc_name = f"Part_{params['id']}"
        doc = App.newDocument(doc_name)
        
        # 创建基础形状
        length = float(params['length'])
        width = float(params['width'])
        height = float(params['height'])
        hole_diameter = float(params['hole_diameter'])
        hole_count = int(params['hole_count'])
        
        # 创建主体
        body = Part.makeBox(length, width, height)
        
        # 添加孔特征
        hole_spacing = (length - 20) / (hole_count - 1) if hole_count > 1 else 0
        
        for i in range(hole_count):
            x_pos = 10 + i * hole_spacing
            hole = Part.makeCylinder(hole_diameter/2, height)
            hole_placement = App.Placement(App.Vector(x_pos, width/2, 0), App.Rotation())
            hole.transformShape(hole_placement.Matrix)
            body = body.cut(hole)
        
        # 添加到文档
        part_obj = doc.addObject("Part::Feature", f"Part_{params['id']}")
        part_obj.Shape = body
        part_obj.Label = params['name']
        
        doc.recompute()
        return doc
    
    def batch_generate(self, output_dir="generated_parts"):
        """批量生成零件"""
        # 创建输出目录
        Path(output_dir).mkdir(exist_ok=True)
        
        for param_set in self.parameters:
            try:
                doc = self.create_part_from_params(param_set)
                # 保存零件
                file_path = Path(output_dir) / f"{param_set['name']}.FCStd"
                doc.saveAs(str(file_path))
                print(f"成功生成零件: {param_set['name']}")
                App.closeDocument(doc.Name)
            except Exception as e:
                print(f"生成零件 {param_set['name']} 失败: {str(e)}")

# 使用示例
if __name__ == "__main__":
    # 假设CSV文件格式: id,name,length,width,height,hole_diameter,hole_count
    generator = ParametricPartGenerator("part_parameters.csv")
    generator.batch_generate()

应用场景:系列化产品设计、根据外部数据自动生成模型、批量零件库创建。

适用场景:当需要根据外部数据(如Excel、数据库)生成大量相似零件时特别有效。

常见误区:参数验证不足导致模型生成失败,应在创建前验证参数有效性。

优化建议:结合多线程处理提高批量生成效率,尤其在处理大量零件时。

3.3 创新实践:工程仿真与数据可视化

痛点描述:传统CAD设计与工程仿真往往是分离的流程,需要在不同软件间切换,导致效率低下和数据传递错误。

解决方案:利用FreeCAD API将建模与有限元分析(FEA)集成,实现设计-分析一体化流程。

代码示例:参数化建模与有限元分析集成

import FreeCAD as App
import Part
import Fem
import ObjectsFem
import femmesh.gmshtools as gmsh_tools
import numpy as np
import matplotlib.pyplot as plt

def parametric_fea_analysis(beam_length=100, beam_width=10, beam_height=20, load=1000):
    """
    参数化梁结构有限元分析
    
    参数:
        beam_length: 梁长度(mm)
        beam_width: 梁宽度(mm)
        beam_height: 梁高度(mm)
        load: 施加的载荷(N)
    """
    # 创建新文档
    doc = App.newDocument("FEAAnalysis")
    
    # 1. 创建几何模型
    beam = Part.makeBox(beam_length, beam_width, beam_height)
    beam_obj = doc.addObject("Part::Feature", "Beam")
    beam_obj.Shape = beam
    
    # 2. 设置有限元分析
    analysis = ObjectsFem.makeAnalysis(doc, "Analysis")
    
    # 添加材料
    material = ObjectsFem.makeMaterialSolid(doc, "Material")
    material.Material = {
        "Name": "Steel",
        "YoungsModulus": "200000 MPa",
        "PoissonRatio": "0.3",
        "Density": "7800 kg/m^3"
    }
    analysis.addObject(material)
    
    # 添加固定约束
    fixed_constraint = ObjectsFem.makeConstraintFixed(doc, "FixedConstraint")
    fixed_face = beam_obj.Shape.Faces[0]  # 选择梁的一个端面
    fixed_constraint.References = [(beam_obj, "Face" + str(beam_obj.Shape.Faces.index(fixed_face) + 1))]
    analysis.addObject(fixed_constraint)
    
    # 添加载荷
    force_constraint = ObjectsFem.makeConstraintForce(doc, "ForceConstraint")
    free_face = beam_obj.Shape.Faces[1]  # 选择梁的另一个端面
    force_constraint.References = [(beam_obj, "Face" + str(beam_obj.Shape.Faces.index(free_face) + 1))]
    force_constraint.Force = (0, 0, -load)  # 向下施加力
    analysis.addObject(force_constraint)
    
    # 3. 网格划分
    mesh = ObjectsFem.makeMeshGmsh(doc, "Mesh")
    mesh.Part = beam_obj
    mesh.ElementDimension = "3D"
    mesh.CharacteristicLengthMax = 5.0  # 网格最大尺寸
    
    # 生成网格
    gmsh_mesh = gmsh_tools.GmshTools(mesh)
    gmsh_mesh.create_mesh()
    
    analysis.addObject(mesh)
    
    # 4. 求解设置
    solver = ObjectsFem.makeSolverCalculiX(doc, "Solver")
    solver.AnalysisType = "static"
    solver.GeometricalNonlinearity = "linear"
    solver.MaterialNonlinearity = "linear"
    analysis.addObject(solver)
    
    # 运行求解
    doc.recompute()
    solver.Run()
    
    # 5. 结果可视化
    displacement_result = ObjectsFem.makeResultMechanical(doc, "DisplacementResult")
    displacement_result.Mesh = mesh
    displacement_result.ResultType = "Displacement"
    displacement_result.Solver = solver
    analysis.addObject(displacement_result)
    
    # 获取位移数据并可视化
    displacement_data = displacement_result.DisplacementLengths
    if displacement_data:
        # 这里简化处理,实际应用中需要更复杂的数据提取
        max_displacement = max(displacement_data)
        print(f"最大位移: {max_displacement:.4f} mm")
        
        # 绘制简单的位移分布图
        plt.figure(figsize=(10, 4))
        plt.plot(displacement_data)
        plt.title(f"梁结构位移分布 (载荷: {load}N)")
        plt.xlabel("节点编号")
        plt.ylabel("位移 (mm)")
        plt.grid(True)
        plt.savefig("displacement_distribution.png")
        plt.close()
    
    return doc

# 执行分析
fea_doc = parametric_fea_analysis(beam_length=150, beam_width=15, beam_height=25, load=1500)

有限元分析结果示意图

应用场景:结构强度分析、产品优化设计、多物理场仿真、工程参数敏感性分析。

适用场景:在产品设计阶段进行快速结构验证,减少物理原型制作成本。

常见误区:网格划分质量不足导致分析结果不准确,应确保适当的网格密度。

优化建议:结合参数优化算法,自动寻找最优设计参数组合。

四、场景拓展:API底层原理与性能优化

4.1 API底层原理:FreeCAD内部工作机制

FreeCAD Python API建立在C++核心之上,通过SWIG(Simplified Wrapper and Interface Generator)实现Python与C++代码的交互。了解其底层工作机制有助于编写更高效的代码:

  1. 对象模型:FreeCAD中的所有实体(文档、零件、特征等)都是基于C++的App::DocumentObject类,Python API提供了这些对象的包装器。

  2. 延迟计算:FreeCAD采用延迟计算机制,修改对象属性后需要调用recompute()方法触发更新。

  3. 几何内核:FreeCAD使用OpenCASCADE作为几何内核,Part模块中的许多功能直接映射到底层几何库函数。

  4. 事务系统:FreeCAD具有完善的事务管理系统,支持操作的撤销和重做,通过App.ActiveDocument.openTransaction()App.ActiveDocument.commitTransaction()控制。

4.2 性能优化:提升大型模型处理效率

处理复杂模型时,API脚本可能面临性能挑战,以下是一些优化建议:

  1. 减少文档重组次数:多次修改后集中调用recompute(),而非每次修改后都调用。

  2. 使用批量操作:优先使用支持批量处理的API方法,减少循环次数。

  3. 几何缓存:对于重复使用的几何形状,缓存其结果避免重复计算。

  4. 禁用视图更新:在批量处理时,通过Gui.ActiveDocument.ActiveView.setEnable(False)暂时禁用视图更新。

  5. 使用低精度模式:在草稿阶段降低几何精度以提高处理速度。

代码示例:性能优化技术应用

import FreeCAD as App
import FreeCADGui as Gui
import Part
import time

def optimized_large_model_creation(feature_count=1000):
    """优化大型模型创建性能的示例"""
    start_time = time.time()
    
    # 创建文档
    doc = App.newDocument("OptimizedModel")
    
    # 禁用视图更新以提高性能
    view = Gui.ActiveDocument.ActiveView
    view.setEnable(False)
    
    # 开始事务
    doc.openTransaction("Create large model")
    
    # 创建多个特征
    for i in range(feature_count):
        # 创建基础立方体
        box = Part.makeBox(10, 10, 10)
        # 设置位置
        placement = App.Placement()
        placement.Base = App.Vector(i % 10 * 15, i // 10 * 15, 0)
        box.transformShape(placement.Matrix)
        
        # 添加到文档
        obj = doc.addObject("Part::Feature", f"Box_{i}")
        obj.Shape = box
        
        # 每100个特征重组一次,平衡性能和内存使用
        if i % 100 == 0 and i > 0:
            doc.recompute()
    
    # 完成事务
    doc.commitTransaction()
    
    # 最后一次重组
    doc.recompute()
    
    # 重新启用视图更新
    view.setEnable(True)
    view.redraw()
    
    end_time = time.time()
    print(f"创建 {feature_count} 个特征耗时: {end_time - start_time:.2f} 秒")
    return doc

# 测试优化效果
optimized_doc = optimized_large_model_creation(1000)

五、技术选型决策树:选择适合的API应用方式

在使用FreeCAD Python API时,根据不同的应用场景选择合适的实现方式至关重要:

  1. 快速原型设计

    • 需求:快速创建简单模型
    • 推荐:使用Draft和Part模块的高层API
    • 示例:Draft.make_cube()Part.makeCylinder()
  2. 参数化零件库

    • 需求:创建可配置的标准零件
    • 推荐:面向对象设计+参数验证
    • 示例:创建ParametricBoltParametricGear
  3. 批量数据处理

    • 需求:从外部数据源生成模型
    • 推荐:CSV/Excel导入+批量处理函数
    • 示例:使用csv模块读取参数,批量生成零件
  4. 工程分析集成

    • 需求:将建模与仿真分析结合
    • 推荐:FEM模块+结果可视化
    • 示例:参数化建模+自动网格划分+有限元分析
  5. 复杂装配自动化

    • 需求:创建包含大量零件的装配体
    • 推荐:装配约束API+位置矩阵计算
    • 示例:使用Assembly模块添加约束关系

六、拓展思考题

  1. 参数化设计挑战:如何设计一个参数化齿轮生成器,使其能够处理不同模数、齿数和压力角的齿轮,并自动计算齿形曲线?

  2. 数据集成实践:如何从CAD模型中自动提取关键尺寸信息,并生成BOM(物料清单)表格,实现设计与生产数据的无缝对接?

  3. 性能优化探索:在处理包含10,000个以上零件的大型装配体时,如何进一步优化API脚本性能,减少内存占用和处理时间?

通过这些实践问题,你可以深入探索FreeCAD Python API的高级应用,进一步提升自动化设计能力。

七、总结

FreeCAD Python API为工程设计提供了强大的自动化工具,通过本文介绍的"问题导向-核心价值-分层实践-场景拓展"框架,你可以系统掌握从基础操作到高级应用的全流程技能。无论是参数化建模、批量处理还是工程仿真集成,FreeCAD API都能显著提升设计效率和质量。

随着开源社区的不断发展,FreeCAD的功能持续增强,其Python API也将提供更多可能性。通过持续学习和实践,你可以将这些技术应用到实际工程问题中,实现从手动设计到自动化设计的转变,成为高效的现代工程设计师。

记住,最好的学习方法是动手实践。选择一个实际项目,应用本文介绍的技术,逐步构建你的自动化设计工具箱。随着经验的积累,你将能够应对更复杂的设计挑战,释放FreeCAD Python API的全部潜力。

登录后查看全文
热门项目推荐
相关项目推荐