#lang scheme/base
(require
scheme/list htdp/image
lang/error
)
(provide
image-above image-above-align-right image-above-align-left image-above-align-center
image-beside image-beside-align-top image-beside-align-bottom image-beside-align-center
reflect-vert reflect-horiz reflect-main-diag reflect-other-diag
rotate-cw rotate-ccw rotate-180
show-pinhole
crop-top crop-bottom crop-left crop-right)
(provide (all-from-out htdp/image))
(define (image-translate image dx dy)
(overlay/xy (rectangle (+ (image-width image) dx)
(+ (image-height image) dy)
'solid
'white)
dx
dy
image))
(define (pinhole-to-bottom img)
(- (image-height img) (pinhole-y img)))
(define (pinhole-to-top img)
(pinhole-y img))
(define (pinhole-to-left img)
(pinhole-x img))
(define (pinhole-to-right img)
(- (image-width img) (pinhole-x img)))
(define (concat-vert under over)
(let [[dy (+ (pinhole-to-bottom over) (pinhole-to-top under))]]
(move-pinhole
(overlay/xy over 0 (round (+ (pinhole-to-bottom over) (pinhole-to-top under))) under)
0 (round (/ dy 2)))))
(define (check-image tag i rank . other-message)
(if (and (pair? other-message) (string? (car other-message)))
(check-arg tag (image? i) (car other-message) rank i)
(check-arg tag (image? i) "image" rank i)))
(define numbered-arg-error "expected <~a> as argument number ~a, given: ~e")
(define (check-numbered-arg pname condition expected arg-posn given)
(unless condition (error pname numbered-arg-error expected arg-posn given)))
(define (check-all tag test? expected args)
(check-all0 tag test? expected args 1))
(define (check-all0 tag test? expected args count)
(cond [(null? args) (void)]
[(cons? args)
(check-numbered-arg tag (test? (car args)) expected count (car args))
(check-all0 tag test? expected (cdr args) (add1 count))]))
(define (check-all-images tag args)
(check-all tag image? "image" args))
(define (image-above . images)
(check-all-images 'image-above images)
(cond [(null? images) (error 'image-above "Expected two or more images; given 0")]
[(null? (cdr images)) (error 'image-above "Expected two or more images; given 1")]
[else (foldl concat-vert (car images) (cdr images))]))
(define (image-above-align-right . images)
(check-all-images 'image-above-align-right images)
(apply image-above
(map (lambda (img)
(move-pinhole img (pinhole-to-right img) 0))
images)))
(define (image-above-align-left . images)
(check-all-images 'image-above-align-left images)
(apply image-above
(map (lambda (img)
(move-pinhole img (- (pinhole-to-left img)) 0))
images)))
(define (concat-horiz right left)
(let [[dx (+ (pinhole-to-right left) (pinhole-to-left right))]]
(move-pinhole
(overlay/xy left dx 0 right)
(round (/ dx 2)) 0)))
(define (image-beside . images)
(check-all-images 'image-beside images)
(cond [(null? images) (error 'image-beside "Expected two or more images; given 0")]
[(null? (cdr images)) (error 'image-beside "Expected two or more images; given 1")]
[else (foldl concat-horiz (car images) (cdr images))]))
(define (image-beside-align-top . images)
(check-all-images 'image-beside-align-top images)
(apply image-beside
(map (lambda (img)
(move-pinhole img 0 (- (pinhole-to-top img))))
images)))
(define (image-beside-align-bottom . images)
(check-all-images 'image-beside-align-bottom images)
(apply image-beside
(map (lambda (img)
(move-pinhole img 0 (pinhole-to-bottom img)))
images)))
(define (center-pinhole img)
(put-pinhole img
(quotient (image-width img) 2)
(quotient (image-height img) 2)))
(define (image-beside-align-center . images)
(check-all-images 'image-beside-align-center images)
(apply image-beside
(map center-pinhole images)))
(define (image-above-align-center . images)
(check-all-images 'image-above-align-center images)
(apply image-above
(map center-pinhole images)))
(define (show-pinhole img)
(check-image 'show-pinhole img "first")
(overlay img (circle 2 "solid" "black")))
(define (first-n L n)
(cond [(null? L) '()]
[(<= n 0) '()]
[else (cons (car L)
(first-n (cdr L) (- n 1)))]))
(define (rest-n L n)
(cond [(null? L) '()]
[(<= n 0) L]
[else (rest-n (cdr L) (- n 1))]))
(define (slice L width)
(cond [(null? L) '()]
[else (cons (first-n L width)
(slice (rest-n L width) width))]))
(define (slice-pic picture)
(slice (image->color-list picture)
(image-width picture)))
(define (unslice lists)
(apply append lists))
(define (unslice-pic pixels width height phx phy)
(cond [(or (zero? width) (zero? height))
(put-pinhole (rectangle width height 'solid 'white) phx phy)]
[else (color-list->image
(unslice pixels)
width height phx phy)
]))
(define (reflect-horiz picture)
(check-image 'reflect-horiz picture "first")
(unslice-pic
(map reverse
(slice-pic picture))
(image-width picture)
(image-height picture)
(pinhole-to-right picture)
(pinhole-to-top picture)))
(define (natural-number? x)
(and (integer? x) (>= x 0)))
(define (crop-left picture pixels)
(check-image 'crop-left picture "first")
(check-arg 'crop-left (natural-number? pixels) 'natural-number "second" pixels)
(let* ((new-width (max 0 (- (image-width picture) pixels)))
(new-phx (max 0 (- (pinhole-x picture) pixels)))
(real-pixels (- (image-width picture) new-width)))
(unslice-pic
(map
(lambda (row) (rest-n row real-pixels))
(slice-pic picture))
new-width
(image-height picture)
new-phx
(pinhole-y picture))))
(define (crop-top picture pixels)
(check-image 'crop-top picture "first")
(check-arg 'crop-top (natural-number? pixels) 'natural-number "second" pixels)
(let* ((new-height (max 0 (- (image-height picture) pixels)))
(new-phy (max 0 (- (pinhole-y picture) pixels)))
(real-pixels (- (image-height picture) new-height)))
(unslice-pic
(rest-n
(slice-pic picture)
real-pixels)
(image-width picture)
new-height
(pinhole-x picture)
new-phy)))
(define (crop-right picture pixels)
(check-image 'crop-right picture "first")
(check-arg 'crop-right (natural-number? pixels) 'natural-number "second" pixels)
(let* ((new-width (max 0 (- (image-width picture) pixels)))
(new-phx (min (pinhole-x picture) new-width)))
(unslice-pic
(map
(lambda (row) (first-n row new-width))
(slice-pic picture))
new-width
(image-height picture)
new-phx
(pinhole-y picture))))
(define (crop-bottom picture pixels)
(check-image 'crop-bottom picture "first")
(check-arg 'crop-bottom (natural-number? pixels) 'natural-number "second" pixels)
(let* ((new-height (max 0 (- (image-height picture) pixels)))
(new-phy (min (pinhole-y picture) new-height)))
(unslice-pic
(first-n
(slice-pic picture)
new-height)
(image-width picture)
new-height
(pinhole-x picture)
new-phy)))
(define (reflect-vert picture)
(check-image 'reflect-vert picture "first")
(unslice-pic
(reverse
(slice-pic picture))
(image-width picture)
(image-height picture)
(pinhole-to-left picture)
(pinhole-to-bottom picture)))
(define (ncons-each L)
(map list L))
(define (transpose rows)
(apply map (cons list rows)))
(define (reflect-main-diag picture)
(check-image 'reflect-main-diag picture "first")
(unslice-pic
(transpose
(slice-pic picture))
(image-height picture)
(image-width picture)
(pinhole-y picture)
(pinhole-x picture)))
(define (reflect-other-diag picture)
(check-image 'reflect-other-diag picture "first")
(unslice-pic
(reverse
(transpose
(reverse
(slice-pic picture))))
(image-height picture)
(image-width picture)
(pinhole-to-bottom picture)
(pinhole-to-right picture)))
(define (rotate-cw picture)
(check-image 'rotate-cw picture "first")
(unslice-pic
(transpose
(reverse
(slice-pic picture)))
(image-height picture)
(image-width picture)
(pinhole-to-bottom picture)
(pinhole-to-left picture)))
(define (rotate-ccw picture)
(check-image 'rotate-ccw picture "first")
(unslice-pic
(reverse
(transpose
(slice-pic picture)))
(image-height picture)
(image-width picture)
(pinhole-to-top picture)
(pinhole-to-right picture)))
(define (rotate-180 picture)
(check-image 'rotate-180 picture "first")
(unslice-pic
(reverse
(map reverse
(slice-pic picture)))
(image-width picture)
(image-height picture)
(pinhole-to-right picture)
(pinhole-to-bottom picture)))