Now make-flomap* and effects functions accept (U (Vectorof Real) FlVector) for color instead of just FlVector

New vector-of-component functions: unsafe-flomap-ref*, flomap-ref*, flomap-bilinear-ref*, build-flomap*, inline-build-flomap*;
REPL interaction with -ref* is a bit weird - still need to make flvectors print nicely

Bilinear ref and scaling fix on top and left borders (bad/missing special cases);
Clarified flomap-bilinear-ref docs on extent of interpolated nonzero rectangle

Various doc fixes
This commit is contained in:
Neil Toronto 2012-05-30 15:04:19 -06:00
parent 4ad700021b
commit 91579f2a6e
6 changed files with 313 additions and 113 deletions

View File

@ -31,8 +31,7 @@
(match-define (flomap _ 4 w h) argb-fm) (match-define (flomap _ 4 w h) argb-fm)
(match-define (flomap _ 1 zw zh) z-fm) (match-define (flomap _ 1 zw zh) z-fm)
(unless (and (= w zw) (= h zh)) (unless (and (= w zw) (= h zh))
(error 'deep-flomap (error 'deep-flomap "expected same-size flomaps; given sizes ~e×~e and ~e×~e" w h zw zh))
"expected flomaps of equal dimension; given dimensions ~e×~e and ~e×~e" w h zw zh))
(values argb-fm z-fm))) (values argb-fm z-fm)))
(: flomap->deep-flomap (flomap -> deep-flomap)) (: flomap->deep-flomap (flomap -> deep-flomap))

View File

