找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 510|回复: 1

【越飞越高讲堂12】最小包围盒和最大距离点对 - AutoLISP/Visual LISP 编程

[复制链接]

0

主题

0

回帖

26

积分

管理员

积分
26
发表于 2024-1-27 07:11:19 | 显示全部楼层 |阅读模式
最小包围盒和最大距离点对在实际生活中有很多运用。
譬如,最小包围盒指的是能包围一个多边形或者一些多边形的最小面积(可能周长)的这样的一个矩形,这个帖子我只是讨论二维的,所以说是矩形,
求得最小包围和就能合理利用材料,选择合适的截面。
在这个算法中,采用了“游标卡尺”的算法,所以运行速度很快,在求出一个形状的凸包之后,就能迅速得到最小矩形。O(n *log(n))的时间复杂度,
大量运算成为可能。
对于CAD本身来说,用getboundingBox得出的结果仅仅是平行与WCS的坐标系统的矩形,而且有时候很不准确,特别对于spline来说,有时候相差很大。
然而,WCS下的boundingBox一般来说不是最小矩形,如果要得到最小的,必须要旋转这个图形很多次才能得到结果,但这时一个费事费力的事情。
所以,我们有必要考虑一种算法。
这些天通过几个比较晚的晚上,终于得出了算法。为了增加讨论效果,我在这里先贴出arx文件,适用于2004-2006平台。至于LISP,以后在贴出。
速度之快,几乎是0秒。

