#lang racket/base ;; Renderers for contour lines and contour intervals (require racket/contract racket/class racket/match racket/list racket/flonum racket/vector "../common/math.rkt" "../common/draw.rkt" "../common/marching-squares.rkt" "../common/contract.rkt" "../common/contract-doc.rkt" "../common/legend.rkt" "../common/sample.rkt" "../common/parameters.rkt" "../common/ticks.rkt" "../common/vector.rkt" "renderer.rkt" "sample.rkt") (provide contours contour-intervals) ;; =================================================================================================== ;; Contour lines (define ((contours-render-proc f levels samples colors widths styles alphas label) area) (let/ec return (define-values (x-min x-max y-min y-max) (send area get-bounds)) (match-define (list xs ys zss) (f x-min x-max samples y-min y-max samples)) (define-values (z-min z-max) (let ([zs (filter regular? (2d-sample->list zss))]) (when (empty? zs) (return empty)) (values (apply min* zs) (apply max* zs)))) (define zs (cond [(list? levels) levels] [(eq? levels 'auto) (auto-contour-zs z-min z-max)] [else (linear-seq z-min z-max levels #:start? #f #:end? #f)])) (define cs (maybe-apply/list colors zs)) (define ws (maybe-apply/list widths zs)) (define ss (maybe-apply/list styles zs)) (define as (maybe-apply/list alphas zs)) (for ([z (in-list zs)] [color (in-cycle cs)] [width (in-cycle ws)] [style (in-cycle ss)] [alpha (in-cycle as)]) (send area set-alpha alpha) (send area set-pen color width style) (for ([ya (in-list ys)] [yb (in-list (rest ys))] [zs0 (in-vector zss)] [zs1 (in-vector zss 1)] #:when #t [xa (in-list xs)] [xb (in-list (rest xs))] [z1 (in-vector zs0)] [z2 (in-vector zs0 1)] [z3 (in-vector zs1 1)] [z4 (in-vector zs1)]) (for/list ([line (in-list (heights->lines (exact->inexact z) (exact->inexact z1) (exact->inexact z2) (exact->inexact z3) (exact->inexact z4)))]) (match-define (vector x1 y1 x2 y2) (scale-normalized-line line xa xb ya yb)) (send area put-line (vector x1 y1) (vector x2 y2))))) (cond [label (line-legend-entries label zs colors widths styles)] [else empty]))) (defproc (contours [f (real? real? . -> . real?)] [x-min (or/c real? #f) #f] [x-max (or/c real? #f) #f] [y-min (or/c real? #f) #f] [y-max (or/c real? #f) #f] [#:levels levels (or/c 'auto exact-positive-integer? (listof real?)) (contour-levels)] [#:samples samples (and/c exact-integer? (>=/c 2)) (contour-samples)] [#:colors colors plot-colors/c (contour-colors)] [#:widths widths pen-widths/c (contour-widths)] [#:styles styles plot-pen-styles/c (contour-styles)] [#:alphas alphas alphas/c (contour-alphas)] [#:label label (or/c string? #f) #f] ) renderer2d? (define g (2d-function->sampler f)) (renderer2d (contours-render-proc g levels samples colors widths styles alphas label) default-2d-ticks-fun null-2d-bounds-fun x-min x-max y-min y-max)) ;; =================================================================================================== ;; Contour intervals (define ((contour-intervals-render-proc f levels samples colors styles contour-colors contour-widths contour-styles alphas label) area) (let/ec return (define-values (x-min x-max y-min y-max) (send area get-bounds)) (match-define (list xs ys zss) (f x-min x-max samples y-min y-max samples)) (define-values (z-min z-max) (let ([flat-zs (filter regular? (2d-sample->list zss))]) (when (empty? flat-zs) (return empty)) (values (apply min* flat-zs) (apply max* flat-zs)))) (define contour-zs (cond [(list? levels) levels] [(eq? levels 'auto) (auto-contour-zs z-min z-max)] [else (linear-seq z-min z-max levels #:start? #f #:end? #f)])) (define zs (append (list z-min) contour-zs (list z-max))) (define cs (map ->brush-color (maybe-apply/list colors zs))) (define fss (map ->brush-style (maybe-apply/list styles zs))) (define pss (map (λ (fill-style) (if (eq? fill-style 'solid) 'solid 'transparent)) fss)) (define as (maybe-apply/list alphas zs)) (for ([za (in-list zs)] [zb (in-list (rest zs))] [color (in-cycle cs)] [fill-style (in-cycle fss)] [poly-line-style (in-cycle pss)] [alpha (in-cycle as)]) (define polys (append* (for/list ([ya (in-list ys)] [yb (in-list (rest ys))] [zs0 (in-vector zss)] [zs1 (in-vector zss 1)] #:when #t [xa (in-list xs)] [xb (in-list (rest xs))] [z1 (in-vector zs0)] [z2 (in-vector zs0 1)] [z3 (in-vector zs1 1)] [z4 (in-vector zs1)]) (for/list ([poly (in-list (heights->mid-polys (exact->inexact za) (exact->inexact zb) (exact->inexact z1) (exact->inexact z2) (exact->inexact z3) (exact->inexact z4)))]) (cond [(equal? poly 'full) (list (vector xa ya) (vector xa yb) (vector xb yb) (vector xb ya))] [else (map (λ (v) (vector-take v 2)) (scale-normalized-poly poly xa xb ya yb))]))))) (define (draw-polys) (for ([poly (in-list polys)]) (send area put-polygon poly))) (cond [(= alpha 1) (send area set-pen color 1 poly-line-style) (send area set-brush color fill-style) (send area set-alpha 1) (draw-polys)] [else ;; draw the outlines with reduced alpha first (send area set-pen color 1 poly-line-style) (send area set-brush color 'transparent) (send area set-alpha (alpha-expt alpha 1/8)) (draw-polys) ;; now draw the centers (send area set-pen color 1 'transparent) (send area set-brush color fill-style) (send area set-alpha alpha) (draw-polys)])) ((contours-render-proc f levels samples contour-colors contour-widths contour-styles alphas #f) area) (cond [label (contour-intervals-legend-entries label z-min z-max contour-zs cs fss cs '(1) pss contour-colors contour-widths contour-styles)] [else empty]))) (defproc (contour-intervals [f (real? real? . -> . real?)] [x-min (or/c real? #f) #f] [x-max (or/c real? #f) #f] [y-min (or/c real? #f) #f] [y-max (or/c real? #f) #f] [#:levels levels (or/c 'auto exact-positive-integer? (listof real?)) (contour-levels)] [#:samples samples (and/c exact-integer? (>=/c 2)) (contour-samples)] [#:colors colors plot-colors/c (contour-interval-colors)] [#:styles styles plot-brush-styles/c (contour-interval-styles)] [#:contour-colors contour-colors plot-colors/c (contour-colors)] [#:contour-widths contour-widths pen-widths/c (contour-widths)] [#:contour-styles contour-styles plot-pen-styles/c (contour-styles)] [#:alphas alphas alphas/c (contour-interval-alphas)] [#:label label (or/c string? #f) #f] ) renderer2d? (define g (2d-function->sampler f)) (renderer2d (contour-intervals-render-proc g levels samples colors styles contour-colors contour-widths contour-styles alphas label) default-2d-ticks-fun null-2d-bounds-fun x-min x-max y-min y-max))