;;; -*- Mode:LISP; Package:HACKS; Base:8; Lowercase: T -*-

;;; This program creates a color window and paints it with a familiar
;;; optical-illusion pattern whose name I can't remember at the moment.
;;; By frobbing the color map, you can see how various color versions
;;; of the illusion look.
;;; It is based on the function "checker" in MC: RWG1; PREHAX.

;;; It only works if you have a color monitor.

;;; The window to use, created when needed by color-checker.
(defvar *cc-window* nil)

;;; Parameters:
(defconst *cc-n-columns* 22)			; Number of columns.
(defconst *cc-n-rows* 22)			; Number of rows.
(defconst *cc-spacer-fraction* .0625s0)		; Fraction of height devoted to spacer.

(defun draw-color-into-rectangle (window color x1 y1 x2 y2)
  (color:color-bitblt (+ (min (max x1 0) (tv:sheet-inside-width window))
			 (tv:sheet-left-margin-size window))
		      (+ (min (max y1 0) (tv:sheet-inside-height window))
			 (tv:sheet-top-margin-size window))			   
		      (+ (min (max x2 0) (tv:sheet-inside-width window))
			 (tv:sheet-left-margin-size window))
		      (+ (min (max y2 0) (tv:sheet-inside-height window))
			 (tv:sheet-top-margin-size window))
		      color
		      tv:alu-seta
		      window))

;;; Create the *cc-window* if it doesn't exist already, expose and clear it.
(defun initialize-cc-window ()
  (if (null *cc-window*)
      (setq *cc-window* (tv:make-window 'tv:window
					':superior color:color-screen
					':blinker-p nil ':borders nil
					':label nil)))
  (funcall *cc-window* ':expose)
  (funcall *cc-window* ':clear-screen))

;;; This function draws the regular cafe wall pattern onto *cc-window*.
;;; The blocks are drawn in colors 1 and 2, and the spacer is drawn in color 3.
(defun draw-color-checker ()
  (initialize-cc-window)
  (multiple-value-bind (window-width window-height)
      (funcall *cc-window* ':inside-size)
    ;; The image is made up of a bunch of rows and columns.  Between successive
    ;; rows are spacers, which use up some of the available height.
    (let* ((width (truncate window-width *cc-n-columns*))
	   (height (truncate window-height *cc-n-rows*))
	   (spacer-height (fixr (* height *cc-spacer-fraction*))))
      (dotimes (row *cc-n-rows*)
	;; Do this for each row:
	(let* ((y1 (* row height))
	       (y2 (- (+ y1 height) spacer-height)))
	  ;; y1 and y2 are the top and bottom of the bricks in this row.
	  (do ((x (if (evenp row) 0 (- (truncate width 2))) (+ x width))
	       (color 1 (- 3 color)))
	      (( x window-width))
	    ;; x is where to start; every other row is offset by half.
	    ;; color is the color to draw; it alternates between 1 and 2.
	    ;; Do this for each column.
	    (draw-color-into-rectangle *cc-window* color x y1 (+ x width) y2))
	  (draw-color-into-rectangle *cc-window* 3 0 y2 window-width (+ y2 spacer-height)))))))

;;; Top level function to produce regular B&W effect.
(defun cch ()
  (color:write-color-map 1 0 0 0)
  (color:write-color-map 2 377 377 377)
  (color:write-color-map 3 200 200 200)
  (draw-color-checker))

(defun cchc (&optional (wait 1000) (speed 0.03s0))
  (cch)
  (do ((angle 0.0s0 (+ angle speed)))
      (nil)
    (let* ((x (max 0 (min 377 (+ 200 (fixr (* 200.0s0 (sin angle)))))))
	   (y (- 377 x)))
      (color:write-color-map 1 x x x)
      (color:write-color-map 2 y y y))
    (dotimes (i wait))))

;;; This has the problem that linear motion would probably look better than sine.
;;; Try making each odd row out of two new colors, and even rows get black->gray-> black
;;; while odd rows get black->gray->white.  Also try using lots of color map
;;; entries in stripes to get real motion in animation, moving every other row
;;; or moving alternate rows in alternate directions.

;;; This function also draws the cafe wall pattern, but in a more complicated
;;; way to allow hairier animation.  Color 0 is reserved for the background
;;; and color 17 for the spacer.  Then, even rows are drawn using colors
;;; 1 through 16, and odd rows are drawn using colors 16 through 1 (i.e. in
;;; the other order.
(defun draw-elaborate-color-checker ()
  (initialize-cc-window)
  (multiple-value-bind (window-width window-height)
      (funcall *cc-window* ':inside-size)
    ;; The image is made up of a bunch of rows and columns.  Between successive
    ;; rows are spacers, which use up some of the available height.
    (let* ((width (truncate window-width *cc-n-columns*))
	   (height (truncate window-height *cc-n-rows*))
	   (spacer-height (fixr (* height *cc-spacer-fraction*))))
      (dotimes (row *cc-n-rows*)
	;; Do this for each row:
	(let* ((y1 (* row height))
	       (y2 (- (+ y1 height) spacer-height)))
	  ;; y1 and y2 are the top and bottom of the bricks in this row.
	  (let* ((parity (evenp row))
		 (strip-width (truncate width 7)))
	    (do ((x 0 (+ x strip-width))
		 (color 0 (if parity
			      (if ( color 16) 1 (1+ color))
			      (if ( color 1) 16 (1- color)))))
		(( x window-width))
	      (draw-color-into-rectangle *cc-window* color x y1 (+ x strip-width) y2)))
	  (draw-color-into-rectangle *cc-window* 17 0 y2
				     window-width (+ y2 spacer-height)))))))

;;; This is the top level function.  It creates a cafe wall and slides alternate
;;; rows in alternate directions.
(defun cafe-slide (&optional (wait 100))
  (cond ((color:color-exists-p)
	 (draw-elaborate-color-checker)
	 (color:write-color-map 0 0 0 0)
	 (loop for color from 1 to 7
	       do (color:write-color-map color 0 0 0))
	 (loop for color from 10 to 16
	       do (color:write-color-map color 377 377 377))
	 (color:write-color-map 17 200 200 200)
	 (funcall standard-input ':tyi)
	 (loop do
	       (loop for b-to-w from 1 to 16
		     do (hack-two-slots b-to-w
					(1+ (\ (+ b-to-w 6) 16))
					wait))
	   ;(if (funcall standard-input ':tyi-no-hang)
	   ;    (return))
	       ))
	(t
	 (no-color-demo))))

(defun hack-two-slots (b-to-w w-to-b &optional (wait 1000))
  (loop for x from 0 to 377
	for y from 377 downto 0
	do (color:write-color-map b-to-w x x x)
	   (color:write-color-map w-to-b y y y)
	   (dotimes (i wait))))
