delaunay,scm -- Please review :) added by alexshendi_ on Sun Aug 13 20:24:39 2017

(use chicken.random chicken.time)

(define-record-type point-2d
  (make-point-2d x y)
  point-2d?
  (x point-2d-x point-2d-x-set!)
  (y point-2d-y point-2d-y-set!))

(define-record-type circle
  (make-circle center radius radius-squared diameter)
  circle?
  (center circle-center)
  (radius circle-radius)
  (radius-squared circle-radius-squared)
  (diameter circle-diameter))

(define (create-circle center radius)
  (make-circle center radius (* radius radius) (* radius 2.0)))

(define-record-type triangle
  (make-triangle point-1 point-2 point-3 index-1 index-2 index-3 
                 centroid circumcircle)
  triangle?
  (point-1 triangle-point-1)
  (point-2 triangle-point-2)
  (point-3 triangle-point-3)
  (index-1 triangle-index-1)
  (index-2 triangle-index-2)
  (index-3 triangle-index-3)
  (centroid triangle-centroid) 
  (circumcircle triangle-circumcircle))

;; =========== Utilities ===============================

(define (filter pred lst)
  (foldr (lambda (x r) (if (pred x) (cons x r) r)) '() lst))

(define frand
  (let ((big (expt 2 16)))
    (lambda ()
      (+ 0.0 (/ (random big) big)))))

(define (random-point-2d)
  (make-point-2d (frand) (frand)))

(define (random-point-array-2d cnt)
  (let ((res (make-vector cnt)))
    (let loop ((i 0))
       (if (= i cnt)
           res
           (begin 
             (vector-set! res i (random-point-2d))
             (loop (+ i 1)))))))

(define (sort-by-x point-seq)
  (sort point-seq (lambda (p1 p2) (< (point-2d-x p1) (point-2d-x p2)))))

(define (sort-by-y point-seq)
  (sort point-seq (lambda (p1 p2) (< (point-2d-y p1) (point-2d-y p2)))))

(define (get-min-max point-vec)
  (let ((min-x (point-2d-x (vector-ref point-vec 0)))
        (min-y (point-2d-y (vector-ref point-vec 0)))
        (max-x (point-2d-x (vector-ref point-vec 0)))
        (max-y (point-2d-y (vector-ref point-vec 0)))
        (n (vector-length point-vec)))
     (let loop ((i 1))
       (if (>= i n)
           `((min-x ,min-x) (min-y ,min-y) (max-x ,max-x) (max-y ,max-y)) 
           (let ((x (point-2d-x (vector-ref point-vec i)))
                 (y (point-2d-y (vector-ref point-vec i))))
              (set! min-x (min x min-x))
              (set! max-x (max x max-x))
              (set! min-y (min y min-y))
              (set! max-y (max y max-y))
              (loop (+ i 1)))))))

(define (get-bounding-triangle point-vec idx)
  (let* ((res (get-min-max point-vec))
         (min-x (cadr (assq 'min-x res)))
         (min-y (cadr (assq 'min-y res)))
         (max-x (cadr (assq 'max-x res)))
         (max-y (cadr (assq 'max-y res)))
         (fact 2.0)
         (dx (* fact (- max-x min-x)))
         (dy (* fact (- max-y min-y))) 
         (pt1 (make-point-2d (- min-x dx) (- min-y (* dy 3.0))))
         (pt2 (make-point-2d (- min-x dx) (+ max-y dx)))
         (pt3 (make-point-2d (+ max-x (* 3.0 dx)) (+ max-y dx)))
         (new-points (list->vector (append  (vector->list point-vec) (list pt1 pt2 pt3)))))
   (list  new-points (create-triangle (+ idx 1) (+ idx 2) (+ idx 3) new-points))))

(define (indices-and-coordinates tri)
  (let ((i1 (triangle-index-1 tri))
        (i2 (triangle-index-2 tri))
        (i3 (triangle-index-3 tri))
        (p1 (triangle-point-1 tri))
        (p2 (triangle-point-2 tri))
        (p3 (triangle-point-3 tri)))
     (let ((x1 (point-2d-x p1))
           (y1 (point-2d-y p1))
           (x2 (point-2d-x p2))
           (y2 (point-2d-y p2))
           (x3 (point-2d-x p3))
           (y3 (point-2d-y p3)))
        (list (list i1 x1 y1) (list i2 x2 y2) (list i3 x3 y3)))))

;; ==========================================================================
;; Meat & Potatoes

(define (distance-sqr v0 v1)
  (let ((dx (- (point-2d-x v0) (point-2d-x v1)))
        (dy (- (point-2d-y v0) (point-2d-y v1))))
     (+ (* dx dx) (* dy dy))))

(define (distance v0 v1)
  (sqrt (distance-sqr v0 v1)))

(define (centroid p0 p1 p2)
  (make-point-2d (/ (+ (point-2d-x p0) (point-2d-x p1) (point-2d-x p2)) 3.0)
                 (/ (+ (point-2d-y p0) (point-2d-y p1) (point-2d-y p2)) 3.0)))

(define (point-2d-add v0 v1)
  (make-point-2d (+ (point-2d-x v0) (point-2d-x v1))
                 (+ (point-2d-y v0) (point-2d-y v1))))

(define (point-2d-sub v0 v1)
  (make-point-2d (- (point-2d-x v0) (point-2d-x v1))
                 (- (point-2d-y v0) (point-2d-y v1))))

(define (circumcircle v0 v1 v2)
  (let* ((v1-v0 (point-2d-sub v1 v0))
         (v2-v0 (point-2d-sub v2 v0))
         (v2-v1 (point-2d-sub v2 v1))
         (v1+v0 (point-2d-add v1 v0))
         (v2+v0 (point-2d-add v2 v0))
         (a (point-2d-x v1-v0)) 
         (b (point-2d-y v1-v0)) 
         (c (point-2d-x v2-v0)) 
         (d (point-2d-y v2-v0)) 
         (e (+ (* a (point-2d-x v1+v0))
               (* b (point-2d-y v1+v0))))
         (f (+ (* c (point-2d-x v2+v0))
               (* d (point-2d-y v2+v0))))
         (g (* 2.0 (- (* a (point-2d-y v2-v1))
                      (* b (point-2d-x v2-v1)))))
         (colinear? (< (abs g) 1.0e-15))
         (cx 0.0) 
         (cy 0.0)
         (dx 0.0)
         (dy 0.0))
    (if colinear?
        (let ((minx (min (point-2d-x v0) (point-2d-x v1) (point-2d-x v2)))
              (miny (min (point-2d-y v0) (point-2d-y v1) (point-2d-y v2)))
              (maxx (max (point-2d-x v0) (point-2d-x v1) (point-2d-x v2)))
              (maxy (max (point-2d-y v0) (point-2d-y v1) (point-2d-y v2))))
          (set! cx (* 0.5 (+ minx maxx)))
          (set! cy (* 0.5 (+ miny maxy)))
          (set! dx (- cx minx))
          (set! dy (- cy miny)))
        (begin
          (set! cx (/ (- (* d e) (* b f)) g))
          (set! cy (/ (- (* a f) (* c e)) g))
          (set! dx (- cx (point-2d-x v0)))
          (set! dy (- cy (point-2d-y v0)))))
    (let* ((r2 (+ (* dx dx) (* dy dy)))
           (r (sqrt r2)))
       (make-circle (make-point-2d cx cy) r r2 (* 2.0 r)))))
                   
(define (create-triangle idx0 idx1 idx2 point-vec)
   (let ((p0 (vector-ref point-vec idx0))
         (p1 (vector-ref point-vec idx1))
         (p2 (vector-ref point-vec idx2)))
     (make-triangle p0 p1 p2 idx0 idx1 idx2 
                    (centroid p0 p1 p2) (circumcircle p0 p1 p2))))

(define (in-circumcircle? tri pt)
  (let* ((cc (triangle-circumcircle tri))
         (c (circle-center cc))
         (d2 (distance-sqr c pt)))
    (<= d2 (circle-radius-squared cc))))

(define (edge=? a b)
  (or (and (= (car a) (car b))
           (= (cadr a) (cadr b)))
      (and (= (car a) (cadr b))
           (= (cadr a) (car b)))))

(define (unique-edge? edges a)
  (let ((n (length (filter (lambda (b) (edge=? a b)) edges))))
    (< n 2)))


(define (has-shared-vertices? tri1 tri2)
  (let ((ia1 (triangle-index-1  tri1))
        (ia2 (triangle-index-2  tri1))
        (ia3 (triangle-index-3  tri1))
        (ib1 (triangle-index-1  tri2))
        (ib2 (triangle-index-2  tri2))
        (ib3 (triangle-index-3  tri2)))
    (or (= ib1 ia1) (= ib1 ia2) (= ib1 ia3)
        (= ib2 ia1) (= ib2 ia2) (= ib2 ia3)
        (= ib3 ia1) (= ib3 ia2) (= ib3 ia3))))

(define (add-vertex iv triangles points)
  (let ((unaffected-tris (list))
        (edges (list)))
     
     (for-each (lambda (tri)
                  (if (not (in-circumcircle? tri (vector-ref points iv)))
                      (set! unaffected-tris (cons tri unaffected-tris)) 
                      (let* ((i1 (triangle-index-1 tri))
                             (i2 (triangle-index-2 tri))
                             (i3 (triangle-index-3 tri))
                             (e1 (list i1 i2))
                             (e2 (list i2 i3))
                             (e3 (list i3 i1)))
                        (set! edges (append (list e1 e2 e3) edges)))))
                triangles)

    (set! edges (filter (lambda (x) (unique-edge? edges x)) edges))

    (append unaffected-tris
            (map (lambda (x)
                    (let ((ii0 (car x))
                          (ii1 (cadr x)))  
                       (create-triangle ii0 ii1 iv points)))
                 edges))))

;;; triaungulate: procedure (triangulate points) --> (Listof Triangles)
;;;   - points: a vector of point-2d records
;;;   - return value: a list of triangle records
;;;                   (representing a delaunay triangulation of the points)
;;;
(define (triangulate points)
  (let* ((res (get-bounding-triangle points (- (vector-length points) 1)))
         (n (vector-length points))
         (super-triangle (cadr res))
         (points1 (car res))
         (triangles (list super-triangle)))
    (let pt-loop ((iv 0))       
      (if (>= iv n)
          triangles
          (begin
            (set! triangles  
                  (add-vertex iv triangles points1))
            (pt-loop (+ iv 1)))))

    (filter (lambda (t) (not (has-shared-vertices? super-triangle t)))
            triangles)))

(define (display-edge e points)
  (let ((i0 (car e))
        (i1 (cadr e)))
    (for-each (lambda (x) (display x (current-error-port)))
              (list (point-2d-x (vector-ref points i0)) "  " 
                    (point-2d-y (vector-ref points i0)) #\newline
                    (point-2d-x (vector-ref points i1)) "  " 
                    (point-2d-y (vector-ref points i1)) #\newline
                    #\newline))))

(define (display-tris tris)
(for-each (lambda (t)
       (for-each (lambda (pt)
                   (for-each display (list (point-2d-x pt) "  " 
                                           (point-2d-y pt) #\newline)))
                         (list (triangle-point-1 t) (triangle-point-2 t) 
                               (triangle-point-3 t) (triangle-point-1 t)))
               (newline))
            tris))

(define (go)
  (randomize)
  (let* ((points (random-point-array-2d (string->number (list-ref (argv) 1))))
         (triangles (triangulate points)))
     (display-tris triangles)))


(go)