夜雨聆风学习资料网

ARTICLE · 1035631

实战|用 AI 写 CAD 插件:一键画门板铰链孔(附源码+提示词)

实战|用 AI 写 CAD 插件:一键画门板铰链孔(附源码+提示词)

这是《零基础 CAD 插件从 0 到 1》第 5 篇。

一、先看效果

以前画铰链孔,我是这么干的:

  • 画一个 35 的圆

  • 偏移门板边 5mm

  • 然后计算铰链的分布位置

  • 复制到每个门板位置

  • 一根一根对尺寸

一个柜子 4 扇门甚至更多,8 个孔,画完几分钟,烦得要死。

现在我用插件:输入命令 → 点门板边 → 自动画出所有铰链孔,一秒钟搞定。

二、我要让 CAD 干什么

我给 AI 提的需求很明确:

我要用 C# 写一个 CAD 插件,命令叫 HingeHole

功能:用户框选门板,然后画出35直径的圆,需要做单开门和对开门,还有多开门的单数和双数,双数是对开,单数需要区分单开是在左边同时朝左边,同理右边也是一样,这个需求和门板开向有点异曲同工之妙。要求:

  1. 可以配置孔位参数,需要出配置文件,文件参数有铰链孔直径

  2. 配置文件的参数有,底边的铰链孔距和顶边的铰链孔距,以孔中心为定位点

  3. 还有铰链孔靠边距默认为22以孔中心为定位点

  4. 需要配置铰链的门板长度区间。例如0-1100=2,1100-1650=3 1650-2300=4 2300-2600=5 2600-3000=6

  5. 画在指定图层上,图层不存在就自动创建,图层名为铰链孔,图层及实体颜色为1

  6. 最好是支持画好的铰链孔自动标注分布的尺寸

这段提示词你可以直接复制去问 AI,它会帮你生成基础代码。

这里有一个重点就是我们这个插件是有配置文件的,所以这里会多一个步骤

需要下载一个JSON 配置文件的库

具体步骤看图

这样就算下载安装好了

三、AI 给我的代码(核心部分)

AI 生成之后,我做了修改和适配。核心代码大概是这样:

