ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SHX形定义详解:从矢量方向代码到自定义符号开发

SHX形定义详解:从矢量方向代码到自定义符号开发 做CAD二次开发、定制字体或者单纯想往图纸里塞几个自己定义的符号几乎都会碰到SHX。第一次打开SHP文件看到一串024, 02C, 038, 03C这样的十六进制数字大多数人都会懵这到底在说什么我当年第一次研究形定义时对着文档翻了半天最大的障碍不是形定义的整体结构而是最基础的那几个数字——矢量方向代码。只要你搞懂了这一小段编码规则形定义基本就学了一半。这篇文章就专门把SHX形定义里的矢量方向代码讲透顺带把形定义的完整语法、编译加载流程、常见坑位都梳理一遍。适合刚开始接触SHP/SHX的人也适合已经会抄代码但一直没搞懂原理的CAD二次开发人员。看完你不仅能看懂别人写的形定义还能自己从零写一个符号出来。1. SHX形定义到底是什么1.1 从SHP到SHX先分清这两个文件很多人会把SHP和SHX搞混其实它们的关系就是“源代码”和“编译后的成品”。SHP是纯文本文件你可以在Notepad、VS Code里直接打开编辑里面全是人能看懂的ASCII字符、数字和逗号。而SHX是SHP经过编译后的二进制文件CAD运行时真正加载的是SHX不是SHP。你每次修改SHP都要重新编译一次生成新的SHXCAD才能看到变化。为什么非要编译一道因为形定义本身是一套极其紧凑的描述语言源文件里用文本表示的数字和逗号经过编译器之后会变成一个个字节加载速度和执行效率高得多。尤其是字体类形文件几千个字符符号如果CAD每次解析纯文本性能根本扛不住。形定义能干什么呢说人话就是自定义符号比如公司logo、水印、特殊标注符号用SHAPE命令插到图纸里。自定义字体把SHP编译成SHX字体文件在文字样式中引用就能用特殊字形写标签。优化重复性工作一些常用块或者复杂线型直接用形定义写比块更轻量。1.2 形定义头部每个形都有一张“身份证”无论多复杂的SHP文件每个形的开头都是一样的格式*形编号, 数据字节数, 形名称 形定义体比如*230, 25, CROS 2, 8, (0,0), 1, 45, 2, 8, (0,-5), 1, C5, 2, 8, (0,5), 1, 05, 2, 8, (-5,0), 1, 85, 0第一行就是身份证*230形的编号全局唯一。字体文件里这个编号直接对应字符的ASCII码比如65就是大写字母A。25数据字节数。这是后面整个定义体占用的字节总数必须数准数少了或数多了编译器都会报错。CROS形名称。这个名称只能由字母、数字、下划线组成不能用数字开头也不能和已有命令冲突。第二行开始才是形定义体就是真正描述“怎么画”的部分。1.3 为什么说矢量方向代码是地基整个形定义体的核心就是一连串的“方向代码 长度代码”。你可以把这套东西理解成一种“极简画图指令集”每遇到一个普通矢量代码笔就朝着某个方向走一小段要么抬着走不画线要么落着走画线。所以看懂SHX形定义第一步就是看懂矢量方向代码。这也是为什么我觉得它值得单独拿出来讲。后面的专用代码、圆弧代码全是在这个基础上扩展出来的。2. 矢量方向代码16方向的底层编码模型2.1 为什么是16个方向而不是8个经常有朋友问画图需要那么多方向吗8个方向不也够用其实这是精度和代码体积之间的平衡。如果只有8个方向那就只能画水平、垂直和45度斜线稍微带点角度的线就画不出来。但如果搞32个方向每个方向要额外占更多信息位一个字节就装不下了。16个方向每个方向间隔22.5度既能表达常见的45度阶梯斜线又能省空间。这16个方向从哪开始编号从正右方向开始按照数学角度的逆时针方向依次编号方向0正右也就是时钟3点钟方向方向1右上偏右22.5度左右方向2右上45度方向3右上偏上67.5度左右方向4正上方向5左上偏上方向6左上方向7左上偏左方向8正左方向9左下偏左方向10左下方向11左下偏下方向12正下方向13右下偏下方向14右下方向15右下偏右这套编号顺序一定要背熟。判断方法很简单方向0在右顺时针往下转按钟表方向从3点转到12点再转到9点或者心里记着“从右开始逆时针增数”。2.2 一个字节同时放下方向和长度这可能是整个形定义里最巧妙的设计也是最容易把人绕晕的地方。一个普通矢量包含两部分信息方向和长度。方向取值范围0到15长度取值范围也是0到15刚好各需要4个二进制位。两个拼在一起就是一个完整的8位字节——高4位是方向低4位是长度。在SHP文件里我们可以直接用十六进制把这个字节写出来SHP代码解析结果含义45方向4长度5向上画5个单位C5方向C长度5向下画5个单位C就是十进制的1205方向0长度5向右画5个单位85方向8长度5向左画5个单位等一下45这种写法是不是很像十进制45不是。形定义里普通矢量如果写成数字默认按十六进制理解45读作“十六进制0x45”高4位是4低4位是5。写成文字解释就是把方向和长度拼在一个十六进制数里高4位方向代码0到F低4位长度代码0到F1到15个单位长度0的特殊含义下文说所以24就是方向2、长度4AC就是方向A10、长度C12。8C则是方向8、长度C。每条指令占1个字节两个形定义之间用逗号隔开这也就是为什么SHP文件里看起来全是一堆两位或三位十六进制数。2.3 方向代码背后还有一套“整数位移表”这里我得多说几句这是很多教程不会细讲的细节。你可能会以为方向1就是精确的22.5度直线。但CAD为了简化计算并没有采用真正的三角函数而是给每个方向指定了一个“网格位移向量”。这是AutoCAD形定义最别扭、也最容易被忽略的地方。各方向对应的位移向量大致如下方向代码角度位置网格位移向量00度(1, 0)122.5度(2, 1)245度(1, 1)367.5度(1, 2)490度(0, 1)5112.5度(-1, 2)6135度(-1, 1)7157.5度(-2, 1)8180度(-1, 0)9202.5度(-2, -1)10225度(-1, -1)11247.5度(-1, -2)12270度(0, -1)13292.5度(1, -2)14315度(1, -1)15337.5度(2, -1)仔细看方向1的位移向量是(2,1)并不是真正的22.5度角而是一条斜率0.5的直线。方向3的位移向量是(1,2)斜率是2。这套向量最重要的意义是在形定义的网格里所有指令最终都落在整数坐标点上方便编译器快速计算线段的终点。由此带来的后果就是你用方向1或方向3这种代码画的斜线放大之后会看到明显的阶梯感它不是一条光滑的直线。如果要求不高视觉上还过得去但要是做精密符号建议直接用代码8/9画绝对位移或者用圆弧代码来补。2.4 长度0与终止码的冲突长度代码的范围是0到15但长度0在实际使用中必须非常小心。普通矢量代码用一个字节表示如果方向0、长度0写出来就是00。但形定义里的终止码也是0或00。编译器读到0就认为这个形画完了直接返回。所以你如果试图用00来表示“方向0长度0不产生任何效果”编译器会直接把它当成结束符后面的定义全部不执行。宁可不写也不要用长度为0的普通矢量。另外长度代码的F即15表示最大单位长度配合方向代码凑出各种斜线段。但实际绘制时长度并不是绝对的绘图单位而是相对单位插入形的时候会要求你输入比例因子。3. 形定义专用代码逐个拆解普通矢量代码解决直线段但形图不可能只有直线段还得有抬笔落笔、圆弧、缩放这些动作。这就需要一套专用代码。专用代码都是几个特定数字开头的数字范围从0到15超过15的交给后续参数来配合。3.1 笔状态控制与矩阵变换先说几个最常见的专用代码代码含义说明1落笔开始画线笔尖接触图纸2抬笔停止画线笔尖离开图纸移动只改变位置3缩放除数后面跟一个数把后续矢量长度都除以它4缩放倍数后面跟一个数把后续矢量长度都乘以它5保存位置把当前笔位置压栈保存6恢复位置把之前保存的位置恢复出来7调用子形后面跟一个形编号直接复用另一个形8绝对位移绘制后面跟一对坐标直接画到(x,y)点9连续位移绘制后面跟多对坐标依次连线10(00A)八分圆弧后面跟半径、起始八分圆、终止八分圆11(00B)非八分圆弧后面跟5个参数画任意角度的弧12(00C)凸度圆弧后面跟dx、dy、凸度用凸度控制弯曲14(00E)字体模式处理水平/垂直书写的文字形态15(00F)重复段后面跟次数和若干子代码重复执行代码1和2就是“画笔的开关”。你写1表示接下来开始画线写2表示接下来只移动不画线。画完一段后抬笔再落笔再画下一段这套节奏特别重要。代码3和4是“缩放开关”。比如4, 10表示从这条指令之后所有矢量长度都放大10倍。这个在画可变比例符号的时候很省事你不需要把每个长度都预先放大。代码5和6可以理解成“坐标记忆”。画图中如果你需要在多个位置落笔中间还要绕来绕去可以先把当前位置保存起来操作完再恢复省得自己算来回位移。代码8是“按坐标画线”格式是8, (x, y)。这个比较适合画任意角度的直线不受16方向限制。坐标值可以是负数但不能把符号算进字节数里。3.2 八分圆弧00A的完整规则圆弧是形定义里的重头戏先讲八分圆弧。八分圆弧代码写作00A后面跟3个参数半径、起始八分圆、终止八分圆。什么是八分圆就是把整个圆从0度开始每45度切一刀一共切成8个扇形区块八分圆编号起始角度位置说明00度正右145度右上290度正上3135度左上4180度正左5225度左下6270度正下7315度右下规则很简单圆弧的圆心是当前位置。从起始八分圆开始按逆时针方向画到终止八分圆。如果起始编号小于终止编号正常逆时针扫过。如果起始编号大于终止编号圆弧会跨越0号八分圆相当于多绕了半圈。如果起始编号等于终止编号AutoCAD会画一个完整圆。举个例子00A, 5, 3, 7意思是圆心在当前位置半径5从八分圆3135度位置开始逆时针画到八分圆7315度位置出来的就是下半圆。这个代码写起来很快适合画标准45度倍数的圆弧。3.3 非八分圆弧与凸度圆弧45度倍数的圆弧毕竟不够自由遇到任意角度的弧段就得用非八分圆弧。非八分圆弧代码写作00B后面跟5个参数00B, 起点x偏移, 起点y偏移, 凸度, 终点x偏移, 终点y偏移同样的当前点是圆心起点和终点由相对于圆心的偏移量决定。凸度取值是-127到1270表示直线正值表示逆时针方向的弧负值表示顺时针方向的弧。凸度绝对值越大弧越鼓接近127时基本就是半圆。多出来的那几个参数怎么用呢我经常用它在圆角轮廓上画过渡弧。比如要画从左上角到右上角的圆弧先根据圆心算好起点偏移和终点偏移再给定一个正凸度编译器会自动补出弧线。另一个代码1200C是凸度圆弧格式是00C, dx, dy, bulge它和前一个的区别在于这次当前位置是起点终点在(dx, dy)处用凸度控制弯曲方向。和代码8配合使用非常顺手适合从当前点直接牵一条弧线到目标点比如画箭头尾部、圆角矩形等。理论上圆的任何一段都能用凸度拟合虽然大角度弧段用凸度会比较吃力但每个圆弧段不超过180度时凸度方式精度是足够的。3.4 面向字体和重复图形的特殊代码如果你在写字体类SHX有两个代码必须知道。代码1400E是字体模式控制负责处理水平书写和垂直书写两种状态。一般的字形不用管它但像日文假名、中文注音这些需要竖排的字符就可能涉及。代码1500F是重复段。格式是00F, 重复次数, 子代码列表, 0它会一遍遍重复执行中间那一堆子代码适合画锯齿、齿轮、波浪线这些周期性图案。省得你把同样的代码复制几十遍。代码7是调用子形后面跟形编号。这个和写代码时的函数调用差不多可以把公共图形抽出来复用减少形定义体积。比如你要画一组同样的小圆圈圆圈单独定义一个形再通过代码7在各个位置调它。4. 实操手写一个带方向代码和圆弧的形4.1 先在网格纸上规划我强烈建议写任何形定义之前先画一张网格草图。坐标原点就是将来形插入的位置。把要画的符号的关键点标出来然后顺着笔的移动路径一步一步记录“抬笔”“落笔”“方向”“长度”。举个例子规划一个十字标记中心在原点(0,0)。从中心向上画5个单位。抬笔回中心。从中心向下画5个单位。抬笔回中心。从中心向右画5个单位。抬笔回中心。从中心向左画5个单位。再规划一个半圆弧圆心在原点半径5从八分圆3画到八分圆7。4.2 写出完整的SHP源文件把规划好的路径写成SHP*230, 25, CROS 2, 8, (0,0), 1, 45, 2, 8, (0,-5), 1, C5, 2, 8, (0,5), 1, 05, 2, 8, (-5,0), 1, 85, 0 *231, 5, SEMI 00A, 5, 3, 7, 0第一行*230是形编号。25是字节数我数过抬笔代码1字节8,(0,0)占3字节落笔代码1字节普通矢量45占1字节……全部加起来正好25。CROS是形名称。第二行的定义体把每个动作用中文描述一遍2抬笔8, (0,0)移动到原点1落笔45方向4上、长度5向上画竖线2抬笔8, (0,-5)从当前位置(0,5)移动到(0,0)1落笔C5方向C下、长度5向下画竖线2抬笔8, (0,5)回到原点1落笔05方向0右、长度5向右画横线2抬笔8, (-5,0)回到原点1落笔85方向8左、长度5向左画横线0形结束第二个半圆弧00A八分圆弧5半径53起始八分圆3即135度位置7终止八分圆7即315度位置0形结束4.3 编译与加载把上面的内容保存为文本文件比如myshapes.shp。注意编码一定要选ANSI或者UTF-8无BOM不要用带BOM的UTF-8否则编译器第一行容易报错。打开AutoCAD命令行输入COMPILE选择myshapes.shp成功后会生成myshapes.shx。然后在命令行输入SHAPE回车。提示输入形名称时输入CROS回车再指定插入点、高度比例、旋转角度十字标记就出现在图纸上了。再输入SHAPE输入SEMI就能看到半圆弧。到了这一步你就已经走通了“写SHP → 编译 → 加载插入”的完整流程。4.4 字节数数不对是一种怎样的体验我到现在还记得自己第一次写形定义怎么编译都报错。后来发现是字节数少算了一位。负号不计、逗号不计这些规则我知道但还是容易漏。这里我把字节数统计规则说得直白一点普通矢量代码每个占1字节。专用代码本身占1字节。特殊代码后面的参数按个数算字节1个参数占1字节一对坐标占2字节负号不计。形定义末尾的0也占1字节。以CROS为例我数一遍2 1字节 8, (0,0) 3字节代码8 0 0 1 1字节 45 1字节 2 1字节 8, (0,-5) 3字节0和5各1字节负号不计 1 1字节 C5 1字节 2 1字节 8, (0,5) 3字节 1 1字节 05 1字节 2 1字节 8, (-5,0) 3字节 1 1字节 85 1字节 0 1字节加起来正好25字节。编译器对字节数很严格多一个少一个都不行。所以建议每写一段就在草稿上累计一次别写完再数那时候真的很痛苦。5. 常见问题与排查手册下面这些问题是我自己踩过、也看群里朋友踩过的坑整理成速查表现象可能原因解决办法编译报错“bad string in shape file”SHP文件里有全角字符、中文注释或不能被识别的控制符纯ASCII编辑删除注释和多余空格检查编码编译报错“byte count (N) is wrong”数据字节数和定义体实际字节数不一致按“代码本身参数个数”重数负号不计SHAPE命令找不到形SHX文件没有加载或形名称输错用LOAD/COMPILE确认加载路径确认名称大小写插入的形特别小或特别大形定义是相对单位插入比例因子不合适插形时输入大比例或在定义内用代码3/4缩放斜线有台阶感普通方向代码用的整数网格位移近似精度有限改用代码8/9画绝对位移或用圆弧代码字体字符间距错乱字形定义末尾缺少宽度控制移动检查是否有抬笔和8指令来补充字符宽度改了SHP重新编译图形还是旧样子SHX被CAD缓存或旧SHX被占用关掉图纸重新打开或清除已加载的SHX后重新LOAD除了这些还有几个实战心得不要在SHP文件里写注释。SHP是很纯粹的数据格式很多写法编译器根本不认。要记录版本信息放在外部文档或者靠形名称区分。不要用Excel编辑SHP。Excel会动单元格格式保存后多一堆不可见字符编译器分分钟报错。每次改完形定义记得重新编译并重新加载SHX。CAD有时候对旧SHX有缓存不重开图纸就一直用旧版排查时特别容易怀疑人生。写复杂形之前把坐标图打印出来或者画在纸上标注好每一步的抬笔落笔。直接对着屏幕写坐标很容易漏掉中间某段。最后分享一个小习惯现在我做形定义会专门准备一个SHP工具文件里面放几个标准模板一个纯直线模板、一个圆弧模板、一个字体模板。每次新符号直接从模板改省得重新数字节数。形定义这东西看起来冷门但一旦用上了是真的能解决不少实际问题。
返回列表