racket/collects/scribblings/slideshow/picts.scrbl

1210 lines
48 KiB
Racket

#lang scribble/doc
@(require "ss.rkt" "pict-diagram.rkt"
(for-label racket/gui slideshow/code slideshow/flash slideshow/face
slideshow/balloon slideshow/pict-convert))
@title[#:style 'toc]{Making Pictures}
@declare-exporting[slideshow/pict slideshow]
@defmodule*/no-declare[(slideshow/pict)]{ The
@racketmodname[slideshow/pict] layer provides core functions for
constructing pictures, and it is independent of the slide viewer. This
layer can be used, for example, to generate a picture as encapsulated
PostScript for inclusion into a larger document. The
@racketmodname[slideshow/pict] layer is re-provided by the
@racketmodname[slideshow] language.}
@local-table-of-contents[]
@; ------------------------------------------------------------------------
@section{Pict Datatype}
A @deftech{pict} is a @racket[pict] structure representing an image.
Some functions, such as
@racket[hline], create new simple picts. Other functions, such as
@racket[ht-append], build new picts out of existing picts. In the
latter case, the embedded picts retain their identity, so that
offset-finding functions, such as @racket[lt-find], can find the
offset of an embedded pict in a larger pict.
In addition to its drawing part, a pict has the following
@deftech{bounding box} structure:
@centerline[pict-diagram]
That is, the bounding box has a width @math{w} and a height
@math{h}. For a single text line, @math{d} is descent below the
baseline, and @math{a+d=h}. For multiple text lines (often created
with a function like @racket[vc-append]), @math{a} is the ascent of
the top line, and @math{d} is the descent of the bottom line, so
@math{a+d<h}. Many picts have @math{d=0} and @math{a=h}.
In addition, a pict can have a @defterm{last} sub-pict that
corresponds to the last item on the last line of text, so that extra
lines can be added to the last line. In particular, the @defterm{last}
element is useful for adding closing parentheses to a block of Racket
code, where the last line of code not the longest line in the block.
The size information for a pict is computed when the pict is
created. This strategy supports programs that create new picts though
arbitrarily complex computations on the size and shape of existing
picts. The functions @racket[pict-width], @racket[pict-height],
@racket[pict-descent], and @racket[pict-ascent] extract bounding box
information from a pict.
A pict is a convertible datatype through the @racketmodname[file/convertible]
protocol. Supported conversions include @racket['png-bytes],
@racket['eps-bytes], and @racket['pdf-bytes].
@defstruct[pict ([draw any/c]
[width real?]
[height real?]
[ascent real?]
[descent real?]
[children (listof child?)]
[panbox (or/c #f any/c)]
[last (or/c #f pict-path?)])]{
A @racket[pict] structure is normally not created directly with
@racket[make-pict]. Instead, functions like @racket[text],
@racket[hline], and @racket[dc] are used to construct a pict.
The @racket[draw] field contains the pict's drawing information in an
internal format. Roughly, the drawing information is a procedure that
takes a @racket[dc<%>] drawing context and an offset for the pict's
top-left corner (i.e., it's @tech{bounding box}'s top left corner relative to
the @racket[dc<%>] origin). The state of the @racket[dc<%>] is
intended to affect the pict's drawing; for example, the pen and brush
will be set for a suitable default drawing mode, and the
@racket[dc<%>] scale will be set to scale the resulting image. Use
@racket[draw-pict] (as opposed to @racket[pict-draw]) to draw the
picture.
The @racket[panbox] field is internal and initialized to @racket[#f].
The @racket[last] field indicates a pict within the @racket[children]
list (transitively) that can be treated as the last element of the
last line in the pict. A @racket[#f] value means that the pict is its
own last sub-pict.}
@defstruct[child ([pict pict?]
[dx real?]
[dy real?]
[sx real?]
[sy real?]
[sxy real?]
[syx real?])]{
Records, for a pict constructed of other picts, the transformation to
arrive at a @tech{inverted point} in the composed pict from an
@tech{inverted point} in a constituent pict's. An @deftech{inverted
point} is a point relative to a pict's lower-left corner with an
increasing value moving upward.
A @racket[child] structure is normally not created directly with
@racket[make-child]. Instead, functions like @racket[hc-append] create
@racket[child] structures when combining picts to create a new one.}
@; ------------------------------------------------------------------------
@section{Basic Pict Constructors}
@defproc*[([(dc [draw ((is-a?/c dc<%>) real? real? . -> . any)]
[w real?]
[h real?])
pict?]
[(dc [draw ((is-a?/c dc<%>) real? real? . -> . any)]
[w real?]
[h real?]
[a real?]
[d real?])
pict?])]{
Creates an arbitrary self-rendering pict. The arguments to the
rendering procedure will be a drawing context and top-left location for
drawing.
The @racket[w] and @racket[h] arguments determine the width and height
of the resulting pict's @tech{bounding box}. In the three-argument case, the
descent is @math{0} and the ascent is @racket[h] for the bounding
box; in the five-argument case, @racket[a] and @racket[d] are used
as the bounding box's ascent and descent.
When the rendering procedure is called, the current pen and brush will
be solid and in the pict's color (and linewidth), and the scale and
offset of the drawing context will be set. The text mode will be transparent, but
the font and text colors are not guaranteed to be anything in
particular.}
@defproc*[([(blank [size real? 0]) pict?]
[(blank [w real?] [h real?]) pict?]
[(blank [w real?] [a real?] [d real?]) pict?]
[(blank [w real?] [h real?] [a real?] [d real?]) pict?])]{
Creates a pict that draws nothing. The one-argument case supplies a
value used for both the width and height of the resulting pict's
@tech{bounding box}. In the one- and two-argument
case, the ascent and descent are @math{0} for the resulting pict's
bounding box; in the three-argument case, the height is computed by
adding the given ascent and descent.}
@defproc[(text [content string?]
[style text-style/c null]
[size (integer-in 1 1024) 12]
[angle real? 0])
pict?]{
Creates a pict that draws text. For creating text picts within a slide
presentation, see @racket[t]. The size of the resulting pict may
depend on the value of @racket[dc-for-text-size].
The @racket[style] argument must be one of the following:
@itemize[
@item{@racket[null] --- the default, same as @racket['default]}
@item{a @racket[font%] object}
@item{a font family symbol, such a @racket['roman] (see @racket[font%])}
@item{a font face string, such as @racket["Helvetica"] (see @racket[font%])}
@item{@racket[(cons _str _sym)] combining a face string and a font
family (in case the face is unavailable; see @racket[font%])}
@item{@racket[(cons 'bold style)] for a valid @racket[style]}
@item{@racket[(cons 'italic style)]}
@item{@racket[(cons 'subscript style)]}
@item{@racket[(cons 'superscript style)]}
@item{@racket[(cons 'caps style)]}
@item{@racket[(cons 'combine style)] --- allows kerning and ligatures
(the default, unless the @racket['modern] family is specified)}
@item{@racket[(cons 'no-combine style)] --- renders characters individually}
@item{@racket[(cons 'aligned style)] --- enables hinting, which
rounds metrics to integers}
@item{@racket[(cons 'unaligned style)] --- disables hinting (which is
the default), so that metrics are scalable}
]
If both @racket['combine] and @racket['no-combine] are specified, the
first one in @racket[style] takes precedence. Similarly, if both
@racket['aligned] and @racket['unaligned] are specified, the first one
in !racket[style] takes precedence. If @racket['caps] is specified,
the @racket[angle] must be zero.
The given @racket[size] is in pixels, but it is ignored if a
@racket[font%] object is provided in the text-style.
The @racket[angle] is in radians, and positive values rotate
counter-clockwise. For a non-zero @racket[angle], the resulting
pict's @tech{bounding box} covers the rotated text, and the descent is zero
and the ascent is the height.}
@defproc*[([(hline [w real?] [h real?]
[#:segment seg-length (or/c #f real?) #f]) pict?]
[(vline [w real?] [h real?]
[#:segment seg-length (or/c #f real?) #f]) pict?])]{
Straight lines, centered within their @tech{bounding box}es.}
@defproc[(frame [pict pict?]
[#:segment seg-length (or/c #f real?) #f]
[#:color color (or/c #f string? (is-a?/c color<%>)) #f]
[#:line-width width (or/c #f real?) #f])
pict?]{
Frames a given pict. If the color or line width are provided, the
override settings supplied by the context.}
@defproc*[([(ellipse [w real?] [h real?]) pict?]
[(circle [diameter real?]) pict?]
[(filled-ellipse [w real?] [h real?] [#:draw-border? draw-border? any/c #t]) pict?]
[(disk [diameter real?] [#:draw-border? draw-border? any/c #t]) pict?])]{
Unfilled and filled ellipses.
If @racket[draw-border?] is @racket[#f], then the pen is set to be transparent
before drawing the ellipse.
}
@defproc*[([(rectangle [w real?] [h real?]) pict?]
[(filled-rectangle [w real?]
[h real?]
[#:draw-border? draw-border? any/c #t])
pict?])]{
Unfilled and filled rectangles.
If @racket[draw-border?] is @racket[#f], then the pen is set to be transparent
before drawing the rectangle.
}
@defproc*[([(rounded-rectangle [w real?] [h real?]
[corner-radius real? -0.25]
[#:angle angle real? 0])
pict?]
[(filled-rounded-rectangle [w real?] [h real?]
[corner-radius real? -0.25]
[#:angle angle real? 0]
[#:draw-border? draw-border? any/c #t])
pict?])]{
Unfilled and filled rectangles with rounded corners. The
@racket[corner-radius] is used to determine how much
rounding occurs in the corners. If it is a positive number,
then it determines the radius of a circle touching the edges
in each corner, and the rounding of the rectangle follow the
edge of those circles. If it is a negative number, then the
radius of the circles in the corners is the absolute value of the
@racket[corner-radius] times the smaller of @racket[width]
and @racket[height].
The @racket[angle] determines how much the rectangle is
rotated, in radians.
If @racket[draw-border?] is @racket[#f], then the pen is set to be transparent
before drawing the rectangle.
}
@defproc[(bitmap [img (or/c path-string?
(is-a?/c bitmap%)
(is-a?/c image-snip%))])
pict]{
A pict that display a bitmap. When a path is provided, the image is
loaded with the @racket['unknown/mask] flag, which means that a mask
bitmap is generated if the file contains a mask.
If the bitmap cannot be loaded, if the given @racket[bitmap%] object
is not valid, or if the @racket[bitmap-draft-mode] parameter is set to
@racket[#t], the result pict draws the word ``bitmap failed''.}
@defproc*[([(arrow [size real?] [radians real?]) pict?]
[(arrowhead [size real?] [radians real?]) pict?])]{
Creates an arrow or arrowhead in the specific direction within a
@racket[size] by @racket[size] pict. Points on the arrow may extend
slightly beyond the @tech{bounding box}.}
@defproc*[([(pip-line [dx real?] [dy real?] [size real?]) pict?]
[(pip-arrow-line [dx real?] [dy real?] [size real?]) pict?]
[(pip-arrows-line [dx real?] [dy real?] [size real?]) pict?])]{
Creates a line (with some number of arrowheads) as a zero-sized pict
suitable for use with @racket[pin-over]. The 0-sized picture contains
the starting point.
The @racket[size] is used for the arrowhead size. Even though
@racket[pip-line] creates no arrowheads, it accepts the @racket[size]
argument for consistency with the other functions.}
@defproc*[([(pin-line [pict pict?]
[src pict-path?]
[find-src (pict? pict-path? . -> . (values real? real?))]
[dest pict-path?]
[find-dest (pict? pict-path? . -> . (values real? real?))]
[#:start-angle start-angle (or/c real? #f) #f]
[#:end-angle end-angle (or/c real? #f) #f]
[#:start-pull start-pull real? 1/4]
[#:end-pull end-pull real? 1/4]
[#:line-width line-width (or/c #f real?) #f]
[#:color color (or/c #f string? (is-a?/c color%)) #f]
[#:style style (one-of/c 'transparent 'solid 'xor 'hilite
'dot 'long-dash 'short-dash 'dot-dash
'xor-dot 'xor-long-dash 'xor-short-dash
'xor-dot-dash)
'solid]
[#:under? under? any/c #f])
pict?]
[(pin-arrow-line [arrow-size real?] [pict pict?]
[src pict-path?]
[find-src (pict? pict-path? . -> . (values real? real?))]
[dest pict-path?]
[find-dest (pict? pict-path? . -> . (values real? real?))]
[#:start-angle start-angle (or/c real? #f) #f]
[#:end-angle end-angle (or/c real? #f) #f]
[#:start-pull start-pull real? 1/4]
[#:end-pull end-pull real? 1/4]
[#:line-width line-width (or/c #f real?) #f]
[#:color color (or/c #f string? (is-a?/c color%)) #f]
[#:style style (one-of/c 'transparent 'solid 'xor 'hilite
'dot 'long-dash 'short-dash 'dot-dash
'xor-dot 'xor-long-dash 'xor-short-dash
'xor-dot-dash)
'solid]
[#:under? under? any/c #f]
[#:solid? solid? any/c #t]
[#:hide-arrowhead? any/c #f])
pict?]
[(pin-arrows-line [arrow-size real?] [pict pict?]
[src pict-path?]
[find-src (pict? pict-path? . -> . (values real? real?))]
[dest pict-path?]
[find-dest (pict? pict-path? . -> . (values real? real?))]
[#:start-angle start-angle (or/c real? #f) #f]
[#:end-angle end-angle (or/c real? #f) #f]
[#:start-pull start-pull real? 1/4]
[#:end-pull end-pull real? 1/4]
[#:line-width line-width (or/c #f real?) #f]
[#:color color (or/c #f string? (is-a?/c color%)) #f]
[#:style style (one-of/c 'transparent 'solid 'xor 'hilite
'dot 'long-dash 'short-dash 'dot-dash
'xor-dot 'xor-long-dash 'xor-short-dash
'xor-dot-dash)]
[#:under? under? any/c #f]
[#:solid? solid? any/c #t]
[#:hide-arrowhead? any/c #f])
pict?])]{
Adds a line or line-with-arrows onto @racket[pict], using one of the
pict-finding functions (e.g., @racket[lt-find]) to extract the source
and destination of the line.
If @racket[under?] is true, then the line and arrows are added under
the existing @racket[pict] drawing, instead of on top. If
@racket[solid?] is false, then the arrowheads are hollow instead of
filled.
The @racket[start-angle], @racket[end-angle], @racket[start-pull], and
@racket[end-pull] arguments control the curve of the line (and the
defaults produce a straight line):
@itemize[
@item{The @racket[start-angle] and @racket[end-angle] arguments
specify the direction of curve at its start and end positions;
if either is @racket[#f], it defaults to the angle of a
straight line from the start position to end position.}
@item{The @racket[start-pull] and @racket[end-pull] arguments specify
a kind of momentum for the starting and ending angles; larger
values preserve the angle longer.}
]
The @racket[line-width], @racket[color], and @racket[style] arguments
apply to the added line.
When the @racket[hide-arrowhead?] argument is a true value, then space
for an arrowhead is kept around the line, but the arrowhead itself is
not drawn.}
@defthing[text-style/c contract?]{
A contract that matches the second argument of @racket[text].}
@defboolparam[bitmap-draft-mode on?]{
A parameter that determines whether @racket[bitmap] loads/uses a
bitmap.}
@; ------------------------------------------------------------------------
@section{Pict Combiners}
@defproc*[([(vl-append [d real? 0.0] [pict pict?] ...) pict?]
[(vc-append [d real? 0.0] [pict pict?] ...) pict?]
[(vr-append [d real? 0.0] [pict pict?] ...) pict?]
[(ht-append [d real? 0.0] [pict pict?] ...) pict?]
[(htl-append [d real? 0.0] [pict pict?] ...) pict?]
[(hc-append [d real? 0.0] [pict pict?] ...) pict?]
[(hbl-append [d real? 0.0] [pict pict?] ...) pict?]
[(hb-append [d real? 0.0] [pict pict?] ...) pict?])]{
Creates a new pict as a column (for @racket[v...-append]) or row (for
@racket[h...-append]) of other picts. The optional @racket[d] argument
specifies amount of space to insert between each pair of pictures in
making the column or row.
Different procedures align pictures in the orthogonal direction in
different ways. For example, @racket[vl-append] left-aligns all of the
pictures.
The descent of the result corresponds to baseline that is lowest in
the result among all of the picts' descent-specified baselines;
similarly, the ascent of the result corresponds to the highest
ascent-specified baseline. If at least one @racket[pict] is supplied,
then the last element (as reported by @racket[pict-last]) for the
result is @racket[(or (pict-last pict) pict)] for the using last
supplied @racket[pict].}
@defproc*[([(lt-superimpose [pict pict?] ...) pict?]
[(ltl-superimpose [pict pict?] ...) pict?]
[(lc-superimpose [pict pict?] ...) pict?]
[(lbl-superimpose [pict pict?] ...) pict?]
[(lb-superimpose [pict pict?] ...) pict?]
[(ct-superimpose [pict pict?] ...) pict?]
[(ctl-superimpose [pict pict?] ...) pict?]
[(cc-superimpose [pict pict?] ...) pict?]
[(cbl-superimpose [pict pict?] ...) pict?]
[(cb-superimpose [pict pict?] ...) pict?]
[(rt-superimpose [pict pict?] ...) pict?]
[(rtl-superimpose [pict pict?] ...) pict?]
[(rc-superimpose [pict pict?] ...) pict?]
[(rbl-superimpose [pict pict?] ...) pict?]
[(rb-superimpose [pict pict?] ...) pict?])]{
Creates a new picture by superimposing a set of pictures. The name
prefixes are alignment indicators: horizontal alignment then vertical
alignment.
The descent of the result corresponds to baseline that is lowest in
the result among all of the picts' descent-specified baselines;
similarly, the ascent of the result corresponds to the highest
ascent-specified baseline. The last element (as reported by
@racket[pict-last]) for the result is the lowest, right-most among the
last-element picts of the @racket[pict] arguments, as determined by
comparing the last-element bottom-right corners.}
@defproc*[([(pin-over [base pict?] [dx real?] [dy real?] [pict pict?])
pict?]
[(pin-over [base pict?]
[find-pict pict-path?]
[find (pict? pict-path? . -> . (values real? real?))]
[pict pict?])
pict?])]{
Creates a pict with the same @tech{bounding box}, ascent, and descent as
@racket[base], but with @racket[pict] placed on top. The @racket[dx]
and @racket[dy] arguments specify how far right and down the second
pict's corner is from the first pict's corner. Alternately, the
@racket[find-pict] and @racket[find] arguments find a point in
@racket[base] for @racket[find-pict]; the @racket[find] procedure
should be something like @racket[lt-find].}
@defproc*[([(pin-under [base pict?] [dx real?] [dy real?] [pict pict?])
pict?]
[(pin-under [base pict?]
[find-pict pict?]
[find (pict? pict? . -> . (values real? real?))]
[pict pict?])
pict?])]{
Like @racket[pin-over], but @racket[pict] is drawn before
@racket[base] in the resulting combination.}
@defproc[(table [ncols exact-positive-integer?]
[picts (listof pict?)]
[col-aligns (table-list-of (pict? pict? -> pict?))]
[row-aligns (table-list-of (pict? pict? -> pict?))]
[col-seps (table-list-of real?)]
[row-seps (table-list-of real?)])
pict?]{
Creates a table given a list of picts. The @racket[picts] list is a
concatenation of the table's rows (which means that a Racket
@racket[list] call can be formatted to reflect the shape of the output
table).
The @racket[col-aligns], @racket[row-aligns], @racket[col-seps], and
@racket[row-seps] arguments are ``lists'' specifying the row and
columns alignments separation between rows and columns. For @math{c}
columns and @math{r} rows, the first two should have @math{c} and
@math{r} superimpose procedures, and the last two should have
@math{c-1} and @math{r-1} numbers, respectively. The lists can be
``improper'' (i.e., ending in a number instead of an empty list), in
which case the non-pair cdr is used as the value for all remaining
list items that were expected. The @racket[col-aligns] and
@racket[row-aligns] procedures are used to superimpose all of the
cells in a column or row; this superimposition determines the total
width or height of the column or row, and also determines the
horizontal or vertical placement of each cell in the column or row.}
@; ------------------------------------------------------------------------
@section{Pict Drawing Adjusters}
@defproc*[([(scale [pict pict?] [factor real?]) pict?]
[(scale [pict pict?] [w-factor real?] [h-factor real?]) pict?])]{
Scales a pict drawing, as well as its @tech{bounding box}. The drawing
is scaled by adjusting the destination @racket[dc<%>]'s scale while
drawing the original @racket[pict].}
@defproc[(rotate [pict pict?] [theta real?]) pict?]{
Rotates a pict's drawing by @racket[theta] radians counter-clockwise.
The @tech{bounding box} of the resulting pict is the box encloses the rotated
corners of @racket[pict] (which inflates the area of the bounding
box, unless @racket[theta] is a multiple of half of @racket[pi]). The
ascent and descent lines of the result's bounding box are the
horizontal lines that bisect the rotated original lines; if the ascent
line drops below the descent line, the two lines are flipped.}
@defproc[(ghost [pict pict?]) pict?]{
Creats a container picture that doesn't draw the child picture,
but uses the child's size.}
@defproc[(linewidth [w (or/c real? #f)] [pict pict?]) pict?]{
Selects a specific pen width for drawing, which applies to pen drawing
for @racket[pict] that does not already use a specific pen width.
A @racket[#f] value for @racket[w] makes the pen transparent (in contrast
to a zero value, which means ``as thin as possible for the target device'').}
@defproc[(linestyle [style (one-of/c 'transparent 'solid 'xor 'hilite
'dot 'long-dash 'short-dash 'dot-dash
'xor-dot 'xor-long-dash 'xor-short-dash
'xor-dot-dash)]
[pict pict?])
pict?]{
Selects a specific pen style for drawing, which applies to pen drawing
for @racket[pict] that does not already use a specific pen style.}
@defproc[(colorize [pict pict?] [color (or/c string?
(is-a?/c color%)
(list (integer-in 0 255)
(integer-in 0 255)
(integer-in 0 255)))])
pict?]{
Selects a specific color drawing, which applies to drawing in
@racket[pict] that does not already use a specific color. The
@racket[black-and-white] parameter causes all non-white colors to be
converted to black.}
@defproc[(cellophane [pict pict?] [opacity (real-in 0 1)])
pict?]{
Makes the given @racket[pict] semi-transparent, where an opacity of
@racket[0] is fully transparent, and an opacity of @racket[1] is fully
opaque. See @method[dc<%> set-alpha] for information about the
contexts and cases when semi-transparent drawing works.}
@defproc[(clip [pict pict?]) pict]{
Clips a pict's drawing to its @tech{bounding box}.}
@defproc*[([(inset/clip [pict pict?] [amt real?]) pict?]
[(inset/clip [pict pict?] [h-amt real?] [v-amt real?]) pict?]
[(inset/clip [pict pict?] [l-amt real?] [t-amt real?]
[r-amt real?] [b-amt real?]) pict?])]{
Insets and clips the pict's drawing to its @tech{bounding
box}. Usually, the inset amounts are negative.}
@defform*[[(scale/improve-new-text pict-expr scale-expr)
(scale/improve-new-text pict-expr x-scale-expr y-scale-expr)]]{
Like the @racket[scale] procedure, but also sets
@racket[current-expected-text-scale] while evaluating @racket[pict-expr].}
@defboolparam[black-and-white on?]{
A parameter that determines whether @racket[colorize] uses color or
black-and-white colors.}
@; ------------------------------------------------------------------------
@section{Bounding Box Adjusters}
@defproc*[([(inset [pict pict?] [amt real?]) pict?]
[(inset [pict pict?] [h-amt real?] [v-amt real?]) pict?]
[(inset [pict pict?] [l-amt real?] [t-amt real?]
[r-amt real?] [b-amt real?]) pict?])]{
Extends @racket[pict]'s @tech{bounding box} by adding the given amounts
to the corresponding sides; ascent and descent are extended, too.}
@defproc[(clip-descent [pict pict?]) pict?]{
Truncates @racket[pict]'s @tech{bounding box} by removing the descent part.}
@defproc[(lift-above-baseline [pict pict?] [amt real?]) pict?]{
Lifts @racket[pict] relative to its baseline, extending the
@tech{bounding box} height if necessary.}
@defproc[(drop-below-ascent [pict pict?] [amt real?]) pict?]{
Drops @racket[pict] relative to its ascent line, extending the
@tech{bounding box} height if necessary.}
@defproc[(baseless [pict pict?]) pict?]{
Makes the descent @racket[0] and the ascent the same as the height.}
@defproc[(refocus [pict pict?] [sub-pict pict?]) pict?]{
Assuming that @racket[sub-pict] can be found within @racket[pict],
shifts the overall bounding box to that of @racket[sub-pict] (but
preserving all the drawing of @racket[pict]). The last element, as
reported by @racket[pict-last] is also set to @racket[(or (pict-last
sub-pict) sub-pict)].}
@defproc[(panorama [pict pict?]) pict?]{
Shifts the given pict's @tech{bounding box} to enclose the bounding boxes of
all sub-picts (even @racket[launder]ed picts).}
@defproc[(use-last [pict pict?] [sub-pict pict-path?]) pict?]{
Returns a pict like @racket[pict], but with the last element (as
reported by @racket[pict-last]) set to @racket[sub-pict]. The
@racket[sub-pict] must exist as a sub-pict (or path of sub-picts)
within @racket[pict].}
@defproc[(use-last* [pict pict?] [sub-pict pict-path?]) pict?]{
Propagates the last element of @racket[sub-pict] to @racket[pict].
That is, @racket[use-last*] is like @racket[use-last], but the last
element of @racket[sub-pict] is used as the new last element for
@racket[pict], instead of @racket[sub-pict] itself---unless
@racket[(pict-last sub-pict)] is @racket[#f], in which case
@racket[sub-pict] is used as the last element of @racket[pict].}
@; ------------------------------------------------------------------------
@section{Pict Finders}
@defproc*[([(lt-find [pict pict?] [find pict-path?]) (values real? real?)]
[(ltl-find [pict pict?] [find pict-path?]) (values real? real?)]
[(lc-find [pict pict?] [find pict-path?]) (values real? real?)]
[(lbl-find [pict pict?] [find pict-path?]) (values real? real?)]
[(lb-find [pict pict?] [find pict-path?]) (values real? real?)]
[(ct-find [pict pict?] [find pict-path?]) (values real? real?)]
[(ctl-find [pict pict?] [find pict-path?]) (values real? real?)]
[(cc-find [pict pict?] [find pict-path?]) (values real? real?)]
[(cbl-find [pict pict?] [find pict-path?]) (values real? real?)]
[(cb-find [pict pict?] [find pict-path?]) (values real? real?)]
[(rt-find [pict pict?] [find pict-path?]) (values real? real?)]
[(rtl-find [pict pict?] [find pict-path?]) (values real? real?)]
[(rc-find [pict pict?] [find pict-path?]) (values real? real?)]
[(rbl-find [pict pict?] [find pict-path?]) (values real? real?)]
[(rb-find [pict pict?] [find pict-path?]) (values real? real?)])]{
Locates a pict designated by @racket[find] is within @racket[pict]. If
@racket[find] is a pict, then the @racket[pict] must have been created
as some combination involving @racket[find].
If @racket[find] is a list, then the first element of @racket[find]
must be within @racket[pict], the second element of @racket[find] must
be within the second element, and so on.}
@defproc[(pict-path? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a @racket[pict] or a non-empty
list of @racket[pict]s.}
@defproc[(launder [pict pict?]) pict?]{
Creates a pict that has the same drawing and @tech{bounding box} of
@racket[pict], but which hides all of its sub-picts so that they
cannot be found with functions like @racket[lt-find]. If @racket[pict]
has a last-line pict, then the laundered pict has a fresh last-line
pict with the same shape and location.}
@; ------------------------------------------------------------------------
@section{More Pict Constructors}
@; ----------------------------------------
@subsection{Dingbats}
@defproc[(cloud [w real?]
[h real?]
[color (or/c string? (is-a?/c color%)) "gray"])
pict?]{
Creates a fluffy cloud.}
@defproc[(file-icon [w real?]
[h real?]
[color (or/c string? (is-a?/c color%) any/c)]
[shaded? any/c #f])
pict?]{
Creates a Mac-like file icon, optionally shaded. If @racket[color] is
not a string or @racket[color%] object, it is treated as a boolean, in
which case true means @racket["gray"] and false means
@racket["white"].}
@defproc[(standard-fish [w real?]
[h real?]
[#:direction direction (or/c 'left 'right) 'left]
[#:color color (or/c string? (is-a?/c color%)) "blue"]
[#:eye-color eye-color (or/c string? (is-a?/c color%) #f) "black"]
[#:open-mouth open-mouth (or/c boolean? real?) #f])
pict?]{
Creates a fish swimming either @racket['left] or @racket['right].
If @racket[eye-color] is @racket[#f], no eye is drawn.
The @racket[open-mouth] argument can be either @racket[#f] (mouth
closed), @racket[#t] (mouth fully open), or a number: @racket[0.0] is
closed, @racket[1.0] is fully open, and numbers in between are
partially open.}
@defproc[(jack-o-lantern [size real?]
[pumpkin-color (or/c string? (is-a?/c color%)) "orange"]
[face-color (or/c string? (is-a?/c color%)) "black"])
pict?]{
Creates a jack-o-lantern; use the same pumpkin and face color to get a
plain pumpkin. The @racket[size] determines the width.}
@defproc[(angel-wing [w real?]
[h real?]
[left? any/c])
pict?]{
Creates an angel wing, left or right, or any size. The color and pen
width for drawing the wing outline is the current one.}
@defproc[(desktop-machine [scale real?]
[style (listof symbol?) null])
pict?]{
Produces a picture of ancient desktop computer. The @racket[scale]
argument scales the size relative to the base size of 120 by 115.
The @racket[style] can include any of the following:
@itemlist[
@item{@racket['plt] --- include a Racket logo on the machine's screen}
@item{@racket['binary] --- put 1s and 0s on the machine's screen}
@item{@racket['devil] --- like @racket['binary], and also give the machine
horns and a tail}
]}
@defproc[(thermometer [#:height-% height-% (between/c 0 1) 1]
[#:color-% color-% (between/c 0 1) height-%]
[#:ticks ticks non-exact-negative-integer? 4]
[#:start-color start-color (or/c string? (is-a?/c color%)) "lightblue"]
[#:end-color end-color (or/c string? (is-a?/c color%)) "lightcoral"]
[#:top-circle-diameter top-circle-diameter positive-real? 40]
[#:bottom-circle-diameter bottom-circle-diameter positive-real? 80]
[#:stem-height stem-height positive-real? 180]
[#:mercury-inset mercury-inset positive-real? 8])
pict?]{
Produces a thermometer that consists of a semi-circle on top of a rectangle on
top of a circle. The sizes of the three components are controlled via the
@racket[top-circle-diameter], @racket[stem-height], and @racket[bottom-circle-diameter]
arguments.
The mercury is drawn the same way, but by creating the three components inset from the
versions that draw the boundary of the thermometer. This inset is conrolled by the
@racket[mercury-inset] argument.
The height of the mercury in the thermometer is controlled by the @racket[height-%] argument.
Its color is interpolated between the @racket[start-color] and @racket[end-color], as
determined by the @racket[color-%] argument.
Finally, some number of ticks are drawn, basd on the @racket[ticks] argument.
}
@; ----------------------------------------
@subsection{Balloon Annotations}
@defmodule[slideshow/balloon]{The @racketmodname[slideshow/balloon]
library provides functions for creating and placing cartoon-speech
balloons.}
@defproc[(wrap-balloon [pict pict?]
[spike (or/c 'n 's 'e 'w 'ne 'se 'sw 'nw)]
[dx real?]
[dy real?]
[color (or/c string? (is-a?/c color%)) balloon-color]
[corner-radius (and/c real? (not/c negative?)) 32])
balloon?]{
Superimposes @racket[pict] on top of a balloon that wraps it.
The @racket[spike] argument indicates the corner from which a spike
protrudes from the balloon (i.e., the spike that points to whatever
the balloon is about). For example, @racket['n] means ``north,'',
which is a spike in the top middle of the balloon.
The @racket[dx] and @racket[dy] arguments specify how far the spike
should protrude. For a @racket['w] spike, @racket[dx] should be
negative, etc.
The @racket[color] argument is the background color for the balloon.
The @racket[corner-radius] argument determines the radius of the cicle
used to roun the balloon's corners. As usual, if it is less than
@racket[1], then it acts as a ratio of the balloon's width or height.
The result is a balloon, not a pict. The @racket[balloon-pict]
function extracts a pict whose @tech{bounding box} does not include the
spike, but includes the rest of the image, and the
@racket[balloon-point-x] and @racket[balloon-point-y] functions
extract the location of the spike point. More typically, the
@racket[pin-balloon] function is used to add a balloon to a pict.}
@defproc[(pip-wrap-balloon [pict pict?]
[spike (or/c 'n 's 'e 'w 'ne 'se 'sw 'nw)]
[dx real?]
[dy real?]
[color (or/c string? (is-a?/c color%)) balloon-color]
[corner-radius (and/c real? (not/c negative?)) 32])
pict?]{
Like @racket[wrap-balloon], but produces a zero-sized pict suitable
for use with @racket[pin-over].}
@defproc*[([(pin-balloon [balloon balloon?]
[base pict?]
[x real?]
[y real?])
pict?]
[(pin-balloon [balloon balloon?]
[base pict?]
[at-pict pict-path?]
[find (pict? pict-path? . -> . (values real? real?))])
pict?])]{
Superimposes the pict in @racket[balloon] onto @racket[base] to
produce a new pict. The balloon is positioned so that its spike points
to the location specified by either @racket[x] and @racket[y]
(numbers) or at the position determined by combining @racket[base] and
@racket[at-pict] with @racket[find]. The @racket[find] function uses
its arguments like @racket[lt-find].
The resulting pict has the same @tech{bounding box}, descent, and ascent as
@racket[base], even if the balloon extends beyond the bounding box.}
@defproc[(balloon [w real?]
[h real?]
[corner-radius (and/c real? (not/c negative?))]
[spike (or/c 'n 's 'e 'w 'ne 'se 'sw 'nw)]
[dx real?]
[dy real?]
[color (or/c string? (is-a?/c color%)) balloon-color])
balloon?]{
Creates a balloon, much like @racket[wrap-balloon] except that the balloon's
width is @racket[w] and its height is @racket[h].}
@defproc*[([(balloon? [v any/c]) boolean?]
[(make-balloon [pict pict?] [x real?] [y real?]) balloon?]
[(balloon-pict [balloon balloon?]) pict?]
[(balloon-point-x [balloon balloon?]) real?]
[(balloon-point-y [balloon balloon?]) real?])]{
A balloon encapsulates a pict and the position of the balloon's spike
relative to the balloon's top-left corner.}
@defthing[balloon-color (or/c string? (is-a?/c color%))]
The default background color for a balloon.
@; ----------------------------------------
@subsection{Face}
@defmodule[slideshow/face]{The @racketmodname[slideshow/face] library
provides functions for a kind of @as-index{Mr. Potatohead}-style face
library.}
@defthing[default-face-color (or/c string (is-a?/c color%))]{
Orange.}
@defproc[(face [mood symbol?]
[color (or/c string (is-a?/c color%)) default-face-color])
pict?]{
Returns a pict for a pre-configured face with the given base
color. The built-in configurations, selected by mood-symbol, are as
follows:
@itemize[
@item{@racket['unhappy] --- @racket[(face* 'none 'plain #t default-face-color 6)]}
@item{@racket['sortof-unhappy] --- @racket[(face* 'worried 'grimace #t default-face-color 6)]}
@item{@racket['sortof-happy] --- @racket[(face* 'worried 'medium #f default-face-color 6)]}
@item{@racket['happy] --- @racket[(face* 'none 'plain #f default-face-color 6)]}
@item{@racket['happier] --- @racket[(face* 'none 'large #f default-face-color 3)]}
@item{@racket['embarrassed] --- @racket[(face* 'worried 'medium #f default-face-color 3)]}
@item{@racket['badly-embarrassed] --- @racket[(face* 'worried 'medium #t default-face-color 3)]}
@item{@racket['unhappier] --- @racket[(face* 'normal 'large #t default-face-color 3)]}
@item{@racket['happiest] --- @racket[(face* 'normal 'huge #f default-face-color 0 -3)]}
@item{@racket['unhappiest] --- @racket[(face* 'normal 'huge #t default-face-color 0 -3)]}
@item{@racket['mad] --- @racket[(face* 'angry 'grimace #t default-face-color 0)]}
@item{@racket['mean] --- @racket[(face* 'angry 'narrow #f default-face-color 0)]}
@item{@racket['surprised] --- @racket[(face* 'worried 'oh #t default-face-color -4 -3 2)]}
]}
@defproc[(face* [eyebrow-kind (or/c 'none 'normal 'worried 'angry)]
[mouth-kind (or/c 'plain 'smaller 'narrow 'medium 'large
'huge 'grimace 'oh 'tongue)]
[frown? any/c]
[color (or/c string (is-a?/c color%))]
[eye-inset real?]
[eyebrow-dy real?]
[pupil-dx real?]
[pupil-dy real?]
[#:eyebrow-shading? eyebrow-on? any/c #t]
[#:mouth-shading? mouth-on? any/c #t]
[#:eye-shading? eye-on? any/c #t]
[#:tongue-shading? tongue-on? any/c #t]
[#:face-background-shading? face-bg-on? any/c #t]
[#:teeth? teeth-on? any/c #t])
pict?]{
Returns a pict for a face:
@itemize[
@item{@racket[eyebrow-kind] determines the eyebrow shape.}
@item{@racket[mouth-kind] determines the mouth shape, combined with
@racket[frown?].}
@item{@racket[frown?] determines whether the mouth is up or down.}
@item{@racket[color] determines the face color.}
@item{@racket[eye-inset] adjusts the eye size; recommend values are
between 0 and 10.}
@item{@racket[eyebrow-dy] adjusts the eyebrows; recommend values:
between -5 and 5.}
@item{@racket[pupil-dx] adjusts the pupil; recommend values are
between -10 and 10.}
@item{@racket[pupil-dy] adjusts the pupil; recommend values are
between -15 and 15.}
]
The @racket[#:eyebrow-shading?] through
@racket[#:face-background-shading?] arguments control whether a
shading is used for on a particular feature in the face (shading tends
to look worse than just anti-aliasing when the face is small). The
@racket[#:teeth?] argument controls the visibility of the teeth for
some mouth shapes.}
@; ----------------------------------------
@subsection{Flash}
@defmodule[slideshow/flash]
@defproc[(filled-flash [width real?]
[height real?]
[n-points exact-positive-integer? 10]
[spike-fraction (real-in 0 1) 0.25]
[rotation real? 0])
pict?]{
Returns a pict for a ``flash'': a spiky oval, like the yellow
background that goes behind a ``new!'' logo on web pages or a box of
cereal.
The @racket[height] and @racket[width] arguments determine the size of
the oval in which the flash is drawn, prior to rotation. The actual
height and width may be smaller if @racket[points] is not a multiple
of 4, and the actual height and width will be different if the flash
is rotated.
The @racket[n-points] argument determines the number of points on the
flash.
The @racket[spike-fraction] argument determines how big the flash
spikes are compared to the bounding oval.
The @racket[rotation] argument specifies an angle in radians for
counter-clockwise rotation.
The flash is drawn in the default color.}
@defproc[(outline-flash [width real?]
[height real?]
[n-points exact-positive-integer? 10]
[spike-fraction (real-in 0 1) 0.25]
[rotation real? 0])
pict?]{
Like @racket[filled-flash], but drawing only the outline.}
@; ------------------------------------------------------------------------
@section{Miscellaneous}
@defproc[(hyperlinkize [pict pict?])
pict?]{
Adds an underline and blue color. The @racket[pict]'s height and
descent are extended.}
@defproc[(scale-color [factor real?]
[color (or/c string (is-a?/c color%))])
(is-a?/c color%)]{
Scales a color, making it brighter or darker. If the factor is less
than 1, the color is darkened by multiplying the RGB components by the
factor. If the factor is greater tham 1, the color is lightened by
dividing the gap between the RGB components and 255 by the factor.}
@defproc[(color-series [dc (is-a?/c dc<%>)]
[max-step exact-nonnegative-integer?]
[step-delta (and/c exact? positive?)]
[start (or/c string? (is-a?/c color%))]
[end (or/c string? (is-a?/c color%))]
[proc (exact? . -> . any)]
[set-pen? any/c]
[set-brush? any/c])
void?]{
Calls a @racket[proc] multiple times, gradually changing the pen
and/or brush color for each call. For the first call, the current pen
and/or brush color matches @racket[start]; for the last call, it
matches @racket[end]; and for intermediate calls, the color is an
intermediate color.
The @racket[max-step] and @racket[step-delta] arguments should be
exact numbers; the procedure is called with each number from 0 to
@racket[max-step] inclusive using a @racket[step-delta] increment.}
@; ------------------------------------------------------------------------
@section{Rendering}
@defparam[dc-for-text-size dc (or/c #f (is-a?/c dc<%>))]{
A parameter that is used to determine the @tech{bounding box} of picts
created with @racket[text].
The drawing context installed in this parameter need not be the same
as the ultimate drawing context, but it should measure text in the same
way. Under normal circumstances, font metrics are the same for all
drawing contexts, so the default value of @racket[dc-for-text-size] is
a @racket[bitmap-dc%] that draws to a 1-by-1 bitmap.}
@defproc[(draw-pict [pict pict?]
[dc (is-a?/c dc<%>)]
[x real?]
[y real?])
void?]{
Draws @racket[pict] to @racket[dc], with its top-left corner at offset
(@racket[x], @racket[y]).}
@defproc[(pict->bitmap [pict pict?])
(is-a?/c bitmap%)]{
Returns a @racket[bitmap%] with an alpha channel, no larger than @racket[pict], with @racket[pict] drawn on it in the top-left corner (@racket[0], @racket[0]).}
@defproc[(make-pict-drawer [pict pict?])
((is-a?/c dc<%>) real? real? . -> . void?)]{
Generates a pict-drawer procedure for multiple renderings of
@racket[pict]. Using the generated procedure can be faster than
repeated calls to @racket[draw-pict].}
@defproc[(show-pict [pict pict?]
[w (or/c #f exact-nonnegative-integer?) #f]
[h (or/c #f exact-nonnegative-integer?) #f])
void?]{
Opens a frame that displays @racket[pict]. The frame adds one method,
@racket[set-pict], which takes a pict to display. The optional
@racket[w] and @racket[h] arguments specify a minimum size for the
frame's drawing area.}
@defparam[current-expected-text-scale scales (list real? real?)]{
A parameter used to refine text measurements to better match an
expected scaling of the image. The @racket[scale/improve-new-text]
form sets this parameter while also scaling the resulting pict.}
@;----------------------------------------
@section{Conversion to Picts}
@defmodule[slideshow/pict-convert]{The
@racketmodname[slideshow/pict-convert] library defines a protocol for
values to convert themselves to @tech{picts}. The protocol
is used by DrRacket's interactions window, for example, to render
values that it prints}
@defthing[prop:pict-convertible struct-type-property?]{
A property whose value should be a procedure matching the
contract @racket[(-> any/c pict?)]. The
procedure is called when a structure with the property is passed to
@racket[pict-convert]; the argument to the procedure is the
structure, and the procedure's result should be a pict.
}
@defthing[prop:pict-convertible? struct-type-property?]{
A property whose value should be a predicate procedure
(i.e., matching the contract @racket[predicate/c]).
If this property is not set, then it is assumed to be
the function @racket[(λ (x) #t)].
If this property is set, then this procedure is called
by @racket[pict-convertible?] to determine if this particular
value is convertible (thereby supporting situations
where some instances of a given struct are convertible
to picts, but others are not).
}
@defproc[(pict-convertible? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] supports the conversion protocol
(by being a struct with the @racket[prop:pict-convertible] property)
and @racket[#f] otherwise.
}
@defproc[(pict-convert [v pict-convertible?]) pict?]{
Requests a data conversion from @racket[v] to a pict.
}