williamhub
发表于 2024-4-24 23:50:25
学习中,顶贴
逍遥无声
发表于 2024-4-25 01:53:55
绝对是大佬小号,此贴会火,我说的,占个楼
Gaudi
发表于 2024-4-25 11:46:57
第二篇第三部分标注桩号线和桩号文字
来到最后一个通用函数。
照样三板斧:功能拆分、代码实现基本功能、错误修复和需求完善。
1 功能拆分
A 确定直线标注起点和长度。
B 计算直线标注终点。
C 标注直线。
D 确定文字标注内容、位置、角度。
E 标注文字。
其中直线长度、文字高度,是开始自己输入。
直线起点是通过vla函数直接读取。
文字内容、切线角度,是前文两个函数返回。
因为桩号线是标注在曲线两侧,所有将直线标注拆分为向左侧标注和右侧标注,每一侧标注一半直线长度。
2代码实现
首先是计算直线标注终点,在已知起点坐标和标注方向的前提下,用 polar 函数计算另外一点。
别的办法一半碰不到,不去纠结。
polar 函数的标注格式如下:
(polar pt ang dist)
照样给个示例:
POLAR Point1 (+ (* PI 0.5) ANG) (/ normalLength 2)
注意 polar 函数需求的角度是弧度形式,逆时针方向旋转为正。
加π/2等于逆时针旋转九十度,切线方向变为曲线左侧垂线方向。
同理可以得到,加3π/2等于逆时针旋转270度,切线方向变为右侧垂线放线。
两个方向上的距离都是一半直线长度。
得到起点、终点坐标后,开始标注直线。
太简单了朋友们,用最简单的办法:
(command "line" Point3 Point2 "")
你就在CAD里输入 line ,然后看提示一步一步写就行。
同理,输入 text ,一步一步看。
确定文字标注样式,这里选择 中下 。
你会发现,文字标注的角度格式需求是角度。
那么通过 angle 获取的文字角度要进行一个角度弧度转换。
代码如下:
(setq ANG2 (/ (* (ANGLE Point2 Point1) 180) PI))
(command "text" "bc" "m2p" Point2 Point3 fontHeight ANG2 ZHK+M)
3错误修复和需求完善
太煎蛋了,一路走下来,第一次没有报错。
这时候就要开始做桩号标注位子的选择了。
根据标注位置的不同,分为左侧、右侧和中间。中间的代码刚刚已经写了。
那左侧和右侧相比已经实现的基础中间代码,有什么区别呢?
第一,标注直线的长度不再除以2。
第二,桩号文字的对齐方式不再是中下,而是左下和右下。
那么实现起来就非常煎蛋了:
(setq Point2 (POLAR Point1 (+ (* PI 0.5) ANG) normalLength))
(command "line" Point1 Point2 "")
(setq ANG2 (/ (* (ANGLE Point2 Point1) 180) PI))
(command "text" "br" Point2 fontHeight ANG2 ZHK+M)
另外一侧一模一样。
到这里就只剩下整合了。
可以用IF语句,判断、执行、判断、执行、判断、执行。
但这里我采用了另外一种函数 COND 。
condition的简称。
形式如下:
(cond
(条件1
(执行1)
)
(条件2)
(执行2)
)
)
不用管那么多,超过两种选择就用cond。
Gaudi
发表于 2024-4-25 17:10:31
第二篇第三部分增补
1
经历了前面一个函数略显复杂的VLA函数,这次的标注函数,煎蛋又轻松。
所有代码涉及到的知识,均未超出开篇所述的几个基础函数。
所以大家一定不要畏难。
代码这点东西,如果你纯粹自用,单个功能的核心实现代码绝对不超过6行。
真得,不信你自己写一写,实现一个功能的核心实现代码,基本上就几行,能到6行了,那都是极其复杂的东西了。
你想想,一个拆分到不能再拆分的原子功能,需要转6个弯,这台大几千上万块的大聪明机器才明白。
是不是有点过于卧龙凤雏了?
那有朋友就会问了,你骗我!随便看看都是几百行。
很多原子功能组合起来的嘛。
况且一个程序的90%代码是用来处理以下功能:
A 读取不在编写者意图中的数据,并提示
B 采用不在编写者意图中的格式,并提示
C 输出不在编写者意图中的数据,并提示
D 实现不在编写者意图中的功能,并提示
E 大量初始设定
F 大量错误提示
G 大量参数读取
H 可能还要附带图形界面
我将其称之为“沟通代码”……
你自己用,这部分代码完全可以舍去。
因为和自己沟通再顺畅不过了。
2
不扯淡,回到具体代码。
这里需要注意CAD中角度的描述。弧度和角度。
一般来说,程序自带的函数需求的都是弧度,需要用户选择的都是角度。
比如 text insert 等等,一般都是要求角度。
回顾下角度和弧度的互转代码:
(* (/ textAngle PI) 180)
(* textAngle (/ PI 180))
角度的起始方向是X轴正,沿逆时针旋转。
3
text 文字标注的时候,不管干啥,一定记得定义对齐方式。
会大大方便以后的操作。
Gaudi
发表于 2024-4-26 12:21:37
第二篇第四部分主代码函数调用
我是不是有点太磨叽了……几天了,才到主代码。
所有准备工作全部就绪,开始写主代码。
1
起点和终点煎蛋,且格式一样,没有什么特殊的地方。
直接调用三个函数。
代码如下:
(setq startZH startZH_Input)
(stringZH startZH)
(setq starPoint (VLAX-CURVE-GETSTARTPOINT OBJCenterLine))
(setq startAngle (angleCAL OBJCenterLine starPoint))
(label normalAngle normalLength startAngle starPoint fontHeight ZHK+M)
(setq endZH (+ totalDistance startZH_Input))
(stringZH endZH)
(setq endPoint (VLAX-CURVE-GETENDPOINT OBJCenterLine))
(setq endAngle (angleCAL OBJCenterLine endPoint))
(label normalAngle normalLength endAngle endPoint fontHeight ZHK+M)
一定一定要把参数命名写好,事半功倍的行为。
2 功能拆分
中间桩号就比较麻烦,这里要引入一个新函数 repeat ,用于循环标注。
照样开始拆分功能。
A 获得主线的实际长度。
B 根据标注长度和标注间距,计算出需要标注多少个桩号。
C 计算实际桩号的标注间距。
D 第二个桩号调整(因为起始桩号不一定是整桩,所以起始桩号与第二个桩号之间的距离不一定和标注间距相等)。
E 标注桩号。
F 将标注点加上实际标注间距。
G 标注桩号。
3 获得曲线长度
通过两个VLA函数获取。
(setq OBJCenterLine_Distance (VLAX-CURVE-GETDISTATPOINT OBJCenterLine (VLAX-CURVE-GETENDPOINT OBJCenterLine)))
VLAX-CURVE-GETENDPOINT (定义 - 曲线 - 获得 终点),传递一条VLA曲线,获得终点坐标。
VLAX-CURVE-GETDISTATPOINT(定义 -曲线 - 获得 起点 距离),传递一个VLA曲线的坐标,获得与起点的距离。
VLA函数真是又长又软啊。
4 计算桩号个数、实际标注间距和实际标注个数
(setq trueLabelSpace (/ OBJCenterLine_Distance (/ totalDistance labelSpace)))
(setq NN (FIX (/ OBJCenterLine_Distance trueLabelSpace)))
这个就不再解释了。
5 第二个桩号调整
(setq midZH (+ startZH_Input (- labelSpace (REM startZH_Input labelSpace))))
首先,将起始桩号除以标注间距,再取余数,得到碎桩数值。
然后将标注间距减去碎桩数值,得到实际间距。
然后将起点桩号加上实际间距,得到实际桩号。
6 非首桩号
(setq midZH (+ labelSpace (* UU labelSpace) (* (FIX (/ startZH_Input labelSpace)) labelSpace)))
起始桩号除以标注间距,取整,再乘标注间距,就能得到起始桩号往小的方向,最近的一个标注间距倍数的桩号。
比如标注间距20米,起始231.11米,先得到11.xxxx,取整为11,乘20得220。
先固定加一个标注间距,得到第二个桩号数值。
这里是240。
UU 代表这是第几个桩号,乘以标注间距,就依次得到了每个桩号。
6 标注点
(REM startZH_Input labelSpace)
首先,将起始桩号除以标注间距,再取余数,得到碎桩数值。
(- trueLabelSpace (REM startZH_Input labelSpace))
然后将实际标注间距,减去碎桩数值,就得到起始桩号和第二个桩号那一段的实际标注距离。
(+ (* UU trueLabelSpace) (- trueLabelSpace (REM startZH_Input labelSpace)))
然后就是加上第几个桩号乘以实际间距,就得到相应的标注点距离了。
(setq midPoint
(VLAX-CURVE-GETPOINTATDIST OBJCenterLine
(+ (* UU trueLabelSpace) (- trueLabelSpace (REM startZH_Input labelSpace)))
)
)
VLAX-CURVE-GETPOINTATDIST ,(定义 - 曲线 - 获得 点 按照 距离)
自然语言编程来袭,传递曲线和距离,获得点坐标。
7 标注
获得桩号文字和标注点后,剩下的就是调用函数了。
(setq midAngle (angleCAL OBJCenterLine midPoint))
(label normalAngle normalLength midAngle midPoint fontHeight ZHK+M)
8 repeat 函数的使用
repeat 函数需要给一个上限,告诉程序运行这么多次。
其通用模板为:
(repeat NN
(XXX)
)
repeat 函数很煎蛋,只适合用于循环次数固定的用法。
如果不知道循环次数,但知道循环结束条件,可以采用 while 。
唯一的变化就是将上限NN数值变为判定语句。
shirker
发表于 2024-4-26 14:30:51
很详细,学习了。
Gaudi
发表于 2024-4-26 17:32:16
本帖最后由 Gaudi 于 2024-4-26 20:52 编辑
第三篇第一部分新建图层
终于写完第二篇了。
终于来到了第三篇。
啰里啰嗦这么久,其实只是为了得到曲线上桩号文字的具体位置信息。
桩号标注完了,才能将外业数据里相应内容定位到CAD主线上。
OK,开始第一步——功能拆分。
1 功能拆分
……哦前面已经拆了。
这里复制下:
A 桩号标注(选择主线、得出实际长度、确定终点桩号、分隔成一米的间距、找到标注点、标注桩号)
B 新建图层(根据参数确定图层名称、根据图层名称确定现型线宽颜色)
C 获取指定文件夹路径(打开包含块的文件夹、获得这个文件夹的路径)
D 获取外业数据(打开excel、找到指定工作簿、找到指定工作表、找到指定数据范围、返回数据到CAD)
E 找到块插入位置(找到桩号位置、返回桩号和旋转角度)
F 插入属性块(根据块名找到块、插入块)
G 移动块(将插入的块移动到指定图层)
H 修改块属性(根据外业找到特定属性标签、修改属性)
进入第一个子函数——新建图层。
仍然是将这个函数的功能进行拆分:
A 定义图层名称、颜色、现型、线宽
B 建立图层
看着真简单啊!开搞。
2 列表
从这里开始,除了数字 real 和文字 string,要引入一个新的数据格式 列表 list。
给个示例:
(setq prefixes '("新建" "利用" "拆除" "更换"))
(setq suffixes '("标志牌" "砼护栏" "波形梁护栏" "挡墙"))
函数仍然是那个 steq 函数,但是后面多了个用 ' 开头的东西。
这个东西就是告诉程序,后面的东西是一起的。
为什么要这么做,原因其实很煎蛋。
观察下函数调用的格式:
(函数名 参数1 参数2)
是不是跟列表一模一样?那你不加个 ' ,电脑怎么知道这个括号里是列表还是函数调用?
它那么笨。
除了示例里给直接用 " 括起来的文本定义的列表,还可以是数字,还可以是参数。
甚至还可以是列表。
3列表中的列表
先看示例:
(setq colors '((新建 . 60) (利用 . 4) (拆除 . 3) (更换 . 30)))
(setq linetypes '((标志牌 . "CONTINUOUS") (砼护栏 . "TRACKS") (波形梁护栏 . "DASHDOT") (挡墙 . "ZIGZAG")))
(setq linewidths '((标志牌 . 15) (砼护栏 . 30) (波形梁护栏 . 30) (挡墙 . 30)))
点对数据,一个点,左右各有一个数据。
我不知道大佬是怎么理解这个东西的。
我理解它TM就是个列表,不过只能是二维,也就是两个数据。
为了简写,不需要每次都加个 ' ,搞得程序复杂,用 . 来代替空格。
4列表的作用
有朋友会问,我搞列表干啥啊?
那肯定是为了偷懒呗。
(foreach prefix prefixes
(foreach suffix suffixes
(setq layername (strcat prefix suffix))
简简单单,定义个图名。
两个 foreach 函数,直接通过 strcat 函数连接起来,形成十六个图名。
现在只要针对每个图名,确定图层参数就OK了。
5建立图层
新函数!entmake, entity make,建立实体。
这函数贯穿整个autolisp。
entmake 后面紧跟着的就是一个 list。
干嘛用的?朋友们打开CAD,对着任何一个实体,输入 list 。
显示的东西,就是 entmake 后面应该跟着的东西。
里面是点对,所以用这个形式:
(entmake (list
(cons 0 "LAYER")
(cons 100 "AcDbSymbolTableRecord")
(cons 100 "AcDbLayerTableRecord")
(cons 70 0)
(cons 2 layername)
(cons 6 (cdr (assoc (read suffix) linetypes)))
(cons 62 (cdr (assoc (read prefix) colors)))
(cons 370 (cdr (assoc (read suffix) linewidths)))
))
重点来了:
前面的数字是干什么的?
6 DXF组码
CAD中所有的实体,底层全是一个带着若干组点对的列表。
点对前面的数字是类型,后面是数据。
比如说,图层这个东西,0代表这是个图层,100代表——我也不知道是啥反正必需,70代表可见性,2代表图名,6代表线型,62代表颜色,370代表线宽,290代表打印。
理解了吗?CAD本质上是通过这东西创建实体的。
7 新函数
cdr,新函数。assoc,新函数。
个人老规矩,先说全称。
说 cdr 之前,先聊聊 car。
car = Contents of the Address part of Register,内容位于这个地址 部分在寄存器。
……什么JB玩意儿。
看不懂就对了,这东西估计有几十年了,是当年程序员们直接操作硬件时候的历史残留。
你可以理解为程序员们的恶意。
功能其实很简单:取列表的第一个数值。
好,那 cdr =Contents of the Decrement part of Register,内容位于这个减少部分在寄存器。
取列表从第二个数值到最后一个数值。
那 cadr 呢? = Contents of the Address-Decrement part of Register
取列表从第二个数值到最后一个数值,再取新列表的第一个数值。
那 caar 呢? = Contents of the Address-Address part of Register
取列表的第一个数值,再取新列表的第一个数值。
那 cddr 呢? = Contents of the Decrement-Decrement part of Register
取列表从第二个数值到最后一个数值,再取新列表从第二个数值到最后一个数值。
那 cadr 呢? = Contents of the Address-Decrement part of Register
取列表从第二个数值到最后一个数值,再取新列表从第二个数值到最后一个数值,再取新列表的第一个数值。
那 cdddr 呢?= Contents of the Decrement-Decrement-Decrement part of Register
取列表从第二个数值到最后一个数值,再取新列表从第二个数值到最后一个数值,再取新列表从第二个数值到最后一个数值。
能无限循环下去吗?
不行。
……你说他们程序员是不是恶意满满且有什么大病。
所以后来一般只用到 car cdr caar cadr cdar cddr ,到后面用得是 nth.
nth = 1rd 2nd 3rd 4th 5th 6th ……
(nth 0 (list))
就是这么简单!——注意电脑从0开始计数。
你说说,就不能干点人事吗。
上述函数,都是当你知道要取第几位,直接取列表中的数值。
如果不知道呢?我只知道点对列表前面那个数值。
难不成一个个取了用 if 和 equal 比对?
熟悉 excel 的朋友,知道有个函数叫 vlookup。打工人本命函数了属于是。
autolisp 中,同样有这么个函数叫 assoc = association.
通过查找第一个参数,返回对应参数的后续数值。
8错误修复
将上述代码进行调试,结果发现报错。
仔细一看,原来是没有加载线型。
这个代码就很简单了,很早之前就放出来过:
(if (not (tblsearch "LTYPE" "DASHDOT"))
(command "_.linetype" "Load" "DASHDOT" (strcat "acadiso" ".lin") "")
)
都是已经学习过的函数,就不再过多赘述了。
yjccwf
发表于 2024-4-26 22:57:07
我建议lsp学点就差不多了少走弯路直接学C#
你有种再说一遍
发表于 2024-4-27 02:04:40
可以学c#了,很多概念lisp学不到的,例如数据结构
kozmosovia
发表于 2024-4-27 10:25:22
又不是专业程序猿,数据结构学不学的意义不大。