@ -13,13 +13,13 @@
flomap-shadow flomap-shadowed flomap-shadow flomap-shadowed
flomap-whirl-morph) flomap-whirl-morph)
(: colorize-alpha (flomap FlVector -> flomap)) (: colorize-alpha (flomap (U (Vectorof Real) FlVector) -> flomap))
(define (colorize-alpha fm vs) (define (colorize-alpha fm vs)
(match-define (flomap _ 1 w h) fm) (match-define (flomap _ 1 w h) fm)
(flomap-append-components fm (fm* fm (make-flomap* w h vs)))) (flomap-append-components fm (fm* fm (make-flomap* w h vs))))
(: flomap-shadow (case-> (flomap Real -> flomap) (: flomap-shadow (case-> (flomap Real -> flomap)
(flomap Real (Option FlVector) -> flomap))) (flomap Real (Option (U (Vectorof Real) FlVector)) -> flomap)))
(define flomap-shadow (define flomap-shadow
(case-lambda (case-lambda
[(fm σ) (flomap-shadow fm σ #f)] [(fm σ) (flomap-shadow fm σ #f)]
@ -27,18 +27,18 @@
(match-define (flomap _ c w h) fm) (match-define (flomap _ c w h) fm)
(cond [(c . = . 0) fm] (cond [(c . = . 0) fm]
[else (define alpha-fm (flomap-ref-component fm 0)) [else (define alpha-fm (flomap-ref-component fm 0))
(define color-vs (if (flvector? color) color (make-flvector (- c 1) 0.0))) (define color-vs (if color color (make-flvector (- c 1) 0.0)))
(colorize-alpha (flomap-blur alpha-fm σ) color-vs)])])) (colorize-alpha (flomap-blur alpha-fm σ) color-vs)])]))
(: flomap-shadowed (case-> (flomap Real -> flomap) (: flomap-shadowed (case-> (flomap Real -> flomap)
(flomap Real (Option FlVector) -> flomap))) (flomap Real (Option (U (Vectorof Real) FlVector)) -> flomap)))
(define flomap-shadowed (define flomap-shadowed
(case-lambda (case-lambda
[(fm σ) (flomap-shadowed fm σ #f)] [(fm σ) (flomap-shadowed fm σ #f)]
[(fm σ c) (flomap-cc-superimpose (flomap-shadow fm σ c) fm)])) [(fm σ c) (flomap-cc-superimpose (flomap-shadow fm σ c) fm)]))
(: flomap-outline (case-> (flomap Real -> flomap) (: flomap-outline (case-> (flomap Real -> flomap)
(flomap Real (Option FlVector) -> flomap))) (flomap Real (Option (U (Vectorof Real) FlVector)) -> flomap)))
(define flomap-outline (define flomap-outline
(case-lambda (case-lambda
[(fm amt) (flomap-outline fm amt #f)] [(fm amt) (flomap-outline fm amt #f)]
@ -57,11 +57,11 @@
(define alpha-fm (flomap-ref-component fm 0)) (define alpha-fm (flomap-ref-component fm 0))
(define new-alpha-fm (fmmax 0.0 (fmmin 1.0 (fm/ (fm- (flomap-blur alpha-fm σ) v-min) (define new-alpha-fm (fmmax 0.0 (fmmin 1.0 (fm/ (fm- (flomap-blur alpha-fm σ) v-min)
(- v-max v-min))))) (- v-max v-min)))))
(define color-vs (if (flvector? color) color (make-flvector (- c 1) 0.0))) (define color-vs (if color color (make-flvector (- c 1) 0.0)))
(colorize-alpha new-alpha-fm color-vs))])) (colorize-alpha new-alpha-fm color-vs))]))
(: flomap-outlined (case-> (flomap Real -> flomap) (: flomap-outlined (case-> (flomap Real -> flomap)
(flomap Real (Option FlVector) -> flomap))) (flomap Real (Option (U (Vectorof Real) FlVector)) -> flomap)))
(define flomap-outlined (define flomap-outlined
(case-lambda (case-lambda
[(fm amt) (flomap-outlined fm amt #f)] [(fm amt) (flomap-outlined fm amt #f)]

View File

@ -164,9 +164,9 @@
[else (let ([height (abs height)]) [else (let ([height (abs height)])
(flomap-scale*-y fm (abs (exact->inexact (/ height h))) height))])) (flomap-scale*-y fm (abs (exact->inexact (/ height h))) height))]))
;; standard deviation of an unscaled box filter (i.e. f([-1/2,1/2]) = {1}, zero elsewhere) ;; variance of an unscaled box filter (i.e. f([-1/2,1/2]) = {1}, zero elsewhere)
(define box-filter-variance (/ 1.0 12.0)) (define box-filter-variance (/ 1.0 12.0))
;; standard deviation of an unscaled triangle filter (simulates effect of linear interpolation) ;; variance of an unscaled triangle filter (simulates effect of linear interpolation)
(define triangle-filter-variance (/ 1.0 24.0)) (define triangle-filter-variance (/ 1.0 24.0))
;; calculates the standard deviation of downscaling blur, assuming linear interpolation will be ;; calculates the standard deviation of downscaling blur, assuming linear interpolation will be
@ -175,7 +175,7 @@
(define (stddev-for-scale scale) (define (stddev-for-scale scale)
(define var (- (/ box-filter-variance (sqr scale)) (define var (- (/ box-filter-variance (sqr scale))
triangle-filter-variance)) triangle-filter-variance))
(abs (flsqrt (max 0.0 var)))) (flsqrt (max 0.0 var)))
(: flomap-scale*-x (flomap Flonum Exact-Nonnegative-Integer -> flomap)) (: flomap-scale*-x (flomap Flonum Exact-Nonnegative-Integer -> flomap))
(define (flomap-scale*-x fm scale width) (define (flomap-scale*-x fm scale width)
@ -196,17 +196,18 @@
(: flomap-scale*-x/linear (flomap Flonum Exact-Nonnegative-Integer -> flomap)) (: flomap-scale*-x/linear (flomap Flonum Exact-Nonnegative-Integer -> flomap))
(define (flomap-scale*-x/linear fm s new-w) (define (flomap-scale*-x/linear fm s new-w)
(match-define (flomap vs c w h) fm) (match-define (flomap vs c w h) fm)
(define w-1 (fx- w 1)) (define w-1 (unsafe-fx+ w -1))
(inline-build-flomap (inline-build-flomap
c new-w h c new-w h
(λ (k new-x y _i) (λ (k new-x y _i)
(define scaled-x (- (/ (+ (fx->fl new-x) 0.5) s) 0.5)) (define scaled-x (- (/ (+ (fx->fl new-x) 0.5) s) 0.5))
(define floor-scaled-x (floor scaled-x)) (define floor-scaled-x (floor scaled-x))
(define x0 (fl->fx floor-scaled-x)) (define x0 (fl->fx floor-scaled-x))
(cond [(or (x0 . fx< . 0) (x0 . fx>= . w)) 0.0] (cond [(or (x0 . fx< . -1) (x0 . fx>= . w)) 0.0]
[else [else
(define i0 (coords->index c w k x0 y)) (define i0 (coords->index c w k x0 y))
(define v0 (flvector-ref vs i0)) (define v0 (cond [(x0 . fx= . -1) 0.0]
[else (flvector-ref vs i0)]))
(define v1 (cond [(x0 . fx= . w-1) 0.0] (define v1 (cond [(x0 . fx= . w-1) 0.0]
[else (flvector-ref vs (unsafe-fx+ i0 c))])) [else (flvector-ref vs (unsafe-fx+ i0 c))]))
(fl-convex-combination v0 v1 (- scaled-x floor-scaled-x))])))) (fl-convex-combination v0 v1 (- scaled-x floor-scaled-x))]))))
@ -214,7 +215,7 @@
(: flomap-scale*-y/linear (flomap Flonum Exact-Nonnegative-Integer -> flomap)) (: flomap-scale*-y/linear (flomap Flonum Exact-Nonnegative-Integer -> flomap))
(define (flomap-scale*-y/linear fm s new-h) (define (flomap-scale*-y/linear fm s new-h)
(match-define (flomap vs c w h) fm) (match-define (flomap vs c w h) fm)
(define h-1 (fx- h 1)) (define h-1 (unsafe-fx+ h -1))
(define cw (* c w)) (define cw (* c w))
(inline-build-flomap (inline-build-flomap
c w new-h c w new-h
@ -222,10 +223,11 @@
(define scaled-y (- (/ (+ (fx->fl new-y) 0.5) s) 0.5)) (define scaled-y (- (/ (+ (fx->fl new-y) 0.5) s) 0.5))
(define floor-scaled-y (floor scaled-y)) (define floor-scaled-y (floor scaled-y))
(define y0 (fl->fx floor-scaled-y)) (define y0 (fl->fx floor-scaled-y))
(cond [(or (y0 . fx< . 0) (y0 . fx>= . h)) 0.0] (cond [(or (y0 . fx< . -1) (y0 . fx>= . h)) 0.0]
[else [else
(define i0 (coords->index c w k x y0)) (define i0 (coords->index c w k x y0))
(define v0 (flvector-ref vs i0)) (define v0 (cond [(y0 . fx= . -1) 0.0]
[else (flvector-ref vs i0)]))
(define v1 (cond [(y0 . fx= . h-1) 0.0] (define v1 (cond [(y0 . fx= . h-1) 0.0]
[else (flvector-ref vs (unsafe-fx+ i0 cw))])) [else (flvector-ref vs (unsafe-fx+ i0 cw))]))
(fl-convex-combination v0 v1 (- scaled-y floor-scaled-y))])))) (fl-convex-combination v0 v1 (- scaled-y floor-scaled-y))]))))

View File

@ -9,9 +9,12 @@
(provide flomap flomap? flomap-values flomap-components flomap-width flomap-height (provide flomap flomap? flomap-values flomap-components flomap-width flomap-height
;; Accessors ;; Accessors
flomap-size unsafe-flomap-ref flomap-ref flomap-bilinear-ref coords->index flomap-size coords->index
unsafe-flomap-ref flomap-ref flomap-bilinear-ref
unsafe-flomap-ref* flomap-ref* flomap-bilinear-ref*
;; Basic constructors ;; Basic constructors
make-flomap make-flomap* build-flomap inline-build-flomap make-flomap build-flomap inline-build-flomap
make-flomap* build-flomap* inline-build-flomap*
flomap-ref-component flomap-take-components flomap-drop-components flomap-append-components) flomap-ref-component flomap-take-components flomap-drop-components flomap-append-components)
(struct: flomap ([values : FlVector] [components : Integer] [width : Integer] [height : Integer]) (struct: flomap ([values : FlVector] [components : Integer] [width : Integer] [height : Integer])
@ -43,6 +46,19 @@
(unsafe-flvector-ref vs (coords->index c w k x y))] (unsafe-flvector-ref vs (coords->index c w k x y))]
[else 0.0])) [else 0.0]))
(: unsafe-flomap-ref* (FlVector Integer Integer Integer Integer Integer -> FlVector))
(define (unsafe-flomap-ref* vs c w h x y)
(cond [(and (x . fx>= . 0) (x . fx< . w)
(y . fx>= . 0) (y . fx< . h))
(define i (coords->index c w 0 x y))
(define point-vs (make-flvector c))
(let: loop : Void ([k : Nonnegative-Fixnum 0])
(when (k . < . c)
(unsafe-flvector-set! point-vs k (unsafe-flvector-ref vs (unsafe-fx+ i k)))
(loop (unsafe-fx+ k 1))))
point-vs]
[else (make-flvector c 0.0)]))
(: flomap-ref (flomap Integer Integer Integer -> Flonum)) (: flomap-ref (flomap Integer Integer Integer -> Flonum))
(define (flomap-ref fm k x y) (define (flomap-ref fm k x y)
(match-define (flomap vs c w h) fm) (match-define (flomap vs c w h) fm)
@ -50,6 +66,11 @@
(raise-type-error 'flomap-ref (format "nonnegative fixnum < ~e" c) k)) (raise-type-error 'flomap-ref (format "nonnegative fixnum < ~e" c) k))
(unsafe-flomap-ref vs c w h k x y)) (unsafe-flomap-ref vs c w h k x y))
(: flomap-ref* (flomap Integer Integer -> FlVector))
(define (flomap-ref* fm x y)
(match-define (flomap vs c w h) fm)
(unsafe-flomap-ref* vs c w h x y))
) ; begin-encourage-inline ) ; begin-encourage-inline
(: flomap-bilinear-ref (flomap Integer Real Real -> Flonum)) (: flomap-bilinear-ref (flomap Integer Real Real -> Flonum))
@ -58,14 +79,14 @@
(cond [(and (k . >= . 0) (k . < . c)) (cond [(and (k . >= . 0) (k . < . c))
(let ([x (- (exact->inexact x) 0.5)] (let ([x (- (exact->inexact x) 0.5)]
[y (- (exact->inexact y) 0.5)]) [y (- (exact->inexact y) 0.5)])
(cond [(and (x . > . -0.5) (x . < . (+ 0.5 (->fl w))) (cond [(and (x . > . -1.0) (x . < . (->fl w))
(y . > . -0.5) (y . < . (+ 0.5 (->fl h)))) (y . > . -1.0) (y . < . (->fl h)))
(define floor-x (floor x)) (define floor-x (floor x))
(define floor-y (floor y)) (define floor-y (floor y))
(define x0 (fl->fx floor-x)) (define x0 (fl->fx floor-x))
(define y0 (fl->fx floor-y)) (define y0 (fl->fx floor-y))
(define x1 (fx+ x0 1)) (define x1 (unsafe-fx+ x0 1))
(define y1 (fx+ y0 1)) (define y1 (unsafe-fx+ y0 1))
(define v00 (unsafe-flomap-ref vs c w h k x0 y0)) (define v00 (unsafe-flomap-ref vs c w h k x0 y0))
(define v10 (unsafe-flomap-ref vs c w h k x1 y0)) (define v10 (unsafe-flomap-ref vs c w h k x1 y0))
(define v01 (unsafe-flomap-ref vs c w h k x0 y1)) (define v01 (unsafe-flomap-ref vs c w h k x0 y1))
@ -78,6 +99,40 @@
[else [else
(raise-type-error 'flomap-bilinear-ref (format "nonnegative fixnum < ~e" c) 1 fm k x y)])) (raise-type-error 'flomap-bilinear-ref (format "nonnegative fixnum < ~e" c) 1 fm k x y)]))
(: flomap-bilinear-ref* (flomap Real Real -> FlVector))
(define (flomap-bilinear-ref* fm x y)
(match-define (flomap vs c w h) fm)
(let ([x (- (exact->inexact x) 0.5)]
[y (- (exact->inexact y) 0.5)])
(cond [(and (x . > . -1.0) (x . < . (->fl w))
(y . > . -1.0) (y . < . (->fl h)))
(define floor-x (floor x))
(define floor-y (floor y))
(define x0 (fl->fx floor-x))
(define y0 (fl->fx floor-y))
(define x1 (unsafe-fx+ x0 1))
(define y1 (unsafe-fx+ y0 1))
(define vs00 (unsafe-flomap-ref* vs c w h x0 y0))
(define vs10 (unsafe-flomap-ref* vs c w h x1 y0))
(define vs01 (unsafe-flomap-ref* vs c w h x0 y1))
(define vs11 (unsafe-flomap-ref* vs c w h x1 y1))
(define xα (- x floor-x))
(define yα (- y floor-y))
(define point-vs (make-flvector c))
(let: loop : FlVector ([k : Nonnegative-Fixnum 0])
(cond [(k . < . c)
(define v00 (unsafe-flvector-ref vs00 k))
(define v10 (unsafe-flvector-ref vs10 k))
(define v01 (unsafe-flvector-ref vs01 k))
(define v11 (unsafe-flvector-ref vs11 k))
(define v (fl-convex-combination (fl-convex-combination v00 v10 xα)
(fl-convex-combination v01 v11 xα)
yα))
(unsafe-flvector-set! point-vs k v)
(loop (unsafe-fx+ k 1))]
[else point-vs]))]
[else (make-flvector c 0.0)])))
;; =================================================================================================== ;; ===================================================================================================
;; Construction and conversion ;; Construction and conversion
@ -98,7 +153,8 @@
[w : Integer width] [w : Integer width]
[h : Integer height]) [h : Integer height])
(with-asserts ([c nonnegative-fixnum?] [w nonnegative-fixnum?] [h nonnegative-fixnum?]) (with-asserts ([c nonnegative-fixnum?] [w nonnegative-fixnum?] [h nonnegative-fixnum?])
(define vs (make-flvector (* c w h))) (define fm (make-flomap c w h))
(define vs (flomap-values fm))
(let: y-loop : flomap ([y : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum 0]) (let: y-loop : flomap ([y : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum 0])
(cond (cond
[(y . fx< . h) [(y . fx< . h)
@ -111,19 +167,63 @@
(k-loop (unsafe-fx+ k 1) (unsafe-fx+ i 1))] (k-loop (unsafe-fx+ k 1) (unsafe-fx+ i 1))]
[else (x-loop (unsafe-fx+ x 1) i)]))] [else (x-loop (unsafe-fx+ x 1) i)]))]
[else (y-loop (unsafe-fx+ y 1) i)]))] [else (y-loop (unsafe-fx+ y 1) i)]))]
[else (flomap vs c w h)]))))) [else fm])))))
(: build-flomap (Integer Integer Integer (: build-flomap (Integer Integer Integer
(Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum (Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum -> Real)
Nonnegative-Fixnum -> Real)
-> flomap)) -> flomap))
(define (build-flomap components width height fun) (define (build-flomap c w h f)
(inline-build-flomap components width height (λ (k x y i) (exact->inexact (fun k x y i))))) (inline-build-flomap c w h (λ (k x y i) (exact->inexact (f k x y)))))
(: make-flomap* (Integer Integer FlVector -> flomap)) #;
(: inline-build-flomap* (Integer Integer Integer
(Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum
-> FlVector)
-> flomap))
(define-syntax-rule (inline-build-flomap* components width height f)
(let: ([c : Integer components]
[w : Integer width]
[h : Integer height])
(with-asserts ([c nonnegative-fixnum?] [w nonnegative-fixnum?] [h nonnegative-fixnum?])
(define fm (make-flomap c w h))
(define vs (flomap-values fm))
(let: y-loop : flomap ([y : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum 0])
(cond
[(y . fx< . h)
(let: x-loop : flomap ([x : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum i])
(cond
[(x . fx< . w)
(define: point-vs : FlVector (f x y i))
(cond
[(fx= (flvector-length point-vs) c)
(let: k-loop : flomap ([k : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum i])
(cond
[(k . fx< . c) (unsafe-flvector-set! vs i (unsafe-flvector-ref point-vs k))
(k-loop (unsafe-fx+ k 1) (unsafe-fx+ i 1))]
[else (x-loop (unsafe-fx+ x 1) i)]))]
[else (raise-type-error 'inline-build-flomap* (format "length-~e FlVector" c)
point-vs)])]
[else (y-loop (unsafe-fx+ y 1) i)]))]
[else fm])))))
(: make-flomap* (Integer Integer (U (Vectorof Real) FlVector) -> flomap))
(define (make-flomap* w h vs) (define (make-flomap* w h vs)
(define c (flvector-length vs)) (let ([vs (->flvector vs)])
(inline-build-flomap c w h (λ (k _x _y _i) (unsafe-flvector-ref vs k)))) (define c (flvector-length vs))
(inline-build-flomap* c w h (λ (_x _y _i) vs))))
(: build-flomap* (Integer Integer Integer
(Nonnegative-Fixnum Nonnegative-Fixnum -> (U (Vectorof Real) FlVector))
-> flomap))
(define (build-flomap* c w h f)
(inline-build-flomap*
c w h
(λ (x y i)
(define point-vs0 (f x y))
(define point-vs1 (->flvector point-vs0))
(cond [(fx= (flvector-length point-vs1) c) point-vs1]
[else (raise-type-error 'build-flomap* (format "length-~e Vector or FlVector" c)
point-vs0)]))))
(: flomap-ref-component (flomap Integer -> flomap)) (: flomap-ref-component (flomap Integer -> flomap))
(define (flomap-ref-component fm k) (define (flomap-ref-component fm k)
@ -155,8 +255,7 @@
(match-define (flomap vs1 d1 w1 h1) fm1) (match-define (flomap vs1 d1 w1 h1) fm1)
(match-define (flomap vs2 d2 w2 h2) fm2) (match-define (flomap vs2 d2 w2 h2) fm2)
(unless (and (= w1 w2) (= h1 h2)) (unless (and (= w1 w2) (= h1 h2))
(error 'flomap-append-components (error 'flomap-append-components "expected same-size flomaps; given sizes ~e×~e and ~e×~e"
"expected flomaps with equal dimension; given dimensions ~e×~e and ~e×~e"
w1 h1 w2 h2)) w1 h1 w2 h2))
(inline-build-flomap (fx+ d1 d2) w1 h1 (inline-build-flomap (fx+ d1 d2) w1 h1
(λ (k x y _i) (λ (k x y _i)

View File

@ -6,7 +6,10 @@
[flvector-set! old:flvector-set!]) [flvector-set! old:flvector-set!])
(except-in racket/fixnum fl->fx fx->fl) ; these two functions are untyped (except-in racket/fixnum fl->fx fx->fl) ; these two functions are untyped
racket/math racket/math
(only-in racket/unsafe/ops unsafe-flvector-set! unsafe-fx+) (only-in racket/unsafe/ops
unsafe-flvector-set! unsafe-flvector-ref
unsafe-vector-set! unsafe-vector-ref
unsafe-fx+)
racket/performance-hint) racket/performance-hint)
(provide (all-defined-out) (provide (all-defined-out)
@ -35,8 +38,31 @@
(loop (unsafe-fx+ i 1))] (loop (unsafe-fx+ i 1))]
[else vs]))))) [else vs])))))
(: flvector->vector (FlVector -> (Vectorof Flonum)))
(define (flvector->vector vs)
(define n (flvector-length vs))
(define new-vs (make-vector n 0.0))
(let: loop : (Vectorof Flonum) ([k : Nonnegative-Fixnum 0])
(cond [(k . < . n) (unsafe-vector-set! new-vs k (unsafe-flvector-ref vs k))
(loop (unsafe-fx+ k 1))]
[else new-vs])))
(: real-vector->flvector ((Vectorof Real) -> FlVector))
(define (real-vector->flvector vs)
(define n (vector-length vs))
(define new-vs (make-flvector n 0.0))
(let: loop : FlVector ([k : Nonnegative-Fixnum 0])
(cond [(k . < . n) (unsafe-flvector-set! new-vs k (exact->inexact (unsafe-vector-ref vs k)))
(loop (unsafe-fx+ k 1))]
[else new-vs])))
(begin-encourage-inline (begin-encourage-inline
(: ->flvector ((U (Vectorof Real) FlVector) -> FlVector))
(define (->flvector vs)
(cond [(flvector? vs) vs]
[else (real-vector->flvector vs)]))
(: fx->fl (Fixnum -> Flonum)) (: fx->fl (Fixnum -> Flonum))
(define fx->fl ->fl) (define fx->fl ->fl)

View File

@ -29,19 +29,19 @@ The following flomap @racket[fm] is used in various examples:
@interaction[#:eval flomap-eval @interaction[#:eval flomap-eval
(define fm (define fm
(draw-flomap (draw-flomap
(λ (bm-dc) (λ (fm-dc)
(send bm-dc set-alpha 0) (send fm-dc set-alpha 0)
(send bm-dc set-background "black") (send fm-dc set-background "black")
(send bm-dc clear) (send fm-dc clear)
(send bm-dc set-alpha 1/3) (send fm-dc set-alpha 1/3)
(send bm-dc translate 2 2) (send fm-dc translate 2 2)
(send bm-dc set-pen "black" 4 'long-dash) (send fm-dc set-pen "black" 4 'long-dash)
(send bm-dc set-brush "red" 'solid) (send fm-dc set-brush "red" 'solid)
(send bm-dc draw-ellipse 0 0 192 192) (send fm-dc draw-ellipse 0 0 192 192)
(send bm-dc set-brush "green" 'solid) (send fm-dc set-brush "green" 'solid)
(send bm-dc draw-ellipse 64 0 192 192) (send fm-dc draw-ellipse 64 0 192 192)
(send bm-dc set-brush "blue" 'solid) (send fm-dc set-brush "blue" 'solid)
(send bm-dc draw-ellipse 32 44 192 192)) (send fm-dc draw-ellipse 32 44 192 192))
260 240)) 260 240))
(flomap->bitmap fm)] (flomap->bitmap fm)]
It is typical to use @racket[flomap->bitmap] to visualize a flomap at the REPL. It is typical to use @racket[flomap->bitmap] to visualize a flomap at the REPL.
@ -68,7 +68,7 @@ There are three main reasons to use flomaps:
@item{@bold{Range.} @item{@bold{Range.}
A floating-point value can also represent about 4.6 quintillion distinct intensities above saturation (@racket[1.0]). A floating-point value can also represent about 4.6 quintillion distinct intensities above saturation (@racket[1.0]).
If distinguishing oversaturated values is important, flomaps have the range for it. If distinguishing oversaturated values is important, flomaps have the range for it.
Further, floating-point images are (approximately) closed under pointwise arithmetic. Further, floating-point images are closed under pointwise arithmetic (up to floating-point error).
} }
@item{@bold{Speed.} @item{@bold{Speed.}
The @racketmodname[images/flomap] module benefits greatly from Typed Racket's type-directed optimizations. The @racketmodname[images/flomap] module benefits greatly from Typed Racket's type-directed optimizations.
@ -76,7 +76,7 @@ There are three main reasons to use flomaps:
} }
) )
For these reasons, other parts of the @racket[images] library use flomaps internally, to represent and operate on For these reasons, other parts of the @racket[images] library use flomaps internally, to represent and operate on
ARGB and RGB images, light maps, shadow maps, height maps, and normal maps. RGB and ARGB images, light maps, shadow maps, height maps, and normal maps.
@subsection[#:tag "flomap:conceptual"]{Conceptual Model} @subsection[#:tag "flomap:conceptual"]{Conceptual Model}
@ -88,23 +88,24 @@ The following example creates a 10×10 bitmap with RGB components, and indexes i
It also attempts to index component @racket[3], which doesn't exist. It also attempts to index component @racket[3], which doesn't exist.
Note that @racket[flomap-ref] accepts its coordinate arguments in a standard order: @racket[k] @racket[x] @racket[y] (with @racket[k] for @bold{k}omponent). Note that @racket[flomap-ref] accepts its coordinate arguments in a standard order: @racket[k] @racket[x] @racket[y] (with @racket[k] for @bold{k}omponent).
@interaction[#:eval flomap-eval @interaction[#:eval flomap-eval
(define magenta-fm (make-flomap* 10 10 (flvector 1.0 0.0 1.0))) (define magenta-fm (make-flomap* 10 10 #(0.5 0.0 1.0)))
(flomap->bitmap magenta-fm) (flomap->bitmap magenta-fm)
(flomap-ref magenta-fm 0 0 0) (flomap-ref* magenta-fm 0 0)
(flomap-ref magenta-fm 0 -1 0) (flomap-ref* magenta-fm -1 0)
(flomap-ref magenta-fm 0 0 1000) (flomap-ref* magenta-fm 0 1000)
(flomap-ref magenta-fm 3 0 0)] (flomap-ref magenta-fm 3 0 0)]
Many flomap functions, such as @racket[flomap-bilinear-ref], treat their arguments as if every @italic{real} @racket[x] @racket[y] coordinate has values. Many flomap functions, such as @racket[flomap-bilinear-ref], treat their arguments as if every @italic{real} @racket[x] @racket[y] coordinate has values.
In all such cases, known nonzero values are at half-integer coordinates and others are interpolated. In all such cases, known nonzero values are at half-integer coordinates and others are interpolated.
@examples[#:eval flomap-eval @examples[#:eval flomap-eval
(flomap-bilinear-ref magenta-fm 0 0.5 0.5) (flomap-bilinear-ref* magenta-fm 0.5 0.5)
(flomap-bilinear-ref magenta-fm 0 0.25 0.25) (flomap-bilinear-ref* magenta-fm 0.25 0.25)
(flomap-bilinear-ref magenta-fm 0 0.0 0.0)] (flomap-bilinear-ref* magenta-fm 0.0 0.0)
(flomap-bilinear-ref* magenta-fm -0.25 -0.25)]
This conceptual model allows us to treat flomaps as if they were multi-valued functions on @racket[Real]×@racket[Real]. This conceptual model allows us to treat flomaps as if they were multi-valued functions on @racket[Real]×@racket[Real].
For example, we might plot the red component of an icon: For example, we might plot the red component of an ARGB icon:
@interaction[#:eval flomap-eval @interaction[#:eval flomap-eval
(require images/icons/misc plot) (require images/icons/misc plot)
(define icon-fm (bomb-flomap "azure" "orange" 48)) (define icon-fm (bomb-flomap "azure" "orange" 48))
@ -112,7 +113,7 @@ For example, we might plot the red component of an icon:
(define-values (icon-width icon-height) (flomap-size icon-fm)) (define-values (icon-width icon-height) (flomap-size icon-fm))
(plot3d-bitmap (contour-intervals3d (plot3d-bitmap (contour-intervals3d
(λ (x y) (flomap-bilinear-ref icon-fm 1 x y)) (λ (x y) (flomap-bilinear-ref icon-fm 1 x y))
0 icon-width 0 icon-height))] -0.5 (+ 0.5 icon-width) -0.5 (+ 0.5 icon-height)))]
Notice that the plot's maximum height is above saturation (@racket[1.0]). Notice that the plot's maximum height is above saturation (@racket[1.0]).
The tallest peak corresponds to the specular highlight (the shiny part) on the bomb. The tallest peak corresponds to the specular highlight (the shiny part) on the bomb.
Specular highlights are one case where it is important to operate on oversaturated values without truncating them---until it is time to display the image. Specular highlights are one case where it is important to operate on oversaturated values without truncating them---until it is time to display the image.
@ -143,10 +144,10 @@ As an example of the last point, consider blur.
The following example creates an alpha-multiplied flomap using @racket[draw-flomap]. The following example creates an alpha-multiplied flomap using @racket[draw-flomap].
It blurs the flomap using a general-purpose (i.e. non-alpha-aware) blur function, then converts the flomap to non-alpha-multiplied and does the same. It blurs the flomap using a general-purpose (i.e. non-alpha-aware) blur function, then converts the flomap to non-alpha-multiplied and does the same.
@interaction[#:eval flomap-eval @interaction[#:eval flomap-eval
(define circle-fm (draw-flomap (λ (dc) (define circle-fm (draw-flomap (λ (fm-dc)
(send dc set-pen "black" 1 'transparent) (send fm-dc set-pen "black" 1 'transparent)
(send dc set-brush "green" 'solid) (send fm-dc set-brush "green" 'solid)
(send dc draw-ellipse 10 10 30 30)) (send fm-dc draw-ellipse 10 10 30 30))
50 50)) 50 50))
(flomap->bitmap circle-fm) (flomap->bitmap circle-fm)
(flomap->bitmap (flomap-blur circle-fm 4 4)) (flomap->bitmap (flomap-blur circle-fm 4 4))
@ -239,10 +240,17 @@ Returns the width and height of @racket[fm] as nonnegative fixnums.
@defproc[(flomap-ref [fm flomap] [k Integer] [x Integer] [y Integer]) Float]{ @defproc[(flomap-ref [fm flomap] [k Integer] [x Integer] [y Integer]) Float]{
Returns @racket[fm]'s value at @racket[k] @racket[x] @racket[y]. Returns @racket[fm]'s value at @racket[k] @racket[x] @racket[y].
If @racket[x] or @racket[y] is out of bounds, this function returns @racket[0.0]. If @racket[x] or @racket[y] is out of bounds, this function returns @racket[0.0].
If @racket[k] is out of bounds, it raises an error. If @racket[k] is out of bounds, it raises an error.
See @secref{flomap:conceptual} to read about why. The @secref{flomap:conceptual} section explains why @racket[k] is treated differently.
}
@defproc[(flomap-ref* [fm flomap] [x Integer] [y Integer]) FlVector]{
Returns @racket[fm]'s component values at @racket[x] @racket[y] as an flvector.
If @racket[x] or @racket[y] is out of bounds, this function returns an flvector filled with @racket[0.0].
It always returns an flvector of length @racket[(flomap-components fm)].
} }
@defproc[(flomap-bilinear-ref [fm flomap] [k Integer] [x Real] [y Real]) Float]{ @defproc[(flomap-bilinear-ref [fm flomap] [k Integer] [x Real] [y Real]) Float]{
@ -252,9 +260,14 @@ Like all other @racket[flomap] functions that operate on real-valued coordinates
Mathematically, if @racket[x] = @racket[(+ i 0.5)] and @racket[y] = @racket[(+ j 0.5)] for any integers @racket[i] and @racket[j], Mathematically, if @racket[x] = @racket[(+ i 0.5)] and @racket[y] = @racket[(+ j 0.5)] for any integers @racket[i] and @racket[j],
then @racket[(flomap-bilinear-ref fm k x y)] = @racket[(flomap-ref fm k i j)]. then @racket[(flomap-bilinear-ref fm k x y)] = @racket[(flomap-ref fm k i j)].
If @racket[x] or @racket[y] is out of bounds, this function returns @racket[0.0]. Suppose @racket[fm] is size @racket[w]×@racket[h].
If @racket[x] ≤ @racket[-0.5] or @racket[x] ≥ @racket[(+ w 0.5)], this function returns @racket[0.0]; similarly for @racket[y] and @racket[h].
If @racket[k] is out of bounds, it raises an error. If @racket[k] is out of bounds, it raises an error.
See @secref{flomap:conceptual} to read about why. The @secref{flomap:conceptual} section explains why @racket[k] is treated differently.
}
@defproc[(flomap-bilinear-ref* [fm flomap] [x Real] [y Real]) FlVector]{
Like @racket[flomap-bilinear-ref], but returns an flvector containing estimates of all the components at @racket[x] @racket[y].
} }
@defproc[(flomap-min-value [fm flomap]) Float] @defproc[(flomap-min-value [fm flomap]) Float]
@ -291,6 +304,12 @@ This function is used by some library functions, such as @racket[flomap-bilinear
From untyped code, applying this function is likely no faster than applying @racket[flomap-ref], because of extra contract checks. From untyped code, applying this function is likely no faster than applying @racket[flomap-ref], because of extra contract checks.
} }
@defproc[(unsafe-flomap-ref* [vs FlVector]
[c Integer] [w Integer] [h Integer]
[x Integer] [y Integer]) FlVector]{
Like @racket[unsafe-flomap-ref], but returns an flvector containing all the component values at @racket[x] @racket[y].
}
@; =================================================================================================== @; ===================================================================================================
@ -316,6 +335,8 @@ See @secref{flomap:opacity} for a discussion of opacity (alpha) representation.
A zero-size @racket[fm] is padded by one point in any zero direction before conversion. A zero-size @racket[fm] is padded by one point in any zero direction before conversion.
For example, if @racket[fm] is size 0×1, the result of @racket[(flomap->bitmap fm)] is size 1×1. For example, if @racket[fm] is size 0×1, the result of @racket[(flomap->bitmap fm)] is size 1×1.
Values are clamped to between @racket[0.0] and @racket[1.0] before conversion.
} }
@defproc[(bitmap->flomap [bm Any]) flomap]{ @defproc[(bitmap->flomap [bm Any]) flomap]{
@ -327,52 +348,68 @@ The argument type is imprecise because Typed Racket does not support the object
@defproc[(make-flomap [c Integer] [w Integer] [h Integer] [v Real 0.0]) flomap]{ @defproc[(make-flomap [c Integer] [w Integer] [h Integer] [v Real 0.0]) flomap]{
Returns a @racket[w]×@racket[h] flomap with @racket[c] components, with every value initialized to @racket[v]. Returns a @racket[w]×@racket[h] flomap with @racket[c] components, with every value initialized to @racket[v].
Analogous to @racket[make-vector].
To create flomaps filled with a solid color, use @racket[make-flomap*]. To create flomaps filled with a solid color, use @racket[make-flomap*].
} }
@defproc[(make-flomap* [w Integer] [h Integer] [vs FlVector]) flomap]{ @defproc[(make-flomap* [w Integer] [h Integer] [vs (U (Vectorof Real) FlVector)]) flomap]{
Returns a @racket[w]×@racket[h] flomap with @racket[(flvector-length vs)] color components, with each known point initialized using the values in @racket[vs]. Returns a @racket[w]×@racket[h] flomap with each point's components initialized using the values in @racket[vs].
Analogous to @racket[make-vector].
The following two examples create magenta bitmaps with an alpha channel: The following two examples create an RGB and an ARGB flomap:
@interaction[#:eval flomap-eval @interaction[#:eval flomap-eval
(flomap->bitmap (make-flomap* 100 100 (flvector 1.0 1.0 0.0 1.0))) (flomap->bitmap (make-flomap* 100 100 #(0.5 0.0 1.0)))
(flomap->bitmap (make-flomap* 100 100 (flvector 0.5 0.5 0.0 0.5)))] (flomap->bitmap (make-flomap* 100 100 #(0.5 0.25 0.0 0.5)))]
See @secref{flomap:opacity} for a discussion of opacity (alpha) representation. See @secref{flomap:opacity} for a discussion of opacity (alpha) representation.
} }
@defproc[(build-flomap [c Integer] [w Integer] [h Integer] @defproc[(build-flomap [c Integer] [w Integer] [h Integer]
[f (Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum [f (Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum -> Real)]) flomap]{
Nonnegative-Fixnum -> Real)]) flomap]{
Returns a @racket[w]×@racket[h] flomap with @racket[c] color components, with values defined by @racket[f]. Returns a @racket[w]×@racket[h] flomap with @racket[c] color components, with values defined by @racket[f].
Analogous to @racket[build-vector].
The function @racket[f] receives three arguments @racket[k] @racket[x] @racket[y]: the color component and two positional coordinates.
The function @racket[f] receives four arguments @racket[k] @racket[x] @racket[y] @racket[i]: the color component, two positional coordinates, and a precalculated index into the flomap's @racketid[values] vector.
@examples[#:eval flomap-eval @examples[#:eval flomap-eval
(flomap->bitmap (flomap->bitmap
(build-flomap 1 100 100 (build-flomap 1 100 100
(λ (k x y i) (/ (+ x y) 200)))) (λ (k x y) (/ (+ x y) 200))))
(define sine-fm (define sine-fm
(build-flomap (build-flomap
1 100 100 1 100 100
(λ (k x y i) (λ (k x y)
(* 1/2 (+ 1 (sin (sqrt (+ (sqr (- x 50)) (* 1/2 (+ 1 (sin (sqrt (+ (sqr (- x 50))
(sqr (- y 50)))))))))) (sqr (- y 50))))))))))
(flomap->bitmap sine-fm)] (flomap->bitmap sine-fm)]
To build a flomap from a function that returns vectors, see @racket[build-flomap*].
} }
@defform[(inline-build-flomap c w h f)]{ @defproc[(build-flomap* [c Integer] [w Integer] [h Integer]
A macro version of @racket[build-flomap]. [f (Nonnegative-Fixnum Nonnegative-Fixnum -> (U (Vectorof Real) FlVector))]) flomap]{
The function or macro @racket[f] must return a @racket[Float], not a @racket[Real] as the @racket[f] argument to @racket[build-flomap] can. Returns a @racket[w]×@racket[h] flomap with @racket[c] color components.
Its values are defined by @racket[f], which returns vectors of point components.
The vectors returned by @racket[f] must be length @racket[c].
Using @racket[inline-build-flomap] instead of @racket[build-flomap] often ensures that @racket[f] is inlined, and therefore floats remain unboxed. Analogous to @racket[build-vector].
Many library functions use @racket[inline-build-flomap] internally for speed, notably @racket[fm+] and the other pointwise arithmetic operators.
@bold{This is not available in untyped Racket.} @examples[#:eval flomap-eval
(flomap->bitmap
(build-flomap* 4 100 100
(λ (x y)
(vector (/ (+ x y) 200)
(/ (+ (- 100 x) y) 200)
(/ (+ (- 100 x) (- 100 y)) 200)
(/ (+ x (- 100 y)) 200)))))
(build-flomap* 4 100 100
(λ (x y) (vector (/ (+ x y) 200))))]
} }
@defproc[(draw-flomap [draw (Any -> Any)] [w Integer] [h Integer]) flomap]{ @defproc[(draw-flomap [draw (Any -> Any)] [w Integer] [h Integer]) flomap]{
Returns a @racket[w]×@racket[h] bitmap drawn by @racket[draw]. Returns a @racket[w]×@racket[h] bitmap drawn by @racket[draw].
Think of it as the flomap version of @racketmodname[slideshow]'s @racket[dc]. Analogous to @racketmodname[slideshow]'s @racket[dc].
The @racket[draw] function should accept a @racket[dc<%>] instance and use its drawing methods to draw on an underlying bitmap. The @racket[draw] function should accept a @racket[dc<%>] instance and use its drawing methods to draw on an underlying bitmap.
The bitmap is converted to a flomap using @racket[bitmap->flomap] and returned. The bitmap is converted to a flomap using @racket[bitmap->flomap] and returned.
@ -395,6 +432,41 @@ You should not generally have to use these functions, because @racket[bitmap->fl
See @secref{flomap:opacity} for a discussion of opacity (alpha) representation. See @secref{flomap:opacity} for a discussion of opacity (alpha) representation.
} }
@defform[(inline-build-flomap c w h f)
#:contracts ([c Integer]
[w Integer]
[h Integer]
[f (Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum
Nonnegative-Fixnum -> Float)])]{
A macro version of @racket[build-flomap].
There are three differences between the function @racket[f] passed to @racket[build-flomap] and the @racket[f] passed to @racket[inline-build-flomap].
First, the @racket[f] passed to @racket[inline-build-flomap] can be a macro.
Second, it receives arguments @racket[k] @racket[x] @racket[y] @racket[i], where @racket[i] is a precalculated index into the result's @racketid[values].
Third, it must return a @racket[Float].
Using @racket[inline-build-flomap] instead of @racket[build-flomap] often ensures that @racket[f] is inlined, and therefore floats remain unboxed.
Many library functions use @racket[inline-build-flomap] internally for speed, notably @racket[fm+] and the other pointwise arithmetic operators.
@bold{This is not available in untyped Racket.}
}
@defform[(inline-build-flomap* c w h f)
#:contracts ([c Integer]
[w Integer]
[h Integer]
[f (Nonnegative-Fixnum Nonnegative-Fixnum
Nonnegative-Fixnum -> FlVector)])]{
A macro version of @racket[build-flomap*].
There are three differences between the function @racket[f] passed to @racket[build-flomap*] and the @racket[f] passed to @racket[inline-build-flomap*].
First, the @racket[f] passed to @racket[inline-build-flomap*] can be a macro.
Second, it receives arguments @racket[x] @racket[y] @racket[i], where @racket[i] is a precalculated index into the result's @racketid[values].
Third, it must return a @racket[FlVector].
@bold{This is not available in untyped Racket.}
}
@; =================================================================================================== @; ===================================================================================================
@ -466,23 +538,14 @@ For example, to estimate the local gradient magnitude at each point in a flomap:
Lifts a unary floating-point function to operate pointwise on flomaps. Lifts a unary floating-point function to operate pointwise on flomaps.
} }
@defform[(inline-flomap-lift f)]{
A macro version of @racket[flomap-lift].
The function or macro @racket[f] must return a @racket[Float], not a @racket[Real] as the @racket[f] argument to @racket[flomap-lift] can.
Using @racket[inline-flomap-lift] instead of @racket[flomap-lift] often ensures that @racket[f] is inlined, and therefore floats remain unboxed.
@bold{This is not available in untyped Racket.}
}
@defproc[(flomap-normalize [fm flomap]) flomap]{ @defproc[(flomap-normalize [fm flomap]) flomap]{
Returns a flomap like @racket[fm], but with values linearly rescaled to be between @racket[0.0] and @racket[1.0] inclusive. Returns a flomap like @racket[fm], but with values linearly rescaled to be between @racket[0.0] and @racket[1.0] inclusive.
@examples[#:eval flomap-eval @examples[#:eval flomap-eval
(define gray-fm (define gray-fm
(build-flomap 1 100 100 (λ (k x y i) (+ 0.375 (/ (+ x y) 800))))) (build-flomap 1 100 100 (λ (k x y) (+ 0.375 (/ (+ x y) 800)))))
(flomap->bitmap gray-fm) (flomap->bitmap gray-fm)
(flomap->bitmap (flomap-normalize gray-fm))] (flomap->bitmap (flomap-normalize gray-fm))]
Besides increasing contrast, you could use this function to debug a rendering pipeline that produces overbright intermediate flomaps. Besides increasing contrast, you could use this function to visualize oversaturated flomaps, or visualize flomaps that don't correspond directly to displayed images, such as height maps and normal maps.
} }
@defproc[(fm+ [fm1 (U Real flomap)] [fm2 (U Real flomap)]) flomap] @defproc[(fm+ [fm1 (U Real flomap)] [fm2 (U Real flomap)]) flomap]
@ -504,7 +567,7 @@ Binary operations accept the following argument combinations, in either order:
Any other argument combination will raise a type error. Any other argument combination will raise a type error.
@examples[#:eval flomap-eval @examples[#:eval flomap-eval
(define fm1 (build-flomap 1 260 240 (λ (k x y i) (/ (+ x y) 500)))) (define fm1 (build-flomap 1 260 240 (λ (k x y) (/ (+ x y) 500))))
(define fm2 (fm- 1.0 fm1)) (define fm2 (fm- 1.0 fm1))
(flomap->bitmap fm1) (flomap->bitmap fm1)
(flomap->bitmap fm2) (flomap->bitmap fm2)
@ -526,7 +589,16 @@ Because @racket[fm] is an alpha-multiplied flomap (see @secref{flomap:opacity}),
Lifts a binary floating-point function to operate pointwise on flomaps, allowing the same argument combinations as @racket[fm+] and others. Lifts a binary floating-point function to operate pointwise on flomaps, allowing the same argument combinations as @racket[fm+] and others.
} }
@defform[(inline-flomap-lift2 f)]{ @defform[(inline-flomap-lift f) #:contracts ([f (Float -> Float)])]{
A macro version of @racket[flomap-lift].
The function or macro @racket[f] must return a @racket[Float], not a @racket[Real] as the @racket[f] argument to @racket[flomap-lift] can.
Using @racket[inline-flomap-lift] instead of @racket[flomap-lift] often ensures that @racket[f] is inlined, and therefore floats remain unboxed.
@bold{This is not available in untyped Racket.}
}
@defform[(inline-flomap-lift2 f) #:contracts ([f (Float Float -> Float)])]{
A macro version of @racket[flomap-lift2]. A macro version of @racket[flomap-lift2].
The function or macro @racket[f] must return a @racket[Float], not a @racket[Real] as the @racket[f] argument to @racket[flomap-lift2] can. The function or macro @racket[f] must return a @racket[Float], not a @racket[Real] as the @racket[f] argument to @racket[flomap-lift2] can.
@ -550,10 +622,10 @@ These return, per-component, estimates of the local @italic{x}- and @italic{y}-d
Equivalent to @racket[(values (flomap-gradient-x fm) (flomap-gradient-y fm))]. Equivalent to @racket[(values (flomap-gradient-x fm) (flomap-gradient-y fm))].
@examples[#:eval flomap-eval @examples[#:eval flomap-eval
(let-values ([(dx-fm dy-fm) (flomap-gradient (define-values (dx-fm dy-fm)
(flomap-drop-components fm 1))]) (flomap-gradient (flomap-drop-components fm 1)))
(values (flomap->bitmap (fm* 0.5 (fm+ 1.0 dx-fm))) (values (flomap->bitmap (fm* 0.5 (fm+ 1.0 dx-fm)))
(flomap->bitmap (fm* 0.5 (fm+ 1.0 dy-fm)))))] (flomap->bitmap (fm* 0.5 (fm+ 1.0 dy-fm))))]
} }
@defproc[(flomap-gradient-normal [fm flomap]) flomap]{ @defproc[(flomap-gradient-normal [fm flomap]) flomap]{
@ -561,7 +633,8 @@ Given a one-component flomap, returns a @racket[3]-component flomap containing e
In other words, @racket[flomap-normal] converts height maps to normal maps. In other words, @racket[flomap-normal] converts height maps to normal maps.
@examples[#:eval flomap-eval @examples[#:eval flomap-eval
(flomap->bitmap sine-fm) (flomap->bitmap sine-fm)
(flomap->bitmap (flomap-gradient-normal sine-fm))] (flomap->bitmap (flomap-gradient-normal sine-fm))
(flomap-gradient-normal fm)]
} }
@ -595,7 +668,7 @@ Like @racket[flomap-gaussian-blur-x], but per-column instead of per-row.
@defproc[(flomap-box-blur [fm flomap] [x-radius Real] [y-radius Real x-radius]) flomap]{ @defproc[(flomap-box-blur [fm flomap] [x-radius Real] [y-radius Real x-radius]) flomap]{
Returns @racket[fm] convolved, per-component, with a box kernel with radii @racket[x-radius] and @racket[y-radius]. Returns @racket[fm] convolved, per-component, with a box kernel with radii @racket[x-radius] and @racket[y-radius].
The radii are of the largest ellipse that would fit in the box. The radii are of the largest axis-aligned ellipse that would fit in the box.
@examples[#:eval flomap-eval @examples[#:eval flomap-eval
(flomap->bitmap (flomap-box-blur (flomap-inset fm 4) 4)) (flomap->bitmap (flomap-box-blur (flomap-inset fm 4) 4))
(flomap->bitmap (flomap-box-blur (flomap-inset fm 4 1) 4 1))] (flomap->bitmap (flomap-box-blur (flomap-inset fm 4 1) 4 1))]
@ -662,8 +735,8 @@ This function cannot return a larger flomap.
@examples[#:eval flomap-eval @examples[#:eval flomap-eval
(define small-circle-fm (define small-circle-fm
(draw-flomap (λ (dc) (draw-flomap (λ (fm-dc)
(send dc draw-ellipse 20 20 10 10)) (send fm-dc draw-ellipse 20 20 10 10))
100 100)) 100 100))
(flomap->bitmap small-circle-fm) (flomap->bitmap small-circle-fm)
(flomap->bitmap (flomap-trim small-circle-fm))] (flomap->bitmap (flomap-trim small-circle-fm))]
@ -741,11 +814,11 @@ The result is expanded as necessary.
@examples[#:eval flomap-eval @examples[#:eval flomap-eval
(flomap-pin fm -10 -10 sine-fm) (flomap-pin fm -10 -10 sine-fm)
(define circle-fm (define circle-fm
(draw-flomap (λ (the-dc) (draw-flomap (λ (fm-dc)
(send the-dc set-pen "black" 4 'short-dash) (send fm-dc set-pen "black" 4 'short-dash)
(send the-dc set-brush "yellow" 'solid) (send fm-dc set-brush "yellow" 'solid)
(send the-dc set-alpha 1/2) (send fm-dc set-alpha 1/2)
(send the-dc draw-ellipse 2 2 124 124)) (send fm-dc draw-ellipse 2 2 124 124))
128 128)) 128 128))
(flomap->bitmap (flomap-pin fm 0 0 circle-fm 64 64)) (flomap->bitmap (flomap-pin fm 0 0 circle-fm 64 64))
(flomap->bitmap (flomap-pin sine-fm 50 0 sine-fm))] (flomap->bitmap (flomap-pin sine-fm 50 0 sine-fm))]
@ -795,9 +868,10 @@ See @racket[flomap-pin] and @racket[flomap-pin*] for implementation details.
@defproc[(flomap-hc-append [fm0 flomap] [fm flomap] ...) flomap] @defproc[(flomap-hc-append [fm0 flomap] [fm flomap] ...) flomap]
@defproc[(flomap-hb-append [fm0 flomap] [fm flomap] ...) flomap]{ @defproc[(flomap-hb-append [fm0 flomap] [fm flomap] ...) flomap]{
These create a new flomap by spatially appending the flomaps in the argument list. These create a new flomap by spatially appending the flomaps in the argument list.
The two-letter abbreviation determines direction (@racket[v] or @racket[h]) and alignment (@racket[l], @racket[c], @racket[r], or @racket[t], @racket[c], @racket[b]). The two-letter abbreviation determines direction (@racketid[v] or @racketid[h]) and alignment (@racketid[l], @racketid[c], @racketid[r], or @racketid[t], @racketid[c], @racketid[b]).
@examples[#:eval flomap-eval @examples[#:eval flomap-eval
(flomap->bitmap (flomap-ht-append circle-fm fm circle-fm))] (flomap->bitmap (flomap-ht-append circle-fm fm
(flomap-scale circle-fm 1/2)))]
See @racket[flomap-pin] and @racket[flomap-pin*] for implementation details. See @racket[flomap-pin] and @racket[flomap-pin*] for implementation details.
} }