用法: 先输入命令 Hull,
然后输入分弧精度---指的是对样条曲线或者弧形的分段数目,取值在100-2000比较合理,太大容易引起问题且也不能有效提高精度。
选择图形中的多边形或者样条曲线,然后你就可以看到结果了。最远距离用红线标出。
  1. ;;;The procedure for Test
  2. (defun C:test (/ PP PTLIST SEL T0 n)
  3.   (princ "\n命令是test(The command is Test).")
  4.   (princ "\nPlease select LWPOLYLINE,LINE,SPLINE,POINT")
  5.   (setq sel (ssget (list '(0 . "POINT,LWPOLYLINE,LINE,SPLINE"))))         ;select curve or point
  6.   (initget 7)
  7.   (setq n (getint "\nPlease Enter the precision of dividing curve(The resonable value is 100 to 2000):"))
  8.   (if sel                                                         
  9.     (progn
  10.       (setq ptlist (getpt sel n))                                        ;construct the set of points
  11.       (setq ptlist (Graham-scan ptlist))                                ;construct the CCW Hull of this set.
  12.       (if (<= (det (car ptlist) (cadr ptlist) (caddr ptlist)) 0.0)      ;ensure the hull is CCW.
  13.         (setq ptlist (reverse ptlist))                                        ;if it isn't CCW,then reverse it
  14.       )        
  15.       (setq t0 (getvar "TDUSRTIMER"))                                        ;The start time of this algorithm
  16.       (setq pp (car (MinAreaRectangle ptlist)))                                ;start calculating
  17.       (princ "\nIt takes :")                                                
  18.       (princ (* (- (getvar "TDUSRTIMER") t0) 86400))                         ;The End time
  19.       (princ "seconds")
  20.       (if pp
  21.         (make-poly pp)                                                        ;draw rectangle.
  22.       )
  23.     )
  24.   )
  25.   (princ)
  26. )
  27. ;;;=======================================================
  28. ;;;Function : Find the minimum area of encasing rectangle.
  29. ;;;Arguments : A CCW HULL                                 
  30. ;;;Return: The Four points of Rectangle and its Area      
  31. ;;;=======================================================
  32. (defun MinAreaRectangle        (ptlist             /           AA         AI    BB    D1
  33.                          D2    EDGE  I           I1X         I1Y   I2X   I2Y
  34.                          IL    INF   IX           IY         J1    J2    MINA
  35.                          MINH  MINW  NORH  NORM         PI1   PI2   PTI0
  36.                          PTI1  PTI2  PTJ1  PTK1         PTM1  PTS1  PTS2
  37.                          PTS3  PTS4  REC1  REC2         REC3  REC4  RECT
  38.                          VECH  VECL  VJ12  VM12
  39.                         )
  40.   (setq INF 1e309)                                                        
  41.   (setq minA INF)                                                        ;Initiating the Minimum area is infinite
  42.   (setq pti0 (car ptlist))                                                ;the first point of Hull.
  43.   (setq pts1 (append ptlist (list pti0)))                                ;add the first point at back of Hull
  44.   (setq pts2 (cdr (append ptlist ptlist (list pti0))))                        ;Construct a loop for the hull
  45.   (setq i 0)                                                               
  46.   ;;Find area of encasing rectangle anchored on each edge.
  47.   (repeat (length ptlist)
  48.     (setq pi1 (car   pts1)                                                
  49.           pi2 (cadr  pts1)
  50.           i1x (car   pi1)
  51.           i1y (cadr  pi1)
  52.           i2x (car   pi2)
  53.           i2y (cadr  pi2)
  54.           ix  (- i2x i1x)
  55.           iy  (- i2y i1y)
  56.           il  (distance (list ix iy) '(0.0 0.0))
  57.     )
  58.     ;;寻找最左点
  59.     ;;Find a vertex on on first perpendicular line of support
  60.     (while (> (DOTPR ix iy pts2) 0.0)
  61.       (setq pts2 (cdr pts2))
  62.     )
  63.     ;;寻找最上点
  64.     ;;Find a vertex on second perpendicular line of suppoer
  65.     (if        (= i 0)
  66.       (setq pts3 pts2)
  67.     )
  68.     (while (> (CROSSPR ix iy pts3) 0.0)
  69.       (setq pts3 (cdr pts3))
  70.     )
  71.     ;;寻找最右点
  72.     ;;Find a vertex on second perpendicular line of suppoer
  73.     (if        (= i 0)
  74.       (setq pts4 pts3)
  75.     )
  76.     (while (< (DOTPR ix iy pts4) 0.0)
  77.       (setq pts4 (cdr pts4))
  78.     )
  79.     ;;得出了每边的矩形
  80.     ;;Find distances between parallel and perpendicular lines of support
  81.     (cond
  82.       ((equal i1x i2x 1e-4)                                                ;如果边两点的X值相同
  83.        (setq d1        (- (caar pts3) i1x)                                        ;那么矩形的高就是最上点与边的X的差值
  84.              d2        (- (cadar pts4) (cadar pts2))                                ;矩形的宽就是最左和最右的Y的差值
  85.        )
  86.       )
  87.       ((equal i1y i2y 1e-4)                                                ;如果边两点的Y值相同
  88.        (setq d1        (- (cadar pts3) i1y)                                        ;那么矩形的高就是最上点与边的Y的差值
  89.              d2        (- (caar pts4) (caar pts2))                                ;矩形的宽就是最左和最右的X的差值
  90.        )
  91.       )
  92.       (T
  93.        (setq aa (det pi1 pi2 (car pts3)))                                ;否则计算边和最上点构成的面积的二倍(det)
  94.        (setq d1 (/ aa il))                                                ;高就是det值除以边长
  95.        (setq j1 (car pts2))                                                ;最右边点
  96.        (setq j2 (list (- (car j1) iy) (+ (cadr j1) ix)))                ;通过最右边点的垂直边的点
  97.        (setq bb (det j1 j2 (car pts4)))                                        ;最右边点,上面的点和最左边的点
  98.        (setq d2 (/ bb il))                                                ;这三点的det除以边长就是宽
  99.       )
  100.     )
  101.     ;;计算矩形的面积,必要时更新最小面积
  102.     ;;Compute area of encasing rectangle anchored on current edge.
  103.     ;;if the area is smaller than the old Minimum area,then update,and record the width,height and five points.
  104.     (setq Ai (abs (* d1 d2)))                                                ;面积就是高和宽的积
  105.     (if        (< Ai MinA)                                                     ;如果面积小于先前的最小面积,则记录:
  106.       (setq MinA Ai                                                        ;更新最小面积
  107.             MinH d1                                                        ;最小面积的高
  108.             MinW d2                                                        ;最小面积的宽
  109.             pti1 pi1                                                        ;最小面积的边的第一个端点
  110.             pti2 pi2                                                        ;最小面积的边的第二个端点
  111.             ptj1 (car pts2)                                                ;最右边的点
  112.             ptk1 (car pts3)                                                ;最上面的点
  113.             ptm1 (car pts4)                                                ;最左边的点
  114.       )
  115.     )
  116.     (setq pts1 (cdr pts1))                                                ;检测下一条边
  117.     (setq i (1+ i))                                                        ;计数器加一
  118.   )
  119.   ;;according to the result ,draw the Minimum Area Rectangle
  120.   (setq edge (mapcar '- pti2 pti1))                                        ;最小面积的边对应的向量
  121.   (setq VecL (distance edge '(0.0 0.0)))                                ;最小面积的边的长度
  122.   (setq NorH (abs (/ MinH VecL)))                                        ;这边的法线
  123.   
  124.   (setq Norm (list (- (cadr edge)) (car edge)))                                ;这边的垂直向量
  125.   (setq vj12 (mapcar '+ ptj1 Norm))                                        ;通过最右点的垂直向量
  126.   (setq vm12 (mapcar '+ ptm1 Norm))                                        ;通过最左点的垂直向量
  127.   (setq vecH (mapcar '* (list NorH NorH) Norm))                                
  128.   (setq rec1 (inters pti1 pti2 ptj1 vj12 nil))                                ;矩形的第一点
  129.   (setq rec4 (inters pti1 pti2 ptm1 vm12 nil))                                ;矩形的第四点
  130.   (setq rec2 (mapcar '+ rec1 vecH))                                        ;矩形的第二点
  131.   (setq rec3 (mapcar '+ rec4 vecH))                                        ;矩形的第三点
  132.   (setq rect (list Rec1 rec2 rec3 rec4))                                ;矩形的点表
  133.   (cons rect MinA)                                                        ;返回这个矩形的点表和最大距离
  134. )
  135. ;;;========================================
  136. ;;;求凸壳的直径的程序                     
  137. ;;;参数:逆时针的凸壳 H-------注意逆时针!!!
  138. ;;;返回值: 直径的两个端点和直径 Pair . MaxD
  139. ;;;========================================
  140. (defun Max-distance (H / D M MAXD P PAIR Q U V W)
  141.   (setq Q (cdr (append H H (list (car H)))))                                ;构造一个首尾循环的凸集,且起始点为凸壳的第二点
  142.   (setq MaxD 0.0)                                                        ;初始化最小距离为0
  143.   (foreach U H                                                                ;依次检查凸壳的边
  144.     (setq V (car Q))                                                        ;循环集的第一点
  145.     (setq W (cadr Q))                                                        ;循环集的第二点
  146.     (setq M (mid-pt V W))                                                ;这两点的中点
  147.     (while (> (dot M U V) 0.0)                                                ;如果夹角小于90度(即点积大于0)
  148.       (setq Q (cdr Q))                                                        ;循环集推进
  149.       (setq V (car Q))                                                        ;取下一点
  150.       (setq W (cadr Q))                                                        ;下下一点
  151.       (setq M (mid-pt V W))                                                ;这两点的中点
  152.     )
  153.     (setq D (distance U V))                                                ;计算这时的最大距离
  154.     (if        (> D MaxD)                                                        ;如果大于前面的最大距离
  155.       (setq MaxD D                                                        ;就替换前面的最大距离
  156.             Pair (list U V)                                                ;并记录这对点
  157.       )
  158.     )
  159.   )
  160.   (cons Pair MaxD)                                                        ;返回这对点和最大距离
  161. )
  162. ;;;==================
  163. ;;;Graham扫描法求凸包
  164. ;;;==================
  165. (defun Graham-scan (ptlist / hullpt maxXpt sortPt P Q)
  166.   (if (< (length ptlist) 3)                                                ;3点以下
  167.     ptlist                                                                ;是本集合
  168.     (progn
  169.       (setq maxXpt (assoc (apply 'max (mapcar 'car ptlist)) ptlist))        ;最右边的点
  170.       (setq sortPt (sort-by-angle-distance ptlist maxXpt))                 ;分类点集
  171.       (setq hullPt (list (cadr sortPt) maxXpt))                                ;开始的两点      
  172.       (foreach n (cddr sortPt)                                                ;从第3点开始
  173.         (setq hullPt (cons n HullPt))                                        ;把Pi加入到凸集
  174.         (setq P (cadr hullPt))                                                ;Pi-1
  175.         (setq Q (caddr hullPt))                                                ;Pi-2
  176.         (while (and q (> (det n P Q) -1e-6))                                 ;如果左转
  177.           (setq hullPt (cons n (cddr hullPt)))                                 ;删除Pi-1点
  178.           (setq P (cadr hullPt))                                        ;得到新的Pi-1点
  179.           (setq Q (caddr hullPt))                                        ;得到新的Pi-2点
  180.         )
  181.       )
  182.       (reverse hullpt)                                                        ;返回凸集
  183.     )
  184.   )
  185. )
  186. ;;;中点函数
  187. (defun mid-pt (p1 p2)
  188.   (list
  189.     (* (+ (car p1) (car p2)) 0.5)
  190.     (* (+ (cadr p1) (cadr p2)) 0.5)
  191.   )
  192. )
  193. ;;;以某点为基点,按照角度和距离分类点集
  194. (defun sort-by-angle-distance (ptlist pt / )
  195.   (vl-sort ptlist
  196.            (function
  197.              (lambda (e1 e2 / ang1 ang2 )
  198.                (setq ang1 (angle pt e1))
  199.                (setq ang2 (angle pt e2))
  200.                (if (= ang1 ang2)
  201.                  (< (distance pt e1) (distance pt e2))
  202.                  (< ang1 ang2)
  203.                )
  204.              )
  205.            )
  206.   )
  207. )
  208. ;;;点积= x1*x2 + y1*y2
  209. (defun DOTPR (ix iy pts / pt1 pt2)
  210.   (setq pt1 (car pts))
  211.   (setq pt2 (cadr pts))
  212.   (+ (* ix (- (car pt2) (car pt1)))
  213.      (* iy (- (cadr pt2) (cadr pt1)))
  214.   )
  215. )
  216. ;;;叉积= x1*y2 - x2*y1
  217. (defun CROSSPR (ix iy pts / pt1 pt2)
  218.   (setq pt1 (car pts))
  219.   (setq pt2 (cadr pts))
  220.   (- (* ix (- (cadr pt2) (cadr pt1)))
  221.      (* iy (- (car pt2) (car pt1)))
  222.   )
  223. )
  224. ;;;中点函数
  225. (defun mid-pt (p1 p2)
  226.   (list
  227.     (* (+ (car p1) (car p2)) 0.5)
  228.     (* (+ (cadr p1) (cadr p2)) 0.5)
  229.   )
  230. )
  231. ;;;定义三点的行列式,即三点之倍面积
  232. (defun det (p1 p2 p3 / x2 y2)
  233.   (setq        x2 (car p2)
  234.         y2 (cadr p2)
  235.   )
  236.   (- (* (- x2 (car p3)) (- y2 (cadr p1)))
  237.      (* (- x2 (car p1)) (- y2 (cadr p3)))
  238.   )
  239. )
  240. ;;;定义向量的点积函数
  241. (defun dot (p1 p2 p3 / x1 y1)
  242.   (setq        x1 (car p1)
  243.         y1 (cadr p1)
  244.   )
  245.   (+ (* (- (car p2) x1) (- (car p3) x1))
  246.      (* (- (cadr p2) y1) (- (cadr p3) y1))
  247.   )
  248. )
  249. ;;;取点函数2
  250. (defun getpt (ss n / i s a b c d e)
  251.   (setq i 0)
  252.   (if ss
  253.     (repeat (sslength ss)
  254.       (setq a (ssname ss i))
  255.       (setq b (entget a))
  256.       (setq e (cdr (assoc 0 b)))
  257.       (cond
  258.         ((= e "LWPOLYLINE")
  259.          (setq c (get-pline-vertexs a n))
  260.          (setq s (append c s))
  261.         )
  262.         ((= e "SPLINE")
  263.          (setq c (get-spline-vertexs a n))
  264.          (setq s (append c s))
  265.         )
  266.         ((= e "LINE")
  267.          (setq c (cdr (assoc 10 b)))
  268.          (setq d (cdr (assoc 11 b)))
  269.          (setq c (list (car c) (cadr c)))
  270.          (setq d (list (car d) (cadr d)))
  271.          (setq s (cons c s))
  272.          (setq s (cons d s))
  273.         )
  274.         ((= e "POINT")
  275.          (setq c (cdr (assoc 10 b)))
  276.          (setq c (list (car c) (cadr c)))
  277.          (setq s (cons c s))
  278.         )
  279.       )
  280.       (setq i (1+ i))
  281.     )
  282.   )
  283.   s
  284. )
  285. ;;取得多边形顶点
  286. (defun get-LWpolyline-vertexs (DXF / lst)
  287.   (foreach n DXF
  288.     (if        (= (car n) 10)
  289.       (setq lst (cons (cdr n) lst))
  290.     )
  291.   )
  292.   (reverse lst)
  293. )
  294. (defun get-3dpolyline-vertexs ( ent / p )
  295.   (if (and (setq ent (entnext ent)) (setq p (cdr (assoc 10 (entget ent)))))
  296.     (cons p (get-3dpolyline-vertexs ent))
  297.   )
  298.   p
  299. )
  300. ;;;取得样条曲线的点
  301. (defun get-spline-vertexs (ent n / DIST ENDPAR I LEN OBJ PT PTS SEG)
  302.   (setq obj (vlax-ename->vla-object ent))
  303.   (setq endpar  (vlax-curve-getEndParam obj))
  304.   (setq len (vlax-curve-getDistAtParam obj endpar))
  305.   (setq seg (/ len n))
  306.   (setq dist 0)
  307.   (while (< dist len)   
  308.     (setq pt (vlax-curve-getPointAtDist obj dist))
  309.     (setq pts (cons pt pts))
  310.     (setq dist (+ seg dist))   
  311.   )
  312.   (if (= (vla-get-closed obj) :vlax-false)
  313.     (setq pt (vlax-curve-getEndPoint obj)
  314.           pts (cons pt pts)
  315.     )
  316.   )
  317.   (reverse pts)
  318. )
  319. ;;;取得含有圆弧的多段线的点
  320. (defun get-pline-vertexs (ent n / BLG DIST ENDPAR I L1 L2 L3 LI OBJ PT PTS VEXNUM)
  321.   (setq obj (vlax-ename->vla-object ent))
  322.   (setq endpar (vlax-curve-getEndParam obj))
  323.   (setq vexNum (fix endPar))
  324.   (setq pts nil)
  325.   (setq i 0)
  326.   (repeat vexNum
  327.     (setq pt (vlax-curve-getPointAtParam obj i))
  328.     (setq pts (cons pt pts))
  329.     (setq blg (vla-getbulge obj i))
  330.     (if (/= blg 0.0)
  331.       (progn
  332.         (setq l1 (vlax-curve-getDistAtParam obj i))
  333.         (setq l2 (vlax-curve-getDistAtParam obj (1+ i)))
  334.         (setq l3 (- l2 l1))
  335.         (setq li (/ l3 n))
  336.         (setq dist l1)
  337.         (repeat (1- n)
  338.           (setq dist (+ dist li))
  339.           (setq pt (vlax-curve-getPointAtDist obj dist))
  340.           (setq pts (cons pt pts))
  341.         )
  342.       )
  343.     )
  344.     (setq i (1+ i))
  345.   )
  346.   (if (= (vla-get-closed obj) :vlax-false)
  347.     (setq pt (vlax-curve-getEndPoint obj)
  348.           pts (cons pt pts)
  349.     )
  350.   )
  351.   pts
  352. )
  353. ;;;绘制多段线
  354. (defun Make-Poly (pp / x)
  355.   (entmake                                                                ;画凸包
  356.     (append
  357.       '((0 . "LWPOLYLINE")
  358.         (100 . "AcDbEntity")
  359.         (100 . "AcDbPolyline")
  360.        )
  361.       (list (cons 90 (length pp)))                                        ;顶点个数
  362.       (mapcar
  363.         (function (lambda (x) (cons 10 x)))
  364.         pp
  365.       )                                                                        ;多段线顶点
  366.       (list (cons 70 1))                                                ;闭合的
  367.       (list (cons 62 1))                                                ;红色的
  368.     )
  369.   )
  370. )
复制代码

0

主题

0

回帖

26

积分

管理员

积分
26
 楼主| 发表于 2024-1-27 09:53:38 | 显示全部楼层
  1. ;;;========================================
  2. ;;;求凸壳的直径的程序                     
  3. ;;;参数:逆时针的凸壳 H-------注意逆时针!!!
  4. ;;;返回值: 直径的两个端点和直径 Pair . MaxD
  5. ;;;========================================
  6. (defun Max-distance (H / D M MAXD P PAIR Q U V W)
  7.   (setq Q (cdr (append H H (list (car H))))) ;构造一个首尾循环的凸集,且起始点为凸壳的第二点
  8.   (setq MaxD 0.0)    ;初始化最小距离为0
  9.   (foreach U H     ;依次检查凸壳的边
  10.     (setq V (car Q))    ;循环集的第一点
  11.     (setq W (cadr Q))    ;循环集的第二点
  12.     (setq M (mid-pt V W))   ;这两点的中点
  13.     (while (> (dot M U V) 0.0)   ;如果夹角小于90度(即点积大于0)
  14.       (setq Q (cdr Q))    ;循环集推进
  15.       (setq V (car Q))    ;取下一点
  16.       (setq W (cadr Q))    ;下下一点
  17.       (setq M (mid-pt V W))   ;这两点的中点
  18.     )
  19.     (setq D (distance U V))   ;计算这时的最大距离
  20.     (if (> D MaxD)    ;如果大于前面的最大距离
  21.       (setq MaxD D    ;就替换前面的最大距离
  22.      Pair (list U V)   ;并记录这对点
  23.       )
  24.     )
  25.   )
  26.   (cons Pair MaxD)    ;返回这对点和最大距离
  27. )
复制代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|膜结构网

GMT+8, 2024-12-28 02:34 , Processed in 0.136566 second(s), 23 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表