diff --git a/contents/graham_scan/code/elisp/graham-scan.el b/contents/graham_scan/code/elisp/graham-scan.el new file mode 100644 index 000000000..2cb93c015 --- /dev/null +++ b/contents/graham_scan/code/elisp/graham-scan.el @@ -0,0 +1,56 @@ +(require 'cl-seq) + +(defun snoc (list elem) + "Append ELEM to the end of the list. + +This is like `cons', but operates on the end of list. + +Adapted from dash.el." + (append list (list elem))) + +(defun nthrev (n lst) + "Return the Nth element of LST from the end." + (nth (- (length lst) (1+ n)) lst)) + +(defun is-ccw (a b c) + "Determines if a turn between three points is counterclockwise." + (>= (* (- (nth 1 c) (nth 1 a)) (- (nth 0 b) (nth 0 a))) + (* (- (nth 1 b) (nth 1 a)) (- (nth 0 c) (nth 0 a))))) + +(defun polar-angle (ref point) + "Returns the polar angle from a point relative to a reference point" + (atan (- (nth 1 point) (nth 1 ref)) (- (nth 0 point) (nth 0 ref)))) + +(defun graham-scan (initial-gift) + "Finds the convex hull of a distribution of points with a Graham scan." + (let* ((gift (cl-remove-duplicates initial-gift)) + ;; This is /only/ to get the starting point. + (min-sorted-gift (sort gift (lambda (p1 p2) (< (nth 1 p1) (nth 1 p2))))) + (start (car min-sorted-gift)) + (trimmed-gift (cdr min-sorted-gift)) + (points (sort trimmed-gift (lambda (p1 p2) (< (polar-angle start p1) + (polar-angle start p2))))) + (hull (list start (car points) (cadr points)))) + (dolist (point (cddr points)) + (while (not (is-ccw (nthrev 1 hull) (nthrev 0 hull) point)) + (setq hull (reverse (cdr (reverse hull))))) + (setq hull (snoc hull point))) + hull)) + +(princ + (graham-scan + '((-5 2) + (5 7) + (-6 -12) + (-14 -14) + (9 9) + (-1 -1) + (-10 11) + (-6 15) + (-6 -8) + (15 -9) + (7 -7) + (-2 -9) + (6 -5) + (0 14) + (2 8)))) diff --git a/contents/graham_scan/graham_scan.md b/contents/graham_scan/graham_scan.md index 586d209db..83c03c546 100644 --- a/contents/graham_scan/graham_scan.md +++ b/contents/graham_scan/graham_scan.md @@ -32,6 +32,8 @@ We can find whether a rotation is counter-clockwise with trigonometric functions [import:18-20, lang="cpp"](code/c++/graham_scan.cpp) {% sample lang="coco" %} [import:4-8, lang="coconut"](code/coconut/graham_scan.coco) +{%sample lang="elisp" %} +[import:15-22, lang="elisp"](code/elisp/graham-scan.el) {% endmethod %} If the output of this function is 0, the points are collinear. @@ -66,6 +68,8 @@ In the end, the code should look something like this: [import:26-62, lang="cpp"](code/c++/graham_scan.cpp) {% sample lang="coco" %} [import:17-30, lang="coconut"](code/coconut/graham_scan.coco) +{%sample lang="elisp" %} +[import:24-38, lang="elisp"](code/elisp/graham-scan.el) {% endmethod %} ### Bibliography @@ -95,6 +99,8 @@ In the end, the code should look something like this: [import, lang="cpp"](code/c++/graham_scan.cpp) {%sample lang="coco" %} [import, lang="coconut"](code/coconut/graham_scan.coco) +{%sample lang="elisp" %} +[import, lang="elisp"](code/elisp/graham-scan.el) {% endmethod %}