solidworks自动标注折弯3 OneBend Select4 GetVisibleComponents

【solidworks自动标注折弯3】 https://www.bilibili.com/video/BV1azdLBTE5E/?share_source=copy_web\&vd_source=6b8044752c092314d4b426afb8ebd370

这段代码是一个基于 SolidWorks API (C#) 开发的插件工具,主要用于在工程图中自动识别折弯(Bend)特征,并为其添加尺寸标注。

从整体来看,这是一段逻辑严谨、工程化程度较高 的代码,它解决了参数化建模中常见的"自动标注"痛点。代码不仅实现了基础的几何识别,还通过 HashSet 去重机制处理了复杂的拓扑关系,避免了重复标注。

以下是对该代码的详细评价:

1. 核心功能与逻辑架构

代码的核心功能是 ProcessBend 方法,其逻辑流程非常清晰,遵循了典型的几何处理步骤:

  1. 特征识别 :遍历模型中的 OneBend 特征,锁定需要处理的对象。
  2. 几何提取 :在折弯特征中寻找面积最大的圆柱面(CylFace),以此作为折弯轴线的基准。
  3. 拓扑遍历
    • 找到与轴线平行的直边(ParallelEdges)。
    • 通过直边找到相邻的"一级面"(通常是折弯的两个延伸面)。
    • 在一级面中寻找最远的平行边,进而找到"二级面"。
  4. 智能判断与递归:代码中有一个亮点是处理了二级面如果是圆柱面的情况,会继续寻找"三级面"。这说明开发者考虑到了复杂的多级折弯或圆角过渡的情况。
  5. 标注生成 :寻找平行面,计算视图位置,并调用 AddDimension2 进行标注。

2. 代码质量与亮点 ✨

  • 防重复机制(去重逻辑)
    代码使用了 HashSet<string> dimensionedPairs 来存储已标注的面组合键(GetFacePairKey)。这是非常必要的,因为在遍历多个折弯时,共享的平面很容易导致重复标注。通过 GetHashCode 排序生成键值,确保了 (A,B)(B,A) 被视为同一组,逻辑非常严密。
  • 视图与模型的坐标映射
    代码处理了 SolidWorks 中常见的难题------3D模型坐标与2D视图坐标的转换 。在 FindVisibleEdge 方法中,它没有简单地依赖 COM 对象的引用(因为视图中的边是模型边的投影,引用通常不同),而是通过端点坐标的数值比对IsPointClose)来匹配可见边。这是一种非常稳健的处理方式,避免了因 COM 封送(Marshaling)导致的对象引用失效问题。
  • 容错与日志
    大量的 Console.WriteLine 输出和 try-catch 块提供了良好的调试信息,方便开发者在运行时了解程序卡在哪一步(例如"找到 X 条边"、"跳过不可见面")。
  • 数学计算准确
    IsParallelPointToAxisDistance 方法展示了扎实的几何数学基础,使用了向量点积和投影来判断平行度和距离,而非简单的坐标比较,保证了算法在不同角度下的鲁棒性。

3. 潜在改进点与优化建议 ⚙️

尽管代码质量很高,但在实际工程应用中,仍有以下优化空间:

维度 现状分析 改进建议
用户交互 目前依赖 Console.WriteLine 输出信息,且强制 CommandInProgress = true 建议改为模态窗体任务窗格,提供更友好的进度条和错误提示,或者将日志写入文件而非控制台。
性能优化 每次标注都调用 AddDimension2,且涉及多次遍历。 对于大型装配体,建议增加批量处理模式,或者在标注前进行更严格的几何筛选,减少不必要的遍历。
配置灵活性 偏移量 offset 和容差值(如 0.001)写死在代码中。 建议提取为配置文件用户参数,允许用户自定义标注样式、字体大小和搜索容差。
异常处理 虽有 try-catch,但部分逻辑(如 GetBodies2 返回 null)处理较为简单。 可以增加更细粒度的异常分类,区分"无文档"、"非工程图"、"非零件"等不同错误类型,并给出针对性的用户提示。
视图定位 标注位置的计算逻辑(bounds[0] + offset)较为简单。 增加智能避让算法,检测视图中已有的尺寸,自动寻找空白区域放置新尺寸,避免尺寸线重叠。

4. 总结

这段代码是一个高质量的 SolidWorks 二次开发范例

