From 723a54a4d9f867351507a3beeda402e22cb3a2a1 Mon Sep 17 00:00:00 2001 From: Neil Toronto Date: Fri, 11 Nov 2011 18:11:52 -0700 Subject: [PATCH] Added "Axis Transforms and Ticks" doc page --- collects/plot/common/axis-transform.rkt | 67 ++-- collects/plot/common/parameters.rkt | 50 +-- collects/plot/common/plot-element.rkt | 10 +- collects/plot/common/samplers.rkt | 4 +- collects/plot/common/ticks.rkt | 220 +++++----- collects/plot/compat.rkt | 12 +- collects/plot/contracted/parameters.rkt | 5 - collects/plot/contracted/ticks.rkt | 9 +- collects/plot/main.rkt | 4 +- collects/plot/plot2d/decoration.rkt | 2 +- collects/plot/plot2d/rectangle.rkt | 14 +- collects/plot/plot3d/rectangle.rkt | 2 +- collects/plot/scribblings/contracts.scrbl | 7 + collects/plot/scribblings/custom.scrbl | 6 +- collects/plot/scribblings/params.scrbl | 56 +-- collects/plot/scribblings/plot.scrbl | 2 + collects/plot/scribblings/ticks.scrbl | 468 ++++++++++++++++++++++ collects/plot/scribblings/utils.scrbl | 103 ++--- collects/plot/tests/plot2d-tests.rkt | 9 +- collects/plot/tests/tick-tests.rkt | 19 +- 20 files changed, 752 insertions(+), 317 deletions(-) create mode 100644 collects/plot/scribblings/ticks.scrbl diff --git a/collects/plot/common/axis-transform.rkt b/collects/plot/common/axis-transform.rkt index e2a8db90cd..b48b424fd8 100644 --- a/collects/plot/common/axis-transform.rkt +++ b/collects/plot/common/axis-transform.rkt @@ -15,23 +15,24 @@ [(invertible-function f2 g2) f2]) (invertible-function (compose f1 f2) (compose g2 g1)))) -(defproc (invertible-inverse [h invertible-function?]) invertible-function? +(defproc (invertible-inverse [h invertible-function?]) invertible-function? #:document-body (match-define (invertible-function f g) h) (invertible-function g f)) (defcontract axis-transform/c (real? real? invertible-function? . -> . invertible-function?)) -(defproc (id-transform [x-min real?] [x-max real?] [old-function invertible-function?] - ) invertible-function? - old-function) +(defthing id-transform axis-transform/c + (λ (x-min x-max old-function) old-function)) (defthing id-function invertible-function? (invertible-function (λ (x) x) (λ (x) x))) -(defproc (apply-axis-transform [t axis-transform/c] [x-min real?] [x-max real?]) invertible-function? +(defproc (apply-axis-transform [t axis-transform/c] [x-min real?] [x-max real?] + ) invertible-function? #:document-body (t x-min x-max id-function)) ;; Turns any total, surjective, monotone real function and its inverse into an axis transform -(defproc (make-axis-transform [f (real? . -> . real?)] [g (real? . -> . real?)]) axis-transform/c +(defproc (make-axis-transform [fun invertible-function?]) axis-transform/c + (match-define (invertible-function f g) fun) (λ (x-min x-max old-function) (define fx-min (f x-min)) (define fx-scale (/ (- x-max x-min) (- (f x-max) fx-min))) @@ -46,24 +47,28 @@ (λ (x-min x-max old-function) (t1 x-min x-max (t2 x-min x-max old-function)))) -(defproc (axis-transform-append [t1 axis-transform/c] [t2 axis-transform/c] [x-mid real?] +(defproc (axis-transform-append [t1 axis-transform/c] [t2 axis-transform/c] [mid real?] ) axis-transform/c (λ (x-min x-max old-function) (match-define (invertible-function old-f old-g) old-function) - (let ([x-mid (old-f x-mid)]) - (cond [(x-mid . >= . x-max) (t1 x-min x-max old-function)] - [(x-mid . <= . x-min) (t2 x-min x-max old-function)] + (let ([mid (old-f mid)]) + (cond [(mid . >= . x-max) (t1 x-min x-max old-function)] + [(mid . <= . x-min) (t2 x-min x-max old-function)] [else - (match-define (invertible-function f1 g1) (t1 x-min x-mid old-function)) - (match-define (invertible-function f2 g2) (t2 x-mid x-max old-function)) - ((make-axis-transform (λ (x) (cond [((old-f x) . < . x-mid) (f1 x)] - [else (f2 x)])) - (λ (x) (cond [(x . < . x-mid) (g1 x)] - [else (g2 x)]))) + (match-define (invertible-function f1 g1) (t1 x-min mid old-function)) + (match-define (invertible-function f2 g2) (t2 mid x-max old-function)) + ((make-axis-transform + (invertible-function + (λ (x) (cond [((old-f x) . < . mid) (f1 x)] + [else (f2 x)])) + (λ (x) (cond [(x . < . mid) (g1 x)] + [else (g2 x)])))) x-min x-max id-function)])))) -(defproc (axis-transform-bound [t axis-transform/c] [x-min real?] [x-max real?]) axis-transform/c - (axis-transform-append (axis-transform-append id-transform t x-min) id-transform x-max)) +(defproc (axis-transform-bound [t axis-transform/c] [a real?] [b real?] + ) axis-transform/c #:document-body + (axis-transform-append + (axis-transform-append id-transform t a) id-transform b)) ;; =================================================================================================== ;; Specific axis transforms @@ -107,20 +112,21 @@ (define (real-exp x) (flexp (exact->inexact x))) -(defproc (log-transform [x-min real?] [x-max real?] [old-function invertible-function?] - ) invertible-function? - (when ((exact->inexact x-min) . <= . 0) - (raise-type-error 'log-transform "positive real" 0 x-min x-max)) - ((make-axis-transform real-log real-exp) x-min x-max old-function)) +(defthing log-transform axis-transform/c + (λ (x-min x-max old-function) + (when ((exact->inexact x-min) . <= . 0) + (raise-type-error 'log-transform "positive real" 0 x-min x-max)) + ((make-axis-transform (invertible-function real-log real-exp)) x-min x-max old-function))) -(defproc (cbrt-transform [x-min real?] [x-max real?] [old-function invertible-function?] - ) invertible-function? - ((make-axis-transform cbrt cube) x-min x-max old-function)) +(defthing cbrt-transform axis-transform/c + (λ (x-min x-max old-function) + ((make-axis-transform (invertible-function cbrt cube)) x-min x-max old-function))) (defproc (hand-drawn-transform [freq (>/c 0)]) axis-transform/c (λ (x-min x-max old-function) (define d (/ freq (- x-max x-min))) - ((make-axis-transform (sine-diag d) (sine-diag-inv d)) x-min x-max old-function))) + ((make-axis-transform (invertible-function (sine-diag d) (sine-diag-inv d))) + x-min x-max old-function))) ;; =================================================================================================== @@ -132,8 +138,7 @@ [(x . > . b) (+ (- x d) ds)] [else (+ a (* (- x a) s))]))) -(defproc (stretch-transform [a real?] [b real?] [scale (and/c real? (not/c (=/c 0)))] - ) axis-transform/c +(defproc (stretch-transform [a real?] [b real?] [scale (>/c 0)]) axis-transform/c (when (a . > . b) (error 'stretch-transform "expected a <= b; given ~e and ~e" a b)) (λ (x-min x-max old-function) (match-define (invertible-function old-f old-g) old-function) @@ -141,7 +146,7 @@ [b (old-f b)]) (define f (stretch a b scale)) (define g (stretch (f a) (f b) (/ 1 scale))) - ((make-axis-transform f g) x-min x-max old-function)))) + ((make-axis-transform (invertible-function f g)) x-min x-max old-function)))) (defproc (collapse-transform [a real?] [b real?]) axis-transform/c (when (a . > . b) (error 'stretch-transform "expected a <= b; given ~e and ~e" a b)) @@ -157,4 +162,4 @@ (define (g x) (cond [(x . < . center) (- x 1/2size)] [(x . > . center) (+ x 1/2size)] [else center])) - ((make-axis-transform f g) x-min x-max old-function)))) + ((make-axis-transform (invertible-function f g)) x-min x-max old-function)))) diff --git a/collects/plot/common/parameters.rkt b/collects/plot/common/parameters.rkt index 5cd7f28e30..a5830baeb1 100644 --- a/collects/plot/common/parameters.rkt +++ b/collects/plot/common/parameters.rkt @@ -41,16 +41,6 @@ (defparam plot-y-far-axis? boolean? #t) (defparam plot-z-far-axis? boolean? #t) -(defparam plot-x-max-ticks exact-positive-integer? 5) -(defparam plot-y-max-ticks exact-positive-integer? 5) -(defparam plot-z-max-ticks exact-positive-integer? 8) -(defparam plot-d-max-ticks exact-positive-integer? 6) -(defparam plot-r-max-ticks exact-positive-integer? 8) - -(defparam plot-x-far-max-ticks exact-positive-integer? 5) -(defparam plot-y-far-max-ticks exact-positive-integer? 5) -(defparam plot-z-far-max-ticks exact-positive-integer? 8) - (defparam plot-decorations? boolean? #t) (define-parameter-group plot-axes? @@ -59,14 +49,6 @@ plot-z-axis? plot-z-far-axis?) #:struct list) -(define-parameter-group plot-max-ticks - (plot-x-max-ticks plot-x-far-max-ticks - plot-y-max-ticks plot-y-far-max-ticks - plot-z-max-ticks plot-z-far-max-ticks - plot-d-max-ticks - plot-r-max-ticks) - #:struct list) - (define-parameter-group plot-appearance (plot-width plot-height @@ -75,7 +57,7 @@ plot-line-width plot-tick-size plot-font-size plot-font-family plot-legend-anchor plot-legend-box-alpha - plot-axes? plot-max-ticks plot-decorations? + plot-axes? plot-decorations? plot-animating?)) (defproc (pen-gap) real? #:document-body @@ -135,9 +117,9 @@ (defparam plot-x-ticks ticks? (linear-ticks)) (defparam plot-y-ticks ticks? (linear-ticks)) -(defparam plot-z-ticks ticks? (linear-ticks)) -(defparam plot-d-ticks ticks? (linear-ticks #:divisors '(1 2 4 5))) -(defparam plot-r-ticks ticks? (linear-ticks)) +(defparam plot-z-ticks ticks? (linear-ticks #:number 8)) +(defparam plot-d-ticks ticks? (linear-ticks #:number 6 #:divisors '(1 2 4 5))) +(defparam plot-r-ticks ticks? (linear-ticks #:number 8)) (defparam plot-x-far-ticks ticks? (ticks-mimic plot-x-ticks)) (defparam plot-y-far-ticks ticks? (ticks-mimic plot-y-ticks)) @@ -151,30 +133,6 @@ (define-parameter-group plot-axes (plot-x-axis plot-y-axis plot-z-axis plot-d-ticks plot-r-ticks) #:struct list) -(defproc (default-x-ticks [x-min real?] [x-max real?]) (listof tick?) #:document-body - ((plot-x-ticks) x-min x-max (plot-x-max-ticks))) - -(defproc (default-y-ticks [y-min real?] [y-max real?]) (listof tick?) #:document-body - ((plot-y-ticks) y-min y-max (plot-y-max-ticks))) - -(defproc (default-z-ticks [z-min real?] [z-max real?]) (listof tick?) #:document-body - ((plot-z-ticks) z-min z-max (plot-z-max-ticks))) - -(defproc (default-d-ticks [d-min real?] [d-max real?]) (listof tick?) #:document-body - ((plot-d-ticks) d-min d-max (plot-d-max-ticks))) - -(defproc (default-r-ticks [r-min real?] [r-max real?]) (listof tick?) #:document-body - ((plot-r-ticks) r-min r-max (plot-r-max-ticks))) - -(defproc (default-x-far-ticks [x-min real?] [x-max real?]) (listof tick?) #:document-body - ((plot-x-far-ticks) x-min x-max (plot-x-far-max-ticks))) - -(defproc (default-y-far-ticks [y-min real?] [y-max real?]) (listof tick?) #:document-body - ((plot-y-far-ticks) y-min y-max (plot-y-far-max-ticks))) - -(defproc (default-z-far-ticks [z-min real?] [z-max real?]) (listof tick?) #:document-body - ((plot-z-far-ticks) z-min z-max (plot-z-far-max-ticks))) - ;; =================================================================================================== (define-parameter-group plot-parameters diff --git a/collects/plot/common/plot-element.rkt b/collects/plot/common/plot-element.rkt index b96bc4dfe6..1460f5cee6 100644 --- a/collects/plot/common/plot-element.rkt +++ b/collects/plot/common/plot-element.rkt @@ -25,12 +25,12 @@ (λ (r) (match r [(vector (ivl xa xb) (ivl ya yb)) - (values (default-x-ticks xa xb) (default-x-far-ticks xa xb) - (default-y-ticks ya yb) (default-y-far-ticks ya yb))] + (values ((plot-x-ticks) xa xb) ((plot-x-far-ticks) xa xb) + ((plot-y-ticks) ya yb) ((plot-y-far-ticks) ya yb))] [(vector (ivl xa xb) (ivl ya yb) (ivl za zb)) - (values (default-x-ticks xa xb) (default-x-far-ticks xa xb) - (default-y-ticks ya yb) (default-y-far-ticks ya yb) - (default-z-ticks za zb) (default-z-far-ticks za zb))] + (values ((plot-x-ticks) xa xb) ((plot-x-far-ticks) xa xb) + ((plot-y-ticks) ya yb) ((plot-y-far-ticks) ya yb) + ((plot-z-ticks) za zb) ((plot-z-far-ticks) za zb))] [_ (raise-type-error 'default-ticks-fun "2- or 3-vector of ivl" r)]))) (defproc (function-bounds-fun [f sampler/c] [samples exact-nonnegative-integer?]) bounds-fun/c diff --git a/collects/plot/common/samplers.rkt b/collects/plot/common/samplers.rkt index 9334b24575..e4c05b1c1f 100644 --- a/collects/plot/common/samplers.rkt +++ b/collects/plot/common/samplers.rkt @@ -29,7 +29,7 @@ (define epsilon (expt 10 (- (digits-for-range z-min z-max)))) (match-define (ticks layout format) (plot-z-ticks)) (define ts - (cond [(eq? levels 'auto) (filter pre-tick-major? (layout z-min z-max (plot-z-max-ticks)))] + (cond [(eq? levels 'auto) (filter pre-tick-major? (layout z-min z-max))] [else (define zs (cond [(list? levels) (filter (λ (z) (<= z-min z z-max)) levels)] [else (linear-seq z-min z-max levels #:start? #f #:end? #f)])) (map (λ (z) (pre-tick z #t)) zs)])) @@ -56,7 +56,7 @@ (define epsilon (expt 10 (- (digits-for-range d-min d-max)))) (match-define (ticks layout format) (plot-d-ticks)) (define ts - (cond [(eq? levels 'auto) (filter pre-tick-major? (layout d-min d-max (plot-d-max-ticks)))] + (cond [(eq? levels 'auto) (filter pre-tick-major? (layout d-min d-max))] [else (define ds (cond [(list? levels) (filter (λ (d) (<= d-min d d-max)) levels)] [else (linear-seq d-min d-max levels #:start? #f #:end? #f)])) (map (λ (d) (pre-tick d #t)) ds)])) diff --git a/collects/plot/common/ticks.rkt b/collects/plot/common/ticks.rkt index 0b23ac2e4b..b6c21af339 100644 --- a/collects/plot/common/ticks.rkt +++ b/collects/plot/common/ticks.rkt @@ -20,17 +20,16 @@ (struct ticks (layout format) #:transparent #:property prop:procedure - (λ (t x-min x-max max-ticks) + (λ (t x-min x-max) (match-define (ticks layout format) t) - (define ts (layout x-min x-max max-ticks)) + (define ts (layout x-min x-max)) (match-define (list (pre-tick xs majors) ...) ts) (map tick xs majors (format x-min x-max ts)))) -(defcontract ticks-layout/c - (real? real? exact-positive-integer? . -> . (listof pre-tick?))) +(defcontract ticks-layout/c (real? real? . -> . (listof pre-tick?))) +(defcontract ticks-format/c (real? real? (listof pre-tick?) . -> . (listof string?))) -(defcontract ticks-format/c - (real? real? (listof pre-tick?) . -> . (listof string?))) +(defparam ticks-default-number exact-positive-integer? 5) ;; =================================================================================================== ;; Helpers @@ -108,11 +107,12 @@ (define minor-xs (linear-minor-values/step major-xs step (- n 1))) (values major-xs (filter (λ (x) (<= x-min x x-max)) minor-xs)))) -(defproc (linear-ticks-layout [#:base base (and/c exact-integer? (>=/c 2)) 10] +(defproc (linear-ticks-layout [#:number number exact-positive-integer? (ticks-default-number)] + [#:base base (and/c exact-integer? (>=/c 2)) 10] [#:divisors divisors (listof exact-positive-integer?) '(1 2 5)] ) ticks-layout/c - (λ (x-min x-max max-ticks) - (define-values (major-xs minor-xs) (linear-tick-values x-min x-max max-ticks base divisors)) + (λ (x-min x-max) + (define-values (major-xs minor-xs) (linear-tick-values x-min x-max number base divisors)) (tick-values->pre-ticks major-xs minor-xs))) (defproc (linear-ticks-format) ticks-format/c @@ -123,32 +123,41 @@ (for/list ([t (in-list ts)]) (real->plot-label (pre-tick-value t) digits))))) -(defproc (linear-ticks [#:base base (and/c exact-integer? (>=/c 2)) 10] - [#:divisors divisors (listof exact-positive-integer?) '(1 2 5)]) ticks? - (ticks (linear-ticks-layout #:base base #:divisors divisors) +(defproc (linear-ticks [#:number number exact-positive-integer? (ticks-default-number)] + [#:base base (and/c exact-integer? (>=/c 2)) 10] + [#:divisors divisors (listof exact-positive-integer?) '(1 2 5)] + ) ticks? #:document-body + (ticks (linear-ticks-layout #:number number #:base base + #:divisors divisors) (linear-ticks-format))) ;; =================================================================================================== ;; No ticks -(defproc (no-ticks-layout) ticks-layout/c - (λ (x-min x-max max-ticks) empty)) +(defthing no-ticks-layout ticks-layout/c #:document-value + (λ (x-min x-max) empty)) -(defproc (no-ticks) ticks? - (ticks (no-ticks-layout) (linear-ticks-format))) +(defthing no-ticks-format ticks-format/c #:document-value + (λ (x-min x-max pre-ticks) + (map (λ (_) "") pre-ticks))) + +(defthing no-ticks ticks? #:document-value + (ticks no-ticks-layout no-ticks-format)) ;; =================================================================================================== ;; Exponential ticks (for log scale) -(defproc (log-ticks-layout [#:base base (and/c exact-integer? (>=/c 2)) 10]) ticks-layout/c - (λ (x-min x-max max-ticks) +(defproc (log-ticks-layout [#:number number exact-positive-integer? (ticks-default-number)] + [#:base base (and/c exact-integer? (>=/c 2)) 10] + ) ticks-layout/c + (λ (x-min x-max) (with-exact-bounds x-min x-max (when ((exact->inexact x-min) . <= . 0) (raise-type-error 'log-ticks-layout "positive real" 0 x-min x-max)) (define log-start (ceiling-log/base base x-min)) (define log-end (floor-log/base base x-max)) - (define skip (max 1 (ceiling (/ (+ 1 (- log-end log-start)) max-ticks)))) + (define skip (max 1 (ceiling (/ (+ 1 (- log-end log-start)) number)))) (filter (λ (t) (<= x-min (pre-tick-value t) x-max)) (append* (for/list ([log-x (in-range (- log-start 1) (+ log-end 2))] @@ -177,8 +186,10 @@ (real->plot-label (/ x (expt base log-x)) base-digits) (major-str))]))))) -(defproc (log-ticks [#:base base (and/c exact-integer? (>=/c 2)) 10]) ticks? - (ticks (log-ticks-layout #:base base) +(defproc (log-ticks [#:number number exact-positive-integer? (ticks-default-number)] + [#:base base (and/c exact-integer? (>=/c 2)) 10] + ) ticks? #:document-body + (ticks (log-ticks-layout #:number number #:base base) (log-ticks-format #:base base))) ;; =================================================================================================== @@ -213,48 +224,40 @@ ;; =================================================================================================== ;; Date ticks -(define 12h-descending-date-ticks-formats - '("~Y-~m-~d ~I:~M:~f ~p" - "~Y-~m-~d ~I:~M ~p" - "~Y-~m-~d ~I ~p" - "~Y-~m-~d" - "~Y-~m" - "~Y" - - "~m-~d ~I:~M:~f ~p" - "~m-~d ~I:~M ~p" - "~m-~d ~I ~p" - "~m-~d" - - "~I:~M:~f ~p" - "~I:~M ~p" - "~I ~p" - - "~M:~fs" - "~Mm" - - "~fs")) - -(define 24h-descending-date-ticks-formats +(defthing 24h-descending-date-ticks-formats (listof string?) #:document-value '("~Y-~m-~d ~H:~M:~f" "~Y-~m-~d ~H:~M" "~Y-~m-~d ~Hh" "~Y-~m-~d" "~Y-~m" "~Y" - "~m-~d ~H:~M:~f" "~m-~d ~H:~M" "~m-~d ~Hh" "~m-~d" - "~H:~M:~f" "~H:~M" "~Hh" - "~M:~fs" "~Mm" - + "~fs")) + +(defthing 12h-descending-date-ticks-formats (listof string?) #:document-value + '("~Y-~m-~d ~I:~M:~f ~p" + "~Y-~m-~d ~I:~M ~p" + "~Y-~m-~d ~I ~p" + "~Y-~m-~d" + "~Y-~m" + "~Y" + "~m-~d ~I:~M:~f ~p" + "~m-~d ~I:~M ~p" + "~m-~d ~I ~p" + "~m-~d" + "~I:~M:~f ~p" + "~I:~M ~p" + "~I ~p" + "~M:~fs" + "~Mm" "~fs")) (defparam date-ticks-formats (listof string?) 24h-descending-date-ticks-formats) @@ -316,9 +319,10 @@ (define major-xs (linear-major-values/step x-min x-max step)) (values (map date-round major-xs) empty))) -(defproc (date-ticks-layout) ticks-layout/c - (λ (x-min x-max max-ticks) - (define-values (major-xs minor-xs) (date-tick-values x-min x-max max-ticks)) +(defproc (date-ticks-layout [#:number number exact-positive-integer? (ticks-default-number)] + ) ticks-layout/c + (λ (x-min x-max) + (define-values (major-xs minor-xs) (date-tick-values x-min x-max number)) (tick-values->pre-ticks major-xs minor-xs))) (defproc (date-ticks-format [#:formats formats (listof string?) (date-ticks-formats)]) ticks-format/c @@ -336,29 +340,40 @@ (define fmt-list (choose-format-list formatter fmt-lists (list last-x x))) (string-append* (apply-formatter formatter fmt-list x))))])))) -(defproc (date-ticks [#:formats formats (listof string?) (date-ticks-formats)]) ticks? - (ticks (date-ticks-layout) +(defproc (date-ticks [#:number number exact-positive-integer? (ticks-default-number)] + [#:formats formats (listof string?) (date-ticks-formats)] + ) ticks? #:document-body + (ticks (date-ticks-layout #:number number) (date-ticks-format #:formats formats))) ;; =================================================================================================== ;; Time ticks -(define descending-time-ticks-formats +(defthing 24h-descending-time-ticks-formats (listof string?) #:document-value '("~dd ~H:~M:~f" "~dd ~H:~M" "~dd ~Hh" "~dd" - "~H:~M:~f" "~H:~M" "~Hh" - "~M:~fs" "~Mm" - "~fs")) -(defparam time-ticks-formats (listof string?) descending-time-ticks-formats) +(defthing 12h-descending-time-ticks-formats (listof string?) #:document-value + '("~dd ~I:~M:~f ~p" + "~dd ~I:~M ~p" + "~dd ~I ~p" + "~dd" + "~I:~M:~f ~p" + "~I:~M ~p" + "~I ~p" + "~M:~fs" + "~Mm" + "~fs")) + +(defparam time-ticks-formats (listof string?) 24h-descending-time-ticks-formats) ;; Tick steps to try, in seconds (define time-steps @@ -408,9 +423,10 @@ (define major-xs (linear-major-values/step x-min x-max step)) (values major-xs empty))) -(defproc (time-ticks-layout) ticks-layout/c - (λ (x-min x-max max-ticks) - (define-values (major-xs minor-xs) (time-tick-values x-min x-max max-ticks)) +(defproc (time-ticks-layout [#:number number exact-positive-integer? (ticks-default-number)] + ) ticks-layout/c + (λ (x-min x-max) + (define-values (major-xs minor-xs) (time-tick-values x-min x-max number)) (tick-values->pre-ticks major-xs minor-xs))) (defproc (time-ticks-format [#:formats formats (listof string?) (time-ticks-formats)]) ticks-format/c @@ -428,8 +444,10 @@ (define fmt-list (choose-format-list formatter fmt-lists (list last-x x))) (string-append* (apply-formatter formatter fmt-list x))))])))) -(defproc (time-ticks [#:formats formats (listof string?) (time-ticks-formats)]) ticks? - (ticks (time-ticks-layout) +(defproc (time-ticks [#:number number exact-positive-integer? (ticks-default-number)] + [#:formats formats (listof string?) (time-ticks-formats)] + ) ticks? #:document-body + (ticks (time-ticks-layout #:number number) (time-ticks-format #:formats formats))) ;; =================================================================================================== @@ -462,33 +480,36 @@ (define unit-x (/ (pre-tick-value t) unit)) (format format-str (real->plot-label unit-x digits #f)))))) -(defproc (bit/byte-ticks [#:size size (or/c 'byte 'bit) 'byte] - [#:kind kind (or/c 'CS 'SI) 'CS]) ticks? - (define layout - (case kind - [(SI) (linear-ticks-layout #:base 10 #:divisors '(1 2 5))] - [else (linear-ticks-layout #:base 2 #:divisors '(1 2))])) - (ticks layout (bit/byte-ticks-format #:size size #:kind kind))) +(defproc (bit/byte-ticks [#:number number exact-positive-integer? (ticks-default-number)] + [#:size size (or/c 'byte 'bit) 'byte] + [#:kind kind (or/c 'CS 'SI) 'CS] + ) ticks? #:document-body + (define si? (eq? kind 'SI)) + (ticks (linear-ticks-layout #:number number #:base (if si? 10 2) + #:divisors (if si? '(1 2 5) '(1 2))) + (bit/byte-ticks-format #:size size #:kind kind))) ;; =================================================================================================== ;; Currency ;; US "short scale" suffixes -(define us-currency-scales '("" "K" "M" "B" "T")) -;; The UK officially uses the short scale now -;; Million is abbreviated "m" instead of "mn" because "mn" stands for minutes; also, the Daily -;; Telegraph Style Guide totally says to use "m" -(define uk-currency-scales '("" "k" "m" "bn" "tr")) +(defthing us-currency-scales (listof string?) #:document-value '("" "K" "M" "B" "T")) +;; The UK officially uses the short scale since 1974 +;; Million is abbreviated "m" instead of "mn" because "mn" stands for minutes +(defthing uk-currency-scales (listof string?) #:document-value '("" "k" "m" "bn" "tr")) ;; European countries use the long scale: million, milliard, billion -(define eu-currency-scales '("" "K" "M" "Md" "B")) -;; The larger the scale suffixes get, the less standardized they are; so we stop at trillion (short) +(defthing eu-currency-scales (listof string?) #:document-value '("" "K" "M" "Md" "B")) +;; The larger the scale suffixes get, the less standardized they are; so we stop at billion (long) ;; US negative amounts are in parenthesis: -(define us-currency-formats '("~$~w.~f~s" "(~$~w.~f~s)" "~$0")) +(defthing us-currency-formats (list/c string? string? string?) #:document-value + '("~$~w.~f~s" "(~$~w.~f~s)" "~$0")) ;; The UK is more reasonable, using a negative sign for negative amounts: -(define uk-currency-formats '("~$~w.~f ~s" "-~$~w.~f ~s" "~$0")) +(defthing uk-currency-formats (list/c string? string? string?) #:document-value + '("~$~w.~f~s" "-~$~w.~f~s" "~$0")) ;; The more common EU format (e.g. France, Germany, Italy, Spain): -(define eu-currency-formats '("~w,~f ~s~$" "-~w,~f ~s~$" "0 ~$")) +(defthing eu-currency-formats (list/c string? string? string?) #:document-value + '("~w,~f ~s~$" "-~w,~f ~s~$" "0 ~$")) (defparam currency-ticks-scales (listof string?) us-currency-scales) (defparam currency-ticks-formats (list/c string? string? string?) us-currency-formats) @@ -546,22 +567,19 @@ (apply-formatter formatter format-list (amount-data sign whole frac unit suffix))))))) -(defproc (currency-ticks-layout) ticks-layout/c - (linear-ticks-layout #:base 10 #:divisors '(1 2 4 5))) - -(defproc (currency-ticks [#:kind kind (or/c string? symbol?) 'USD] +(defproc (currency-ticks [#:number number exact-positive-integer? (ticks-default-number)] + [#:kind kind (or/c string? symbol?) 'USD] [#:scales scales (listof string?) (currency-ticks-scales)] [#:formats formats (list/c string? string? string?) (currency-ticks-formats)] - ) ticks? - (ticks (currency-ticks-layout) - (currency-ticks-format #:kind kind #:scales scales #:formats formats))) + ) ticks? #:document-body + (ticks (linear-ticks-layout #:number number #:base 10 + #:divisors '(1 2 4 5)) + (currency-ticks-format #:kind kind #:scales scales + #:formats formats))) ;; =================================================================================================== ;; Fractions -(defparam fraction-ticks-base (and/c exact-integer? (>=/c 2)) 10) -(defparam fraction-ticks-divisors (listof exact-positive-integer?) '(1 2 3 4 5)) - (define (format-fraction x) (cond [(inexact? x) (format-fraction (inexact->exact x))] [(x . < . 0) (format "-~a" (format-fraction (- x)))] @@ -580,9 +598,9 @@ (for/list ([t (in-list ts)]) (format-fraction (pre-tick-value t))))) -(defproc (fraction-ticks [#:base base (and/c exact-integer? (>=/c 2)) (fraction-ticks-base)] - [#:divisors divisors (listof exact-positive-integer?) - (fraction-ticks-divisors)]) ticks? +(defproc (fraction-ticks [#:base base (and/c exact-integer? (>=/c 2)) 10] + [#:divisors divisors (listof exact-positive-integer?) '(1 2 3 4 5)] + ) ticks? #:document-body (ticks (linear-ticks #:base base #:divisors divisors) (fraction-ticks-format))) @@ -590,16 +608,14 @@ ;; Tick combinators (defproc (ticks-mimic [thunk (-> ticks?)]) ticks? - (ticks (λ (x-min x-max max-ticks) - ((ticks-layout (thunk)) x-min x-max max-ticks)) - (λ (x-min x-max ts) - ((ticks-format (thunk)) x-min x-max ts)))) + (ticks (λ (x-min x-max) ((ticks-layout (thunk)) x-min x-max)) + (λ (x-min x-max ts) ((ticks-format (thunk)) x-min x-max ts)))) (defproc (ticks-scale [t ticks?] [fun invertible-function?]) ticks? (match-define (invertible-function f g) fun) (match-define (ticks layout format) t) - (ticks (λ (x-min x-max max-ticks) - (define ts (layout (f x-min) (f x-max) max-ticks)) + (ticks (λ (x-min x-max) + (define ts (layout (f x-min) (f x-max))) (for/list ([t (in-list ts)]) (match-define (pre-tick x major?) t) (pre-tick (g x) major?))) @@ -611,13 +627,13 @@ (defproc (ticks-add [t ticks?] [xs (listof real?)] [major? boolean? #t]) ticks? (match-define (ticks layout format) t) - (ticks (λ (x-min x-max max-ticks) - (append (layout x-min x-max max-ticks) + (ticks (λ (x-min x-max) + (append (layout x-min x-max) (for/list ([x (in-list xs)]) (pre-tick x major?)))) format)) -(defproc (linear-scale [m real?] [b real? 0]) invertible-function? +(defproc (linear-scale [m real?] [b real? 0]) invertible-function? #:document-body (invertible-function (λ (x) (+ (* m x) b)) (λ (y) (/ (- y b) m)))) diff --git a/collects/plot/compat.rkt b/collects/plot/compat.rkt index 21a4a98af8..1f643a79a0 100644 --- a/collects/plot/compat.rkt +++ b/collects/plot/compat.rkt @@ -13,7 +13,7 @@ "plot3d/plot-area.rkt" (prefix-in new. (only-in "main.rkt" x-axis y-axis - default-x-ticks default-y-ticks default-z-ticks + plot-x-ticks plot-y-ticks plot-z-ticks points error-bars vector-field plot-title plot-x-label plot-y-label plot-z-label plot-foreground plot-background @@ -75,8 +75,8 @@ [#:lncolor lncolor (list/c byte? byte? byte?) '(255 0 0)] [#:out-file out-file (or/c path-string? output-port? #f) #f] ) (is-a?/c image-snip%) - (define x-ticks (new.default-x-ticks x-min x-max)) - (define y-ticks (new.default-y-ticks y-min y-max)) + (define x-ticks ((new.plot-x-ticks) x-min x-max)) + (define y-ticks ((new.plot-y-ticks) y-min y-max)) (define bounds-rect (vector (ivl x-min x-max) (ivl y-min y-max))) (parameterize ([new.plot-title title] @@ -117,9 +117,9 @@ [#:lncolor lncolor (list/c byte? byte? byte?) '(255 0 0)] [#:out-file out-file (or/c path-string? output-port? #f) #f] ) (is-a?/c image-snip%) - (define x-ticks (new.default-x-ticks x-min x-max)) - (define y-ticks (new.default-y-ticks y-min y-max)) - (define z-ticks (new.default-z-ticks z-min z-max)) + (define x-ticks ((new.plot-x-ticks) x-min x-max)) + (define y-ticks ((new.plot-y-ticks) y-min y-max)) + (define z-ticks ((new.plot-z-ticks) z-min z-max)) (define bounds-rect (vector (ivl x-min x-max) (ivl y-min y-max) (ivl z-min z-max))) (parameterize ([new.plot-title title] diff --git a/collects/plot/contracted/parameters.rkt b/collects/plot/contracted/parameters.rkt index 6ac723eb65..18b8f939d0 100644 --- a/collects/plot/contracted/parameters.rkt +++ b/collects/plot/contracted/parameters.rkt @@ -9,8 +9,6 @@ ;; General plot parameters plot-x-axis? plot-y-axis? plot-z-axis? plot-x-far-axis? plot-y-far-axis? plot-z-far-axis? - plot-x-max-ticks plot-y-max-ticks plot-z-max-ticks plot-d-max-ticks plot-r-max-ticks - plot-x-far-max-ticks plot-y-far-max-ticks plot-z-far-max-ticks plot-width plot-height plot-foreground plot-foreground-alpha plot-background plot-background-alpha @@ -68,14 +66,11 @@ ;; Functions pen-gap animated-samples - default-x-ticks default-y-ticks default-z-ticks default-d-ticks default-r-ticks - default-x-far-ticks default-y-far-ticks default-z-far-ticks default-contour-colors default-contour-fill-colors default-isosurface-colors default-isosurface-line-colors) ;; Parameter groups plot-parameters plot-axes? - plot-max-ticks plot-appearance plot3d-appearance plot-output diff --git a/collects/plot/contracted/ticks.rkt b/collects/plot/contracted/ticks.rkt index 5dab593683..f1ac65092b 100644 --- a/collects/plot/contracted/ticks.rkt +++ b/collects/plot/contracted/ticks.rkt @@ -8,18 +8,19 @@ (struct (tick pre-tick) ([value real?] [major? boolean?] [label string?])) (struct ticks ([layout ticks-layout/c] [format ticks-format/c]))) 24h-descending-date-ticks-formats 12h-descending-date-ticks-formats - descending-time-ticks-formats + 24h-descending-time-ticks-formats 12h-descending-time-ticks-formats us-currency-scales uk-currency-scales eu-currency-scales us-currency-formats uk-currency-formats eu-currency-formats ticks-layout/c ticks-format/c - (activate-contract-out ticks-mimic ticks-scale ticks-add linear-scale - no-ticks-layout no-ticks + no-ticks-layout no-ticks-format no-ticks + (activate-contract-out ticks-default-number + ticks-mimic ticks-scale ticks-add linear-scale linear-ticks-layout linear-ticks-format linear-ticks log-ticks-layout log-ticks-format log-ticks date-ticks-formats date-ticks-layout date-ticks-format date-ticks time-ticks-formats time-ticks-layout time-ticks-format time-ticks bit/byte-ticks-format bit/byte-ticks currency-ticks-scales currency-ticks-formats - currency-ticks-layout currency-ticks-format currency-ticks + currency-ticks-format currency-ticks fraction-ticks-format fraction-ticks collapse-ticks)) diff --git a/collects/plot/main.rkt b/collects/plot/main.rkt index 8743c29b73..95aa9f559a 100644 --- a/collects/plot/main.rkt +++ b/collects/plot/main.rkt @@ -12,9 +12,7 @@ (provide (struct-out ivl)) (require "contracted/axis-transform.rkt") -(provide axis-transform-compose axis-transform-append axis-transform-bound - id-transform log-transform cbrt-transform hand-drawn-transform - stretch-transform collapse-transform) +(provide (all-from-out "contracted/axis-transform.rkt")) (require "contracted/ticks.rkt") (provide (all-from-out "contracted/ticks.rkt")) diff --git a/collects/plot/plot2d/decoration.rkt b/collects/plot/plot2d/decoration.rkt index 657ed6e587..b5220d0dec 100644 --- a/collects/plot/plot2d/decoration.rkt +++ b/collects/plot/plot2d/decoration.rkt @@ -120,7 +120,7 @@ (define r-min (if (and (<= x-min 0 x-max) (<= y-min 0 y-max)) 0 (apply min corner-rs))) (define r-max (apply max corner-rs)) (define ts (filter (λ (t) (not (zero? (pre-tick-value t)))) - (default-r-ticks r-min r-max))) + ((plot-r-ticks) r-min r-max))) ;; Draw the tick lines (for ([t (in-list ts)]) (match-define (tick r major? label) t) diff --git a/collects/plot/plot2d/rectangle.rkt b/collects/plot/plot2d/rectangle.rkt index f175814cde..bf4ff6d7cc 100644 --- a/collects/plot/plot2d/rectangle.rkt +++ b/collects/plot/plot2d/rectangle.rkt @@ -96,14 +96,12 @@ (tick x #t (->plot-label cat)))]) (if far-ticks? (values empty ticks) (values ticks empty)))) (match-let* - ([(vector default-x-ticks default-y-ticks) - (maybe-invert default-x-ticks default-y-ticks)] - [(vector default-x-far-ticks default-y-far-ticks) - (maybe-invert default-x-far-ticks default-y-far-ticks)] - [(vector x-ticks y-ticks) - (maybe-invert x-ticks (default-y-ticks y-min y-max))] - [(vector x-far-ticks y-far-ticks) - (maybe-invert x-far-ticks (default-y-far-ticks y-min y-max))]) + ([(vector plot-x-ticks plot-y-ticks) (maybe-invert (plot-x-ticks) + (plot-y-ticks))] + [(vector plot-x-far-ticks plot-y-far-ticks) (maybe-invert (plot-x-far-ticks) + (plot-y-far-ticks))] + [(vector x-ticks y-ticks) (maybe-invert x-ticks (plot-y-ticks y-min y-max))] + [(vector x-far-ticks y-far-ticks) (maybe-invert x-far-ticks (plot-y-far-ticks y-min y-max))]) (values x-ticks x-far-ticks y-ticks y-far-ticks))) (defproc (discrete-histogram diff --git a/collects/plot/plot3d/rectangle.rkt b/collects/plot/plot3d/rectangle.rkt index 344069514c..2ed3f6a096 100644 --- a/collects/plot/plot3d/rectangle.rkt +++ b/collects/plot/plot3d/rectangle.rkt @@ -69,7 +69,7 @@ (if y-far-ticks? (values empty ts) (values ts empty)))) (values x-ticks x-far-ticks y-ticks y-far-ticks - (default-z-ticks z-min z-max) (default-z-far-ticks z-min z-max))) + ((plot-z-ticks) z-min z-max) ((plot-z-far-ticks) z-min z-max))) (define (adjust/gap i gap) (match-define (ivl x1 x2) i) diff --git a/collects/plot/scribblings/contracts.scrbl b/collects/plot/scribblings/contracts.scrbl index c55d501d79..f46d45d47e 100644 --- a/collects/plot/scribblings/contracts.scrbl +++ b/collects/plot/scribblings/contracts.scrbl @@ -8,6 +8,10 @@ @section{Convenience Contracts} +@doc-apply[contract/c]{ +Identifies @racket[contract?]s and predicates that can be used as contracts. +} + @doc-apply[treeof]{ Identifies trees of values that meet the contract @(racket ct). Used by @(racket plot) and @(racket plot3d) to construct the contract for a tree of @(racket renderer2d?) or @(racket renderer3d?). @@ -49,6 +53,9 @@ A list containing the symbols that are valid @(racket points) symbols. @section{Appearance Argument Sequence Contracts} +@doc-apply[maybe-function/c]{ +} + @doc-apply[plot-colors/c]{ The contract for @(racket #:colors) arguments, as in @(racket contours). If the contracted value is a function, it is intended to take a list of values, such as contour values, as input, and return a list of colors. diff --git a/collects/plot/scribblings/custom.scrbl b/collects/plot/scribblings/custom.scrbl index 19684bc3d3..6990e3ba4f 100644 --- a/collects/plot/scribblings/custom.scrbl +++ b/collects/plot/scribblings/custom.scrbl @@ -4,8 +4,4 @@ @title[#:tag "custom"]{Making Custom Plot Renderers} -@defmodule[plot/custom] - -Eventually, enough of the underlying PLoT API will be exposed that anyone can create new @tech{renderers}. -However, the underlying API still changes too often. -As soon as it settles, @racketmodname[plot/custom] will export it, and this page will document how to use it. +@declare-exporting[plot/utils] diff --git a/collects/plot/scribblings/params.scrbl b/collects/plot/scribblings/params.scrbl index 89654e4cbb..6b51c8faaf 100644 --- a/collects/plot/scribblings/params.scrbl +++ b/collects/plot/scribblings/params.scrbl @@ -31,42 +31,6 @@ The quality of JPEG images written by @(racket plot-file) and @(racket plot3d-fi If @(racket #t), @(racket plot-file) and @(racket plot3d-file) open a dialog when writing PostScript or PDF files. See @(racket post-script-dc%) and @(racket pdf-dc%). } -@section{Axis Transforms} - -@doc-apply[plot-x-transform] -@doc-apply[plot-y-transform] -@doc-apply[plot-z-transform]{ -Per-axis, nonlinear transforms. Set these, for example, to plot with log-scale axes. See @(racket log-transform). -} - -@doc-apply[id-transform]{ -The default transform for all axes. -} - -@doc-apply[log-transform]{ -A log transform. Use this to generate plots with log-scale axes. Any log-scaled axis must be on a positive interval. - -@examples[#:eval plot-eval - (parameterize ([plot-y-transform log-transform]) - (plot (function (λ (x) x) 0.01 1))) - (parameterize ([plot-x-transform log-transform]) - (plot (function (λ (x) x) -1 1)))] -} - -@doc-apply[cbrt-transform]{ -A "cube-root" transform. Unlike the log transform, it is defined on the entire real line, making it better for testing the appearance of plots with nonlinearly transformed axes. -} - -@doc-apply[hand-drawn-transform]{ -An @italic{extremely important} test case, which makes sure that @(plot-name) can use any monotone, invertible function as an axis transform. - -The @(racket freq) parameter controls the ``shakiness'' of the transform. At high values, it makes plots look like Peanuts cartoons. For example, - -@interaction[#:eval plot-eval (parameterize ([plot-x-transform (hand-drawn-transform 200)] - [plot-y-transform (hand-drawn-transform 200)]) - (plot (function sqr -1 1)))] -} - @section{General Appearance} @doc-apply[plot-foreground] @@ -90,6 +54,9 @@ See @(racket ->pen-color) and @(racket ->brush-color) for details on how PLoT in @doc-apply[plot-y-label] @doc-apply[plot-z-label]{The title and axis labels. A @(racket #f) value means the label is not drawn and takes no space. A @(racket "") value effectively means the label is not drawn, but it takes space. } +@doc-apply[plot-x-far-label] +@doc-apply[plot-y-far-label] +@doc-apply[plot-z-far-label] @doc-apply[plot-animating?]{ When @(racket #t), certain renderers draw simplified plots to speed up drawing. PLoT sets it to @(racket #t), for example, when a user is clicking and dragging a 3D plot to rotate it. @@ -168,17 +135,33 @@ When @(racket #t), certain renderers draw simplified plots to speed up drawing. @doc-apply[rectangle3d-line-width] @doc-apply[discrete-histogram-gap] +@doc-apply[discrete-histogram-skip] +@doc-apply[discrete-histogram-invert?] @section{Decorations} These parameters do not control the @italic{typical} appearance of plots. Instead, they control the look of renderers that add specific decorations, such as labeled points. +@doc-apply[x-axis-alpha] +@doc-apply[y-axis-alpha] +@doc-apply[z-axis-alpha] + +@doc-apply[x-axis-far?] +@doc-apply[y-axis-far?] +@doc-apply[z-axis-far?] + @doc-apply[x-axis-ticks?] @doc-apply[y-axis-ticks?] @doc-apply[z-axis-ticks?] +@doc-apply[x-axis-labels?] +@doc-apply[y-axis-labels?] +@doc-apply[z-axis-labels?] + @doc-apply[polar-axes-number] +@doc-apply[polar-axes-alpha] @doc-apply[polar-axes-ticks?] +@doc-apply[polar-axes-labels?] @doc-apply[label-anchor] @doc-apply[label-angle] @@ -220,6 +203,7 @@ Single isosurfaces (@(racket isosurface3d)) use surface parameters. Nested isosu @doc-apply[isosurface-levels] @doc-apply[isosurface-colors] +@doc-apply[isosurface-styles] @doc-apply[isosurface-line-colors] @doc-apply[isosurface-line-widths] @doc-apply[isosurface-line-styles] diff --git a/collects/plot/scribblings/plot.scrbl b/collects/plot/scribblings/plot.scrbl index 4c6dc0a0cf..28337f28a1 100644 --- a/collects/plot/scribblings/plot.scrbl +++ b/collects/plot/scribblings/plot.scrbl @@ -31,6 +31,8 @@ If you have code written for PLoT 5.1.3 or earlier, please see @secref["porting" @include-section["renderer3d.scrbl"] +@include-section["ticks.scrbl"] + @include-section["utils.scrbl"] @include-section["params.scrbl"] diff --git a/collects/plot/scribblings/ticks.scrbl b/collects/plot/scribblings/ticks.scrbl new file mode 100644 index 0000000000..fa8824e1c6 --- /dev/null +++ b/collects/plot/scribblings/ticks.scrbl @@ -0,0 +1,468 @@ +#lang scribble/manual + +@(require "common.rkt" (for-label racket/date db)) + +@declare-exporting[plot] + +@title[#:tag "ticks and transforms"]{Axis Transforms and Ticks} + +@section[#:tag "transforms"]{Axis Transforms} + +The @italic{x}, @italic{y} and @italic{z} axes for any plot can be independently transformed by parameterizing the plot on different @racket[plot-x-transform], @racket[plot-y-transform] and @racket[plot-z-transform] values. +For example, to plot the @italic{x} axis with a log transform: +@interaction[#:eval plot-eval + (parameterize ([plot-x-transform log-transform]) + (plot (function sin 1 100)))] +Most @racket[log-transform]ed plots use different ticks than the default, uniformly spaced ticks, however. +To put log ticks on the @italic{x} axis, set the @racket[plot-x-ticks] parameter: +@interaction[#:eval plot-eval + (parameterize ([plot-x-transform log-transform] + [plot-x-ticks (log-ticks)]) + (plot (function sin 1 100)))] +See @secref["ticks"] for more details on parameterizing a plot's axis ticks. + +@doc-apply[plot-x-transform] +@doc-apply[plot-y-transform] +@doc-apply[plot-z-transform]{ +Independent, per-axis, monotone, nonlinear transforms. PLoT comes with some typical (and some atypical) axis transforms, documented immediately below. +} + +@doc-apply[id-transform]{ +The identity axis transform, the default transform for all axes. +} + +@doc-apply[log-transform]{ +A log transform. Use this to generate plots with log-scale axes. Any such axis must have positive bounds. + +The beginning of the @secref["ticks and transforms"] section has a working example. An example of exceeding the bounds is +@interaction[#:eval plot-eval + (parameterize ([plot-x-transform log-transform]) + (plot (function (λ (x) x) -1 1)))] +See @racket[axis-transform-bound] and @racket[axis-transform-append] for ways to get around an axis transform's bounds limitations. +} + +@doc-apply[stretch-transform]{ +Returns an axis transform that stretches a finite interval. + +The following example uses a @racket[stretch-transform] to draw attention to the interval [-1,1] in an illustration of the limit of @italic{sin(x)/x} as @italic{x} approaches zero (a critical part of proving the derivative of @italic{sin(x)}): +@interaction[#:eval plot-eval + (parameterize ([plot-x-transform (stretch-transform -1 1 20)] + [plot-x-ticks (ticks-add (plot-x-ticks) '(-1 1))]) + (plot (list (y-axis -1 #:ticks? #f) (y-axis 1 #:ticks? #f) + (function (λ (x) (/ (sin x) x)) -14 14 + #:width 2 #:color 4 #:label "y = sin(x)/x") + (point-label (vector 0 1) "y → 1 as x → 0" + #:anchor 'bottom-right)) + #:y-max 1.2))] +} + +@doc-apply[collapse-transform]{ +Returns an axis transform that collapses a finite interval to its midpoint. +For example, to remove part of the long, boring asymptotic approach of @italic{atan(x)} toward π/2: +@interaction[#:eval plot-eval + (parameterize ([plot-x-transform (collapse-transform 50 150)]) + (plot (function atan 10 200 #:label "y = atan(x)") + #:legend-anchor 'center))] +In this case, there were already ticks at the collapsed interval's endpoints. +If there had not been, it would have been necessary to use @racket[ticks-add] to let viewers know precisely the interval that was collapsed. +(See @racket[stretch-transform] for an example.) +} + +@doc-apply[cbrt-transform]{ +A ``cube-root'' transform, mostly used for testing. +Unlike the log transform, it is defined on the entire real line, making it better for testing the appearance of plots with nonlinearly transformed axes. +} + +@doc-apply[hand-drawn-transform]{ +An @italic{extremely important} test case, which makes sure that @(plot-name) can use any monotone, invertible function as an axis transform. +The @(racket freq) parameter controls the ``shakiness'' of the transform. At high values, it makes plots look like Peanuts cartoons. +@examples[#:eval plot-eval + (parameterize ([plot-x-transform (hand-drawn-transform 200)] + [plot-y-transform (hand-drawn-transform 200)]) + (plot (function sqr -1 1))) + (parameterize ([plot-x-transform (hand-drawn-transform 50)] + [plot-y-transform (hand-drawn-transform 50)] + [plot-z-transform (hand-drawn-transform 50)]) + (plot3d (contour-intervals3d (λ (x y) (- (sqr x) (sqr y))) + -1 1 -1 1 #:samples 9)))] + +The last example shows that the transform is applied to the primitive shapes that comprise the plot (by recursive subdivision). +} + +@doc-apply[axis-transform/c]{ +The contract for axis transforms. + +The easiest ways to construct novel axis transforms are to use the axis transform combinators @racket[axis-transform-append], @racket[axis-transform-bound] and @racket[axis-transform-compose], or to apply @racket[make-axis-transform] to an @racket[invertible-function]. +} + +@doc-apply[axis-transform-append]{ +Returns an axis transform that transforms values less than @racket[mid] like @racket[t1], and transforms values greater than @racket[mid] like @racket[t2]. +(Whether it transforms @racket[mid] like @racket[t1] or @racket[t2] is immaterial, as a transformed @racket[mid] is equal to @racket[mid] either way.) +@examples[#:eval plot-eval + (parameterize ([plot-x-transform (axis-transform-append + (stretch-transform -2 -1 10) + (stretch-transform 1 2 10) + 0)]) + (plot (function (λ (x) x) -3 3)))] +} + +@doc-apply[axis-transform-bound]{ +Returns an axis transform that transforms values like @racket[t] does in the interval [@racket[a],@racket[b]], but like the identity transform outside of it. +For example, to bound @racket[log-transform] to an interval in which it is well-defined, +@interaction[#:eval plot-eval + (parameterize ([plot-x-transform (axis-transform-bound + log-transform 0.01 +inf.0)]) + (plot (function (λ (x) x) -4 8 #:label "y = x")))] +} + +@doc-apply[axis-transform-compose]{ +Composes two axis transforms. +For example, to collapse part of a @racket[log-transform]ed axis, try something like +@interaction[#:eval plot-eval + (parameterize ([plot-x-transform (axis-transform-compose + log-transform + (collapse-transform 2 4))]) + (plot (function (λ (x) x) 1 5)))] +Argument order matters, but predicting the effects of exchanging arguments can be difficult. +Fortunately, the effects are usually slight. +} + +@doc-apply[make-axis-transform]{ +Given a monotone @racket[invertible-function], returns an axis transform. +Monotonicity is necessary, but cannot be enforced. +The inverse is used to take samples uniformly along transformed axes (see @racket[nonlinear-seq]). + +@examples[#:eval plot-eval + (parameterize ([plot-y-transform (make-axis-transform + (invertible-function sqrt sqr))]) + (plot (function (λ (x) x) 0 5)))] + +An axis transform created by @racket[make-axis-transform] (or by any of the above combinators) does not transform the endpoints of an axis's bounds, to within floating-point error. +For example, +@interaction[#:eval plot-eval + (match-let ([(invertible-function f g) + (apply-axis-transform log-transform 1 3)]) + (values (list (f 1) (f 2) (f 3)) + (list (g 1) (g 2.2618595071429146) (g 3))))] + +Technically, @racket[fun] does not need to be truly invertible. +Given @racket[fun] = @racket[(invertible-function f g)], it is enough for @racket[f] to be a @hyperlink["http://en.wikipedia.org/wiki/Inverse_function#Left_and_right_inverses"]{left inverse} of @racket[g]. +If @racket[f] and @racket[g] had to be strict inverses of each other, there could be no @racket[collapse-transform]. +} + +@doc-apply[apply-axis-transform]{ +Returns an invertible function that transforms axis points within the given axis bounds. +This convenience function is used internally to transform points before rendering, but is provided for completeness. +} + +@section[#:tag "ticks"]{Axis Ticks} + +Each plot axis has two indepedent sets of ticks: the @italic{near} ticks and the @italic{far} ticks. + +@doc-apply[plot-x-ticks] +@doc-apply[plot-x-far-ticks] +@doc-apply[plot-y-ticks] +@doc-apply[plot-y-far-ticks] +@doc-apply[plot-z-ticks] +@doc-apply[plot-z-far-ticks]{ +@examples[#:eval plot-eval + (parameterize ([plot-x-label "Near x axis"] + [plot-y-label "Near y axis"] + [plot-z-label "Near z axis"] + [plot-x-ticks (date-ticks)] + [plot-y-ticks (time-ticks)] + [plot-z-ticks (fraction-ticks)] + [plot-x-far-label "Far x axis"] + [plot-y-far-label "Far y axis"] + [plot-z-far-label "Far z axis"] + [plot-x-far-ticks (linear-ticks)] + [plot-y-far-ticks (currency-ticks)] + [plot-z-far-ticks (log-ticks #:base 2)]) + (plot3d (lines3d '(#(1 1 1) #(40000000 4 4)) #:style 'transparent) + #:angle 45 #:altitude 50 + #:title "Axis Names and Tick Locations"))] +At any @racket[#:angle], the far @italic{x} and @italic{y} ticks are behind the plot, and the far @italic{z} ticks are on the right. +Far ticks are drawn, but not labeled, if they are identical to their corresponding near ticks. +They are always identical by default. + +@deftech{Major ticks} are longer than @deftech{minor ticks}. Major tick labels are always drawn unless collapsed with a nearby tick. +Minor tick labels are never drawn. + +Renderers produced by @racket[contours] and @racket[contour-intervals] use the value of @racket[plot-z-ticks] to place and label contour lines. +For example, compare plots of the same function renderered using both @racket[contour-intervals] and @racket[contour-intervals3d]: +@interaction[#:eval plot-eval + (parameterize ([plot-z-ticks (currency-ticks)]) + (define (saddle x y) (- (sqr x) (sqr y))) + (values + (plot (contour-intervals saddle -1 1 -1 1 #:label "z") + #:legend-anchor 'center) + (plot3d (contour-intervals3d saddle -1 1 -1 1 #:label "z") + #:legend-anchor 'center)))] +} + +@defstruct[ticks ([layout ticks-layout/c] [format ticks-format/c])]{ +A @racket[ticks] for a near or far axis consists of a @racket[layout] function, which determines the number of ticks and where they will be placed, and a @racket[format] function, which determines the ticks' labels. +} + +@doc-apply[ticks-default-number]{ +Most tick layout functions (and thus their corresponding @racket[ticks]-constructing functions) have a @racket[#:number] keyword argument with default @racket[(ticks-default-number)]. What the number means depends on the tick layout function. Most use it for the maximum number of major ticks. + +It is unlikely to mean the exact number of major ticks. +Without adjusting the number of ticks, layout functions usually cannot find uniformly spaced ticks that will have simple labels after formatting. +For example, the following plot shows the actual number of major ticks for the interval [0,@italic{x}] when the requested number of ticks is 8, as generated by @racket[linear-ticks-layout]: +@interaction[#:eval plot-eval + (plot (function (λ (x) + (count pre-tick-major? + ((linear-ticks-layout #:number 8) 0 x))) + 0.1 10) + #:x-label "Interval [0,x]" #:y-label "Number of ticks")] +} + +@subsection{Linear Ticks} + +@doc-apply[linear-ticks-layout] +@doc-apply[linear-ticks-format] +@doc-apply[linear-ticks]{ +The layout function, format function, and combined @racket[ticks] for uniformly spaced ticks. + +To lay out ticks, @racket[linear-ticks-layout] finds the power of @racket[base] closest to the axis interval size, chooses a simple first tick, and then chooses a skip length using @racket[divisors] that maximizes the number of ticks without exceeding @racket[number]. +@margin-note*{For strategic use of non-default arguments, see @racket[bit/byte-ticks], @racket[currency-ticks], and @racket[fraction-ticks].} +The default arguments correspond to the standard 1-2-5-in-base-10 rule used almost everywhere in plot tick layout. + +To format ticks, @racket[linear-ticks-format] uses @racket[real->plot-label], and uses @racket[digits-for-range] to determine the maximum number of fractional digits in the decimal expansion. +} + +@subsection{Log Ticks} + +@doc-apply[log-ticks-layout] +@doc-apply[log-ticks-format] +@doc-apply[log-ticks]{ +The layout function, format function, and combined @racket[ticks] for exponentially spaced major ticks. +(The minor ticks between are uniformly spaced.) +Use these ticks for @racket[log-transform]ed axes, because when exponentially spaced tick positions are @racket[log-transform]ed, they become uniformly spaced. + +The @racket[#:base] keyword argument is the logarithm base. +See @racket[plot-z-far-ticks] for an example of use. +} + +@subsection{Date Ticks} + +@doc-apply[date-ticks-layout] +@doc-apply[date-ticks-format] +@doc-apply[date-ticks]{ +The layout function, format function, and combined @racket[ticks] for uniformly spaced ticks with date labels. + +These axis ticks regard values as being in seconds since @italic{a system-dependent Universal Coordinated Time (UTC) epoch}. +(For example, the Unix and Mac OS X epoch is January 1, 1970 UTC, and the Windows epoch is January 1, 1601 UTC.) +Use @racket[date->seconds] to convert local dates to seconds, or @racket[datetime->real] to convert dates to UTC seconds in a way that accounts for time zone offsets. + +Actually, @racket[date-ticks-layout] does not always space ticks @italic{quite} uniformly. +For example, it rounds ticks that are spaced about one month apart or more to the nearest month. +Generally, @racket[date-ticks-layout] tries to place ticks at minute, hour, day, week, month and year boundaries, as well as common multiples such as 90 days or 6 months. + +To avoid displaying overlapping labels, @racket[date-ticks-format] chooses date formats from @racket[formats] for which labels will contain no redundant information. + +All the format specifiers given in @racketmodname[srfi/19] (which are derived from Unix's @tt{date} command), except those that represent time zones, are allowed in date format strings. +} + +@doc-apply[date-ticks-formats]{ +The default date formats. +} + +@doc-apply[24h-descending-date-ticks-formats] +@doc-apply[12h-descending-date-ticks-formats] + +@subsection{Time Ticks} + +@doc-apply[time-ticks-layout] +@doc-apply[time-ticks-format] +@doc-apply[time-ticks]{ +The layout function, format function, and combined @racket[ticks] for uniformly spaced ticks with time labels. + +These axis ticks regard values as being in seconds. +Use @racket[datetime->real] to convert @racket[sql-time] or @racket[plot-time] values to seconds. + +Generally, @racket[time-ticks-layout] tries to place ticks at minute, hour and day boundaries, as well as common multiples such as 12 hours or 30 days. + +To avoid displaying overlapping labels, @racket[time-ticks-format] chooses a date format from @racket[formats] for which labels will contain no redundant information. + +All the time-related format specifiers given in @racketmodname[srfi/19] (which are derived from Unix's @tt{date} command) are allowed in time format strings. +} + +@doc-apply[time-ticks-formats]{ +The default time formats. +} + +@doc-apply[24h-descending-time-ticks-formats] +@doc-apply[12h-descending-time-ticks-formats] + +@subsection{Currency Ticks} + +@doc-apply[currency-ticks-format] +@doc-apply[currency-ticks]{ +The format function and combined @racket[ticks] for uniformly spaced ticks with currency labels. + +The @racket[#:kind] keyword argument is either a string containing the currency symbol, or a currency code such as @racket['USD], @racket['GBP] or @racket['EUR]. +The @racket[currency-ticks-format] function can map most ISO 4217 currency codes to their corresponding currency symbol. + +The @racket[#:scales] keyword argument is a list of suffixes for each 10@superscript{3} scale, such as @racket["K"] (US thousand, or kilo), @racket["bn"] (UK short-scale billion) or @racket["Md"] (EU long-scale milliard). Off-scale amounts are given power-of-ten suffixes such as ``×10@superscript{21}.'' + +The @racket[#:formats] keyword argument is a list of three format strings, representing the formats of positive, negative, and zero amounts, respectively. The format specifiers are: +@itemlist[@item{@racket["~$"]: replaced by the currency symbol} + @item{@racket["~w"]: replaced by the whole part of the amount} + @item{@racket["~f"]: replaced by the fractional part, with 2 or more decimal digits} + @item{@racket["~s"]: replaced by the scale suffix} + @item{@racket["~~"]: replaced by ``~''}] + +Note that the @racket[#:divisors] passed to @racket[linear-ticks-layout] are @racket['(1 2 4 5)]. This allows quarter divisions to be used for tick positions, corresponding to 25/100 denominations such as the US quarter dollar. +} + +@doc-apply[currency-ticks-scales] +@doc-apply[currency-ticks-formats]{ +The default currency scales and formats. + +For example, a PLoT user in France would probably begin programs with +@racketblock[(require plot) + (currency-ticks-scales eu-currency-scales) + (currency-ticks-formats eu-currency-formats)] +and use @racket[(currency-ticks #:kind 'EUR)] for local currency or @racket[(currency-ticks #:kind 'JPY)] for Japanese Yen. + +Cultural sensitivity notwithstanding, when writing for a local audience, it is generally considered proper to use local currency scales and formats for foreign currencies. +} + +@doc-apply[us-currency-scales]{ +Short-scale suffix abbreviations as commonly used in the United States, Canada, and some other English-speaking countries. These stand for ``kilo,'' ``million,'' ``billion,'' and ``trillion.'' +} + +@doc-apply[uk-currency-scales]{ +Short-scale suffix abbreviations as commonly used in the United Kingdom since switching to the short scale in 1974, and as currently recommended by the Daily Telegraph and Times style guides. +} + +@doc-apply[eu-currency-scales]{ +European Union long-scale suffix abbreviations, which stand for ``kilo,'' ``million,'' ``milliard,'' and ``billion.'' + +The abbreviations actually used vary with geography, even within countries, but these seem to be common. +Further long-scale suffix abbreviations such as for ``billiard'' are ommitted due to lack of even weak consensus. +} + +@doc-apply[us-currency-formats]{ +Common currency formats used in the United States. +} + +@doc-apply[uk-currency-formats]{ +Common currency formats used in the United Kingdom. +Note that it sensibly uses a negative sign to denote negative amounts. +} + +@doc-apply[eu-currency-formats]{ +A guess at common currency formats for the European Union. +Like scale suffixes, actual formats vary with geography, but currency formats can even vary with audience or tone. +} + +@subsection{Other Ticks} + +@doc-apply[no-ticks-layout] +@doc-apply[no-ticks-format] +@doc-apply[no-ticks]{ +The layout function, format function, and combined @racket[ticks] for no ticks whatsoever. +@examples[#:eval plot-eval + (parameterize ([plot-x-ticks no-ticks] + [plot-y-ticks no-ticks] + [plot-x-label #f] + [plot-y-label #f]) + (plot (list (polar-axes) (polar (λ (θ) 1/3)))))] +} + +@doc-apply[bit/byte-ticks-format] +@doc-apply[bit/byte-ticks]{ +The format function and and combined @racket[ticks] for bit or byte values. + +The @racket[#:kind] keyword argument indicates either International System of Units (@racket['SI]) suffixes, as used to communicate hard drive capacities, or Computer Science (@racket['CS]) suffixes, as used to communicate memory capacities. +} + +@doc-apply[fraction-ticks-format] +@doc-apply[fraction-ticks]{ +The format function and and combined @racket[ticks] for fraction-formatted values. +} + +@subsection{Tick Combinators} + +@doc-apply[ticks-mimic]{ +Returns a @racket[ticks] that mimics the given @racket[ticks] returned by @racket[thunk]. +Used in default values for @racket[plot-x-far-ticks], @racket[plot-y-far-ticks] and @racket[plot-z-far-ticks] to ensure that, unless one of these parameters is changed, the far tick labels are not drawn. +} + +@doc-apply[ticks-add]{ +Returns a new @racket[ticks] that acts like @racket[t], except that it puts additional ticks at positions @racket[xs]. If @racket[major?] is true, the ticks at positions @racket[xs] are all @tech{major ticks}; otherwise, they are minor ticks. +} + +@doc-apply[ticks-scale]{ +Returns a new @racket[ticks] that acts like @racket[t], but for an axis transformed by @racket[fun]. +Unlike with typical @secref["transforms"], @racket[fun] is allowed to transform axis endpoints. +(See @racket[make-axis-transform] for an explanation about transforming endpoints.) + +Use @racket[ticks-scale] to plot values at multiple scales simultaneously, with one scale on the near axis and one scale on the far axis. +The following example plots degrees Celsius on the left and degrees Farenheit on the right: +@interaction[#:eval plot-eval + (parameterize + ([plot-x-ticks (time-ticks)] + [plot-y-far-ticks (ticks-scale (plot-y-ticks) + (linear-scale 9/5 32))] + [plot-y-label "Temperature (\u00b0C)"] + [plot-y-far-label "Temperature (\u00b0F)"]) + (define data + (list #(0 0) #(15 0.6) #(30 9.5) #(45 10.0) #(60 16.6) + #(75 41.6) #(90 42.7) #(105 65.5) #(120 78.9) + #(135 78.9) #(150 131.1) #(165 151.1) #(180 176.2))) + (plot (list + (function (λ (x) (/ (sqr x) 180)) 0 180 + #:style 'long-dash #:color 3 #:label "Trend") + (lines data #:color 2 #:width 2) + (points data #:color 1 #:line-width 2 #:label "Measured")) + #:y-min -25 #:x-label "Time"))] +} + +@subsection{Tick Data Types and Contracts} + +@defstruct[pre-tick ([value real?] [major? boolean?])]{ +Represents a tick that has not yet been labeled. +} +@defstruct[(tick pre-tick) ([label string?])]{ +Represents a tick with a label. +} + +@doc-apply[ticks-layout/c]{ +The contract for tick layout functions. Note that a layout function returns @racket[pre-tick]s, or unlabeled ticks. +} + +@doc-apply[ticks-format/c]{ +The contract for tick format functions. A format function receives axis bounds so it can determine how many decimal digits to display (usually by applying @racket[digits-for-range] to the bounds). +} + +@section[#:tag "invertible"]{Invertible Functions} + +@defstruct[invertible-function ([f (real? . -> . real?)] [g (real? . -> . real?)])]{ +Represents an invertible function. Used for @secref["transforms"] and by @racket[ticks-scale]. + +The function itself is @racket[f], and its inverse is @racket[g]. +Because @racket[real?]s can be inexact, this invariant must be approximate and therefore cannot be enforced. +(For example, @racket[(exp (log 10))] = @racket[10.000000000000002].) +The obligation to maintain it rests on whomever constructs one. +} + +@doc-apply[id-function]{ +The identity function as an @racket[invertible-function]. +} + +@doc-apply[invertible-compose]{ +Returns the composition of two invertible functions. +} + +@doc-apply[invertible-inverse]{ +Returns the inverse of an invertible function. +} + +@doc-apply[linear-scale]{ +Returns a one-dimensional linear scaling function, as an @racket[invertible-function]. +This function constructs the most common arguments to @racket[ticks-scale]. +} diff --git a/collects/plot/scribblings/utils.scrbl b/collects/plot/scribblings/utils.scrbl index 06e41ca7a3..0848052ace 100644 --- a/collects/plot/scribblings/utils.scrbl +++ b/collects/plot/scribblings/utils.scrbl @@ -6,13 +6,7 @@ @defmodule[plot/utils] -@doc-apply[degrees->radians]{ -Converts degrees to radians. -} - -@doc-apply[radians->degrees]{ -Converts radians to degrees. -} +@section{Formatting} @doc-apply[digits-for-range]{ Given a range, returns the number of decimal places necessary to distinguish numbers in the range. This may return negative numbers for large ranges. @@ -41,37 +35,7 @@ Converts a Racket value to a label. Used by @(racket discrete-histogram) and @(r Like @(racket real->decimal-string), but removes trailing zeros and a trailing decimal point. } -@doc-apply[linear-seq]{ -Returns a list of evenly spaced real numbers between @(racket start) and @(racket end). -If @(racket start?) is @(racket #t), the list includes @(racket start). -If @(racket end?) is @(racket #t), the list includes @(racket end). - -This function is used internally to generate sample points. - -@examples[#:eval plot-eval - (linear-seq 0 1 5) - (linear-seq 0 1 5 #:start? #f) - (linear-seq 0 1 5 #:end? #f) - (linear-seq 0 1 5 #:start? #f #:end? #f)] -} - -@doc-apply[linear-seq*]{ -Like @(racket linear-seq), but accepts a list of reals instead of a start and end. -The @(racket #:start?) and @(racket #:end?) keyword arguments work as in @(racket linear-seq). -This function does not guarantee that each inner value will be in the returned list. - -@examples[#:eval plot-eval - (linear-seq* '(0 1 2) 5) - (linear-seq* '(0 1 2) 6) - (linear-seq* '(0 1 0) 5)] -} - -@doc-apply[bounds->intervals]{ -Given a list of points, returns intervals between each pair. - -Use this to construct inputs for @(racket rectangles) and @(racket rectangles3d). -@examples[#:eval plot-eval (bounds->intervals (linear-seq 0 1 5))] -} +@section{Plot Colors and Styles} @doc-apply[color-seq]{ Interpolates between colors---red, green and blue components separately---using @(racket linear-seq). @@ -169,18 +133,44 @@ Integer brush styles repeat starting at @(racket 7). (map ->brush-style '(4 5 6))] } -@defstruct[invertible-function ([f (real? . -> . real?)] [finv (real? . -> . real?)])]{ -Represents an invertible function. +@section{Plot-Specific Math} -The function itself is @(racket f), and its inverse is @(racket finv). -Because @(racket real?)s can be inexact, this invariant must be approximate and therefore cannot be enforced. -(For example, @(racket (exp (log 10))) = @(racket 10.000000000000002).) -The obligation to maintain it rests on whomever constructs one. +@subsection{Real Numbers} -An axis transform such as @(racket plot-x-transform) is a function from bounds @(racket start end) to an @(racket invertible-function) for which @(racket (f start)) = @(racket start) and @(racket (f end)) = @(racket end) (approximately), and the same is true of @(racket finv). -The function @(racket f) is used to transform points before drawing; its inverse @(racket finv) is used to generate samples that will be evenly spaced after being transformed by @(racket f). +@doc-apply[regular-real?]{ +} -(Technically, because of the way PLoT uses @(racket invertible-function), @(racket f) must only be a left inverse of @(racket finv); there is no requirement that @(racket f) also be a right inverse of @(racket finv).) +@doc-apply[degrees->radians]{ +Converts degrees to radians. +} + +@doc-apply[radians->degrees]{ +Converts radians to degrees. +} + +@doc-apply[linear-seq]{ +Returns a list of evenly spaced real numbers between @(racket start) and @(racket end). +If @(racket start?) is @(racket #t), the list includes @(racket start). +If @(racket end?) is @(racket #t), the list includes @(racket end). + +This function is used internally to generate sample points. + +@examples[#:eval plot-eval + (linear-seq 0 1 5) + (linear-seq 0 1 5 #:start? #f) + (linear-seq 0 1 5 #:end? #f) + (linear-seq 0 1 5 #:start? #f #:end? #f)] +} + +@doc-apply[linear-seq*]{ +Like @(racket linear-seq), but accepts a list of reals instead of a start and end. +The @(racket #:start?) and @(racket #:end?) keyword arguments work as in @(racket linear-seq). +This function does not guarantee that each inner value will be in the returned list. + +@examples[#:eval plot-eval + (linear-seq* '(0 1 2) 5) + (linear-seq* '(0 1 2) 6) + (linear-seq* '(0 1 0) 5)] } @doc-apply[nonlinear-seq]{ @@ -193,6 +183,23 @@ This is used to generate samples for transformed axes. (plot (area-histogram sqr (nonlinear-seq 1 10 4 log-transform))))] } +@subsection[#:tag "math.vectors"]{Vectors} + +@subsection[#:tag "math.intervals"]{Intervals} + +@doc-apply[bounds->intervals]{ +Given a list of points, returns intervals between each pair. + +Use this to construct inputs for @(racket rectangles) and @(racket rectangles3d). +@examples[#:eval plot-eval (bounds->intervals (linear-seq 0 1 5))] +} + +@subsection[#:tag "math.rectangles"]{Rectangles} + +@section{Dates and Times} + +@section{Sampling} + @defstruct[mapped-function ([f (any/c . -> . any/c)] [fmap ((listof any/c) . -> . (listof any/c))])]{ Represents a function that maps over lists differently than @(racket (map f xs)). @@ -201,6 +208,8 @@ With some functions, mapping over a list can be done much more quickly if done s Renderer-producing functions that accept a @(racket (real? . -> . real?)) also accept a @(racket mapped-function), and use its @(racket fmap) to sample more efficiently. } +@section{Denity Estimation} + @doc-apply[kde]{ Given samples and a kernel bandwidth, returns a @(racket mapped-function) representing a kernel density estimate, and bounds, outside of which the density estimate is zero. Used by @(racket density). } diff --git a/collects/plot/tests/plot2d-tests.rkt b/collects/plot/tests/plot2d-tests.rkt index e068d529da..e278707766 100644 --- a/collects/plot/tests/plot2d-tests.rkt +++ b/collects/plot/tests/plot2d-tests.rkt @@ -18,11 +18,10 @@ (time (plot (function values 0 1000))) -(parameterize ([plot-x-ticks (log-ticks #:base 4)] - [plot-x-transform log-transform] - [plot-y-max-ticks 10] - [plot-y-ticks (linear-ticks)] - [plot-y-transform log-transform]) +(parameterize ([plot-x-transform log-transform] + [plot-x-ticks (log-ticks #:base 4)] + [plot-y-transform log-transform] + [plot-y-ticks (linear-ticks #:number 10)]) (plot (function values 1 243))) (parameterize ([plot-background "black"] diff --git a/collects/plot/tests/tick-tests.rkt b/collects/plot/tests/tick-tests.rkt index 4118f26671..8ce19a7e00 100644 --- a/collects/plot/tests/tick-tests.rkt +++ b/collects/plot/tests/tick-tests.rkt @@ -4,10 +4,10 @@ (plot-font-family 'swiss) -(plot (function (λ (x) (count pre-tick-major? ((linear-ticks) 0 x 8))) +(plot (function (λ (x) (count pre-tick-major? ((linear-ticks #:number 8) 0 x))) 0.1 10)) -(plot (function (λ (x) (count pre-tick-major? ((linear-ticks) 0 x 40))) +(plot (function (λ (x) (count pre-tick-major? ((linear-ticks #:number 40) 0 x))) 1 100)) (parameterize ([plot-x-ticks (linear-ticks #:base 2 #:divisors '(1 2))] @@ -20,8 +20,7 @@ [plot-y-ticks (fraction-ticks)]) (plot (function (λ (x) (+ 1 (cos x))) 0.0001 12))) -(parameterize ([plot-x-ticks (date-ticks)] - [plot-x-max-ticks 3] +(parameterize ([plot-x-ticks (date-ticks #:number 3)] [plot-y-ticks (currency-ticks)]) (plot (function values -1 1))) @@ -40,7 +39,7 @@ #:x-label "Euros" #:y-label "Dollars")) -(parameterize ([plot-x-ticks (no-ticks)]) +(parameterize ([plot-x-ticks no-ticks]) (plot (function sin -1 4))) (parameterize ([plot-x-transform log-transform] @@ -90,7 +89,8 @@ (parameterize ([plot-y-ticks (ticks-scale (log-ticks) exp-scale)]) (plot (function values -10 10))) -(parameterize ([plot-y-ticks (ticks-add (no-ticks) '(1/3 2/3))]) +(parameterize ([plot-y-ticks (ticks-add (ticks no-ticks-layout (linear-ticks-format)) + '(1/3 2/3))]) (plot (function sin -4 4))) (plot (list (function sin -4 4) @@ -98,7 +98,7 @@ (x-ticks (list (tick 1.5 #t "3/2") (tick 3 #t "Three"))) (y-ticks (list (tick 1/4 #t "1/4") (tick -1/4 #f ""))))) -(parameterize ([plot-z-max-ticks 5]) +(parameterize ([plot-z-ticks (linear-ticks #:number 5)]) (plot3d (list (surface3d (λ (x y) (* 2 (+ (sin x) (cos y)))) -4 4 -4 4 #:alpha 1/2) (x-ticks (list (tick 1.5 #t "3/2") (tick 3 #t "Three"))) (y-ticks (list (tick 1/3 #t "1/3") (tick -1/3 #f "1/3"))) @@ -127,13 +127,12 @@ (plot (function sin (- pi) pi))) (parameterize ([plot-x-far-label "x far axis"] - [plot-x-max-ticks 10] + [plot-x-ticks (linear-ticks #:number 10)] [plot-y-far-label "y far axis"] [plot-y-far-ticks (date-ticks)] [plot-z-label "z axis"] [plot-z-far-label "z far axis"] - [plot-z-far-ticks (currency-ticks)] - [plot-z-far-max-ticks 5]) + [plot-z-far-ticks (currency-ticks #:number 5)]) (plot3d (surface3d (λ (x y) (+ (sin x) (cos y))) -2 2 -2 2 #:alpha 1/2) #:angle 60 #:altitude 35))