Progressive Picts and Slides
1 Progressive Picts
ppict-do
ppict-do*
ppict-do-state
ppict?
ppict-go
ppict-add
ppict-add*
placer?
refpoint-placer?
coord
grid
cascade
tile
at-find-pict
merge-refpoints
2 Progressive Slides
pslide
2.1 Progressive Slides Legacy Library
pslide
pslide-base-pict
pslide-default-placer
3 Tagged Picts
tag-pict
pict-tag
find-tag
find-tag*
tag-path?
tag-pict-regions
4 Alignment
align/  c
halign/  c
valign/  c
align->h
align->v
align->frac
halign->vcompose
valign->hcompose
pin-over/  align
7.7

Progressive Picts and Slides

Ryan Culpepper <ryanc@racket-lang.org>

 (require ppict/2) package: ppict

The ppict/2 module re-exports the contents of ppict/pict, ppict/tag, ppict/align, and ppict/slideshow2.

Added in version 1.1 of package ppict.

 (require ppict) package: ppict

Deprecated. Like ppict/2 but re-exports the deprecated module ppict/slideshow instead of ppict/slideshow2.

Changed in version 1.1 of package ppict: Deprecated ppict.

1 Progressive Picts

 (require ppict/pict) package: ppict

A progressive pict or “ppict” is a kind of pict that has an associated “pict placer,” which generally represents a position and alignment. New picts can be placed on the progressive pict by calling ppict-add, and the placer can be updated by calling ppict-go. The ppict-do form provides a compact notation for sequences of those two operations.

syntax

(ppict-do base-expr ppict-do-fragment ...)

syntax