它不仅仅是一个简单的脚本,而是一个具备了几何识别、拓扑遍历、坐标变换、去重逻辑的完整工具。开发者对 SolidWorks API 的底层机制(如 COM 对象的特性、视图与模型的关系)有深刻的理解。

适用场景: 适用于钣金件设计流程自动化,特别是针对包含大量折弯特征的零件,能显著减少工程师手动标注尺寸的时间。

评分: ⭐⭐⭐⭐ (4/5) ------ 扣分点主要在于缺乏用户配置界面和硬编码的参数,但在核心算法和逻辑实现上表现优异。

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
using View = SolidWorks.Interop.sldworks.View;

namespace tools
{
    public class benddim
    {
        public static void AddBendDimensions(ISldWorks swApp)
        {
            try
            {
                var swModel = (ModelDoc2)swApp.ActiveDoc;
                swApp.CommandInProgress = true;
                if (swModel == null) { Console.WriteLine("没有活动文档"); return; }

                var swSelMgr = (SelectionMgr)swModel.SelectionManager;
                if (swSelMgr.GetSelectedObjectType3(1, -1) != (int)swSelectType_e.swSelDRAWINGVIEWS)
                { Console.WriteLine("请先选择一个视图"); return; }

                var view = (View)swSelMgr.GetSelectedObject(1);
                var partDoc = (PartDoc)view.ReferencedDocument;
                if (partDoc == null) { Console.WriteLine("无法获取零件文档"); return; }

                var xform = (MathTransform)view.ModelToViewTransform;
                var xformData = (double[])xform.ArrayData;
                var bounds = (double[])view.GetOutline();
                var mathUtils = swApp.IGetMathUtility();

                int count = 0;
                double offset = 0.05;
                
                // 全局已标注面组合集合,用于跨折弯去重
                var dimensionedPairs = new HashSet<string>();

                // 遍历 Body 的特征找折弯
                foreach (Body2 body in (object[])partDoc.GetBodies2((int)swBodyType_e.swSolidBody, false))
                {
                    foreach (Feature feat in (object[])body.GetFeatures())
                    {
                        var subFeat = (Feature)feat.GetFirstSubFeature();
                        while (subFeat != null)
                        {
                            if (subFeat.GetTypeName() == "OneBend" )
                            {
                                Console.WriteLine("找到折弯特征" + subFeat.Name);
                                if (ProcessBend(swModel, view, mathUtils, xform, xformData, bounds, subFeat, ref offset, dimensionedPairs))
                                    count++;
                            }

                            subFeat = (Feature)subFeat.GetNextSubFeature();
                        }
                    }
                }

                Console.WriteLine($"标注完成,共 {count} 个折弯");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"错误: {ex.Message}");
            }
        }

