乐于分享
好东西不私藏

[免费|干货]CAD二次开发插件源码分享:C#精准定位!“右侧回溯法”获取字符笔画重心

[免费|干货]CAD二次开发插件源码分享:C#精准定位!“右侧回溯法”获取字符笔画重心

在处理 CAD 文字时,我们经常遇到这种棘手的“微操”需求:

🎯 场景: 给文字 “A.1” 中的小数点 “.” 画一个圈。

😫 痛点: 字间距(Kerning)很难算准。简单的“字符宽度累加”会忽略 CAD 字体渲染时的重叠调整。

今天分享一种“右侧回溯算法”:利用 CAD 渲染引擎计算出的绝对右边界,反向推算字符中心,精度达到像素级。

01 核心算法:累计边界与回溯

为了彻底消除字间距带来的累积误差,我们采用以下策略:

  1. 获取绝对右边界 (MaxX):
     创建一个临时文字,内容包含当前字符及之前的所有字符。测量它的最右侧 X 坐标。这是该字符在整行文字中绝对准确的结束位置。
  2. 获取自身宽度 (Width):
     单独创建一个目标字符,测量其实际笔画宽度。
  3. X轴回溯:
    中心X
     = 绝对右边界 – (自身宽度 / 2)
  4. Y轴独立:
    中心Y
     = (独立字符MinY + 独立字符MaxY) / 2
    (使用独立字符高度,确保”.”的中心在底部,而不是悬在半空)
02 完整源码分享

新建类 CharMeasureCmd,加载后使用命令 MEASURECHAR。程序会自动画出目标字符的中心点连线和包围圈。

