Skip to content

Commit 686c2ef

Browse files
committed
First Emacs Lisp implementation of Graham scan
1 parent 2000ab3 commit 686c2ef

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
(require 'cl-seq)
2+
3+
(defun -concat (&rest lists)
4+
"Return a new list with the concatenation of the elements in the supplied LISTS."
5+
(declare (pure t) (side-effect-free t))
6+
(apply 'append lists))
7+
8+
(defun -snoc (list elem &rest elements)
9+
"Append ELEM to the end of the list.
10+
11+
This is like `cons', but operates on the end of list.
12+
13+
If ELEMENTS is non nil, append these to the list as well."
14+
(-concat list (list elem) elements))
15+
16+
(defun nthrev (n lst)
17+
"Return the Nth element of LST from the end."
18+
;; (car (nthcdr n (reverse lst)))
19+
(nth (- (length lst) (1+ n)) lst))
20+
21+
(defun is-ccw (a b c)
22+
(>= (* (- (nth 1 c) (nth 1 a)) (- (nth 0 b) (nth 0 a)))
23+
(* (- (nth 1 b) (nth 1 a)) (- (nth 0 c) (nth 0 a)))))
24+
25+
(defun polar-angle (ref point)
26+
(atan (- (nth 1 point) (nth 1 ref)) (- (nth 0 point) (nth 0 ref))))
27+
28+
(require 'dash)
29+
30+
(defun graham-scan (initial-gift)
31+
(let* ((gift (cl-remove-duplicates initial-gift))
32+
;; this is /only/ to get the starting point
33+
(min-sorted-gift (sort gift (lambda (p1 p2) (< (nth 1 p1) (nth 1 p2)))))
34+
(start (car min-sorted-gift))
35+
(trimmed-gift (cdr min-sorted-gift))
36+
(points (sort trimmed-gift (lambda (p1 p2) (< (polar-angle start p1)
37+
(polar-angle start p2)))))
38+
(hull (list start (car points) (cadr points))))
39+
(dolist (point (cddr points))
40+
(while (not (is-ccw (nthrev 1 hull) (nthrev 0 hull) point))
41+
(setq hull (-remove-at (1- (length hull)) hull)))
42+
(setq hull (-snoc hull point)))
43+
hull))
44+
45+
(princ
46+
(graham-scan
47+
'((-5 2)
48+
(5 7)
49+
(-6 -12)
50+
(-14 -14)
51+
(9 9)
52+
(-1 -1)
53+
(-10 11)
54+
(-6 15)
55+
(-6 -8)
56+
(15 -9)
57+
(7 -7)
58+
(-2 -9)
59+
(6 -5)
60+
(0 14)
61+
(2 8))))

0 commit comments

Comments
 (0)