### Commands

- reset()
- rotate()
- scale()
- skew()
- transform()
- translate()

### Objects

- Point
- Region
- Size
- Transform

### Legacy Commands

- push()

#### Commands

# reset()

## Syntax

    reset()

## Description

Resets the current transform to its default state (the identity matrix).

Transformations in PlotDevice build up an incremental state. For example, once `rotate(90)` is called, all subsequent shapes, paths, text, and images are rotated 90 degrees (i.e., the rotation ‘state’ is 90°). If called again – e.g., `rotate(30)` – the rotation state becomes 120 degrees (90+30).

Once reset() is called, the rotation state becomes 0 degrees (the default rotation) again. Since reset() affects the entire transform state, it will also wipe out any previously applied translations, skews, and scale-factors.

## Context Manager

As with the other transform commands, reset() can be used in a `with` statement to limit its scope to drawing commands within the following block of code.

## Tutorial

Geometry

## Example

    font(14), fill(0.2)

    rotate(90)
    text("one", 30, 80)
    text("two", 45, 80)

    reset()
    text("three", 70, 80)

# rotate()

## Syntax

    rotate(amount) # amount to rotate (in default unit)

    rotate(percent=0.5) # 0 ... 1.0
    rotate(degrees=180) # 0 ... 360
    rotate(radians=pi)  # 0 ... 2*pi (a.k.a. tau)

## Description

Rotates all subsequent drawing commands. The rotate() command accepts one parameter to specify the angle (in degrees by default). The angle-unit can be changed by calling geometry() with `DEGREES`, `RADIANS`, or `PERCENT`. Subsequent calls to rotate() will then be interpreted in the chosen units.

Alternatively, a rotation can specify its units explicitly by using a keyword argument. For example, you can use radians to flip the canvas upside down (regardless of the current geometry-unit) wtih:

    rotate(radians=tau/2)

Like other transform operations, the rotate() command works incrementally: if you call `rotate(30)`, and later on call `rotate(60)`, all commands following that second rotate() will be rotated 90° (30+60).

## Context Manager

As with the other transform commands, rotate() can be used in a `with` statement to limit its scope to drawing commands within the indented block of code.

## Tutorial

Geometry

## Example

    fill(0.2)
    rotate(-45)
    rect(30, 30, 40, 40)

# scale()

## Syntax

    scale(x, y=None)

## Description

Increases, decreases, or streches the size of all subsequent drawing commands. The first parameter sets the horizontal scale and the optional second parameter the vertical scale. You can also call scale() with a single parameter that sets both the horizontal and vertical scale. Scale values are specified as floating-point (decimal) numbers with 1.0 corresponding to 100%.

The scale() command works incrementally: if you call `scale(0.5)`, and later on call `scale(0.2)`, all subsequent drawing commands will be sized to 10% (0.2 of 0.5).

## Context Manager

As with the other transform commands, scale() can be used in a `with` statement to limit its scope to drawing commands within the following block of code.

## Tutorial

Geometry

## Example

    fill(0.2)
    poly(30,30, 20)
    scale(0.5)
    poly(70,30, 20)

# skew()

## Syntax

    skew(x, y=None)

## Description

Slants the direction of all subsequent drawing commands. The first parameter sets the horizontal skew. The second parameter is optional and sets the vertical skew.

Skew values are expressed as angles using the current geometry() unit (`DEGREES` by default). The skew() command works incrementally: if you call `skew(10)`, and later on call `skew(20)`, all subsequent drawing commands will be skewed by 30° (10+20).

## Context Manager

As with the other transform commands, skew() can be used in a `with` statement to limit its scope to drawing commands within the indented block of code.

## Tutorial

Geometry

## Example

    fill(0.2)
    skew(10.0)
    rect(20, 10, 40, 40)

# transform()

## Syntax

    transform(mode)

Set the current transform mode (to `CENTER` or `CORNER`)

    transform(matrix=[m11, m21, m12, m22, tX, tY])

Overwrite the current transform matrix with a new one

    with transform(mode=None, matrix=None):
        ... # drawing & transformation commands

Restore the transformation state at the end of the indented block (and optionally change the transform mode/matrix for the duration)

## Description

The `mode` parameter sets the registration point – the offset for rotate(), scale() and skew() commands. By default, primitives, text, and images rotate around their own centerpoints. But if you call transform() with `CORNER` as its `mode` parameter, transformations will be applied relative to canvas’s ‘origin point’ rather than being relative to the objects’ centerpoint origins.