(ppict-do* base-expr ppict-do-fragment ...)

 
ppict-do-fragment = #:go placer-expr
  | #:set pict-expr
  | #:next
  | #:alt (ppict-do-fragment ...)
  | #:do [def-or-expr ...]
  | elem-expr
 
  base-expr : pict?
  placer-expr : placer?
  pict-expr : pict?
  elem-expr : (or/c pict? real? #f)
Builds a pict (and optionally a list of intermediate picts) progressively. The ppict-do form returns only the final pict; any uses of #:next are ignored. The ppict-do* form returns two values: the final pict and a list of all partial picts emitted due to #:next (the final pict is not included).

A #:go fragment changes the current placer. A #:set fragment replaces the current pict state altogether with a new computed pict. A #:next fragment saves a pict including only the contents emitted so far (but whose alignment takes into account picts yet to come). A #:alt fragment saves the current pict state, executes the sub-sequence that follows, saves the result (as if the sub-sequence ended with #:next), then restores the saved pict state before continuing.

A #:do fragment embeds definitions and expressions which are run when the pict state is computed. The definitions are bound in the rest of the fragments in the pict.

The elem-exprs are interpreted by the current placer. A numeric elem-expr usually represents a spacing change, but some placers do not support them. A spacing change only affects added picts up until the next placer is installed; when a new placer is installed, the spacing is reset, usually to 0.

The ppict-do-state form tracks the current state of the pict. It is updated before a #:go or #:set fragment or before a sequence of elem-exprs. It is not updated in the middle of a chain of elem-exprs, however.

Examples:
> (define base
    (ppict-do (colorize (rectangle 200 200) "gray")
              #:go (coord 1/2 1/2 'cc)
              (colorize (hline 200 1) "gray")
              #:go (coord 1/2 1/2 'cc)
              (colorize (vline 1 200) "gray")))
> base

image

The use of ppict-do in the definition of base above is equivalent to
(let* ([pp (colorize (rectangle 200 200) "gray")]
       [pp (ppict-go pp (coord 1/2 1/2 'cc))]
       [pp (ppict-add pp (colorize (hline 200 1) "gray"))]
       [pp (ppict-go pp (coord 1/2 1/2 'cc))]
       [pp (ppict-add pp (colorize (vline 1 200) "gray"))])
  pp)

Examples:
> (define circles-down-1
    (ppict-do base
              #:go (grid 2 2 2 1 'ct)
              10
              (circle 20)
              (circle 20)
              30
              (circle 20)))
> circles-down-1

image

> (define circles-down-2
    (ppict-do circles-down-1
              (colorize (circle 20) "red")
              40
              (colorize (circle 20) "red")))
> (inset circles-down-2 20) ; draws outside its bounding box

image

> (inset (clip circles-down-2) 20)

image

> (ppict-do base
            #:go (coord 0 0 'lt)
            (tag-pict (circle 20) 'circA)
            #:go (coord 1 1 'rb)
            (tag-pict (circle 20) 'circB)
            #:set (let ([p ppict-do-state])
                    (pin-arrow-line 10 p
                                    (find-tag p 'circA) rb-find
                                    (find-tag p 'circB) lt-find)))

image

> (let-values ([(final intermediates)
                (ppict-do* base
                           #:go (coord 1/4 1/2 'cb)
                           (text "shapes:")
                           #:go (coord 1/2 1/2 'lb)
                           #:alt [(circle 20)]
                           #:alt [(rectangle 20 20)]
                           (text "and more!"))])
    (append intermediates (list final)))

'(image image image)

The following demonstrates the use of the #:do fragment:

Example:
> (ppict-do base
            #:do [(define c (circle 20))
                  (define r (rectangle 20 20))
                  (set! c (circle 25))]
            #:go (coord 0.5 0.5)
            (hc-append c r))

image

More examples of ppict-do are scattered throughout this section.

Tracks the current state of a ppict-do or ppict-do* form.

procedure

(ppict? x)  boolean?

  x : any/c
Returns #t if x is a progressive pict, #f otherwise.

procedure

(ppict-go p pl)  ppict?

  p : pict?
  pl : placer?
Creates a progressive pict with the given base pict p and the placer pl.

procedure

(ppict-add pp elem ...)  pict?

  pp : ppict?
  elem : (or/c pict? real? #f 'next)

procedure

(ppict-add* pp elem ...)  
pict? (listof pict?)
  pp : ppict?
  elem : (or/c pict? real? #f 'next)
Creates a new pict by adding each elem pict on top of pp according to pp’s placer. The result pict may or may not be a progressive pict, depending on the placer used. The ppict-add function only the final pict; any occurrences of 'next are ignored. The ppict-add* function returns two values: the final pict and a list of all partial picts emitted due to 'next (the final pict is not included).

An elem that is a real number changes the spacing for subsequent additions. A elem that is #f is discarded; it is permitted as a convenience for conditionally including sub-picts. Note that #f is not equivalent to (blank 0), since the latter will cause spacing to be added around it.

procedure

(placer? x)  boolean?

  x : any/c
Returns #t if x is a placer, #f otherwise.

procedure

(refpoint-placer? x)  boolean?

  x : any/c
Returns #t if x is a placer based on a reference point, #f otherwise.

procedure

(coord rel-x    
  rel-y    
  [align    
  #:abs-x abs-x    
  #:abs-y abs-y    
  #:sep sep    
  #:compose composer])  refpoint-placer?
  rel-x : real?
  rel-y : real?
  align : (or/c 'lt 'ct 'rt 'lc 'cc 'rc 'lb 'cb 'rb) = 'cc
  abs-x : real? = 0
  abs-y : real? = 0
  sep : real? = 0
  composer : procedure? = computed from align
Returns a placer that places picts according to rel-x and rel-y, which are interpeted as fractions of the width and height of the base progressive pict. That is, 0, 0 is the top left corner of the base’s bounding box, and 1, 1 is the bottom right. Then abs-x and abs-y offsets are added to get the final reference point.

Additions are aligned according to align, a symbol whose name consists of a horizontal alignment character followed by a vertical alignment character. For example, if align is 'lt, the pict is placed so that its left-top corner is at the reference point; if align is 'rc, the pict is placed so that the center of its bounding box’s right edge coincides with the reference point.

By default, if there are multiple picts to be placed, they are vertically appended, aligned according to the horizontal component of align. For example, if align is 'cc, the default composer is vc-append; for 'lt, the default composer is vl-append. The spacing is initially sep.

Examples:
> (ppict-do base
            #:go (coord 1/2 1/2 'rb)
            (colorize (circle 20) "red")
            #:go (coord 1/2 1/2 'lt)
            (colorize (circle 20) "darkgreen"))

image

> (ppict-do base
            #:go (coord 1 0 'rt #:abs-x -5 #:abs-y 10)
            50 ; change spacing
            (text "abc")
            (text "12345")
            0  ; and again
            (text "ok done"))

image

> (ppict-do base
            #:go (coord 0 0 'lt #:compose ht-append)
            (circle 10)
            (circle 20)
            (circle 30))

image

Changed in version 1.1 of package ppict: Added #:sep argument.

procedure

(grid cols    
  rows    
  col    
  row    
  [align    
  #:abs-x abs-x    
  #:abs-y abs-y    
  #:sep sep    
  #:compose composer])  refpoint-placer?
  cols : exact-positive-integer?
  rows : exact-positive-integer?
  col : exact-integer?
  row : exact-integer?
  align : (or/c 'lt 'ct 'rt 'lc 'cc 'rc 'lb 'cb 'rb) = 'cc
  abs-x : real? = 0
  abs-y : real? = 0
  sep : real? = 0
  composer : procedure? = computed from align
Returns a placer that places picts according to a position in a virtual grid. The row and col indexes are numbered starting at 1.

Uses of grid can be translated into uses of coord, but the translation depends on the alignment. For example, (grid 2 2 1 1 'lt) is equivalent to (coord 0 0 'lt), but (grid 2 2 1 1 'rt) is equivalent to (coord 1/2 0 'rt).

Examples:
> (define none-for-me-thanks
    (ppict-do base
              #:go (grid 2 2 1 1 'lt)
              (text "You do not like")
              (colorize (text "green eggs and ham?") "darkgreen")))
> none-for-me-thanks

image

> (ppict-do none-for-me-thanks
            #:go (grid 2 2 2 1 'rb)
            (colorize (text "I do not like them,") "red")
            (text "Sam-I-am."))

image

Changed in version 1.1 of package ppict: Added #:sep argument.

procedure

(cascade [step-x step-y])  placer?

  step-x : (or/c real? 'auto) = 'auto
  step-y : (or/c real? 'auto) = 'auto
Returns a placer that places picts by evenly spreading them diagonally across the base pict in “cascade” style. This placer does not support changing the spacing by including a real number within the pict sequence.

When a list picts is to be placed, their bounding boxes are normalized to the maximum width and height of all picts in the list; each pict is centered in its new bounding box. The picts are then cascaded so there is step-x space between each of the picts’ left edges; there is also step-x space between the base pict’s left edge and the first pict’s left edge. Similarly for step-y and the vertical spacing.

If step-x or step-y is 'auto, the spacing between the centers of the picts to be placed is determined automatically so that the inter-pict spacing is the same as the spacing between the last pict and the base.

Examples:
> (ppict-do base
            #:go (cascade)
            (colorize (filled-rectangle 100 100) "red")
            (colorize (filled-rectangle 100 100) "blue"))

image

> (ppict-do base
            #:go (cascade 40 20)
            (colorize (filled-rectangle 100 100) "red")
            (colorize (filled-rectangle 100 100) "blue"))

image

procedure

(tile cols rows)  placer?

  cols : exact-positive-integer?
  rows : exact-positive-integer?
Returns a placer that places picts by tiling them in a grid cols columns wide and rows rows high.

Example:
> (ppict-do base
            #:go (tile 2 2)
            (circle 50)
            (rectangle 50 50)
            (jack-o-lantern 50)
            (standard-fish 50 30 #:color "red"))

image

procedure

(at-find-pict find-path    
  [finder    
  align    
  #:abs-x abs-x    
  #:abs-y abs-y    
  #:sep sep    
  #:compose composer])  refpoint-placer?
  find-path : (or/c tag-path? pict-path?)
  finder : procedure? = cc-find
  align : (or/c 'lt 'ct 'rt 'lc 'cc 'rc 'lb 'cb 'rb) = 'cc
  abs-x : real? = 0
  abs-y : real? = 0
  sep : real? = 0
  composer : procedure? = computed from align
Returns a placer that places picts according to a reference point based on an existing pict within the base.

Example:
> (ppict-do base
            #:go (cascade)
            (tag-pict (standard-fish 40 20 #:direction 'right #:color "red") 'red-fish)
            (tag-pict (standard-fish 50 30 #:direction 'left #:color "blue") 'blue-fish)
            #:go (at-find-pict 'red-fish rc-find 'lc #:abs-x 10)
            (text "red fish"))

image

Changed in version 1.1 of package ppict: Added #:sep argument.

procedure

(merge-refpoints x-placer y-placer)  refpoint-placer?

  x-placer : refpoint-placer?
  y-placer : refpoint-placer?
Returns a placer like x-placer except that the y-coordinate of its reference point is computed by y-placer.

Example:
> (ppict-do base
            #:go (cascade)
            (tag-pict (standard-fish 40 20 #:direction 'right #:color "red") 'red-fish)
            (tag-pict (standard-fish 50 30 #:direction 'left #:color "blue") 'blue-fish)
            #:go (merge-refpoints (coord 1 0 'rc)
                                  (at-find-pict 'red-fish))
            (text "red fish"))

image

2 Progressive Slides

 (require ppict/slideshow2) package: ppict

Added in version 1.1 of package ppict.

syntax

(pslide slide-option ... ppict-do-fragment ...)

 
slide-option = #:title title-expr
  | #:name name-expr
  | #:layout layout-expr
  | #:gap-size gap-expr
  | #:inset inset-expr
  | #:timeout timeout-expr
  | #:condense? condense?-expr
 
  title-expr : (or/c string? #f)
  name-expr : (or/c string? #f)
  layout-expr : (or/c 'auto 'center 'full-center 'top 'tall)
  gap-expr : real?
  inset-expr : slide-inset?
  timeout-expr : (or/c real? #f)
  condense?-expr : any/c
Produce slide(s) using progressive picts. The slide body is constructed from the ppict-do-fragments using ppict-do with an initial ppict that depends on layout (and potentially title and gap-size as well).

The slide-options are interpreted the same as for the slide procedure with the exception of #:layout. The result of layout-expr is interpreted as follows:

2.1 Progressive Slides Legacy Library

 (require ppict/slideshow) package: ppict

Changed in version 1.1 of package ppict: Deprecated ppict/slideshow.

syntax

(pslide ppict-do-fragment ...)

parameter

(pslide-base-pict)  (-> pict)

(pslide-base-pict make-base-pict)  void?
  make-base-pict : (-> pict)

parameter

(pslide-default-placer)  placer?

(pslide-default-placer placer)  void?
  placer : placer?
Deprecated; use ppict/slideshow2 instead.

3 Tagged Picts

 (require ppict/tag) package: ppict

procedure

(tag-pict p tag)  pict?

  p : pict?
  tag : symbol?
Returns a pict like p that carries a symbolic tag. The tag can be used with find-tag to locate the pict.

procedure

(pict-tag p)  (or/c symbol? #f)

  p : pict?
Return the symbolic tag carried by p.

Examples:
> (pict-tag (blank))

#f

> (pict-tag (tag-pict (blank) 'a-blank))

'a-blank

procedure

(find-tag p find)  (or/c pict-path? #f)

  p : pict?
  find : tag-path?
Locates a sub-pict of p. Returns a pict-path that can be used with functions like lt-find, etc.

Example:
> (let* ([a (tag-pict (colorize (disk 20) "red") 'a)]
         [b (tag-pict (colorize (filled-rectangle 20 20) "blue") 'b)]
         [p (vl-append a (hb-append (blank 100) b))])
    (pin-arrow-line 10 p
                    (find-tag p 'a) rb-find
                    (find-tag p 'b) lt-find))

image

procedure

(find-tag* p find)  (listof pict-path?)

  p : pict?
  find : tag-path?
Like find-tag, but returns all pict-paths corresponding to the given tag-path.

Example:
> (let* ([a (lambda () (tag-pict (colorize (disk 20) "red") 'a))]
         [b (lambda () (tag-pict (colorize (filled-rectangle 20 20) "blue") 'b))]
         [as (vc-append 10 (a) (a) (a))]
         [bs (vc-append 10 (b) (b) (b))]
         [p (hc-append as (blank 60 0) bs)])
    (for*/fold ([p p])
        ([apath (in-list (find-tag* p 'a))]
         [bpath (in-list (find-tag* p 'b))])
      (pin-arrow-line 4 p
                      apath rc-find
                      bpath lc-find)))

image

procedure

(tag-path? x)  boolean?

  x : any/c
Returns #t if x is a symbol or a non-empty list of symbols, #f otherwise.

procedure

(tag-pict-regions p regions)  pict?

  p : pict?
  regions : (listof (list/c real? real? real? real? symbol?))
For each entry (list x1 y1 x2 y2 tag) in regions, places a blank pict at the region (x1, y1)-(x2, y2) tagged with tag.

For example, use tag-pict-regions with pixel-based regions to identify features within a bitmap-based pict so that they can be the targets of arrows, anchors for balloons, etc.

Added in version 1.1 of package ppict.

4 Alignment

 (require ppict/align) package: ppict

Equivalent to (or/c 'lt 'ct 'rt 'lc 'cc 'rc 'lb 'cb 'rb).

Equivalent to (or/c 'l 'c 'r).

Equivalent to (or/c 't 'c 'b).

procedure

(align->h a)  halign/c

  a : align/c

procedure

(align->v a)  valign/c

  a : align/c
Extracts the halign/c or valign/c part from a, respectively.

procedure

(align->frac a)  real?

  a : (or/c halign/c valign/c)
Computes the fraction corresponding to an alignment where the top-left is 0.

procedure

(halign->vcompose ha)  procedure?

  ha : halign/c

procedure

(valign->hcompose va)  procedure?

  va : valign/c
Returns the h*-append or v*-append function for the given horizontal or vertical alignment, respectively.

procedure

(pin-over/align scene x y halign valign pict)  pict?

  scene : pict?
  x : real?
  y : real?
  halign : halign/c
  valign : valign/c
  pict : pict?
Pins pict over scene centered at xxy aligned as specified in halign and valign.