明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 2189|回复: 6

[函数] graham-scan算法求点集凸包函数

[复制链接]
发表于 2016-5-29 22:04:36 | 显示全部楼层 |阅读模式
本帖最后由 落魄山人 于 2016-5-31 11:00 编辑

根据graham-scan算法编写的凸包函数,欢迎测试及提出bug。。。
其中用到高飞鸟的向量相关函数/飞诗的排序函数(黄总修改版)/多段线生成函数是某位我不知名的前辈的,再此感谢。。
更新:根据user2128的反馈,进行修改。。。
更新2:取消大量的自定义函数,只保留判断左转函数,同时优化程序代码,使其更清晰可读,该算法的时间复杂度为O(nlogn)
  1. ;;;name:graham-scan
  2. ;;;desc:graham-scan算法计算点集凸包
  3. ;;;arg:ptlst:点表
  4. ;;;return:凸包点表
  5. ;;;example:(graham-scan '(pt1 pt2 pt3 ...))
  6. (defun graham-scan (ptlst / d i p0)
  7.   (setq ptlst
  8.   (vl-sort
  9.     ptlst
  10.     '(lambda (p1 p2)
  11.        (cond
  12.   ((< (cadr p1) (cadr p2)))
  13.   ((equal (cadr p1) (cadr p2) 1e-8)
  14.    (< (car p1) (car p2))
  15.   )
  16.        )
  17.      )
  18.   )
  19.   )     ;点集坐标排序
  20.   (setq p0 (car ptlst))   ;根据坐标排序结果选取Y值最小,同时X最小的点作为凸包的第一个点
  21.   (setq ptlst
  22.   (vl-sort
  23.     (cdr ptlst)
  24.     (function
  25.       (lambda (p1 p2 / m n)
  26.         (cond
  27.    ((< (setq m (angle p1 p0)) (setq n (angle p2 p0))))
  28.    ((equal m n 1e-8)
  29.     (< (distance p1 p0) (distance p2 p0))
  30.    )
  31.         )
  32.       )
  33.     )
  34.   )
  35.   )     ;极角排序
  36.      ;写凸包算法
  37.   (setq d (list (cadr ptlst) (car ptlst) p0)) ;构建初始凸包点集
  38.   (foreach curpt (cddr ptlst)  ;遍历剩余点
  39.     (setq d (cons curpt d))  ;当前点入栈   
  40.     (while (and (caddr d) (isLeft (caddr d) (cadr d) curpt))
  41.       (setq d (cons curpt (cddr d))) ;判断这时候的凸包前三点是否左转,如果非左转,将第二点删除
  42.     )
  43.   )
  44. )
  45. ;;;name:isLeft
  46. ;;;desc:判断点3-点2 与点2-点1 是否左转
  47. ;;;arg:pt1:点1
  48. ;;;arg:pt2:点2
  49. ;;;arg:pt3:点3
  50. ;;;return:nil为左转,t为右转或共线
  51. ;;;example:(isLeft '(0 1) '(3 2) '(4 3))
  52. (defun isLeft (pt1 pt2 pt3)
  53.   (< (- (* (- (car pt2) (car pt1)) (- (cadr pt3) (cadr pt1)))
  54. (* (- (cadr pt2) (cadr pt1)) (- (car pt3) (car pt1)))
  55.      )
  56.      1e-8
  57.   )
  58. )
  59. ;;;测试函数
  60. (defun c:tt (/ ss i lst)
  61.   (if (setq ss (ssget '((0 . "POINT"))))
  62.     (progn
  63.       (repeat (setq i (sslength ss))
  64. (setq
  65.    lst
  66.     (cons (cdr (assoc 10 (entget (ssname ss (setq i (1- i))))))
  67.    lst
  68.     )
  69. )
  70.       )
  71.       (setq lst (graham-scan lst))
  72.       (entmakex
  73. (append
  74.    (list
  75.      '(000 . "LWPOLYLINE")
  76.      '(100 . "AcDbEntity")
  77.      '(100 . "AcDbPolyline")
  78.      (cons 90 (length lst))
  79.      '(070 . 1)
  80.    )
  81.    (mapcar '(lambda (x) (cons 10 x)) lst)
  82. )
  83.       )
  84.     )
  85.   )

评分

参与人数 1明经币 +1 收起 理由
USER2128 + 1 鼓励一下

查看全部评分

本帖被以下淘专辑推荐:

发表于 2016-5-30 08:28:27 | 显示全部楼层
点取一个矩形的4个角点,运行正确,点取一个矩形的每边端点和中点时,不正确
发表于 2016-5-30 09:35:27 | 显示全部楼层
支持一下楼主,留个脚印先!
发表于 2016-5-30 22:01:51 | 显示全部楼层
graham-scan 已经包括排序,只不过是按角度排序的。
 楼主| 发表于 2016-5-30 22:31:04 | 显示全部楼层
自贡黄明儒 发表于 2016-5-30 22:01
graham-scan 已经包括排序,只不过是按角度排序的。

是的,不过第一次按坐标排序是为了找到一个起点,找到这个起点后,其余点按跟起点的极角排序
发表于 2024-6-5 12:04:24 | 显示全部楼层
经过我的测试,您这个速度是比较快,但是有点小bug,函数有时候会返回nil导致无法画线。
正在想法修复中。。。
发表于 2024-6-6 20:28:21 | 显示全部楼层
本帖最后由 nzl1116 于 2024-6-6 20:30 编辑
cchessbd 发表于 2024-6-5 12:04
经过我的测试,您这个速度是比较快,但是有点小bug,函数有时候会返回nil导致无法画线。
正在想法修复中。 ...


如果只选三个点,就画不出来了,就是在(foreach curpt (cddr ptlst) ...)结束后多写一个d就能解决了。
vl-sort排序也有问题,应该先equal。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-11-25 15:26 , Processed in 0.175009 second(s), 28 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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