color-egg!? added by jjf on Sat Jul 12 03:59:17 2014

;; Copyright 2014 John J Foerch. All rights reserved.
;;
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions are
;; met:
;;
;;    1. Redistributions of source code must retain the above copyright
;;       notice, this list of conditions and the following disclaimer.
;;
;;    2. Redistributions in binary form must reproduce the above copyright
;;       notice, this list of conditions and the following disclaimer in
;;       the documentation and/or other materials provided with the
;;       distribution.
;;
;; THIS SOFTWARE IS PROVIDED BY JOHN J FOERCH ''AS IS'' AND ANY EXPRESS OR
;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
;; DISCLAIMED. IN NO EVENT SHALL JOHN J FOERCH OR CONTRIBUTORS BE LIABLE
;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
;; BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
;; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
;; ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

(module color
    (get-colorspace
     color-values
     <colorspace> <color>
     <colorspace-rgb> <rgb-color>
     <colorspace-hsv> <hsv-color>)

(import chicken scheme)

(use (srfi 1)
     coops
     extras)

;; colorspace
;;

(define colorspaces (make-parameter (list)))

(define (get-colorspace name)
  (let ((r (assq name (colorspaces))))
    (cdr r)))

(define-generic (colorspace-convert color dest-colorspace))

(define-class <colorspace> ()
  ((name)
   (channels initform: (list))))


;; color
;;

(define-generic (color-values c))

(define-class <color> ()
  ((colorspace)))

(define-method (color-values (c <color>))
  (cond
   ((slot-initialized? c 'colorspace)
    (let ((cs (get-colorspace (slot-value c 'colorspace))))
      (map (lambda (x) (slot-value c x)) (slot-value cs 'channels))))
   (else (list))))


;; colorspace-rgb
;;

(define-class <colorspace-rgb> (<colorspace>)
  ((name initform: 'rgb)
   (channels initform: '(r g b))))

(colorspaces (cons `(rgb . ,(make <colorspace-rgb>))
                   (colorspaces)))


;; rgb-color
;;

(define-class <rgb-color> (<color>)
  ((colorspace initform: 'rgb)
   (r initform: 0)
   (g initform: 0)
   (b initform: 0)))


;; colorspace-hsv
;;

(define-class <colorspace-hsv> (<colorspace>)
  ((name initform: 'hsv)
   (channels initform: '(h s v))))

(colorspaces (cons `(hsv . ,(make <colorspace-hsv>))
                   (colorspaces)))


;; hsv-color
;;

(define-class <hsv-color> (<color>)
  ((colorspace initform: 'hsv)
   (h initform: 0)
   (s initform: 0)
   (v initform: 0)))


;; conversions
;;

;; rgb -> hsv
(define-method (colorspace-convert (c <rgb-color>) (cs <colorspace-hsv>))
  (let* ((r (slot-value c 'r))
         (g (slot-value c 'g))
         (b (slot-value c 'b))
         (v (max r g b))
         (mn (min r g b)))
    (cond
     ((= v mn)
      (make <hsv-color> 'h 0.0 's 0.0 'v v))
     (else
      (let* ((s (/ (- v mn) v))
             (h (cond
                 ((= r v)
                  (- (/ (- v b) (- v mn))
                     (/ (- v g) (- v mn))))
                 ((= g v)
                  (+ 2.0 (- (/ (- v r) (- v mn))
                            (/ (- v b) (- v mn)))))
                 (else
                  (+ 4.0 (- (/ (- v g) (- v mn))
                            (/ (- v r) (- v mn)))))))
             (h (mod (/ h 6.0) 1)))
        (make <hsv-color> 'h h 's s 'v v))))))

;; hsv -> rgb
(define-method (colorspace-convert (c <hsv-color>) (cs <colorspace-rgb>))
  (define (rgb r g b)
    (make <rgb-color> 'r r 'g g 'b b))
  (let ((h (slot-value c 'h))
        (s (slot-value c 's))
        (v (slot-value c 'v)))
    (cond
     ((<= s 0.0) (rgb v v v))
     (else
      (let* ((hh (* h 6.0))
             (i (truncate hh))
             (f (- hh i))
             (p (* v (- 1.0 s)))
             (q (* v (- 1.0 (* s f))))
             (tv (* v (- 1.0 (* s (- 1.0 f))))))
        (cond
         ((= (mod i 6) 0) (rgb v tv p))
         ((= i 1) (rgb q v p))
         ((= i 2) (rgb p v tv))
         ((= i 3) (rgb p q v))
         ((= i 4) (rgb tv p v))
         ((= i 5) (rgb v p q))))))))

)