本帖最后由 落魄山人 于 2016-5-31 11:00 编辑
根据graham-scan算法编写的凸包函数,欢迎测试及提出bug。。。
其中用到高飞鸟的向量相关函数/飞诗的排序函数(黄总修改版)/多段线生成函数是某位我不知名的前辈的,再此感谢。。
更新:根据user2128的反馈,进行修改。。。
更新2:取消大量的自定义函数,只保留判断左转函数,同时优化程序代码,使其更清晰可读,该算法的时间复杂度为O(nlogn)- ;;;name:graham-scan
- ;;;desc:graham-scan算法计算点集凸包
- ;;;arg:ptlst:点表
- ;;;return:凸包点表
- ;;;example:(graham-scan '(pt1 pt2 pt3 ...))
- (defun graham-scan (ptlst / d i p0)
- (setq ptlst
- (vl-sort
- ptlst
- '(lambda (p1 p2)
- (cond
- ((< (cadr p1) (cadr p2)))
- ((equal (cadr p1) (cadr p2) 1e-8)
- (< (car p1) (car p2))
- )
- )
- )
- )
- ) ;点集坐标排序
- (setq p0 (car ptlst)) ;根据坐标排序结果选取Y值最小,同时X最小的点作为凸包的第一个点
- (setq ptlst
- (vl-sort
- (cdr ptlst)
- (function
- (lambda (p1 p2 / m n)
- (cond
- ((< (setq m (angle p1 p0)) (setq n (angle p2 p0))))
- ((equal m n 1e-8)
- (< (distance p1 p0) (distance p2 p0))
- )
- )
- )
- )
- )
- ) ;极角排序
- ;写凸包算法
- (setq d (list (cadr ptlst) (car ptlst) p0)) ;构建初始凸包点集
- (foreach curpt (cddr ptlst) ;遍历剩余点
- (setq d (cons curpt d)) ;当前点入栈
- (while (and (caddr d) (isLeft (caddr d) (cadr d) curpt))
- (setq d (cons curpt (cddr d))) ;判断这时候的凸包前三点是否左转,如果非左转,将第二点删除
- )
- )
- )
- ;;;name:isLeft
- ;;;desc:判断点3-点2 与点2-点1 是否左转
- ;;;arg:pt1:点1
- ;;;arg:pt2:点2
- ;;;arg:pt3:点3
- ;;;return:nil为左转,t为右转或共线
- ;;;example:(isLeft '(0 1) '(3 2) '(4 3))
- (defun isLeft (pt1 pt2 pt3)
- (< (- (* (- (car pt2) (car pt1)) (- (cadr pt3) (cadr pt1)))
- (* (- (cadr pt2) (cadr pt1)) (- (car pt3) (car pt1)))
- )
- 1e-8
- )
- )
- ;;;测试函数
- (defun c:tt (/ ss i lst)
- (if (setq ss (ssget '((0 . "POINT"))))
- (progn
- (repeat (setq i (sslength ss))
- (setq
- lst
- (cons (cdr (assoc 10 (entget (ssname ss (setq i (1- i))))))
- lst
- )
- )
- )
- (setq lst (graham-scan lst))
- (entmakex
- (append
- (list
- '(000 . "LWPOLYLINE")
- '(100 . "AcDbEntity")
- '(100 . "AcDbPolyline")
- (cons 90 (length lst))
- '(070 . 1)
- )
- (mapcar '(lambda (x) (cons 10 x)) lst)
- )
- )
- )
- )
|