阶段 0:入口与交互 0-1 用 ssget 让用户依次点选: • 实体1:AcDb3dPolyline(或轻量多段线)→ 记为 pl1 • 实体2:AcDb3dSpline(或 3dPolyline、Spline)→ 记为 sp2 0-2 用 getpoint 让用户指定“展开基线起点”→ 记为 basePt(二维平面上的左下角插入点)。 可行性:AutoLISP 的 entsel/ssget 可 100% 拿到实体,再 vlax-ename->vla-object 即可。 阶段 1:顶点对齐 1-1 用 vlax-curve-getEndParam + vlax-curve-getPointAtParam 把 pl1 与 sp2 的所有顶点抽成两个等长列表 L1、L2。 1-2 若顶点数不同,用 vlax-curve-getPointAtDist 按固定步长重采样,使 L1、L2 等长 n。 可行性:AutoLISP 的 ActiveX 接口完全支持,步长由用户可选,重采样误差可控。 阶段 2:三角形片构建 2-1 从 i = 0 到 n-2 循环,每轮生成一个三角形 T(i): • P1 = L1[i] • P2 = L2[i] • P3 = L2[i+1] (注意:P1 与 P3 不再闭合回 P1,因为 3 点即三角形;P1 仍可视作“母线”起点) 2-2 把 3 个三维点压成局部坐标: • 计算三边长 d12、d13、d23 • 用余弦定理求出二维坐标: p1' = (0,0) p2' = (d12,0) p3' = (x3,y3) 见上一条回复公式 可行性:纯代数运算,无矩阵库亦可完成;浮点精度足够。 阶段 3:二维布局 3-1 将每个三角形 T(i) 的二维顶点平移: offsetX = basePt[0] + (i * (d12 + gap)) // gap 为用户指定间距 offsetY = basePt[1] 3-2 用 vla-AddLightWeightPolyline 一次性写入 4 个点(p1' p2' p3' p1'),形成闭合三角形。 3-3 可选:在三角形左侧标注边长、面积;用 vla-AddText 或 command "_DIMLINEAR" 完成。 可行性:二维图元生成、文本标注均可在 LISP 中完成,无需外部 API。
时间: 2025-07-19 15:34:42 浏览: 9
<think>我们正在使用AutoLISP(或Visual LISP)编写一个AutoCAD插件。
目标:根据用户选择的两条线(一条多段线pl1和一条样条曲线sp2)以及一个基点,生成一系列三角形并展开在二维平面上。
步骤:
阶段0:交互输入
阶段1:顶点对齐(采样等长点列表)
阶段2:构建三角形片(每两个相邻点与对应点构成三角形)
阶段3:二维布局(将每个三角形平移到二维平面上的指定位置,并绘制)
注意:由于AutoLISP没有内置的矩阵运算,我们将使用向量运算(点积、叉积)和余弦定理来计算三角形的二维坐标。
我们将定义一些辅助函数:
1. 向量减法
2. 向量点积
3. 向量模长
4. 两点间距离
5. 根据三个点计算三角形的第三个点在二维平面上的坐标(已知两个点,求第三个点相对于这两个点所在基下的坐标)
在阶段2中,对于每个三角形,我们有三个点:P1, P2, P3(三维点)。我们需要将这个三角形展开到二维平面(局部坐标系):
- 设P1'为(0,0)
- P2'在x轴上,坐标为(d12, 0),其中d12是P1到P2的距离
- P3'的坐标计算:我们可以通过向量P1P2和向量P1P3来计算夹角,然后用余弦定理计算P3'的坐标。
具体计算P3'的方法:
设向量a = P1P2,向量b = P1P3
则P3'的x坐标 = |b| * cosθ = (a·b) / |a|
P3'的y坐标 = sqrt(|b|^2 - x^2) 注意:这里取正负号?实际上,我们只能取正号,因为我们假设三角形在基线的上方。但为了通用,我们可以使用叉积判断方向(在三维中叉积可以判断法向量,但这里我们展开到二维,可以假设在xy平面,所以用二维叉积判断方向)。
然而,我们也可以使用另一种方法:用向量投影和垂直分量。
设向量a = P1P2,向量b = P1P3
则向量b可以分解为平行于a的分量b_parallel和垂直于a的分量b_perp。
那么:
x = |b_parallel| = (a·b) / |a| (注意方向:如果与a同向则为正,反向为负?但距离是标量,所以这里我们取标量长度,但方向由点积符号决定)
但是,点积可能为负,所以x = (a·b) / |a| 这个值可能是负的,但我们希望它相对于a的方向为正,所以实际上我们取的是投影的长度(带符号),然后y取垂直分量的大小,但方向由右手定则(或左手定则)?实际上,我们展开的平面是任意的,但为了统一,我们可以规定:从P1->P2的方向为x轴正方向,从P1->P3的方向在x轴上的投影为正,然后垂直方向取正(即逆时针旋转90度为正方向)。
然而,在三角形中,我们只需要知道第三个点相对于前两个点构成的边的位置。我们可以通过以下公式计算:
设向量v1 = P2 - P1
向量v2 = P3 - P1
则:
dot = v1 · v2
len1 = |v1| (即d12)
len2 = |v2| (即d13)
angle = acos(dot/(len1*len2)) [0, pi]
但是,这样只能得到角度,不能得到方向。所以我们需要一个垂直向量。在二维平面上,我们可以用:
x = (v1 · v2) / len1 -> 投影到v1上的长度
y = |v1 × v2| / len1 -> 垂直于v1的长度(叉积的模长)
注意:三维叉积是一个向量,其模长等于|v1||v2|sinθ,所以y = |v1×v2| / |v1| = |v2|*sinθ
因此,P3'的坐标为(x, y) 或者 (x, -y)?这取决于我们如何规定方向。由于原始三维空间中的三角形可能有不同的朝向,我们无法直接确定。但我们可以规定:在展开图中,所有三角形都按照相同的方向展开(比如都放在基线的上方,即y为正)。
所以,我们取:
x = (v1 · v2) / len1
y = (|v1 × v2|) / len1 (注意:这个y值恒为正,因为叉积的模长是正的)
但是,这样计算出来的点P3'总是在x轴的上方(y>0)。在原始三维空间中,如果点P3在向量v1的逆时针方向(从v1转向v2为逆时针),那么这样计算是正确的;如果是顺时针,那么实际上应该y为负。但是,我们无法从叉积的模长得到符号。因此,我们需要计算叉积的符号(在三维空间中,我们可以用z分量的符号来表示方向,因为我们的三角形在三维空间中可能不在xy平面,但展开到二维平面时,我们希望统一在一个平面内)。
然而,我们展开的三角形是独立的,每个三角形单独展开,所以我们可以规定:在展开图中,所有的三角形都放在基线的上方(即y>0)。这样,我们就不需要关心原始三维空间中的方向了,因为展开后我们统一将第三个点放在上方。
所以,我们采用:
x = dot_product / len1
y = sqrt(len2*len2 - x*x) [因为x^2+y^2 = len2^2]
但是,这样计算会丢失方向信息,因为y取正。这样也可以,因为我们将所有三角形都放在基线的上方。
因此,步骤:
d12 = 两点距离(P1, P2)
d13 = 两点距离(P1, P3)
d23 = 两点距离(P2, P3)
然后,我们可以用余弦定理计算夹角(在P1处的角):
cos_theta = (d12^2 + d13^2 - d23^2) / (2*d12*d13)
那么,x = d13 * cos_theta
y = d13 * sin_theta [sin_theta = sqrt(1-cos_theta^2)] 注意取正值
这样也可以,但需要计算三个距离。我们之前用向量点积的方法其实更直接,因为向量v1和v2我们已经有了。
因此,我们选择向量方法:
v1 = P2 - P1
v2 = P3 - P1
dot = v1·v2
len1 = |v1| (即d12)
len2 = |v2| (即d13)
x = dot / len1
y = sqrt(len2^2 - x^2) [因为x^2+y^2 = len2^2]
注意:这里y取正,所以第三个点总是在x轴上方。
阶段3:绘制三角形
对于每个三角形,我们得到三个二维点:P1'(0,0), P2'(d12,0), P3'(x,y)
然后,我们将这个三角形整体平移到二维平面上的位置:
basePt是用户指定的基点(二维点,如'(0 0))
每个三角形在水平方向依次排列,相邻三角形之间有一个间隔gap(由用户指定)
所以第i个三角形的平移量: (basePt_x + i*(三角形宽度+gap), basePt_y)
注意:三角形的宽度是多少?我们可以取三角形在x方向的最大值,即max(0, d12, x)。但为了简单,我们直接用d12作为宽度(因为P2'在(d12,0)),然后再加上一个间隔gap。
但是,注意:每个三角形的宽度可能不同,所以我们需要计算每个三角形的最大x坐标。然而,为了排列整齐,我们可以用当前三角形的最大x坐标(即max(0, d12, x))作为这个三角形的宽度,然后下一个三角形的起始位置为当前起始位置+当前宽度+gap。
然而,题目要求是:
offsetX = basePt[0] + (i * (d12 + gap))
这里,它用d12作为宽度,并且乘以i,然后加上gap。这样每个三角形的起始位置都是前一个三角形起始位置加上一个固定宽度(d12+gap)。但是,如果每个三角形的d12不同,那么这样排列就会有问题(重叠或间隙不对)。所以,我们最好用每个三角形实际占据的宽度(即最大x坐标)来排列。
因此,我们修改为:
第0个三角形:起始x = basePt_x
第1个三角形:起始x = basePt_x + (第0个三角形的宽度 + gap)
第2个三角形:起始x = basePt_x + (第0个三角形的宽度+gap) + (第1个三角形的宽度+gap)
其中,每个三角形的宽度 = max(0, d12, x) 即三角形在x方向的最大值。
但是,题目中要求的是“母线”方向,而母线方向是沿着d12(即P1P2)的方向,所以每个三角形的基线都是d12,因此用d12作为宽度是合理的。因为d12是基线的长度,而第三个点P3'的x坐标可能小于0或大于d12,但题目没有要求,我们暂时按照题目的方式:用d12作为宽度。
因此,我们按照题目要求:offsetX = basePt[0] + (i * (d12 + gap))
注意:这样排列,如果某个三角形的第三个点超出了d12,那么相邻三角形可能会重叠。所以,我们也可以考虑用三角形的实际宽度(最大x-最小x)来排列。但题目要求用d12+gap,我们就按题目要求。
另外,题目要求绘制的是闭合三角形,所以绘制多段线时,我们给出四个点:P1', P2', P3', P1'(回到起点)。
阶段0:交互
使用ssget让用户选择两个实体,然后使用getpoint获取基点。
步骤:
(setq pl1 (car (entsel "\n选择AcDb3dPolyline(或轻量多段线): ")))
(setq sp2 (car (entsel "\n选择AcDb3dSpline(或3dPolyline、Spline): ")))
(setq basePt (getpoint "\n指定展开基线起点(二维点): "))
注意:由于用户可能选择不同的实体类型,我们需要处理曲线。vlax-curve-*函数可以处理多种曲线。
阶段1:顶点对齐
我们首先获取两条曲线的长度,然后按固定步长采样(或者按参数等分)。但题目要求等长列表,如果顶点数不同则重采样。
方法:
n1 = (vlax-curve-getEndParam pl1) -> 注意:对于多段线,参数可能是整数(顶点索引),对于样条曲线,参数可能是实数(从0.0到最大参数)
n2 = (vlax-curve-getEndParam sp2)
但是,vlax-curve-getEndParam返回的是参数的最大值,不一定是整数。我们可以用vlax-curve-getPointAtParam按参数递增获取点,但参数递增的步长如何确定?
另一种方法是:按曲线长度等分。我们可以获取曲线总长,然后按固定步长采样。
步骤:
totalLen1 = (vlax-curve-getDistAtParam pl1 (vlax-curve-getEndParam pl1))
totalLen2 = (vlax-curve-getDistAtParam sp2 (vlax-curve-getEndParam sp2))
然后,我们取两个曲线中采样点数多的那个作为n?或者用户指定一个步长step,然后分别采样:
L1 = 从0到totalLen1,步长step,取点: (vlax-curve-getPointAtDist pl1 dist)
L2 = 从0到totalLen2,步长step,取点
这样,两个列表的长度可能不同,我们取两个曲线中采样点数的最小值,然后截取到相同长度?或者按最大长度采样,但可能超出曲线长度,则取端点。
我们要求等长n,所以可以取采样点数n = (max totalLen1 totalLen2) / step 然后向上取整,然后分别采样,但总长超过曲线长度时,取端点。
但是,题目要求“顶点对齐”,所以我们需要两个列表等长。我们可以取n为两个曲线中采样点数较少的那个,然后调整步长?或者按相同的点数n(由用户指定或默认)来采样。
这里,我们采用固定步长采样,步长由用户输入(或者我们取一个默认值,比如曲线总长的1/50)。然后,我们取两个曲线中采样点数的最小值作为n。
步骤:
step = (getdist "\n请输入采样步长: ") ; 用户输入一个距离
n1 = (fix (/ totalLen1 step)) ; 采样点数,不包括起点?包括起点和终点,所以点数应该是n1+1
但我们希望等长,所以我们可以分别采样,然后取两个列表的最小长度,然后截取。
实际上,我们可以这样:
L1 = 从0开始,每次增加step,直到超过totalLen1,最后取totalLen1处的点(终点)
L2 = 同理
然后,我们取两个列表的长度较小值作为n,然后两个列表都截取前n个点。
但是,这样会导致两条曲线的采样长度不一致(一个取前n*step长度,另一个也是前n*step长度)。这样对齐是否合理?题目要求顶点对齐,所以这样是合理的。
所以,我们分别采样,然后取最小长度。
代码:
(setq dist 0.0)
(setq L1 nil)
(while (<= dist totalLen1)
(setq pt (vlax-curve-getPointAtDist pl1 dist))
(setq L1 (cons pt L1))
(setq dist (+ dist step))
)
(setq L1 (reverse L1)) ; 按距离顺序排列
同理得到L2,然后:
(setq n (min (length L1) (length L2)))
(setq L1 (reverse (nthcdr (- (length L1) n) (reverse L1)))) ; 取前n个?实际上,我们采样时可能超过总长,所以最后一步已经取了终点。我们直接取前n个点即可,因为采样点是从起点到终点按顺序的。
(setq L2 (reverse (nthcdr (- (length L2) n) (reverse L2))))
但这样截取,我们保留了前n个点,也就是从起点开始到n*step(不超过总长)的点。这样对齐是合理的。
阶段2:构建三角形片
我们有了两个等长列表L1和L2(长度为n),然后我们循环i从0到n-2:
P1 = L1[i]
P2 = L2[i]
P3 = L2[i+1]
注意:题目要求是:
P1 = L1[i]
P2 = L2[i]
P3 = L2[i+1]
所以,每个三角形由L1上的一个点,L2上的两个相邻点构成。
然后,我们计算每个三角形的二维展开坐标。
阶段3:绘制
循环每个三角形,计算二维坐标,然后平移,绘制闭合多段线(3个点,加一个点回到起点)。
辅助函数:
(defun vectSub (p1 p2) ; 向量减法,返回向量p1-p2
(mapcar '- p1 p2)
)
(defun dotProduct (v1 v2) ; 点积
(apply '+ (mapcar '* v1 v2))
)
(defun vectLength (v) ; 向量模长
(sqrt (apply '+ (mapcar '* v v)))
)
(defun distance (p1 p2) ; 两点距离
(vectLength (vectSub p1 p2))
)
(defun get2DCoords (p1 p2 p3)
; 输入三个三维点,返回三个二维点(局部坐标系)
; 局部坐标系:P1在原点,P2在x轴正方向,P3在xy平面(y>=0)
(setq v1 (vectSub p2 p1)) ; 向量P1->P2
(setq v2 (vectSub p3 p1)) ; 向量P1->P3
(setq d12 (vectLength v1))
(setq d13 (vectLength v2))
(if (equal d12 0.0 1e-6) ; 如果d12为0,则无法确定坐标系,返回错误
(progn
(princ "\n错误:两点重合!")
(exit)
)
)
(setq dot (dotProduct v1 v2))
(setq x (/ dot d12)) ; 投影长度
(setq y (sqrt (- (* d13 d13) (* x x)))) ; 取正值,所以P3在x轴上方
; 返回三个二维点:P1'(0,0), P2'(d12,0), P3'(x,y)
(list '(0 0) (list d12 0) (list x y))
)
注意:由于浮点误差,sqrt的参数可能为负数(当x^2接近d13^2时),所以我们要避免:
(setq y2 (- (* d13 d13) (* x x)))
(if (< y2 0)
(setq y 0.0)
(setq y (sqrt y2))
)
然后,我们绘制三角形:
对于每个三角形,我们得到三个二维点(局部坐标): pt1_local, pt2_local, pt3_local
然后,我们进行平移:加上偏移量 (offsetX, offsetY)
offsetX = basePt_x + i * (d12 + gap) ; 其中d12是当前三角形的基线长度(即v1的长度)
offsetY = basePt_y
注意:题目要求每个三角形的基线起点在basePt的基础上水平移动,移动量为 i*(d12+gap)
但是,这样绘制,每个三角形都是独立的,并且相邻三角形之间水平间隔为gap。
最后,用vla-AddLightWeightPolyline绘制一个闭合的多段线(3个点,然后回到第一个点,所以一共4个点)。
代码结构:
(setq acadObj (vlax-get-acad-object))
(setq doc (vla-get-ActiveDocument acadObj))
(setq modelSpace (vla-get-ModelSpace doc))
(setq gap (getdist "\n请输入三角形之间的间距: "))
(setq i 0)
(repeat (setq n (- n 1)) ; 因为循环次数是n-1,即0到n-2
(setq p1 (nth i L1))
(setq p2 (nth i L2))
(setq p3 (nth (1+ i) L2))
; 计算局部坐标
(setq localPts (get2DCoords p1 p2 p3)) ; 返回三个二维点(二维列表,每个点有两个元素)
(setq d12 (distance p1 p2)) ; 基线的长度
(setq offsetX (+ (car basePt) (* i (+ d12 gap))))
(setq offsetY (cadr basePt))
; 构建平移后的点列表(四个点:pt1, pt2, pt3, pt1)
(setq pts (list))
(foreach pt localPts
(setq x (+ offsetX (car pt)))
(setq y (+ offsetY (cadr pt)))
(setq pts (cons y (cons x pts))) ; 注意:AutoCAD点列表是(x y)形式,但是我们cons时先x后y,但这里我们cons的顺序是反的,所以最后要reverse
)
; 还要把第一个点再添加一次,形成闭合?或者我们绘制时指定闭合,也可以只给三个点然后设置闭合。但题目要求四个点。
; 所以,我们添加第四个点(即第一个点)
(setq pt0 (car localPts)) ; 第一个点
(setq x0 (+ offsetX (car pt0)))
(setq y0 (+ offsetY (cadr pt0)))
(setq pts (cons y0 (cons x0 pts))) ; 现在pts是 (x0 y0 ...) 但是顺序是反的
(setq pts (reverse pts)) ; 现在顺序是 (x0 y0 x1 y1 x2 y2 x0 y0) 但是不对,我们只需要三个点加一个重复点?我们只需要四个点:第一个点,第二个点,第三个点,再回到第一个点。
实际上,我们已经有三个点,然后我们再把第一个点加入列表,这样就有四个点。
但上面的循环中,我们已经将三个点都加入了,然后我们再加入第一个点(第四个点),所以一共四个点。
(setq pts (list x0 y0 ; 第一个点
(+ offsetX (car (nth 0 localPts))) (+ offsetY (cadr (nth 0 localPts))) ; 不对,注意:我们在循环中已经将三个点都加入了,然后加入第四个点(即第一个点),所以现在列表里有四个点,但顺序是:第四个点,然后三个点(倒序)。所以我们需要重新组织。
重新组织:我们构建一个四个点的列表,顺序为:第一个点,第二个点,第三个点,第一个点。
因此,我们可以:
(setq pt1 (nth 0 localPts))
(setq pt2 (nth 1 localPts))
(setq pt3 (nth 2 localPts))
(setq pts (list
(+ offsetX (car pt1)) (+ offsetY (cadr pt1)) ; 第一个点
(+ offsetX (car pt2)) (+ offsetY (cadr pt2)) ; 第二个点
(+ offsetX (car pt3)) (+ offsetY (cadr pt3)) ; 第三个点
(+ offsetX (car pt1)) (+ offsetY (cadr pt1)) ; 回到第一个点
))
这样更清晰。
然后,创建多段线:
(setq pts (vlax-make-variant (vlax-safearray-fill (vlax-make-safearray vlax-vbDouble (cons 0 (1- (length pts)))) pts)))
(setq poly (vla-AddLightWeightPolyline modelSpace pts))
(vla-put-Closed poly :vlax-true) ; 闭合多段线
然后,i增加1,处理下一个三角形。
注意:由于三角形可能很小,所以需要放大查看。
另外,题目要求可选标注边长和面积。我们可以:
在三角形左侧标注:边长(d12, d13, d23)和面积。
面积可以用海伦公式:s = (d12+d13+d23)/2, area = sqrt(s*(s-d12)*(s-d13)*(s-d23))
标注位置:在三角形的第一个点(P1')附近,或者三角形的中心。
使用vla-AddText添加文本。
由于时间关系,我们先实现基本功能,标注功能作为可选。
现在,我们编写完整代码。
注意:由于代码较长,我们分步骤编写。
但是,由于AutoLISP的局限性,我们需要注意:
- 三维点与二维点的转换:在计算二维坐标时,我们忽略z坐标?不,我们使用的是三维向量运算,所以是三维的。
- 在计算二维展开时,我们只关心距离,所以三维距离是没问题的。
开始编写代码:</think>以下是一个完整的AutoLISP解决方案,实现根据用户选择的两条曲线生成展开三角形网格的功能。代码包含详细注释,并遵循AutoLISP最佳实践。
```lisp
;;;-----------------------------------------------------
;;; 主函数:生成展开三角形网格
;;;-----------------------------------------------------
(defun c:GenTriMesh (/ *error* pl1 sp2 basePt gap step
totalLen1 totalLen2 L1 L2 n i
p1 p2 p3 localPts d12 offsetX)
;; 错误处理函数
(defun *error* (msg)
(if (not (wcmatch (strcase msg) "*BREAK,*CANCEL*,*EXIT*"))
(princ (strcat "\n错误: " msg)))
(princ)
)
(vl-load-com) ; 加载ActiveX支持
;; 阶段0:用户交互输入
(setq pl1 (car (entsel "\n选择母线(AcDb3dPolyline或轻量多段线): ")))
(setq sp2 (car (entsel "\n选择轨迹线(AcDb3dSpline或3dPolyline): ")))
(setq basePt (getpoint "\n指定展开基线起点(二维点): "))
(setq gap (getdist basePt "\n输入三角形间距: "))
(setq step (getdist basePt "\n输入采样步长: "))
;; 阶段1:顶点对齐(重采样曲线)
(setq totalLen1 (vlax-curve-getDistAtParam pl1 (vlax-curve-getEndParam pl1)))
(setq totalLen2 (vlax-curve-getDistAtParam sp2 (vlax-curve-getEndParam sp2)))
(setq L1 (SampleCurve pl1 totalLen1 step))
(setq L2 (SampleCurve sp2 totalLen2 step))
(setq n (min (length L1) (length L2)))
(setq L1 (TruncateList L1 n))
(setq L2 (TruncateList L2 n))
;; 阶段2-3:构建三角形并展开
(setq i 0)
(repeat (1- n) ; 循环n-1次
(setq p1 (nth i L1)) ; L1当前点
(setq p2 (nth i L2)) ; L2当前点
(setq p3 (nth (1+ i) L2)) ; L2下一点
(setq d12 (distance p1 p2)) ; 母线长度
(setq localPts (Get2DCoords p1 p2 p3)) ; 获取二维坐标
;; 计算偏移量
(setq offsetX (+ (car basePt) (* i (+ d12 gap))))
;; 绘制三角形
(DrawTriangle localPts offsetX (cadr basePt))
;; 可选:标注尺寸
(if (and (> d12 0) (> (length localPts) 2))
(LabelTriangle (car localPts) (cadr localPts) (caddr localPts)
offsetX (cadr basePt) d12)
)
(setq i (1+ i))
)
(princ (strcat "\n生成完成! 共创建 " (itoa (1- n)) " 个三角形"))
(princ)
)
;;;-----------------------------------------------------
;;; 辅助函数1:曲线重采样
;;;-----------------------------------------------------
(defun SampleCurve (curve totalLen step / dist points)
(setq dist 0.0)
(setq points nil)
(while (<= dist totalLen)
(setq points (cons (vlax-curve-getPointAtDist curve dist) points))
(setq dist (+ dist step))
)
;; 确保包含终点
(setq points (cons (vlax-curve-getPointAtDist curve totalLen) points))
(reverse points)
)
;;;-----------------------------------------------------
;;; 辅助函数2:截断列表到指定长度
;;;-----------------------------------------------------
(defun TruncateList (lst n)
(if (> (length lst) n)
(setq lst (reverse (nthcdr (- (length lst) n) (reverse lst))))
lst
)
)
;;;-----------------------------------------------------
;;; 辅助函数3:计算三角形二维坐标
;;;-----------------------------------------------------
(defun Get2DCoords (p1 p2 p3 / v1 v2 d12 d13 dot x y y2)
;; 向量计算
(setq v1 (mapcar '- p2 p1)) ; P1->P2向量
(setq v2 (mapcar '- p3 p1)) ; P1->P3向量
(setq d12 (sqrt (apply '+ (mapcar '* v1 v1))))
(setq d13 (sqrt (apply '+ (mapcar '* v2 v2))))
;; 处理零长度边
(if (equal d12 0.0 1e-6)
(progn
(princ "\n警告: 母线长度为零!")
(setq d12 0.001)
)
)
(setq dot (apply '+ (mapcar '* v1 v2))) ; 点积
(setq x (/ dot d12)) ; x坐标
;; 计算y坐标(确保非负)
(setq y2 (- (* d13 d13) (* x x)))
(setq y (if (> y2 0) (sqrt y2) 0.0))
;; 返回三个二维点: P1(0,0), P2(d12,0), P3(x,y)
(list '(0 0) (list d12 0) (list x y))
)
;;;-----------------------------------------------------
;;; 辅助函数4:绘制三角形
;;;-----------------------------------------------------
(defun DrawTriangle (pts offsetX offsetY / pt1 pt2 pt3 flatPts)
(setq pt1 (car pts)) ; (0 0)
(setq pt2 (cadr pts)) ; (d12 0)
(setq pt3 (caddr pts)) ; (x y)
;; 构建平移后的点列表(4个点形成闭合)
(setq flatPts
(list
(+ offsetX (car pt1)) (+ offsetY (cadr pt1)) ; P1'
(+ offsetX (car pt2)) (+ offsetY (cadr pt2)) ; P2'
(+ offsetX (car pt3)) (+ offsetY (cadr pt3)) ; P3'
(+ offsetX (car pt1)) (+ offsetY (cadr pt1)) ; 闭合回P1'
)
)
;; 创建轻量多段线
(entmakex
(list
'(0 . "LWPOLYLINE")
'(100 . "AcDbEntity")
'(100 . "AcDbPolyline")
'(90 . 4) ; 顶点数
'(70 . 1) ; 闭合标志
(cons 10 (list (nth 0 flatPts) (nth 1 flatPts))) ; 顶点1
(cons 10 (list (nth 2 flatPts) (nth 3 flatPts))) ; 顶点2
(cons 10 (list (nth 4 flatPts) (nth 5 flatPts))) ; 顶点3
(cons 10 (list (nth 6 flatPts) (nth 7 flatPts))) ; 顶点4
)
)
)
;;;-----------------------------------------------------
;;; 辅助函数5:标注三角形尺寸(可选)
;;;-----------------------------------------------------
(defun LabelTriangle (pt1 pt2 pt3 offsetX offsetY d12 / midPt area s a b c)
;; 计算边长
(setq a (distance pt1 pt2)) ; 母线长度
(setq b (distance pt1 pt3)) ; P1-P3距离
(setq c (distance pt2 pt3)) ; P2-P3距离
;; 计算面积(海伦公式)
(setq s (/ (+ a b c) 2.0))
(setq area (sqrt (abs (* s (- s a) (- s b) (- s c)))))
;; 计算标注位置(三角形左侧中点)
(setq midPt (list
(- offsetX (* 0.5 gap))
(+ offsetY (* 0.5 (cadr pt3)))
))
;; 创建标注文本
(entmakex
(list
'(0 . "TEXT")
(cons 10 midPt) ; 插入点
(cons 40 (* 0.5 gap)) ; 文字高度
(cons 1 (strcat "L=" (rtos d12 2 2) "\\nA=" (rtos area 2 2))) ; 内容
(cons 50 (* pi 0.5)) ; 旋转90度(垂直)
(cons 72 1) ; 水平居中
(cons 73 2) ; 垂直居中
)
)
)
;;;-----------------------------------------------------
;;; 执行主函数
;;;-----------------------------------------------------
(princ "\n命令: GenTriMesh") (princ)
```
### 代码详细解释
#### 1. 主函数 `c:GenTriMesh`
- **交互输入**:用户选择两条曲线(母线pl1和轨迹线sp2),指定展开起点basePt、三角形间距gap和采样步长step
- **顶点对齐**:
- 使用`vlax-curve-*`函数获取曲线总长
- `SampleCurve`函数按步长采样曲线点
- 截取相同长度点列表L1和L2
- **三角形处理**:
- 循环构建三角形:`P1=L1[i]`, `P2=L2[i]`, `P3=L2[i+1]`
- `Get2DCoords`计算三角形在局部坐标系中的二维坐标
- **二维布局**:
- 计算水平偏移量:`offsetX = basePt_x + i*(d12 + gap)`
- `DrawTriangle`绘制平移后的三角形
- `LabelTriangle`可选标注边长和面积
#### 2. 关键辅助函数
- **`SampleCurve`**:使用`vlax-curve-getPointAtDist`按步长采样曲线点
- **`Get2DCoords`**:核心算法实现三维到二维的转换
- 计算向量`v1=P1P2`和`v2=P1P3`
- 通过点积和向量模长计算P3的局部坐标:
```lisp
x = (v1·v2) / |v1|
y = sqrt(|v2|² - x²) ; 确保y≥0
```
- **`DrawTriangle`**:使用`entmakex`创建轻量多段线
- 构建4个点形成闭合三角形
- 设置LWPOLYLINE的DXF组码(70=1表示闭合)
- **`LabelTriangle`**:使用海伦公式计算面积
- 创建垂直文本标注边长和面积
#### 3. 技术要点
1. **曲线采样**:采用固定步长重采样确保点列等长
2. **三维转二维**:基于向量投影原理,保留原始距离关系
3. **错误处理**:
- 零长度母线检查(`equal d12 0.0 1e-6`)
- 负值平方根保护(`(setq y (if (> y2 0) (sqrt y2) 0.0))`)
4. **性能优化**:
- 使用ActiveX接口提高曲线操作效率
- 批量化实体创建(避免command调用)
### 使用说明
1. 加载LSP文件后,在AutoCAD中输入`GenTriMesh`
2. 按提示选择两条曲线(母线+轨迹线)
3. 指定展开起点和间距参数
4. 程序自动生成展开的三角形网格
### 注意事项
1. 母线应为`AcDb3dPolyline`或轻量多段线
2. 轨迹线支持`AcDb3dSpline`、`3dPolyline`或样条曲线
3. 采样步长建议为曲线总长的1%~5%
4. 三角形间距应大于0避免重叠
阅读全文
相关推荐



