        static bool ProcessBend(ModelDoc2 swModel, View view, MathUtility mathUtils, MathTransform xform,
            double[] xformData, double[] bounds, Feature bendFeat, ref double offset, HashSet<string> dimensionedPairs)
        {
            // 1. 找最大圆柱面
            Face cylFace = null;
            double[] axis = null;
            double[] center = null;
            double maxArea = 0;

            foreach (Face f in (object[])bendFeat.GetFaces())
            {
                var s = (Surface)f.GetSurface();
                if (s.IsCylinder())
                {
                    double area = f.GetArea();
                    if (area > maxArea)
                    {
                        maxArea = area;
                        var p = (double[])s.CylinderParams;
                        axis = new[] { p[3], p[4], p[5] };
                        center = new[] { p[0], p[1], p[2] };
                        cylFace = f;
                    }
                }
            }
            if (cylFace == null) return false;
            Console.WriteLine("最大圆柱面面积: " + maxArea * 1000000 + " mm^2");

            // 2. 找圆柱面的edge里和圆柱轴线平行的edge(直边)
            var parallelEdges = new List<Edge>();
            foreach (Edge e in (object[])cylFace.GetEdges())
            {
                var c = (Curve)e.GetCurve();
                if (c.IsLine())
                {
                    var lineParams = (double[])c.LineParams;
                    var edgeDir = new[] { lineParams[3], lineParams[4], lineParams[5] };
                    if (IsParallel(edgeDir, axis))
                    {
                        parallelEdges.Add(e);
                    }
                }
            }
            if (parallelEdges.Count == 0) return false;
            Console.WriteLine("找到 " + parallelEdges.Count + " 条与轴线平行的边");

            // 3. 用edge的相交面找下一级面
            var firstLevelFaces = new List<Face>();
            foreach (var edge in parallelEdges)
            {
                foreach (Face f in (object[])edge.GetTwoAdjacentFaces())
                {
                    if (f != cylFace && !firstLevelFaces.Contains(f))
                    {
                        firstLevelFaces.Add(f);
                    }
                }
            }
            if (firstLevelFaces.Count == 0) return false;
            Console.WriteLine("找到 " + firstLevelFaces.Count + " 个下一级面");

            // 4. 在下一级面里找与圆柱轴平行的最远的线,然后取相交面
            var secondLevelFaces = new List<Face>();
            foreach (var face in firstLevelFaces)
            {
                Edge farthestEdge = null;
                double maxDist = -1;

                foreach (Edge e in (object[])face.GetEdges())
                {
                    var c = (Curve)e.GetCurve();
                    if (c.IsLine())
                    {
                        var lineParams = (double[])c.LineParams;
                        var edgeDir = new[] { lineParams[3], lineParams[4], lineParams[5] };
                        if (IsParallel(edgeDir, axis))
                        {
                            // 计算边到圆柱中心的距离
                            var pt = new[] { lineParams[0], lineParams[1], lineParams[2] };
                            double dist = PointToAxisDistance(pt, center, axis);
                            if (dist > maxDist)
                            {
                                maxDist = dist;
                                farthestEdge = e;
                            }
                        }
                    }
                }

                if (farthestEdge != null)
                {
                    foreach (Face f in (object[])farthestEdge.GetTwoAdjacentFaces())
                    {
                        if (f != face && !secondLevelFaces.Contains(f))
                        {
                            secondLevelFaces.Add(f);
                        }
                    }
                }
            }
            if (secondLevelFaces.Count < 2) return false;
            Console.WriteLine("找到 " + secondLevelFaces.Count + " 个二级面");

            // 5. 检查二级面是否为圆柱面,如果是则获取第三级面
            var secondaryFaces = new List<(Face face, string level)>(); // 面及其级别
            foreach (var face in secondLevelFaces)
            {
                var s = (Surface)face.GetSurface();
                if (s.IsCylinder())
                {
                    Console.WriteLine("二级面为圆柱面,继续获取第三级面");
                    // 二级面是圆柱面,获取与轴线平行的边的相邻面作为三级面
                    int thirdLevelCount = 0;
                    foreach (Edge e in (object[])face.GetEdges())
                    {
                        var c = (Curve)e.GetCurve();
                        if (c.IsLine())
                        {
                            var lineParams = (double[])c.LineParams;
                            var edgeDir = new[] { lineParams[3], lineParams[4], lineParams[5] };
                            if (IsParallel(edgeDir, axis))
                            {
                                foreach (Face adjFace in (object[])e.GetTwoAdjacentFaces())
                                {
                                    // 排除:自身、一级面、折弯圆柱面
                                    if (adjFace == face || firstLevelFaces.Contains(adjFace) || adjFace == cylFace) continue;
                                    if (!secondaryFaces.Any(x => x.face == adjFace))
                                    {
                                        secondaryFaces.Add((adjFace, "三级面"));
                                        thirdLevelCount++;
                                    }
                                }
                            }
                        }
                    }
                    Console.WriteLine($"  找到 {thirdLevelCount} 个三级面");
                }
                else
                {
                    // 不是圆柱面,直接使用
                    secondaryFaces.Add((face, "二级面"));
                }
            }

            // 6. 为每个一级面找到对应的平行二级/三级面并标注
            // 使用全局已标注面集合防止重复标注(跨折弯共享)
            int dimensionCount = 0;
            
            foreach (var firstFace in firstLevelFaces)
            {
                var s1 = (Surface)firstFace.GetSurface();
                if (!s1.IsPlane()) continue;
                var p1 = (double[])s1.PlaneParams;
                var n1 = new[] { p1[0], p1[1], p1[2] };

                // 为当前一级面找平行的二级/三级面
                Face matchedSecondFace = null;
                string matchedLevel = "";
                foreach (var item in secondaryFaces)
                {
                    var secFace = item.face;
                    var s2 = (Surface)secFace.GetSurface();
                    if (!s2.IsPlane()) continue;
                    var p2 = (double[])s2.PlaneParams;
                    var n2 = new[] { p2[0], p2[1], p2[2] };

                    // 检查两面是否平行(法向量平行)且不是同一个面
                    if (IsParallel(n1, n2) && secFace != firstFace)
                    {
                        // 生成唯一键:两面法向量、面积和配对级别的组合
                        var pairKey = GetFacePairKey(firstFace, secFace, item.level);
                        if (dimensionedPairs.Contains(pairKey))
                        {
                            Console.WriteLine($"跳过已标注的平行面组合:一级面面积 = {firstFace.GetArea() * 1000000:F2} mm^2, {item.level}面积 = {secFace.GetArea() * 1000000:F2} mm^2");
                            continue;
                        }
                        
                        matchedSecondFace = secFace;
                        matchedLevel = item.level;
                        dimensionedPairs.Add(pairKey);
                        Console.WriteLine($"找到一对平行面:一级面面积 = {firstFace.GetArea() * 1000000:F2} mm^2, {matchedLevel}面积 = {secFace.GetArea() * 1000000:F2} mm^2");
                        break;
                    }
                }

                if (matchedSecondFace == null) continue;

                // 确定放置方式
                string placement = GetPlacement(firstFace, xformData);
                if (placement == "none") continue;

                // 标注 - 获取3D边并在视图中查找对应可见边
                // 获取所有候选边(按长度降序),逐个尝试找到有可见边的
                var edgeCandidates1 = GetEdgesForDimension(firstFace, axis, "一级面");
                var edgeCandidates2 = GetEdgesForDimension(matchedSecondFace, axis, matchedLevel);
                
                Edge visEdge1 = null, visEdge2 = null;
                
                foreach (var e1 in edgeCandidates1)
                {
                    visEdge1 = FindVisibleEdge(view, e1);
                    if (visEdge1 != null) break;
                }
                
                if (visEdge1 == null)
                {
                    Console.WriteLine("一级面的标注边在视图中均不可见,跳过此对");
                    continue;
                }
                
                foreach (var e2 in edgeCandidates2)
                {
                    visEdge2 = FindVisibleEdge(view, e2);
                    if (visEdge2 != null) break;
                }
                
                if (visEdge2 == null)
                {
                    Console.WriteLine("二级面的标注边在视图中均不可见,跳过此对");
                    continue;
                }

                // 创建 SelectData 并设置视图上下文
                var selMgr = (SelectionMgr)swModel.SelectionManager;
                var selData = selMgr.CreateSelectData();
                selData.View = view;

                // 将可见边转换为 Entity 并选择
                ((Entity)visEdge1).Select4(true, selData);
                ((Entity)visEdge2).Select4(true, selData);

                double x, y;
                if (placement == "h")
                {
                    x = (bounds[0] + bounds[2]) / 2;
                    y = bounds[3] - offset;
                }
                else
                {
                    y = (bounds[1] + bounds[3]) / 2;
                    x = bounds[0] + offset;
                }
                swModel.AddDimension2(x, y, 0);
                offset += 0.005;
                swModel.ClearSelection2(true);
                dimensionCount++;
            }

            return dimensionCount > 0;
        }

