(import (scheme base)
(scheme write)
(scheme cxr)
(scheme inexact)
(srfi 1)
(srfi 27)
(srfi 132))
(define (point-x pt) (car pt))
(define (point-y pt) (cadr pt))
(define (point= pt1 pt2)
(and (= (point-x pt1) (point-x pt2))
(= (point-y pt1) (point-y pt2))))
(define (point< pt1 pt2)
(cond ((< (point-y pt1) (point-y pt2))
#t)
((= (point-y pt1) (point-y pt2))
(< (point-x pt1) (point-x pt2)))
(else #f)))
(define (angle< pt0 pt1 pt2)
(let* ((dx1 (- (point-x pt1) (point-x pt0)))
(dy1 (- (point-y pt1) (point-y pt0)))
(dx2 (- (point-x pt2) (point-x pt0)))
(dy2 (- (point-y pt2) (point-y pt0)))
(a1 (sqrt (+ (* dx1 dx1) (* dy1 dy1))))
(a2 (sqrt (+ (* dx2 dx2) (* dy2 dy2))))
(ca1 (/ dx1 a1))
(ca2 (/ dx2 a2)))
(> ca1 ca2)))
(define (random-points n max-x max-y)
(let loop ((i n)
(res (list)))
(if (<= i 0)
res
(loop (- i 1)
(let ((new-point (list (random-integer max-x)
(random-integer max-y))))
(cons new-point res))))))
(define (find-lowest-y pts)
(if (null? pts)
#f
(foldl (lambda (pt1 pt2) (if (point< pt1 pt2) pt1 pt2))
(car pts)
(cdr pts))))
(define (point-orientation pt1 pt2 pt3)
(let* ((dx21 (- (point-x pt2) (point-x pt1)))
(dy21 (- (point-y pt2) (point-y pt1)))
(dx31 (- (point-x pt3) (point-x pt1)))
(dy31 (- (point-y pt3) (point-y pt1)))
(t (- (* dx21 dy31) (* dx31 dy21))))
(cond ((negative? t) 'right)
((zero? t) 'collinear)
(else 'left))))
(define (graham-scan pts1)
(if (< (length pts1) 4)
#f
(let* ((pt0 (find-lowest-y pts1))
(pts (list-sort (lambda (pt1 pt2) (angle< pt0 pt1 pt2))
(filter (lambda (pt) (not (point= pt pt0)))
pts1))))
(let sloop ((hull (list (car pts) pt0))
(pts2 (cdr pts)))
(cond ((null? pts2)
hull)
((< (length hull) 2)
(sloop (cons (car pts2) hull)
(cdr pts2)))
(else
(let ((o (point-orientation (cadr hull)
(car hull)
(car pts2))))
(if (eq? o 'left)
(sloop (cons (car pts2) hull)
(cdr pts2))
(sloop (cdr hull) pts2)))))))))
(define (main)
(random-source-randomize! default-random-source)
(let* ((pts1 (random-points 200 320 200))
(ch (graham-scan pts1))
(header "
"))
(display header) (newline)
(for-each (lambda (pt)
(display "")
(newline))
pts1)
(display "")
(newline)
(display trailer)
(newline)))
(main)