In addition to changing the mode, the transform() command also allows you to overwrite the underlying transformation matrix with a new set of values. Pass a 6-element list or tuple as the `matrix` argument to update the current transform (see Apple’s docs for all the math-y details). Note that you can omit the `mode` arg when setting a new matrix if you don’t want the transformation origin to change.

## Context Manager

When called as part of a `with` block, transform() will ensure that any subsequent transformation commands will apply only within the block. At the end of the block, the transformation state will be reset to what it was before the transform() call. This is equivalent to calling push() and pop() at the beginning and end of the block respectively, though transform() will also reset the `mode` on exit.

    with transform(CORNER):
        translate(100,20)
        line(0,0, 40,0)

is equivalent to:

    oldmode = CENTER
    transform(CORNER)
    push()
    translate(100,20)
    line(0,0, 40,0)
    pop()
    transform(oldmode)

## Returns

a Transform object with the canvas’s ‘current’ state. You can use this object’s apply() method with Points or Beziers to calculate the ‘screen coordinates’ that would result from drawing with the current transformation state.

## Tutorial

Geometry

## Examples

    fill(0.2)
    fontsize(14)
    rotate(90)
    text("one", 40, 80)

    with transform():
        rotate(-90)
        text("two", 40, 40)

    text("three", 50, 80)

# translate()

## Syntax

    translate(x, y)

## Description

Shifts the origin used when interpreting drawing commands’ coordinates. The first parameter sets the horizontal offset, the second parameter the vertical offset. Translating is the same as dragging something across the screen. Or the same as moving the canvas registration point, which is situated in the upper left corner by default.

After calling translate(), all subsequent drawing commands are shifted by the specified offsets. This can be useful when positioning groups of multiple elements.

## Context Manager

As with the other transform commands, translate() can be used in a `with` statement to limit its scope to drawing commands within the following block of code.

## Tutorial

Geometry

## Example

    fill(0.2)
    arc(10,10, 20)
    translate(50, 50)
    arc(10,10, 20)

#### Objects

# Point

## Syntax

    Point(x, y)

## Description

A pair of x & y coordinates wrapped into a single value. You can access its properties by name in addition to using the object as a ‘normal’ tuple. Point objects support basic arithmetic and provide utility methods for geometry calculations.

PlotDevice commands that take separate x & y arguments (e.g., poly(), arc(), text()) will typically also accept a Point object in their place.

## Properties

    pt.x
    pt.y

## Methods

    pt.angle(point)

Returns the angle from the object to a target Point or pair of (x,y) coordinates. By default, the angle will be represented in degrees, but this can be altered by setting an alternate unit with the geometry() command.

    src = Point(25,75)
    dst = Point(75,25)
    theta = src.angle(dst)
    arc(src, 4, fill='red')
    arc(dst, 4, fill='orange')
    with pen(dash=5), stroke(.5), nofill():
        arc(src, 32, range=theta)
    print(theta)
    >>> -45.0

    pt.distance(point)

Returns the linear distance between the object and a second Point or pair of (x,y) coordinates.

    src = Point(25,25)
    dst = Point(100,100)
    length = src.distance(dst)
    arc(src, 4, fill='red')
    arc(dst, 4, fill='orange')
    with pen(dash=5), stroke(.8):
        line(src, dst)
    print(length)
    >>> 106.066017178

    pt.reflect(point, d=1.0, a=180)

Uses the object as an ‘origin’ and returns a Point containing the reflection of a target Point or pair of (x,y) coordinates. The optional `d` parameter is a magnification factor relative to the distance between the mirror and the target point.

The `a` parameter controls the angle of reflection. When set to 180° (the default), the reflected point will be directly opposite the target, but other values will allow you to ‘ricochet’ the target point off of the mirror instead.

    origin = Point(50,50)
    src = Point(25,40)
    dst = origin.reflect(src, d=2.0)
    arc(src, 4, fill='red')
    arc(dst, 4, fill='orange')
    arc(origin, 4, fill=.7)

    pt.coordinates(distance, angle)

Uses the object as the ‘origin’ of a polar coordinate system and returns a Point at a given distance and orientation relative to the origin. The `distance` parameter uses the current canvas size() unit and the `angle` should be expressed in the current geometry() unit.

    origin = Point(50,50)
    dst = origin.coordinates(50, 45)
    arc(origin, 4, fill=.7)
    arc(dst, 4, fill='orange')
    with pen(dash=3), stroke(.5), nofill():
        arc(origin, 25, range=45)