        static bool IsParallel(double[] a, double[] b)
        {
            double dot = Math.Abs(a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
            double ma = Math.Sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
            double mb = Math.Sqrt(b[0] * b[0] + b[1] * b[1] + b[2] * b[2]);
            return Math.Abs(dot - ma * mb) < 0.001;
        }

        static double PointToAxisDistance(double[] point, double[] axisCenter, double[] axisDir)
        {
            // 计算点到轴线的距离
            // 向量从轴线中心指向点
            double[] v = { point[0] - axisCenter[0], point[1] - axisCenter[1], point[2] - axisCenter[2] };
            
            // 投影到轴线方向
            double dot = v[0] * axisDir[0] + v[1] * axisDir[1] + v[2] * axisDir[2];
            double axisLen = Math.Sqrt(axisDir[0] * axisDir[0] + axisDir[1] * axisDir[1] + axisDir[2] * axisDir[2]);
            double proj = dot / axisLen;
            
            // 投影点
            double[] projPoint = {
                axisCenter[0] + proj * axisDir[0] / axisLen,
                axisCenter[1] + proj * axisDir[1] / axisLen,
                axisCenter[2] + proj * axisDir[2] / axisLen
            };
            
            // 距离
            double dx = point[0] - projPoint[0];
            double dy = point[1] - projPoint[1];
            double dz = point[2] - projPoint[2];
            return Math.Sqrt(dx * dx + dy * dy + dz * dz);
        }

        static string GetPlacement(Face face, double[] xf)
        {
            var s = (Surface)face.GetSurface();
            if (!s.IsPlane()) return "none";
            var p = (double[])s.PlaneParams;
            double nx = Math.Abs(p[0]), ny = Math.Abs(p[1]), nz = Math.Abs(p[2]);

            if (nx > ny && nx > nz)
                return Math.Round(xf[0]) != 0 ? "h" : (Math.Round(xf[1]) != 0 ? "v" : "none");
            if (ny > nx && ny > nz)
                return Math.Round(xf[3]) != 0 ? "h" : (Math.Round(xf[4]) != 0 ? "v" : "none");
            if (nz > nx && nz > ny)
                return Math.Round(xf[6]) != 0 ? "h" : (Math.Round(xf[7]) != 0 ? "v" : "none");
            return "none";
        }

        /// <summary>
        /// 获取面上所有不与轴线平行的直边,按长度降序排列(用于尺寸标注)
        /// 折弯标注需要选择不平行于折弯轴线的边,返回多条候选边供视图中逐个匹配
        /// </summary>
        static List<Edge> GetEdgesForDimension(Face face, double[] axis, string faceLevel = "")
        {
            var candidates = new List<(Edge edge, double len)>();

            foreach (Edge e in (object[])face.GetEdges())
            {
                var c = (Curve)e.GetCurve();
                if (c.IsLine())
                {
                    var lineParams = (double[])c.LineParams;
                    var edgeDir = new[] { lineParams[3], lineParams[4], lineParams[5] };
                    
                    // 跳过与轴线平行的边,折弯标注需要不平行的边
                    if (IsParallel(edgeDir, axis))
                        continue;

                    double startParam = 0, endParam = 0;
                    bool isClosed = false, isPeriodic = false;
                    c.GetEndParams(out startParam, out endParam, out isClosed, out isPeriodic);
                    double len = Math.Abs(endParam - startParam);
                    
                    candidates.Add((e, len));
                }
            }

            // 按长度降序排列,优先使用长边
            candidates.Sort((a, b) => b.len.CompareTo(a.len));
            
            var label = string.IsNullOrEmpty(faceLevel) ? "" : $"[{faceLevel}] ";
            foreach (var item in candidates)
                Console.WriteLine($"  {label}候选标注边,长度: {item.len * 1000:F2} mm");
            
            if (candidates.Count == 0)
                Console.WriteLine($"  {label}未找到不平行于轴线的直边");

            return candidates.Select(x => x.edge).ToList();
        }

        /// <summary>
        /// 在视图中查找3D边对应的可见边
        /// 按组件获取可见边(返回可转换为Edge的对象),通过3D端点坐标近似匹配解决COM封送问题
        /// 注意:IDrawingEdge接口在SolidWorks C#互操作中不存在,不能类型转换;
        ///       GetVisibleEntities(null,...) 返回的对象也是__ComObject无法转换;
        ///       正确做法是用 GetVisibleEntities(comp,...) 按组件获取,返回的对象可转换为Edge
        /// </summary>
        static Edge FindVisibleEdge(View view, Edge modelEdge)
        {
            // 获取目标3D边的端点坐标
            var startVert = (Vertex)modelEdge.GetStartVertex();
            var endVert = (Vertex)modelEdge.GetEndVertex();
            if (startVert == null || endVert == null) return null;

            var edgeStart = (double[])startVert.GetPoint();
            var edgeEnd = (double[])endVert.GetPoint();
            if (edgeStart == null || edgeEnd == null) return null;

            // 获取视图中的可见组件
            var visibleComps = (object[])view.GetVisibleComponents();
            if (visibleComps == null) return null;

            foreach (Component2 comp in visibleComps)

            {
                if (comp == null) continue;

                // 按组件获取可见边(与 GetVisibleEntities(null,...) 不同,按组件获取返回可转换为Edge的对象)
                var visibleEdges = (object[])view.GetVisibleEntities(
                    comp, (int)swViewEntityType_e.swViewEntityType_Edge);
                if (visibleEdges == null) continue;

                foreach (object obj in visibleEdges)
                {
                    if (obj == null) continue;

                    // GetVisibleEntities(comp,...) 返回的对象可转换为 Edge
                    if (!(obj is Edge visEdge)) continue;

                    try
                    {
                        // 先用引用判断(速度快)
                        if (Object.ReferenceEquals(visEdge, modelEdge)) return visEdge;

                        // 再用3D端点坐标近似判断(解决COM封送导致的引用不一致问题)
                        var meStartVert = (Vertex)visEdge.GetStartVertex();
                        var meEndVert = (Vertex)visEdge.GetEndVertex();
                        if (meStartVert == null || meEndVert == null) continue;

                        var meStart = (double[])meStartVert.GetPoint();
                        var meEnd = (double[])meEndVert.GetPoint();
                        if (meStart == null || meEnd == null) continue;

                        bool coordsMatch =
                            (IsPointClose(edgeStart, meStart) && IsPointClose(edgeEnd, meEnd))
                            ||
                            (IsPointClose(edgeStart, meEnd) && IsPointClose(edgeEnd, meStart));

                        if (coordsMatch) return visEdge;
                    }
                    catch
                    {
                        continue;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// 判断两个3D点是否在容差范围内近似相等
        /// </summary>
        static bool IsPointClose(double[] p1, double[] p2, double tol = 0.001)
        {
            if (p1 == null || p2 == null || p1.Length < 3 || p2.Length < 3) return false;
            var dx = Math.Abs(p1[0] - p2[0]);
            var dy = Math.Abs(p1[1] - p2[1]);
            var dz = Math.Abs(p1[2] - p2[2]);
            var dist = Math.Sqrt(dx * dx + dy * dy + dz * dz);
            var close = dx < tol && dy < tol && dz < tol;
           // Console.WriteLine($"[IsPointClose] p1=({p1[0]:F6},{p1[1]:F6},{p1[2]:F6}) p2=({p2[0]:F6},{p2[1]:F6},{p2[2]:F6}) dx={dx:F6} dy={dy:F6} dz={dz:F6} dist={dist:F6} tol={tol} => {close}");
            return close;
        }

        static double[] TransformPoint(MathUtility math, MathTransform xf, double[] pt)
        {
            var mp = (MathPoint)math.CreatePoint(pt);
            mp = (MathPoint)mp.MultiplyTransform(xf);
            return (double[])mp.ArrayData;
        }

        /// <summary>
        /// 生成一对面的唯一标识键,用于防止重复标注
        /// 基于Face对象的引用生成唯一键,确保只有完全相同的物理面才会被去重
        /// </summary>
        static string GetFacePairKey(Face face1, Face face2, string level)
        {
            // 使用Face对象的HashCode生成唯一标识
            // 按HashCode排序生成键,确保 (A,B) 和 (B,A) 生成相同的键
            int hash1 = face1.GetHashCode();
            int hash2 = face2.GetHashCode();
            
            var hashes = new[] { hash1, hash2 };
            Array.Sort(hashes);
            
            // 加入配对级别,确保一级对二级 与 一级对三级 不会互相去重
            return $"{hashes[0]}|{hashes[1]}|{level}";
        }
    }
}
相关推荐
solidwork_s7 天前
SOLIDWORKS格式处理干货:STEP、IGS、X_T 优化技巧
solidworks
njsgcs16 天前
导出glb可视化让vlm看图找面 自己配合
智能制造·solidworks
花开月满西楼17 天前
零基础速学SolidWorks2026【曲面】曲面缝合
3d建模·solidworks·机械设计·机械绘图绘制
花开月满西楼17 天前
零基础速学SolidWorks2026【曲面】删除面
3d建模·solidworks·机械设计·机械绘图绘制
花开月满西楼17 天前
零基础速学SolidWorks2026【曲面】加厚曲面
3d建模·solidworks·机械设计·机械绘图绘制
花开月满西楼20 天前
零基础速学SolidWorks2026【曲面】边界曲面和等距曲面
3d建模·solidworks·机械设计·机械绘图绘制
花开月满西楼20 天前
零基础速学SolidWorks2026【曲面】平面区域和填充曲面
3d建模·solidworks·机械设计·机械绘图绘制
蓝悦无人机21 天前
SOLIDWORKS高级应用之方程
solidworks·参数化建模·if 判断语句
花开月满西楼21 天前
零基础速学SolidWorks2026【曲面】分割线
3d建模·solidworks·机械设计·机械绘图绘制
花开月满西楼23 天前
零基础速学SolidWorks2026【曲面】建模核心基础
solidworks·机械设计·机械绘图绘制