;;;; Copyright (C) 2009  Luca Saiu

;;;; This program is free software: you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation, either version 3 of the License, or
;;;; (at your option) any later version.

;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;;; GNU General Public License for more details.

;;;; You should have received a copy of the GNU General Public License
;;;; along with this program.  If not, see <http://www.gnu.org/licenses/>.


;;; Here comes the numeric derivative...

(define (numeric-derivative function)
  (let ((h 0.0001))
    (lambda (x)
      (/ (- (function (+ x h))
            (function x))
         h))))

;;; ...And here comes the function plotter.


(initialize-graphics "Plotter" 1000 600)

(load "color.scm") ; this must be loaded *after* graphics is initialized
(load "utility.scm")
(load "graphics.scm")
(load "interactive.scm")

(define x0 (/ (window-width) 2))
(define y0 (/ (window-height) 2))
(define unit-length 100)

(define (math->screen x y)
  (list (+ x0 (* x unit-length))
        (- y0 (* y unit-length))))

(define (screen-x->math screen-x)
  (/ (- screen-x x0) unit-length))

(define (plot-point x y color)
  (apply draw-pixel (append (math->screen x y)
                            (list color))))
(define (plot-segment x1 y1 x2 y2 color)
  (apply draw-line (append (math->screen x1 y1)
                           (math->screen x2 y2)
                           (list color))))
(define (plot-rectangle-border x1 y1 width height color)
  (apply draw-rectangle-border
         (append (math->screen x1 y1)
                 (list (* width unit-length) (- (* height unit-length)))
                 (list color))))
(define (plot-cross x1 y1 width height color)
  (let ((half-width (/ width 2))
        (half-height (/ height 2)))
    (plot-segment (- x1 half-width) y1
                  (+ x1 half-width) y1
                  color)
    (plot-segment x1 (- y1 half-height)
                  x1 (+ y1 half-height)
                  color)))

(define (draw-axes color)
  (let ((maximum (ceiling (max (abs (screen-x->math 0))
                               (abs (screen-x->math (window-width))))))
        (cross-side (/ 12 unit-length))
        (crosslet-side (/ 6 unit-length)))
    (plot-segment (- maximum) 0 maximum 0 color)
    (plot-segment 0 maximum 0 (- maximum) color)
    (for-step x (- maximum) maximum 1/2
      (for-step y (- maximum) maximum 1/2
        (plot-cross x y crosslet-side crosslet-side color)))
    (for x (- maximum) maximum
      (for y (- maximum) maximum
        (plot-cross x y cross-side cross-side color)))))

(define (plot-function function color)
  (let ((minimum-x (screen-x->math 0))
        (maximum-x (screen-x->math (window-width))))
    (for-step x minimum-x maximum-x (/ 1 unit-length)
      (plot-point x (function x) color))))

;;; Ok, let's try it:
(clear black)
(draw-axes red)
(plot-function sin (light yellow))
(plot-function (numeric-derivative sin) blue) ; here I plot the derivative of sinus
(refresh)

;;; Wait till the user exits:
(while #t
  (handle-quit-event-only))