using System;using System.Collections.Generic;using System.IO;using System.Linq;using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.DatabaseServices;using Autodesk.AutoCAD.EditorInput;using Autodesk.AutoCAD.Geometry;using Newtonsoft.Json;namespace HingeHolePlugin{    public class HingeConfig    {        public double HoleDiameter { getset; }        public double BottomHoleOffset { getset; }        public double TopHoleOffset { getset; }        public double EdgeOffset { getset; }        public List<HeightRule> HeightRule { getset; }    }    public class HeightRule    {        public double MinH { getset; }        public double MaxH { getset; }        public int HingeCount { getset; }    }    public class HingeHoleCmd    {        private static HingeConfig _cfg;        private const string LayerName = "铰链孔";        [CommandMethod("HingeHole")]        publicvoidHingeHole()        {            Document doc = Application.DocumentManager.MdiActiveDocument;            Database db = doc.Database;            Editor ed = doc.Editor;            //1.加载配置文件            string jsonPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "HingeConfig.json");            try            {                if (!File.Exists(jsonPath))                {                    var defaultCfg = new HingeConfig                    {                        HoleDiameter = 35,                        BottomHoleOffset = 100,                        TopHoleOffset = 100,                        EdgeOffset = 22,                        HeightRule = new List<HeightRule>()                        {                            new HeightRule{MinH=0,MaxH=1100,HingeCount=2},                            new HeightRule{MinH=1100,MaxH=1650,HingeCount=3},                            new HeightRule{MinH=1650,MaxH=2300,HingeCount=4},                            new HeightRule{MinH=2300,MaxH=2600,HingeCount=5},                            new HeightRule{MinH=2600,MaxH=3000,HingeCount=6}                        }                    };                    File.WriteAllText(jsonPath, JsonConvert.SerializeObject(defaultCfg, Formatting.Indented));                    ed.WriteMessage($"\n未找到配置文件,已生成默认配置:{jsonPath}");                }                _cfg = JsonConvert.DeserializeObject<HingeConfig>(File.ReadAllText(jsonPath));            }            catch (Exception ex)            {                ed.WriteMessage($"\n读取配置失败:{ex.Message}");                return;            }            //2.框选门板(要求是闭合多段线PLINE)            PromptSelectionResult selRes = ed.GetSelection();            if (selRes.Status != PromptStatus.OK) return;            SelectionSet ss = selRes.Value;            //3.门扇数量判断            List<Polyline> doorPlines = new List<Polyline>();            using (Transaction tr = db.TransactionManager.StartTransaction())            {                foreach (ObjectId oid in ss.GetObjectIds())                {                    DBObject obj = tr.GetObject(oid, OpenMode.ForRead);                    if (obj is Polyline pl && pl.Closed)                    {                        doorPlines.Add(pl);                    }                }                tr.Commit();            }            int doorCount = doorPlines.Count;            if (doorCount == 0)            {                ed.WriteMessage("\n没有选中闭合多段线门板!");                return;            }            //4.区分开门类型            bool isDoubleOpen = doorCount % 2 == 0;            ed.WriteMessage($"\n选中门扇数:{doorCount}{(isDoubleOpen ? "对开门" : "单开门")}");            //5.创建图层 铰链孔,颜色1红色            CreateHingeLayer(db);            //6.循环每扇门板绘制铰链孔+标注            using (Transaction tr = db.TransactionManager.StartTransaction())            {                foreach (var pl in doorPlines)                {                    //获取门板包围盒,计算门板宽、高                    Extents3d ext = pl.GeometricExtents;                    Point3d minPt = ext.MinPoint;                    Point3d maxPt = ext.MaxPoint;                    double doorH = maxPt.Y - minPt.Y;                    double doorW = maxPt.X - minPt.X;                    //根据门板高度获取铰链数量                    int hingeNum = GetHingeCount(doorH);                    ed.WriteMessage($"\n门板高度 {doorH:F2},铰链数量:{hingeNum}");                    //==== 开门朝向判断逻辑(核心)====                    bool isLeftHinge;                    if (isDoubleOpen)                    {                        //偶数门扇:对开门,门扇成对,一扇左装铰链,一扇右装铰链                        isLeftHinge = doorPlines.IndexOf(pl) % 2 == 0;                    }                    else                    {                        //单数门扇:单开门,弹窗让用户选择【左开/右开】                        PromptKeywordOptions pko = new PromptKeywordOptions("\n请选择单开门铰链侧 [左开(L)/右开(R)]");                        pko.Keywords.Add("L");                        pko.Keywords.Add("R");                        pko.AllowNone = false;                        PromptResult pr = ed.GetKeywords(pko);                        if (pr.Status != PromptStatus.OK) return;                        isLeftHinge = pr.StringResult == "L";                    }                    //计算铰链孔Y坐标,均分分布                    List<double> yList = CalcHingeY(doorH, hingeNum);                    double edgeDist = _cfg.EdgeOffset;                    //铰链孔X坐标:左铰链靠门板左侧,右铰链靠门板右侧                    double holeX;                    if (isLeftHinge)                        holeX = minPt.X + edgeDist;                    else                        holeX = maxPt.X - edgeDist;                    //绘制圆+尺寸标注                    foreach (double y in yList)                    {                        Point3d center = new Point3d(holeX, minPt.Y + y, 0);                        DrawHoleCircle(db, tr, center, _cfg.HoleDiameter / 2.0);                    }                    AddHingeDimension(db, tr, minPt, maxPt, holeX, yList, isLeftHinge);                }                tr.Commit();            }            ed.WriteMessage("\nHingeHole执行完成!");        }        ///<summary>根据门板高度获取铰链数量</summary>        privateintGetHingeCount(double h)        {            foreach (var rule in _cfg.HeightRule)            {                if (h >= rule.MinH && h <= rule.MaxH)                    return rule.HingeCount;            }            return 2;        }        ///<summary>计算各个铰链孔距离门板底边的Y值</summary>        private List<doubleCalcHingeY(double doorH, int count)        {            List<double> ys = new List<double>();            double bottomOff = _cfg.BottomHoleOffset;            double topOff = _cfg.TopHoleOffset;            double usableH = doorH - bottomOff - topOff;            double step = usableH / (count - 1);            ys.Add(bottomOff);            for (int i = 1; i < count - 1; i++)            {                ys.Add(bottomOff + step * i);            }            ys.Add(doorH - topOff);            return ys;        }        ///<summary>创建铰链孔图层,颜色1红色</summary>        privatevoidCreateHingeLayer(Database db)        {            using (Transaction tr = db.TransactionManager.StartTransaction())            {                LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;                if (!lt.Has(LayerName))                {                    LayerTableRecord ltr = new LayerTableRecord();                    ltr.Name = LayerName;                    ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 1);                    lt.UpgradeOpen();                    lt.Add(ltr);                    tr.AddNewlyCreatedDBObject(ltr, true);                }                tr.Commit();            }        }        ///<summary>画铰链圆孔</summary>        privatevoidDrawHoleCircle(Database db, Transaction tr, Point3d center, double radius)        {            Circle cir = new Circle(new Point3d(center.X, center.Y, 0), Vector3d.ZAxis, radius);            cir.Layer = LayerName;            cir.ColorIndex = 1;            BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;            BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;            btr.AppendEntity(cir);            tr.AddNewlyCreatedDBObject(cir, true);        }        ///<summary>铰链孔竖向尺寸标注</summary>        privatevoidAddHingeDimension(Database db, Transaction tr, Point3d minPt, Point3d maxPt, double holeX, List<double> yList, bool leftSide)        {            BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;            BlockTableRecord ms = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;            double dimOffset = leftSide ? -40 : 40;            foreach (double y in yList)            {                Point3d pt1 = new Point3d(holeX, minPt.Y + y, 0);                Point3d pt2 = new Point3d(holeX + dimOffset, minPt.Y + y, 0);                AlignedDimension dim = new AlignedDimension(pt1, pt2, pt2, "", DimensionStyleOverrideType.None);                dim.Layer = LayerName;                dim.ColorIndex = 1;                ms.AppendEntity(dim);                tr.AddNewlyCreatedDBObject(dim, true);            }        }    }}

四、编译、加载、测试

编译步骤和环境配置那篇一样,不重复了。不懂的小伙伴可以看这篇

加载之后,在 CAD 里输入 HingeHole,框选需要绘制的门板,插件会根据配置文件的参数自动画出铰链孔。

五、我踩的坑

坑 1:孔的位置算错了

AI 第一版给的代码,孔距是按比例算的,结果 4 个孔的时候,中间两个孔重叠了。

原因:AI 默认把孔均匀分布在整条线上,但没考虑门板两端要留距离。

怎么解决:我加了 edgeDist 参数,让孔从距两端 100mm 的位置开始算,中间再均匀分布。

坑 2:图层没创建,报错

AI 第一版直接往一个不存在的图层上画圆,CAD 直接报错。

怎么解决:加了判断逻辑,图层不存在就自动创建。

坑 3:孔的方向反了

门板边线如果是从右往左画的,孔的偏移方向会跑到门板外面去。

怎么解决:用向量的垂直方向计算偏移,同时判断方向,确保孔始终在门板内侧。

六、这个插件还能怎么升级

现在这个版本是基础版,后面可以继续加:

  • 参数可视化设置:弹窗让用户输入孔数、间距、孔径

  • 支持不同类型的铰链:如异形铰链、厚板铰链、,孔位边距都不一样

这些后面慢慢加,先把基础版跑通。

七、获取源码和提示词

这篇的完整代码和 AI 提示词,我整理好了。

回复“铰链孔”,直接领取。

如果你也是全屋定制设计师、拆单师,想利用下班时间学一门真正能变现的技能,关注我,我按打工人的节奏更新,全程带新手落地。

上一篇:环境配置一篇讲透

下一篇预告:一键层板

回复“铰链孔”,领取本篇完整源码 + AI 提示词。

如果你也是全屋定制 / 拆单 / CAD 同行,关注我,我按打工人节奏更新。

相关学习资料