PLoT documentation for new renderers and plot/utils (code changes are only to move code around so a previously public function could be public again)

Many little doc fixes

Closes PR 12433

Closes PR 12435

Please please please merge into release
This commit is contained in:
Neil Toronto 2012-01-23 15:48:08 -07:00
parent 080b6095c4
commit 015625e732
19 changed files with 704 additions and 235 deletions

View File

@ -2,15 +2,12 @@
(require racket/flonum racket/list racket/promise racket/math racket/contract (require racket/flonum racket/list racket/promise racket/math racket/contract
unstable/latent-contract/defthing unstable/latent-contract/defthing
plot/utils "math.rkt"
"../common/utils.rkt" "utils.rkt"
"line.rkt") "sample.rkt")
(provide (all-defined-out)) (provide (all-defined-out))
(define (factorial n)
(if (zero? n) 1 (* n (factorial (sub1 n)))))
;; make-kde/windowed : (vectorof flonum) flonum flonum flonum -> (listof flonum) -> (listof flonum) ;; make-kde/windowed : (vectorof flonum) flonum flonum flonum -> (listof flonum) -> (listof flonum)
;; (can assume that xs is sorted) ;; (can assume that xs is sorted)
;; Make a naive KDE, but uses windows to keep from adding Gaussians more than max-dist away ;; Make a naive KDE, but uses windows to keep from adding Gaussians more than max-dist away
@ -130,22 +127,3 @@
(map (λ (p) (fl* p c)) mid-ps) (map (λ (p) (fl* p c)) mid-ps)
last-ps))))) last-ps)))))
(values (mapped-function (λ (x) (first (fmap (list x)))) fmap) x-min x-max)))) (values (mapped-function (λ (x) (first (fmap (list x)))) fmap) x-min x-max))))
(defproc (density [xs (listof real?)] [bw-adjust real? 1]
[#:x-min x-min (or/c rational? #f) #f] [#:x-max x-max (or/c rational? #f) #f]
[#:y-min y-min (or/c rational? #f) #f] [#:y-max y-max (or/c rational? #f) #f]
[#:samples samples (and/c exact-integer? (>=/c 2)) (line-samples)]
[#:color color plot-color/c (line-color)]
[#:width width (>=/c 0) (line-width)]
[#:style style plot-pen-style/c (line-style)]
[#:alpha alpha (real-in 0 1) (line-alpha)]
[#:label label (or/c string? #f) #f]
) renderer2d?
(define n (length xs))
(define sd (sqrt (- (/ (sum sqr xs) n) (sqr (/ (sum values xs) n)))))
(define h (* bw-adjust 1.06 sd (expt n -0.2)))
(define-values (f fx-min fx-max) (kde xs h))
(let ([x-min (if x-min x-min fx-min)]
[x-max (if x-max x-max fx-max)])
(function f x-min x-max #:y-min y-min #:y-max y-max #:samples samples
#:color color #:width width #:style style #:alpha alpha #:label label)))

View File

@ -4,6 +4,12 @@
(provide (all-defined-out)) (provide (all-defined-out))
;; ===================================================================================================
;; Integers
(defproc (factorial [n exact-nonnegative-integer?]) exact-nonnegative-integer?
(if (zero? n) 1 (* n (factorial (sub1 n)))))
;; =================================================================================================== ;; ===================================================================================================
;; Flonums ;; Flonums

View File

@ -0,0 +1,7 @@
#lang racket/base
(require unstable/latent-contract)
(require "../common/kde.rkt")
(provide (activate-contract-out
kde))

View File

@ -18,7 +18,8 @@
"common/date-time.rkt" "common/date-time.rkt"
"common/marching-squares.rkt" "common/marching-squares.rkt"
"common/marching-cubes.rkt" "common/marching-cubes.rkt"
"common/legend.rkt") "common/legend.rkt"
"common/kde.rkt")
(provide (only-doc-out (provide (only-doc-out
(combine-out (all-from-out "common/parameters.rkt") (combine-out (all-from-out "common/parameters.rkt")
@ -34,7 +35,8 @@
(all-from-out "common/date-time.rkt") (all-from-out "common/date-time.rkt")
(all-from-out "common/marching-squares.rkt") (all-from-out "common/marching-squares.rkt")
(all-from-out "common/marching-cubes.rkt") (all-from-out "common/marching-cubes.rkt")
(all-from-out "common/legend.rkt")))) (all-from-out "common/legend.rkt")
(all-from-out "common/kde.rkt"))))
;; =================================================================================================== ;; ===================================================================================================
;; 2D exports ;; 2D exports
@ -45,8 +47,7 @@
"plot2d/interval.rkt" "plot2d/interval.rkt"
"plot2d/contour.rkt" "plot2d/contour.rkt"
"plot2d/rectangle.rkt" "plot2d/rectangle.rkt"
"plot2d/decoration.rkt" "plot2d/decoration.rkt")
"plot2d/kde.rkt")
(provide (only-doc-out (provide (only-doc-out
(combine-out (all-from-out "plot2d/plot.rkt") (combine-out (all-from-out "plot2d/plot.rkt")
@ -55,8 +56,7 @@
(all-from-out "plot2d/interval.rkt") (all-from-out "plot2d/interval.rkt")
(all-from-out "plot2d/contour.rkt") (all-from-out "plot2d/contour.rkt")
(all-from-out "plot2d/rectangle.rkt") (all-from-out "plot2d/rectangle.rkt")
(all-from-out "plot2d/decoration.rkt") (all-from-out "plot2d/decoration.rkt"))))
(all-from-out "plot2d/kde.rkt"))))
;; =================================================================================================== ;; ===================================================================================================
;; 3D exports ;; 3D exports

View File

@ -35,7 +35,7 @@
(provide (activate-contract-out points vector-field error-bars)) (provide (activate-contract-out points vector-field error-bars))
(require "plot2d/line.rkt") (require "plot2d/line.rkt")
(provide (activate-contract-out lines parametric polar function inverse)) (provide (activate-contract-out lines parametric polar function inverse density))
(require "plot2d/interval.rkt") (require "plot2d/interval.rkt")
(provide (activate-contract-out (provide (activate-contract-out
@ -53,9 +53,6 @@
x-tick-lines y-tick-lines tick-grid x-tick-lines y-tick-lines tick-grid
point-label parametric-label polar-label function-label inverse-label)) point-label parametric-label polar-label function-label inverse-label))
(require "plot2d/kde.rkt")
(provide (activate-contract-out density))
;; =================================================================================================== ;; ===================================================================================================
;; 3D exports ;; 3D exports

View File

@ -131,3 +131,25 @@
(inverse-bounds-fun g samples) (inverse-bounds-fun g samples)
default-ticks-fun default-ticks-fun
(inverse-render-proc g samples color width style alpha label))) (inverse-render-proc g samples color width style alpha label)))
;; ===================================================================================================
;; Kernel density estimation
(defproc (density [xs (listof real?)] [bw-adjust real? 1]
[#:x-min x-min (or/c rational? #f) #f] [#:x-max x-max (or/c rational? #f) #f]
[#:y-min y-min (or/c rational? #f) #f] [#:y-max y-max (or/c rational? #f) #f]
[#:samples samples (and/c exact-integer? (>=/c 2)) (line-samples)]
[#:color color plot-color/c (line-color)]
[#:width width (>=/c 0) (line-width)]
[#:style style plot-pen-style/c (line-style)]
[#:alpha alpha (real-in 0 1) (line-alpha)]
[#:label label (or/c string? #f) #f]
) renderer2d?
(define n (length xs))
(define sd (sqrt (- (/ (sum sqr xs) n) (sqr (/ (sum values xs) n)))))
(define h (* bw-adjust 1.06 sd (expt n -0.2)))
(define-values (f fx-min fx-max) (kde xs h))
(let ([x-min (if x-min x-min fx-min)]
[x-max (if x-max x-max fx-max)])
(function f x-min x-max #:y-min y-min #:y-max y-max #:samples samples
#:color color #:width width #:style style #:alpha alpha #:label label)))

View File

@ -93,7 +93,8 @@
(defproc (vector-field3d (defproc (vector-field3d
[f (or/c (real? real? real? . -> . (vector/c real? real? real?)) [f (or/c (real? real? real? . -> . (vector/c real? real? real?))
((vector/c real? real? real?) . -> . (vector/c real? real? real?)))] ((vector/c real? real? real?)
. -> . (vector/c real? real? real?)))]
[x-min (or/c rational? #f) #f] [x-max (or/c rational? #f) #f] [x-min (or/c rational? #f) #f] [x-max (or/c rational? #f) #f]
[y-min (or/c rational? #f) #f] [y-max (or/c rational? #f) #f] [y-min (or/c rational? #f) #f] [y-max (or/c rational? #f) #f]
[z-min (or/c rational? #f) #f] [z-max (or/c rational? #f) #f] [z-min (or/c rational? #f) #f] [z-max (or/c rational? #f) #f]

View File

@ -4,6 +4,7 @@
(for-label racket (for-label racket
racket/gui/base racket/gui/base
slideshow/pict slideshow/pict
db
plot plot
plot/utils) plot/utils)
plot plot
@ -16,6 +17,7 @@
(for-label (all-from-out racket (for-label (all-from-out racket
racket/gui/base racket/gui/base
slideshow/pict slideshow/pict
db
plot plot
plot/utils)) plot/utils))
(all-from-out plot) (all-from-out plot)

View File

@ -6,15 +6,20 @@
@title[#:tag "contracts"]{Plot Contracts} @title[#:tag "contracts"]{Plot Contracts}
@section{Convenience Contracts} @section{Plot Element Contracts}
@doc-apply[contract/c]{ @defproc[(renderer2d? [value any/c]) boolean?]{
Identifies @racket[contract?]s and predicates that can be used as contracts. Returns @racket[#t] if @racket[value] is a 2D @tech{renderer}; that is, if @racket[plot] can plot @racket[value].
See @secref["renderer2d"] for functions that construct them.
} }
@doc-apply[treeof]{ @defproc[(renderer3d? [value any/c]) boolean?]{
Identifies trees of values that meet the contract @(racket ct). Returns @racket[#t] if @racket[value] is a 3D @tech{renderer}; that is, if @racket[plot3d] can plot @racket[value].
Used by @(racket plot) and @(racket plot3d) to construct the contract for a tree of @(racket renderer2d?) or @(racket renderer3d?). See @secref["renderer3d"] for functions that construct them.
}
@defproc[(nonrenderer? [value any/c]) boolean?]{
Returns @racket[#t] if @racket[value] is a @tech{nonrenderer}. See @secref["nonrenderer"] for functions that construct them.
} }
@section{Appearance Argument Contracts} @section{Appearance Argument Contracts}
@ -23,7 +28,11 @@ Used by @(racket plot) and @(racket plot3d) to construct the contract for a tree
The contract for @(racket anchor) arguments and parameters, such as @(racket plot-legend-anchor). The contract for @(racket anchor) arguments and parameters, such as @(racket plot-legend-anchor).
} }
@doc-apply[color/c] @doc-apply[color/c]{
A contract for very flexible color arguments.
Functions that accept a @racket[color/c] almost always convert it to an RGB triplet using @racket[->color].
}
@doc-apply[plot-color/c]{ @doc-apply[plot-color/c]{
The contract for @(racket #:color) arguments, and parameters such as @(racket line-color) and @(racket surface-color). The contract for @(racket #:color) arguments, and parameters such as @(racket line-color) and @(racket surface-color).
For the meaning of integer colors, see @(racket ->pen-color) and @(racket ->brush-color). For the meaning of integer colors, see @(racket ->pen-color) and @(racket ->brush-color).
@ -51,18 +60,61 @@ The contract for the @(racket #:sym) arguments in @(racket points) and @(racket
A list containing the symbols that are valid @(racket points) symbols. A list containing the symbols that are valid @(racket points) symbols.
} }
@section{Appearance Argument Sequence Contracts} @section{Appearance Argument List Contracts}
@doc-apply[maybe-function/c]{ @doc-apply[maybe-function/c]{
Returns a contract that accepts either a function from @racket[in-contract] to @racket[out-contract], or a plain @racket[out-contract] value.
@interaction[#:eval plot-eval
(require racket/contract)
(define/contract (maybe-function-of-real-consumer x)
((maybe-function/c real? real?) . -> . real?)
(maybe-apply x 10))
(maybe-function-of-real-consumer 4)
(maybe-function-of-real-consumer (λ (x) x))]
Many @racketmodname[plot] functions, such as @racket[contours] and @racket[isosurfaces3d], optionally take lists of appearance values (such as @racket[(listof plot-color/c)]) as arguments.
A very flexible argument contract would accept @italic{functions} that produce lists of appearance values.
For example, @racket[contours] would accept any @racket[f] with contract @racket[((listof real?) . -> . (listof plot-color/c))] for its @racket[#:colors] argument.
When rendering a contour plot, @racket[contours] would apply @racket[f] to a list of the contour @italic{z} values to get the contour colors.
However, most uses do not need this flexibility.
Therefore, @racketmodname[plot]'s functions accept @italic{either} a list of appearance values @italic{or} a function from a list of appropriate values to a list of appearance values.
The @racket[maybe-function/c] function constructs contracts for such arguments.
In @racketmodname[plot] functions, if @racket[in-contract] is a @racket[listof] contract, the output list's length need not be the same as the input list's length.
If it is shorter, the appearance values will cycle; if longer, the tail will not be used.
}
@doc-apply[maybe-apply]{
If @racket[f] is a function, applies @racket[f] to @racket[args]; otherwise returns @racket[f].
This is used inside many renderer-producing @racket[plot] functions to convert @racket[maybe-function/c] values to lists of appearance values.
} }
@doc-apply[plot-colors/c]{ @doc-apply[plot-colors/c]{
The contract for @(racket #:colors) arguments, as in @(racket contours). Returns a contract for @(racket #:colors) arguments, as in @(racket contours) and @racket[contour-intervals].
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. See @racket[maybe-function/c] for a discussion of the returned contract.
The number of colors need not be the same as the number of values.
If shorter, they will cycle; if longer, some will not be used.
See @(racket color-seq) and @(racket color-seq*) for a demonstration of constructing arguments with this contract. The following example sends a @italic{list}-valued @racket[(plot-colors/c ivl?)] to @racket[contour-intervals], which then cycles through the colors:
@interaction[#:eval plot-eval
(plot (contour-intervals (λ (x y) (+ x y)) 0 1 0 1
#:colors '(1 2)))]
This is equivalent to sending @racket[(λ _ '(1 2))].
The next example is more sophisticated: it sends a @italic{function}-valued @racket[(plot-colors/c ivl?)] to @racket[contour-intervals].
The function constructs colors from the values of the contour intervals.
@interaction[#:eval plot-eval
(define (brown-interval-colors ivls)
(define z-size (- (ivl-max (last ivls))
(ivl-min (first ivls))))
(for/list ([i (in-list ivls)])
(match-define (ivl z-min z-max) i)
(define z-mid (/ (* 1/2 (+ z-min z-max)) z-size))
(list (* 255 z-mid) (* 128 z-mid) (* 64 z-mid))))
(plot (contour-intervals (λ (x y) (+ x y)) 0 1 0 1
#:colors brown-interval-colors))]
} }
@doc-apply[plot-pen-styles/c]{ @doc-apply[plot-pen-styles/c]{
@ -80,3 +132,19 @@ Like @(racket plot-colors/c), but for fill styles.
@doc-apply[alphas/c]{ @doc-apply[alphas/c]{
Like @(racket plot-colors/c), but for opacities. Like @(racket plot-colors/c), but for opacities.
} }
@doc-apply[labels/c]{
Like @racket[plot-colors/c], but for strings.
This is used, for example, to label @racket[stacked-histogram]s.
}
@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?).
}

View File

@ -6,16 +6,41 @@
@title[#:tag "nonrenderer"]{Nonrenderers} @title[#:tag "nonrenderer"]{Nonrenderers}
@defproc[(nonrenderer? [value any/c]) boolean?]{ The following functions create @deftech{nonrenderers}, or plot elements that draw nothing in the plot.
}
@doc-apply[x-ticks] @doc-apply[x-ticks]
@doc-apply[y-ticks] @doc-apply[y-ticks]
@doc-apply[z-ticks]{
The @racket[x-ticks], @racket[y-ticks] and @racket[z-ticks] return a nonrenderer that adds custom ticks to a 2D or 3D plot.
@doc-apply[z-ticks] Although @racket[ticks-add] allows placing arbitrary major and minor ticks on an axis, it does not allow them to be formatted differently from the other ticks on the same axis.
Use one of these functions to get maximum control.
@doc-apply[invisible-rect] @examples[#:eval plot-eval
(parameterize ([plot-x-ticks no-ticks])
(plot (list (function sin (- pi) pi)
(x-ticks (list (tick (- pi) #t "-π")
(tick (* -3/4 pi) #f "")
(tick (* -1/2 pi) #t "-π/2")
(tick (* -1/4 pi) #f "")
(tick 0 #t "0")
(tick (* 1/4 pi) #f "")
(tick (* 1/2 pi) #t "π/2")
(tick (* 3/4 pi) #f "")
(tick pi #t "π")))
(axes))))]
When considering using one of these functions, remember that minor tick labels are never drawn,
and that including a @racket[z-ticks] nonrenderer will not add extra contour lines to contour plots.
}
@doc-apply[invisible-rect3d] @doc-apply[invisible-rect]{
Returns a nonrenderer that simply takes up space in the plot. Use this to cause the plot area to include a minimal rectangle.
@examples[#:eval plot-eval
(plot (list (function sin (- pi) pi)
(invisible-rect #f #f -2 2)))]
}
@doc-apply[invisible-rect3d]{
Returns a nonrenderer that simply takes up space in the plot. Use this to cause the plot area to include a minimal rectangle.
See @racket[invisible-rect] for a 2D example.
}

View File

@ -56,12 +56,30 @@ See @(racket ->pen-color) and @(racket ->brush-color) for details on how PLoT in
} }
@doc-apply[plot-x-far-label] @doc-apply[plot-x-far-label]
@doc-apply[plot-y-far-label] @doc-apply[plot-y-far-label]
@doc-apply[plot-z-far-label] @doc-apply[plot-z-far-label]{
The tick labels for ``far'' axes. See @racket[plot-x-ticks] for a discussion of near and far axes.
}
@doc-apply[plot-x-axis?]
@doc-apply[plot-x-far-axis?]
@doc-apply[plot-y-axis?]
@doc-apply[plot-y-far-axis?]
@doc-apply[plot-z-axis?]
@doc-apply[plot-z-far-axis?]{
When any of these is @racket[#f], the corresponding axis is not drawn.
Use these along with @racket[x-axis] and @racket[y-axis] if you want axes that intersect the origin or some other point.
}
@doc-apply[plot-animating?]{ @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. 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.
} }
@doc-apply[animated-samples]{
Given a number of samples, returns the number of samples to use.
This returns @racket[samples] when @racket[plot-animating?] is @racket[#f].
}
@doc-apply[plot-decorations?]{ @doc-apply[plot-decorations?]{
When @(racket #f), axes, axis labels, ticks, tick labels, and the title are not drawn. When @(racket #f), axes, axis labels, ticks, tick labels, and the title are not drawn.
} }
@ -103,6 +121,8 @@ When @(racket #f), axes, axis labels, ticks, tick labels, and the title are not
@doc-apply[vector-field-scale] @doc-apply[vector-field-scale]
@doc-apply[vector-field-alpha] @doc-apply[vector-field-alpha]
@doc-apply[vector-field3d-samples]
@section{Error Bars} @section{Error Bars}
@doc-apply[error-bar-width] @doc-apply[error-bar-width]
@ -142,9 +162,17 @@ When @(racket #f), axes, axis labels, ticks, tick labels, and the title are not
@doc-apply[discrete-histogram-skip] @doc-apply[discrete-histogram-skip]
@doc-apply[discrete-histogram-invert?] @doc-apply[discrete-histogram-invert?]
@doc-apply[stacked-histogram-alphas]
@doc-apply[stacked-histogram-colors]
@doc-apply[stacked-histogram-line-colors]
@doc-apply[stacked-histogram-line-styles]
@doc-apply[stacked-histogram-line-widths]
@doc-apply[stacked-histogram-styles]
@section{Decorations} @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. 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[x-axis-alpha]
@doc-apply[y-axis-alpha] @doc-apply[y-axis-alpha]
@ -200,7 +228,8 @@ Contour surface renderers use shared contour parameters except for the following
@section{Isosurfaces} @section{Isosurfaces}
Single isosurfaces (@(racket isosurface3d)) use surface parameters. Nested isosurfaces (@(racket isosurfaces3d)) use the following. Single isosurfaces (@(racket isosurface3d)) use surface parameters.
Nested isosurfaces (@(racket isosurfaces3d)) use the following.
@doc-apply[default-isosurface-colors] @doc-apply[default-isosurface-colors]
@doc-apply[default-isosurface-line-colors] @doc-apply[default-isosurface-line-colors]

View File

@ -41,10 +41,8 @@ If you have code written for PLoT 5.1.3 or earlier, please see @secref["porting"
@include-section["contracts.scrbl"] @include-section["contracts.scrbl"]
@include-section["custom.scrbl"] @;@include-section["custom.scrbl"]
@include-section["porting.scrbl"] @include-section["porting.scrbl"]
@include-section["compat.scrbl"] @include-section["compat.scrbl"]
@include-section["todo.scrbl"]

View File

@ -18,7 +18,7 @@ The update from PLoT version 5.1.3 to 5.2 introduces a few incompatibilities:
The argument change in @(racket plot3d) is similar. The argument change in @(racket plot3d) is similar.
This should not affect most code because PLoT encourages regarding these data types as black boxes.} This should not affect most code because PLoT encourages regarding these data types as black boxes.}
@item{The @(racket plot-extend) module no longer exists.} @item{The @(racket plot-extend) module no longer exists.}
@item{The @racket[fit] function and @racket[fit-result] functions have been removed.} @item{The @racket[fit] function and @racket[fit-result] struct type have been removed.}
] ]

View File

@ -6,11 +6,6 @@
@title[#:tag "renderer2d"]{2D Renderers} @title[#:tag "renderer2d"]{2D Renderers}
@defproc[(renderer2d? [value any/c]) boolean?]{
Returns @racket[#t] if @racket[value] is a 2D @tech{renderer}; that is, if @racket[plot] can plot @racket[value].
The following functions create such renderers.
}
@section[#:tag "renderer2d-function-arguments"]{2D Renderer Function Arguments} @section[#:tag "renderer2d-function-arguments"]{2D Renderer Function Arguments}
Functions that return 2D renderers always have these kinds of arguments: Functions that return 2D renderers always have these kinds of arguments:
@ -191,23 +186,21 @@ Corresponds with @(racket lines).
Corresponds with @(racket parametric). Corresponds with @(racket parametric).
@interaction[#:eval plot-eval @interaction[#:eval plot-eval
(let ()
(define (f1 t) (vector (* 2 (cos (* 4/5 t))) (define (f1 t) (vector (* 2 (cos (* 4/5 t)))
(* 2 (sin (* 4/5 t))))) (* 2 (sin (* 4/5 t)))))
(define (f2 t) (vector (* 1/2 (cos t)) (define (f2 t) (vector (* 1/2 (cos t))
(* 1/2 (sin t)))) (* 1/2 (sin t))))
(plot (parametric-interval f1 f2 (- pi) pi)))] (plot (parametric-interval f1 f2 (- pi) pi))]
} }
@doc-apply[polar-interval]{ @doc-apply[polar-interval]{
Corresponds with @(racket polar). Corresponds with @(racket polar).
@interaction[#:eval plot-eval @interaction[#:eval plot-eval
(let ()
(define (f1 θ) (+ 1/2 (* 1/6 (cos (* 5 θ))))) (define (f1 θ) (+ 1/2 (* 1/6 (cos (* 5 θ)))))
(define (f2 θ) (+ 1 (* 1/4 (cos (* 10 θ))))) (define (f2 θ) (+ 1 (* 1/4 (cos (* 10 θ)))))
(plot (list (polar-axes #:number 10) (plot (list (polar-axes #:number 10)
(polar-interval f1 f2 #:label "[f1,f2]"))))] (polar-interval f1 f2 #:label "[f1,f2]")))]
} }
@section{2D Contour (Isoline) Renderers} @section{2D Contour (Isoline) Renderers}
@ -220,6 +213,9 @@ A circle of radius @(racket r), for example, is the line of constant value @(rac
} }
In this case, @(racket r) = @(racket 1.5). In this case, @(racket r) = @(racket 1.5).
This function would have been named @racket[contour], except the name was already used by a deprecated function.
It may be renamed in the future, with @racket[isoline] as an alias.
@doc-apply[contours]{ @doc-apply[contours]{
Returns a renderer that plots contour lines, or lines of constant value (height). Returns a renderer that plots contour lines, or lines of constant value (height).
@ -256,10 +252,6 @@ For example, the canonical saddle, with its gradient field superimposed:
@section{2D Rectangle Renderers} @section{2D Rectangle Renderers}
@defstruct[ivl ([min real?] [max real?])]{
Represents a closed interval. Used to give bounds to rectangles in @(racket rectangles), @(racket rectangles3d), and functions derived from them.
}
@doc-apply[rectangles]{ @doc-apply[rectangles]{
Returns a renderer that draws rectangles. Returns a renderer that draws rectangles.
The rectanges are given as a list of vectors of intervals---each vector defines the bounds of a rectangle. For example, The rectanges are given as a list of vectors of intervals---each vector defines the bounds of a rectangle. For example,
@ -271,10 +263,10 @@ The rectanges are given as a list of vectors of intervals---each vector defines
Returns a renderer that draws a histogram approximating the area under a curve. Returns a renderer that draws a histogram approximating the area under a curve.
The @(racket #:samples) argument determines the accuracy of the calculated areas. The @(racket #:samples) argument determines the accuracy of the calculated areas.
@interaction[#:eval plot-eval @interaction[#:eval plot-eval
(let () (require (only-in plot/utils linear-seq))
(define (f x) (exp (* -1/2 (sqr x)))) (define (f x) (exp (* -1/2 (sqr x))))
(plot (list (area-histogram f (linear-seq -4 4 10)) (plot (list (area-histogram f (linear-seq -4 4 10))
(function f -4 4))))] (function f -4 4)))]
} }
@doc-apply[discrete-histogram]{ @doc-apply[discrete-histogram]{
@ -292,6 +284,17 @@ To plot histograms side-by-side, pass the appropriate @(racket #:x-min) value to
#:label "Numbers per number")))] #:label "Numbers per number")))]
} }
@doc-apply[stacked-histogram]{
Returns a renderer that draws a stacked histogram.
The heights of each bar section are given as a list.
@examples[#:eval plot-eval
(plot (stacked-histogram (list #(a (1 1 1)) #(b (1.5 3))
#(c ()) #(d (1/2)))
#:invert? #t
#:labels '("Red" #f "Blue"))
#:legend-anchor 'top-right)]
}
@section{2D Plot Decoration Renderers} @section{2D Plot Decoration Renderers}
@doc-apply[x-axis]{ @doc-apply[x-axis]{

View File

@ -6,11 +6,6 @@
@title[#:tag "renderer3d"]{3D Renderers} @title[#:tag "renderer3d"]{3D Renderers}
@defproc[(renderer3d? [value any/c]) boolean?]{
Returns @racket[#t] if @racket[value] is a 3D @tech{renderer}; that is, if @racket[plot3d] can plot @racket[value].
The following functions create such renderers.
}
@section{3D Renderer Function Arguments} @section{3D Renderer Function Arguments}
As with functions that return 2D renderers, functions that return 3D renderers always have these kinds of arguments: As with functions that return 2D renderers, functions that return 3D renderers always have these kinds of arguments:
@ -29,7 +24,6 @@ Returns a renderer that draws points in 3D space.
For example, a scatter plot of points sampled uniformly from the surface of a sphere: For example, a scatter plot of points sampled uniformly from the surface of a sphere:
@interaction[#:eval plot-eval @interaction[#:eval plot-eval
(let ()
(define (runif) (- (* 2 (random)) 1)) (define (runif) (- (* 2 (random)) 1))
(define (rnormish) (+ (runif) (runif) (runif) (runif))) (define (rnormish) (+ (runif) (runif) (runif) (runif)))
@ -43,24 +37,30 @@ For example, a scatter plot of points sampled uniformly from the surface of a sp
(define zs (map / zs0 mags)) (define zs (map / zs0 mags))
(plot3d (points3d (map vector xs ys zs) #:sym 'dot) (plot3d (points3d (map vector xs ys zs) #:sym 'dot)
#:altitude 25))] #:altitude 25)]
}
@doc-apply[vector-field3d]{
Returns a renderer that draws a vector field in 3D space.
The arguments are interpreted identically to the corresponding arguments to @racket[vector-field].
@examples[#:eval plot-eval
(plot3d (vector-field3d (λ (x y z) (vector x z y))
-2 2 -2 2 -2 2))]
} }
@section{3D Line Renderers} @section{3D Line Renderers}
@doc-apply[lines3d]{ @doc-apply[lines3d]{
Returns a renderer that draws connected lines, with points in 3D space. Returns a renderer that draws connected lines.
The @racket[parametric3d] function is defined in terms of this one.
} }
@doc-apply[parametric3d]{ @doc-apply[parametric3d]{
Returns a renderer that plots a vector-valued function of time. For example, Returns a renderer that plots a vector-valued function of time. For example,
@interaction[#:eval plot-eval @interaction[#:eval plot-eval
(plot3d (parametric3d (λ (t) (require (only-in plot/utils 3d-polar->3d-cartesian))
(vector (* (cos (* 80 t)) (cos t)) (plot3d (parametric3d (λ (t) (3d-polar->3d-cartesian (* t 80) t 1))
(* (sin (* 80 t)) (cos t)) (- pi) pi #:samples 3000 #:alpha 0.5)
(sin t)))
(- pi) pi
#:samples 3000 #:alpha 0.5)
#:altitude 25)] #:altitude 25)]
} }
@ -79,27 +79,36 @@ Returns a renderer that plots a two-input, one-output function. For example,
Returns a renderer that plots a function from latitude and longitude to radius. Returns a renderer that plots a function from latitude and longitude to radius.
Currently, latitudes range from @(racket 0) to @(racket (* 2 pi)), and longitudes from @(racket (* -1/2 pi)) to @(racket (* 1/2 pi)). Currently, latitudes range from @(racket 0) to @(racket (* 2 pi)), and longitudes from @(racket (* -1/2 pi)) to @(racket (* 1/2 pi)).
These intervals may become optional arguments to @racket[polar3d] in the future.
A sphere is the graph of a polar function of constant radius: A sphere is the graph of a polar function of constant radius:
@interaction[#:eval plot-eval (plot3d (polar3d (λ (θ ρ) 1)) #:altitude 25)] @interaction[#:eval plot-eval (plot3d (polar3d (λ (θ ρ) 1)) #:altitude 25)]
Combining polar function renderers allows faking latitudes or longitudes in larger ranges, to get, for example, a seashell plot: Combining polar function renderers allows faking latitudes or longitudes in larger ranges, to get, for example, a seashell plot:
@interaction[#:eval plot-eval @interaction[#:eval plot-eval
(let () (parameterize ([plot-decorations? #f]
[plot3d-samples 75])
(define (f1 θ ρ) (+ 1 (/ θ 2 pi) (* 1/8 (sin (* 8 ρ))))) (define (f1 θ ρ) (+ 1 (/ θ 2 pi) (* 1/8 (sin (* 8 ρ)))))
(define (f2 θ ρ) (+ 0 (/ θ 2 pi) (* 1/8 (sin (* 8 ρ))))) (define (f2 θ ρ) (+ 0 (/ θ 2 pi) (* 1/8 (sin (* 8 ρ)))))
(plot3d (list (polar3d f1 #:color "navajowhite" (plot3d (list (polar3d f1 #:color "navajowhite"
#:line-style 'transparent #:alpha 2/3) #:line-style 'transparent #:alpha 2/3)
(polar3d f2 #:color "navajowhite" (polar3d f2 #:color "navajowhite"
#:line-style 'transparent #:alpha 2/3)) #:line-style 'transparent #:alpha 2/3))))]
#:title "A Seashell" #:x-label #f #:y-label #f))]
} }
@section{3D Contour (Isoline) Renderers} @section{3D Contour (Isoline) Renderers}
@doc-apply[isoline3d]{ @doc-apply[isoline3d]{
Returns a renderer that plots a single contour line on the surface of a function. Returns a renderer that plots a single contour line on the surface of a function.
The appearance keyword arguments are interpreted identically to the appearance keyword arguments to @(racket isoline).
This function is not terribly useful by itself, but can be when combined with others:
@interaction[#:eval plot-eval
(define (saddle x y) (- (sqr x) (sqr y)))
(plot3d (list (surface3d saddle -1 1 -1 1)
(isoline3d saddle 1/4 #:width 2 #:style 'long-dash)))]
} }
@doc-apply[contours3d]{ @doc-apply[contours3d]{
@ -110,8 +119,7 @@ In particular, when @(racket levels) is @(racket 'auto), contour values correspo
For example, For example,
@interaction[#:eval plot-eval (plot3d (contours3d (λ (x y) (+ (sqr x) (sqr y))) -1.1 1.1 -1.1 1.1 @interaction[#:eval plot-eval (plot3d (contours3d (λ (x y) (+ (sqr x) (sqr y))) -1.1 1.1 -1.1 1.1
#:label "z = x^2 + y^2") #:label "z = x^2 + y^2"))]
#:legend-anchor 'top-left)]
} }
@doc-apply[contour-intervals3d]{ @doc-apply[contour-intervals3d]{
@ -121,8 +129,7 @@ The appearance keyword arguments are interpreted identically to the appearance k
For example, For example,
@interaction[#:eval plot-eval (plot3d (contour-intervals3d (λ (x y) (+ (sqr x) (sqr y))) @interaction[#:eval plot-eval (plot3d (contour-intervals3d (λ (x y) (+ (sqr x) (sqr y)))
-1.1 1.1 -1.1 1.1 -1.1 1.1 -1.1 1.1
#:label "z = x^2 + y^2") #:label "z = x^2 + y^2"))]
#:legend-anchor 'top-left)]
} }
@section{3D Isosurface Renderers} @section{3D Isosurface Renderers}
@ -141,13 +148,12 @@ For example, a sphere is all the points in which the Euclidean distance function
Returns a renderer that plots multiple isosurfaces. The appearance keyword arguments are interpreted similarly to those of @(racket contours). Returns a renderer that plots multiple isosurfaces. The appearance keyword arguments are interpreted similarly to those of @(racket contours).
Use this to visualize functions from three inputs to one output; for example: Use this to visualize functions from three inputs to one output; for example:
@interaction[#:eval plot-eval (let () @interaction[#:eval plot-eval
(define (saddle x y z) (- (sqr x) (* 1/2 (+ (sqr y) (sqr z))))) (define (saddle x y z) (- (sqr x) (* 1/2 (+ (sqr y) (sqr z)))))
(plot3d (isosurfaces3d saddle #:d-min -1 #:d-max 1 #:label "") (plot3d (isosurfaces3d saddle #:d-min -1 #:d-max 1 #:label "")
#:x-min -2 #:x-max 2 #:x-min -2 #:x-max 2
#:y-min -2 #:y-max 2 #:y-min -2 #:y-max 2
#:z-min -2 #:z-max 2 #:z-min -2 #:z-max 2)]
#:legend-anchor 'top-left))]
If it helps, think of the output of @(racket f) as a density or charge. If it helps, think of the output of @(racket f) as a density or charge.
} }
@ -159,12 +165,14 @@ Returns a renderer that draws rectangles.
This can be used to draw histograms; for example, This can be used to draw histograms; for example,
@interaction[#:eval plot-eval @interaction[#:eval plot-eval
(let () (require (only-in plot/utils bounds->intervals linear-seq))
(define (norm2 x y) (exp (* -1/2 (+ (sqr (- x 5)) (sqr y))))) (define (norm2 x y) (exp (* -1/2 (+ (sqr (- x 5)) (sqr y)))))
(define x-ivls (bounds->intervals (linear-seq 2 8 10))) (define x-ivls (bounds->intervals (linear-seq 2 8 16)))
(define y-ivls (bounds->intervals (linear-seq -5 5 10))) (define y-ivls (bounds->intervals (linear-seq -5 5 16)))
(define x-mids (linear-seq 2 8 9 #:start? #f #:end? #f)) (define x-mids (linear-seq 2 8 15 #:start? #f #:end? #f))
(define y-mids (linear-seq -5 5 9 #:start? #f #:end? #f)) (define y-mids (linear-seq -5 5 15 #:start? #f #:end? #f))
(plot3d (rectangles3d (append* (plot3d (rectangles3d (append*
(for/list ([y-ivl (in-list y-ivls)] (for/list ([y-ivl (in-list y-ivls)]
[y (in-list y-mids)]) [y (in-list y-mids)])
@ -173,7 +181,7 @@ This can be used to draw histograms; for example,
(define z (norm2 x y)) (define z (norm2 x y))
(vector x-ivl y-ivl (ivl 0 z))))) (vector x-ivl y-ivl (ivl 0 z)))))
#:alpha 3/4 #:alpha 3/4
#:label "Appx. 2D Normal")))] #:label "Appx. 2D Normal"))]
} }
@doc-apply[discrete-histogram3d]{ @doc-apply[discrete-histogram3d]{
@ -185,3 +193,12 @@ Missing pairs are not drawn; for example,
#:label "Missing (b,a)" #:label "Missing (b,a)"
#:color 4 #:line-color 4))] #:color 4 #:line-color 4))]
} }
@doc-apply[stacked-histogram3d]{
Returns a renderer that draws a stacked histogram.
Think of it as a version of @racket[discrete-histogram] that allows multiple values to be specified for each pair of categories.
@examples[#:eval plot-eval
(define data '(#(a a (1 1 1)) #(a b (1.5 3)) #(b b ()) #(b a (1/2))))
(plot3d (stacked-histogram3d data #:labels '("Red" #f "Blue")
#:alphas '(2/3 1 2/3)))]
}

View File

@ -21,6 +21,20 @@ To put log ticks on the @italic{x} axis, set the @racket[plot-x-ticks] parameter
(plot (function sin 1 100)))] (plot (function sin 1 100)))]
See @secref["ticks"] for more details on parameterizing a plot's axis ticks. See @secref["ticks"] for more details on parameterizing a plot's axis ticks.
@margin-note*{
To sample nonlinearly, the @italic{inverse} of a transform is applied to linearly sampled points. See @racket[make-axis-transform] and @racket[nonlinear-seq].}
Renderers cooperate with the current transforms by sampling nonlinearly. For example,
@interaction[#:eval plot-eval
(parameterize ([plot-x-transform log-transform])
(plot3d (surface3d + 0.01 1 0.01 1)))]
Notice that the surface is sampled uniformly in appearance even though the @italic{x}-axis ticks are not spaced uniformly.
Transforms are applied to the primitive shapes that comprise a plot:
@interaction[#:eval plot-eval
(parameterize ([plot-x-transform log-transform])
(plot3d (surface3d + 0.01 1 0.01 1 #:samples 3)))]
Here, the renderer returned by @racket[surface3d] does not have to bend the polygons it draws; @racket[plot3d] does this automatically (by recursive subdivision).
@doc-apply[plot-x-transform] @doc-apply[plot-x-transform]
@doc-apply[plot-y-transform] @doc-apply[plot-y-transform]
@doc-apply[plot-z-transform]{ @doc-apply[plot-z-transform]{
@ -85,8 +99,6 @@ The @(racket freq) parameter controls the ``shakiness'' of the transform. At hig
[plot-z-transform (hand-drawn-transform 50)]) [plot-z-transform (hand-drawn-transform 50)])
(plot3d (contour-intervals3d (λ (x y) (- (sqr x) (sqr y))) (plot3d (contour-intervals3d (λ (x y) (- (sqr x) (sqr y)))
-1 1 -1 1 #:samples 9)))] -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]{ @doc-apply[axis-transform/c]{
@ -142,11 +154,14 @@ For example,
@interaction[#:eval plot-eval @interaction[#:eval plot-eval
(match-let ([(invertible-function f g) (match-let ([(invertible-function f g)
(apply-axis-transform log-transform 1 3)]) (apply-axis-transform log-transform 1 3)])
(values (list (f 1) (f 2) (f 3)) (define xs '(1 2 3))
(list (g 1) (g 2.2618595071429146) (g 3))))] (define new-xs (map f xs))
(define old-xs (map g new-xs))
(values new-xs old-xs))]
Technically, @racket[fun] does not need to be truly invertible. 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]. 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];
that is, always @racket[(f (g x)) = x] but not necessarily @racket[(g (f x)) = x].
If @racket[f] and @racket[g] had to be strict inverses of each other, there could be no @racket[collapse-transform]. If @racket[f] and @racket[g] had to be strict inverses of each other, there could be no @racket[collapse-transform].
} }
@ -200,12 +215,34 @@ For example, compare plots of the same function renderered using both @racket[co
#:legend-anchor 'center)))] #:legend-anchor 'center)))]
} }
@doc-apply[contour-ticks]{
Returns the ticks used for contour values.
This is used internally by renderers returned from @racket[contours], @racket[contour-intervals], @racket[contours3d], @racket[contour-intervals3d], and @racket[isosurfaces3d], but is provided for completeness.
When @racket[levels] is @racket['auto], the returned values do not correspond @italic{exactly} with the values of ticks returned by @racket[z-ticks]: they might be missing the endpoint values. For example,
@interaction[#:eval plot-eval
(map pre-tick-value
(filter pre-tick-major? ((plot-z-ticks) 0 1)))
(map pre-tick-value
(contour-ticks (plot-z-ticks) 0 1 'auto #f))]
}
@doc-apply[plot-d-ticks]{
The ticks used for default isosurface values in @racket[isosurfaces3d].
}
@doc-apply[plot-r-ticks]{
The ticks used for radius lines in @racket[polar-axes].
}
@defstruct[ticks ([layout ticks-layout/c] [format ticks-format/c])]{ @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. 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]{ @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. 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 an average number of major ticks.
It is unlikely to mean the exact 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. Without adjusting the number of ticks, layout functions usually cannot find uniformly spaced ticks that will have simple labels after formatting.
@ -260,7 +297,7 @@ Actually, @racket[date-ticks-layout] does not always space ticks @italic{quite}
For example, it rounds ticks that are spaced about one month apart or more to the nearest month. 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. 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. To try 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. 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.
} }
@ -284,7 +321,7 @@ Use @racket[datetime->real] to convert @racket[sql-time] or @racket[plot-time] v
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. 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. To try 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. 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.
} }
@ -313,8 +350,6 @@ The @racket[#:formats] keyword argument is a list of three format strings, repre
@item{@racket["~f"]: replaced by the fractional part, with 2 or more decimal digits} @item{@racket["~f"]: replaced by the fractional part, with 2 or more decimal digits}
@item{@racket["~s"]: replaced by the scale suffix} @item{@racket["~s"]: replaced by the scale suffix}
@item{@racket["~~"]: replaced by ``~''}] @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-scales]
@ -327,7 +362,7 @@ For example, a PLoT user in France would probably begin programs with
(currency-ticks-formats eu-currency-formats)] (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. 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. Cultural sensitivity notwithstanding, when writing for a local audience, it is generally considered proper to use local currency scales and formats for foreign currencies, but use the foreign currency symbol.
} }
@doc-apply[us-currency-scales]{ @doc-apply[us-currency-scales]{

View File

@ -1,27 +0,0 @@
#lang scribble/manual
@(require "common.rkt")
@title[#:tag "todo"]{To Do}
@itemlist[
@item{Planned new renderers
@itemlist[
@item{Functions with integer domains}
@item{2D kernel density estimator}
@item{3D kernel density estimator}
@item{3D decorations: labeled points, axes, grids}
]
}
@item{Possible minor enhancements
@itemlist[
@item{Better depth sorting (possibly split intersecting polygons; look into BSP tree)}
@item{Legend entries have minimum sizes}
@item{Label contour heights on the contour lines}
@item{3D support for exact rational functions (i.e. polynomial at [big..big+ε])}
@item{Join 2D contour lines}
@item{Manually exclude discontinuous points from function renderers: allow values @(racket (hole p1 p2)), @(racket (left-hole p1 p2)), @(racket (right-hole p1 p2))}
@item{@(racket histogram-list) to plot multiple histograms without manually calculating @(racket #:x-min)}
]
}
]

View File

@ -6,10 +6,12 @@
@defmodule[plot/utils] @defmodule[plot/utils]
@;====================================================================================================
@section{Formatting} @section{Formatting}
@doc-apply[digits-for-range]{ @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. Given a range, returns the number of decimal places necessary to distinguish numbers in the range.
This may return negative numbers for large ranges.
@examples[#:eval plot-eval @examples[#:eval plot-eval
(digits-for-range 0.01 0.02) (digits-for-range 0.01 0.02)
@ -17,7 +19,8 @@ Given a range, returns the number of decimal places necessary to distinguish num
} }
@doc-apply[real->plot-label]{ @doc-apply[real->plot-label]{
Converts a real number to a plot label. Used to format axis tick labels, @(racket point-label)s, and numbers in legend entries. Converts a real number to a plot label.
Used to format axis tick labels, @(racket point-label)s, and numbers in legend entries.
@examples[#:eval plot-eval @examples[#:eval plot-eval
(let ([d (digits-for-range 0.01 0.03)]) (let ([d (digits-for-range 0.01 0.03)])
@ -27,14 +30,110 @@ Converts a real number to a plot label. Used to format axis tick labels, @(racke
(real->plot-label 1000000000.1234 4)] (real->plot-label 1000000000.1234 4)]
} }
@doc-apply[ivl->plot-label]{
Converts an interval to a plot label.
If @racket[i] = @racket[(ivl x-min x-max)], the number of digits used is @racket[(digits-for-range x-min x-max 10 extra-digits)] when both endpoints are @racket[rational?].
Otherwise, it is unspecified---but will probably remain @racket[15].
@examples[#:eval plot-eval
(ivl->plot-label (ivl -10.52312 10.99232))
(ivl->plot-label (ivl -inf.0 pi))]
}
@doc-apply[->plot-label]{ @doc-apply[->plot-label]{
Converts a Racket value to a label. Used by @(racket discrete-histogram) and @(racket discrete-histogram3d). Converts a Racket value to a label. Used by @(racket discrete-histogram) and @(racket discrete-histogram3d).
} }
@doc-apply[real->string/trunc]{ @doc-apply[real->string/trunc]{
Like @(racket real->decimal-string), but removes trailing zeros and a trailing decimal point. Like @(racket real->decimal-string), but removes any trailing zeros and any trailing decimal point.
} }
@doc-apply[real->decimal-string*]{
Like @racket[real->decimal-string], but accepts both a maximum and minimum number of digits.
@examples[#:eval plot-eval
(real->decimal-string* 1 5 10)
(real->decimal-string* 1.123456 5 10)
(real->decimal-string* 1.123456789123456 5 10)]
Applying @racket[(real->decimal-string* x min-digits)] yields the same value as @racket[(real->decimal-string x min-digits)].
}
@doc-apply[integer->superscript]{
Converts an integer into a string of superscript Unicode characters.
@examples[#:eval plot-eval
(integer->superscript -1234567890)]
Systems running some out-of-date versions of Windows XP have difficulty with Unicode superscripts for 4 and up.
Because @racket[integer->superscript] is used by every number formatting function to format exponents, if you have such a system, PLoT will apparently not format all numbers with exponents correctly (until you update it).
}
@;{
@doc-apply[format-tick-labels]{
}
}
@;====================================================================================================
@section{Sampling}
@doc-apply[linear-seq]{
Returns a list of uniformly 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)
(define xs (linear-seq -1 1 11))
(plot (lines (map vector xs (map sqr xs))))]
}
@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]{
Generates a list of reals that, if transformed using @(racket transform), would be uniformly spaced.
This is used to generate samples for transformed axes.
@examples[#:eval plot-eval
(linear-seq 1 10 4)
(nonlinear-seq 1 10 4 log-transform)
(parameterize ([plot-x-transform log-transform])
(plot (area-histogram sqr (nonlinear-seq 1 10 4 log-transform))))]
}
@;{
@doc-apply[build-linear-seq]
@doc-apply[make-function->sampler]
@doc-apply[make-2d-function->sampler]
@doc-apply[make-3d-function->sampler]
@doc-apply[sample-exact->inexact]
@doc-apply[2d-sample-exact->inexact]
@doc-apply[3d-sample-exact->inexact]
}
@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)).
With some functions, mapping over a list can be done much more quickly if done specially.
(An example is a piecewise function with many pieces that first must decide which interval its input belongs to. Deciding that for many inputs can be done more efficiently by sorting all the inputs first.)
Renderer-producing functions that accept a @(racket (real? . -> . real?)) also accept a @(racket mapped-function), and use its @(racket fmap) to sample more efficiently.
}
@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).
}
@;====================================================================================================
@section{Plot Colors and Styles} @section{Plot Colors and Styles}
@doc-apply[color-seq]{ @doc-apply[color-seq]{
@ -133,9 +232,11 @@ Integer brush styles repeat starting at @(racket 7).
(map ->brush-style '(4 5 6))] (map ->brush-style '(4 5 6))]
} }
@;====================================================================================================
@section{Plot-Specific Math} @section{Plot-Specific Math}
@subsection{Real Numbers} @;----------------------------------------------------------------------------------------------------
@subsection{Real Functions}
@doc-apply[degrees->radians]{ @doc-apply[degrees->radians]{
Converts degrees to radians. Converts degrees to radians.
@ -145,45 +246,209 @@ Converts degrees to radians.
Converts radians to degrees. Converts radians to degrees.
} }
@doc-apply[linear-seq]{ @doc-apply[polar->cartesian]{
Returns a list of evenly spaced real numbers between @(racket start) and @(racket end). Converts 2D polar coordinates to 3D cartesian coordinates.
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*]{ @doc-apply[3d-polar->3d-cartesian]{
Like @(racket linear-seq), but accepts a list of reals instead of a start and end. Converts 3D polar coordinates to 3D cartesian coordinates.
The @(racket #:start?) and @(racket #:end?) keyword arguments work as in @(racket linear-seq). See @racket[parametric3d] for an example of use.
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]{ @doc-apply[infinite?]{
Generates a list of reals that, if transformed using @(racket transform), would be evenly spaced. Returns @racket[#t] if @racket[x] is either @racket[+inf.0] or @racket[-inf.0].
This is used to generate samples for transformed axes.
@examples[#:eval plot-eval @examples[#:eval plot-eval
(linear-seq 1 10 4) (map infinite? (list +inf.0 -inf.0 0 'bob))]
(nonlinear-seq 1 10 4 log-transform)
(parameterize ([plot-x-transform log-transform])
(plot (area-histogram sqr (nonlinear-seq 1 10 4 log-transform))))]
} }
@subsection[#:tag "math.vectors"]{Vectors} @doc-apply[nan?]{
Returns @racket[#t] if @racket[x] is @racket[+nan.0].
@examples[#:eval plot-eval
(map nan? (list +nan.0 +inf.0 0 'bob))]
}
@doc-apply[ceiling-log/base]{
Like @racket[(ceiling (/ (log x) (log b)))], but @racket[ceiling-log/base] is not susceptible to floating-point error when given an exact @racket[x].
@examples[#:eval plot-eval
(ceiling (/ (log 1/1000) (log 10)))
(ceiling-log/base 10 1/1000)]
Various number-formatting functions use this.
}
@subsection[#:tag "math.intervals"]{Intervals} @doc-apply[floor-log/base]{
Like @racket[(floor (/ (log x) (log b)))], but @racket[floor-log/base] is not susceptible to floating-point error when given an exact @racket[x].
@examples[#:eval plot-eval
(floor (/ (log 1000) (log 10)))
(floor-log/base 10 1000)]
Various number-formatting functions use this.
}
@doc-apply[maybe-inexact->exact]{
Returns @racket[#f] if @racket[x] is @racket[#f]; otherwise @racket[(inexact->exact x)].
Use this to convert interval endpoints, which may be @racket[#f], to exact numbers.
}
@;----------------------------------------------------------------------------------------------------
@subsection[#:tag "math.vectors"]{Vector Functions}
@doc-apply[v+]
@doc-apply[v-]
@doc-apply[vneg]
@doc-apply[v*]
@doc-apply[v/]{
Vector arithmetic. Equivalent to @racket[vector-map]p-ing arithmetic operators over vectors, but specialized so that 2- and 3-vector operations are much faster.
@examples[#:eval plot-eval
(v+ #(1 2) #(3 4))
(v- #(1 2) #(3 4))
(vneg #(1 2))
(v* #(1 2 3) 2)
(v/ #(1 2 3) 2)]
}
@doc-apply[v=]{
Like @racket[equal?] specialized to numeric vectors, but compares elements using @racket[=].
@examples[#:eval plot-eval
(equal? #(1 2) #(1 2))
(equal? #(1 2) #(1.0 2.0))
(v= #(1 2) #(1.0 2.0))]
}
@doc-apply[vcross]{
Returns the right-hand vector cross product of @racket[v1] and @racket[v2].
@examples[#:eval plot-eval
(vcross #(1 0 0) #(0 1 0))
(vcross #(0 1 0) #(1 0 0))
(vcross #(0 0 1) #(0 0 1))]
}
@doc-apply[vcross2]{
Returns the signed area of the 2D parallelogram with sides @racket[v1] and @racket[v2].
Equivalent to @racket[(vector-ref (vcross (vector-append v1 #(0)) (vector-append v2 #(0))) 2)] but faster.
@examples[#:eval plot-eval
(vcross2 #(1 0) #(0 1))
(vcross2 #(0 1) #(1 0))]
}
@doc-apply[vdot]{
Returns the dot product of @racket[v1] and @racket[v2].
}
@doc-apply[vmag^2]{
Returns the squared magnitude of @racket[v]. Equivalent to @racket[(vdot v v)].
}
@doc-apply[vmag]{
Returns the magnitude of @racket[v]. Equivalent to @racket[(sqrt (vmag^2 v))].
}
@doc-apply[vnormalize]{
Returns a normal vector in the same direction as @racket[v]. If @racket[v] is a zero vector, returns @racket[v].
@examples[#:eval plot-eval
(vnormalize #(1 1 0))
(vnormalize #(1 1 1))
(vnormalize #(0 0 0.0))]
}
@doc-apply[vcenter]{
Returns the center of the smallest bounding box that contains @racket[vs].
@examples[#:eval plot-eval
(vcenter '(#(1 1) #(2 2)))]
}
@doc-apply[vrational?]{
Returns @racket[#t] if every element of @racket[v] is @racket[rational?].
@examples[#:eval plot-eval
(vrational? #(1 2))
(vrational? #(+inf.0 2))
(vrational? #(#f 1))]
}
@;----------------------------------------------------------------------------------------------------
@subsection[#:tag "math.intervals"]{Intervals and Interval Functions}
@defstruct[ivl ([min real?] [max real?])]{
Represents a closed interval.
An interval with two real-valued endpoints always contains the endpoints in order:
@interaction[#:eval plot-eval (ivl 0 1) (ivl 1 0)]
@;{
If either endpoint is @racket[+nan.0], both are, and the interval represents the empty interval:
@interaction[#:eval plot-eval (ivl +nan.0 0) (ivl 0 +nan.0)]
}
An interval can have infinite endpoints:
@interaction[#:eval plot-eval (ivl -inf.0 0) (ivl 0 +inf.0) (ivl -inf.0 +inf.0)]
Functions that return rectangle renderers, such as @racket[rectangles] and @racket[discrete-histogram3d], accept vectors of @racket[ivl]s as arguments.
The @racket[ivl] struct type is also provided by @racketmodname[plot] so users of such renderers do not have to require @racketmodname[plot/utils].
}
@doc-apply[rational-ivl?]{
Returns @racket[#t] if @racket[i] is an interval and each of its endpoints is @racket[rational?].
@examples[#:eval plot-eval
(map rational-ivl? (list (ivl -1 1) (ivl -inf.0 2) 'bob))]
}
@;{
@doc-apply[empty-ivl]{
The empty interval.
}
@defproc[(ivl-meet [i ivl?] ...) ivl?]{
Returns the intersection of the given intervals.
@examples[#:eval plot-eval
(ivl-meet)
(ivl-meet (ivl 0 1) (ivl 2 3))
(ivl-meet (ivl 0 2) (ivl 1 3))
(ivl-meet empty-ivl (ivl 0 1))]
}
@defproc[(ivl-join [i ivl?] ...) ivl?]{
Returns the smallest interval that contains all the points in the given intervals.
@examples[#:eval plot-eval
(ivl-join)
(ivl-join (ivl 0 1) (ivl 2 3))
(ivl-join (ivl 0 2) (ivl 1 3))
(ivl-join empty-ivl (ivl 0 1))]
Think of it as returning an interval union, but with any gaps filled.
}
@doc-apply[ivl-center]{
@examples[#:eval plot-eval
(ivl-center (ivl -1 1))
(ivl-center empty-ivl)
(ivl-center (ivl -inf.0 +inf.0))]
}
@doc-apply[ivl-contains?]{
@examples[#:eval plot-eval
(ivl-contains? (ivl -1 1) 0)
(ivl-contains? (ivl -1 1) 2)
(ivl-contains? (ivl -inf.0 +inf.0) 0)
(ivl-contains? empty-ivl 0)]
}
@doc-apply[ivl-empty?]{
@examples[#:eval plot-eval
(ivl-empty? empty-ivl)
(ivl-empty? (ivl 0 0))]
}
@doc-apply[ivl-length]{
@examples[#:eval plot-eval
(ivl-length empty-ivl)
(ivl-length (ivl 0 0))
(ivl-length (ivl -1 1))
(ivl-length (ivl -inf.0 +inf.0))]
}
@doc-apply[ivl-inexact->exact]
@doc-apply[ivl-rational?]
@doc-apply[ivl-singular?]
@doc-apply[ivl-translate]
@doc-apply[ivl-zero-length?]
}
@doc-apply[bounds->intervals]{ @doc-apply[bounds->intervals]{
Given a list of points, returns intervals between each pair. Given a list of points, returns intervals between each pair.
@ -192,37 +457,78 @@ Use this to construct inputs for @(racket rectangles) and @(racket rectangles3d)
@examples[#:eval plot-eval (bounds->intervals (linear-seq 0 1 5))] @examples[#:eval plot-eval (bounds->intervals (linear-seq 0 1 5))]
} }
@subsection[#:tag "math.rectangles"]{Rectangles} @;----------------------------------------------------------------------------------------------------
@;{
@subsection[#:tag "math.rectangles"]{Rectangles and Rectangle Functions}
@margin-note*{The @racket[rect-meet] and @racket[rect-join] functions define a @link["http://en.wikipedia.org/wiki/Lattice_%28order%29"]{pointed lattice} over rectangles.
This fact may seem esoteric, but it allows PLoT to combine multiple renderers with different rectangular bounds in a way that is intuitive and mathematically sound.}
@defproc[(rect-meet [i (vectorof ivl?)] ...) (vectorof ivl?)]{
}
@defproc[(rect-join [i (vectorof ivl?)] ...) (vectorof ivl?)]{
}
@doc-apply[empty-rect]
@doc-apply[rect-area]
@doc-apply[rect-center]
@doc-apply[rect-contains?]
@doc-apply[rect-empty?]
@doc-apply[rect-inexact->exact]
@doc-apply[rect-known?]
@doc-apply[rect-rational?]
@doc-apply[rect-singular?]
@doc-apply[rect-translate]
@doc-apply[rect-zero-area?]
@doc-apply[rational-rect?]
@doc-apply[bounding-rect]
}
@;====================================================================================================
@;{
@section{Marching Squares and Cubes}
@doc-apply[heights->cube-polys]
@doc-apply[heights->lines]
@doc-apply[heights->polys]{
winding warning
}
}
@;====================================================================================================
@section{Dates and Times} @section{Dates and Times}
@doc-apply[datetime->real]{ @doc-apply[datetime->real]{
Converts various date/time representations into UTC seconds, respecting time zone offsets.
For dates, the value returned is the number of seconds since @italic{a system-dependent UTC epoch}.
See @racket[date-ticks] for more information.
To plot a time series using dates pulled from an SQL database, simply set the relevant axis ticks (probably @racket[plot-x-ticks]) to @racket[date-ticks], and convert the dates to seconds using @racket[datetime->real] before passing them to @racket[lines].
To keep time zone offsets from influencing the plot, set them to @racket[0] first.
Does @racket[sql-time?] work?
@racketmodname[db/base]
} }
@defstruct[plot-time ([second (and/c (>=/c 0) (</c 60))] @defstruct[plot-time ([second (and/c (>=/c 0) (</c 60))]
[minute (integer-in 0 59)] [minute (integer-in 0 59)]
[hour (integer-in 0 23)] [hour (integer-in 0 23)]
[day exact-integer?])]{ [day exact-integer?])]{
A time representation that accounts for days, negative times (using negative days), and fractional seconds.
PLoT (specifically @racket[time-ticks]) uses @racket[plot-time] internally to format times, but because renderer-producing functions require only real values,
user code should not need it. It is provided just in case.
} }
@doc-apply[plot-time->seconds] @doc-apply[plot-time->seconds]
@doc-apply[seconds->plot-time]{
@doc-apply[seconds->plot-time] Convert @racket[plot-time]s to real seconds, and vice-versa.
@examples[#:eval plot-eval
(define (plot-time+ t1 t2)
@section{Sampling} (seconds->plot-time (+ (plot-time->seconds t1)
(plot-time->seconds t2))))
@defstruct[mapped-function ([f (any/c . -> . any/c)] [fmap ((listof any/c) . -> . (listof any/c))])]{ (plot-time+ (plot-time 32 0 12 1)
Represents a function that maps over lists differently than @(racket (map f xs)). (plot-time 32 0 14 1))]
With some functions, mapping over a list can be done much more quickly if done specially.
(An example is a piecewise function with many pieces that first must decide which interval its input belongs to. Deciding that for many inputs can be done more efficiently by sorting all the inputs first.)
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).
} }

View File

@ -13,7 +13,8 @@
"contracted/samplers.rkt" "contracted/samplers.rkt"
"contracted/legend.rkt" "contracted/legend.rkt"
"contracted/plot-element.rkt" "contracted/plot-element.rkt"
"contracted/date-time.rkt") "contracted/date-time.rkt"
"contracted/kde.rkt")
(provide (all-from-out "common/contract.rkt") (provide (all-from-out "common/contract.rkt")
(all-from-out "common/marching-squares.rkt") (all-from-out "common/marching-squares.rkt")
@ -28,4 +29,5 @@
(all-from-out "contracted/samplers.rkt") (all-from-out "contracted/samplers.rkt")
(all-from-out "contracted/legend.rkt") (all-from-out "contracted/legend.rkt")
(all-from-out "contracted/plot-element.rkt") (all-from-out "contracted/plot-element.rkt")
(all-from-out "contracted/date-time.rkt")) (all-from-out "contracted/date-time.rkt")
(all-from-out "contracted/kde.rkt"))