If you’d prefer to work with raw numbers rather than Point objects, you can also import the numerical versions of these methods for use in your scripts:

    from plotdevice.lib.pathmatics import angle, distance, coordinates, reflect

## Tutorial

Geometry

# Region

## Syntax

    Region(x, y, w, h)
    Region(Point, Size)

## Description

An origin Point and a Size which together describe a rectangular region. Region objects have properties to read and modify the rectangle geometry. Methods allow you to generate new regions from combinations of old ones or by systematically shifting their values.

PlotDevice commands that take `x`/`y`/`w`/`h` arguments (e.g., rect(), oval(), image()) will also accept a Region object in their place.

## Geometry Properties

    r.x, r.y # position
    r.w, r.h # dimensions
    r.origin # Point(r.x, r.y)
    r.size   # Size(r.w, r.h)

## Convenience Properties

    r.top, r.bottom, r.left, r.right # edge positions
    r.w/h/t/b/l/r # shorthand aliases

## Methods

    r.intersect(region)

return a new Region describing the overlapping portions of the existing Region object and a second Region

    r1 = Region(20,20, 40,30)
    r2 = Region(40,40, 30,40)
    overlap = r1.intersect(r2)
    with nofill(), stroke(.7):
        rect(r1)
        rect(r2)
        rect(overlap, stroke='red', dash=3)

    r.union(region)

return a new Region that contains the combined bounds of the existing Region object and a second Region

    r1 = Region(20,20, 40,30)
    r2 = Region(40,40, 30,40)
    union = r1.union(r2)
    with nofill(), stroke(.7):
        rect(r1)
        rect(r2)
        rect(union, stroke='red', dash=3)

    r.shift(point)

return a copy of the Region with its origin shifted by a Point

    r = Region(20,20, 40,30)
    shifted = r.shift(20,15)
    nofill()
    rect(r, stroke=.7)
    rect(shifted, stroke='red', dash=3)

    r.inset(dx, dy)

return a copy of the Region whose horizontal dimensions have been shrunk by `dx` and vertical by `dy`. If `dy` is omitted, shrink both dimensions by `dx`. Passing a negative value for `dx` or `dy` will enlarge the dimension rather than shrinking it.

    r = Region(30,40, 60,40)
    shrunk = r.inset(20,5)
    grown = r.inset(-20)
    nofill()
    rect(r, stroke=.7)
    rect(shrunk, stroke='orange', dash=3)
    rect(grown, stroke='red', dash=3)

## Tutorial

Geometry

# Size

## Syntax

    Size(width, height)

## Description

A pair of width & height values wrapped into a single object. Size objects are very similar to Points and arithmetic between the two types is supported.

## Properties

    s.width
    s.height
    s.w, s.h # shorthand aliases

## Tutorial

Geometry

# Transform

## Syntax

    Transform()

## Description

A local transformation state (independent of the global, canvas transform). You can use Transform objects to apply translation, rotation, scaling and skewing to a path before drawing it.

## Methods

    t.translate(x=0, y=0)
    t.rotate(degrees=0, radians=0)
    t.scale(x=1, y=None)
    t.skew(x=0, y=0)
    t.invert()

    t.append(t)
    t.prepend(t)

    t.apply(path_or_point)

    t.copy()

See the tutorial on paths to learn more about the methods.

The apply() method returns a new `Bezier` or `Point` with the defined transformations applied to it.

#### Legacy Commands

# push() & pop()

## Syntax

    push()

    pop()

## Description

PlotDevice is a so-called state-machine in which certain types of modifications are cumulative. For example, once `rotate(45)` is called, all subsequent shapes, paths, text and images are rotated 45 degrees – the rotation ‘state’ has been set to 45°.

All transformations, such as rotate() and skew(), defined between a push() and pop() call last only until pop() is called. Thereafter the previous state is used. For example, a rotate(-30) within a rotate(45) means a rotation state of 15° until pop() is called and the rotation state becomes 45 degrees again.

## PlotDevice Equivalent

While push() & pop() allow your script to save and restore the state manually (and leave it up to you to ensure that a call to one is balanced by a call to the other). The transform() command provides a context manager to limit the scope of these state changes to an indented block of code.

## Tutorial

Geometry

## Example

    fill(0.2)
    fontsize(14)
    rotate(90)
    text("one", 40, 80)

    push()
    rotate(-90)
    text("two", 40, 40)
    pop()

    text("three", 50, 80)