using System;using Autodesk.AutoCAD.Runtime;using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.DatabaseServices;using Autodesk.AutoCAD.EditorInput;using Autodesk.AutoCAD.Geometry;namespace CADDevTools{    public static class CharMeasureCmd    {        [CommandMethod("MEASURECHAR")]        public static void MeasureCharDistance()        {            Document doc = Application.DocumentManager.MdiActiveDocument;            Database db = doc.Database;            Editor ed = doc.Editor;            // 1. 选择文字            PromptEntityOptions peo = new PromptEntityOptions("\n请选择单行文字(DBText): ");            peo.SetRejectMessage("\n需选择DBText");            peo.AddAllowedClass(typeof(DBText), true);            var per = ed.GetEntity(peo);            if (per.Status != PromptStatus.OK) return;            // 2. 输入索引            PromptIntegerOptions pio = new PromptIntegerOptions("\n请输入目标字符索引 (从0开始): ");            pio.AllowNegative = false;            var pir = ed.GetInteger(pio);            if (pir.Status != PromptStatus.OK) return;            using (Transaction tr = db.TransactionManager.StartTransaction())            {                DBText textEnt = tr.GetObject(per.ObjectId, OpenMode.ForRead) as DBText;                int index = pir.Value;                if (index >= textEnt.TextString.Length)                {                    ed.WriteMessage("\n❌ 索引超出文字长度!");                    return;                }                // --- 核心调用 ---                Point3d charCenter = GetTrueStrokeCenter(textEnt, index);                // 获取插入点用于连线展示                // 仅用于视觉参考,不影响计算                Point3d insertPt = (textEnt.HorizontalMode == TextHorizontalMode.TextLeft &&                                     textEnt.VerticalMode == TextVerticalMode.TextBase)                                     ? textEnt.Position : textEnt.AlignmentPoint;                // 3. 可视化绘制 (画红线和绿圈)                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);                Line line = new Line(insertPt, charCenter);                line.ColorIndex = 1// 红线连接                btr.AppendEntity(line);                tr.AddNewlyCreatedDBObject(line, true);                // 画一个小圈标记重心                // 半径取字高的0.2倍,方便观察                Circle circle = new Circle(charCenter, Vector3d.ZAxis, textEnt.Height * 0.2);                circle.ColorIndex = 3// 绿圈                btr.AppendEntity(circle);                tr.AddNewlyCreatedDBObject(circle, true);                tr.Commit();            }        }        ///        /// [核心算法] 获取指定字符的“实际笔画重心” (世界坐标)        ///        public static Point3d GetTrueStrokeCenter(DBText sourceText, int index)        {            string content = sourceText.TextString;            // 字符串A: "当前字符及之前的所有字符" (用于计算绝对右边界)            string strCombined = content.Substring(0, index + 1);            // 字符串B: "目标字符" (用于计算自身宽高和Y重心)            string strTarget = content.Substring(index, 1);            // 1. 创建虚拟文字 (内存对象) 用于测量            // 关键:强制归零(左对齐、0角度、原点位置),构建纯净的局部坐标系            using (DBText tempCombined = new DBText())            using (DBText tempTarget = new DBText())            {                CopyStyle(sourceText, tempCombined);                CopyStyle(sourceText, tempTarget);                tempCombined.TextString = strCombined;                tempTarget.TextString = strTarget;                // 2. 测量几何尺寸 (AABB)                Extents3d extComb = tempCombined.GeometricExtents;                Extents3d extTar = tempTarget.GeometricExtents;                // 【步骤A - 确定X坐标】                // 利用 combined 的 MaxPoint.X,这是包含所有字间距的绝对右边缘                double absoluteRightX = extComb.MaxPoint.X;                // 计算目标字符自身的宽度                double charWidth = extTar.MaxPoint.X - extTar.MinPoint.X;                // 回溯计算中心 X = 绝对右边缘 - 半个字宽                double localCenterX = absoluteRightX - (charWidth / 2.0);                // 【步骤B - 确定Y坐标】                // 利用 target 自己的边界计算 Y 重心                // 这样 "." 的重心在底部,"1" 的重心在中间,互不干扰                double localCenterY = (extTar.MinPoint.Y + extTar.MaxPoint.Y) / 2.0;                Point3d localCenter = new Point3d(localCenterX, localCenterY, 0);                // 3. 矩阵变换:从局部坐标 -> 世界坐标                // 变换顺序:先旋转 -> 再移动到位置                Point3d basePoint = sourceText.Position;                Matrix3d matRot = Matrix3d.Rotation(sourceText.Rotation, sourceText.Normal, Point3d.Origin);                Matrix3d matMov = Matrix3d.Displacement(basePoint.GetAsVector());                // 应用变换                return localCenter.TransformBy(matRot).TransformBy(matMov);            }        }        // 辅助:复制样式 (确保测量环境与原图一致)        private static void CopyStyle(DBText source, DBText target)        {            target.SetDatabaseDefaults();            target.TextStyleId = source.TextStyleId;            target.Height = source.Height;            target.WidthFactor = source.WidthFactor;            target.Oblique = source.Oblique;            // 强制设为左对齐、原点,方便数学计算            target.HorizontalMode = TextHorizontalMode.TextLeft;            target.VerticalMode = TextVerticalMode.TextBase;            target.Position = Point3d.Origin;            target.Rotation = 0;        }    }}
03 为什么“回溯法”更准?

🔥 自动处理 Kerning (字距调整)

在某些字体中,”AV” 两个字母会靠得非常近(重叠)。如果简单地把 “A” 的宽加上 “V” 的宽,算出来的坐标会偏大。
使用 tempCombined 直接测量 “AV” 的总长,CAD 引擎会自动处理这些复杂的字距细节,得到的右边界是绝对准确的。

🔥 高度独立性

Y 坐标完全依赖 tempTarget (单个字符) 的边界。这意味着:
👉 对于句号 “.”,中心点就在脚下。
👉 对于连字符 “-“,中心点在中间。
👉 对于上标 “²”,中心点在头顶。
完美符合视觉直觉!

📌 总结

这个“右侧回溯 + 高度独立”的混合算法,是解决 CAD 文字定位问题的终极方案。它既照顾了水平方向的排版规则,又保留了垂直方向的笔画特征。

📚 往期推荐

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » [免费|干货]CAD二次开发插件源码分享:C#精准定位!“右侧回溯法”获取字符笔画重心

评论 抢沙发

1 + 8 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