Transforms and matrices

Transforms and matrices

For basic transformations of the drawing space, use scale(sx, sy), rotate(a), and translate(tx, ty).

translate() shifts the current axes by the specified amounts in x and y. It's relative and cumulative, rather than absolute:

origin()
for i in range(0, step=30, length=6)
sethue(HSV(i, 1, 1)) # from Colors
setopacity(0.5)
circle(0, 0, 40, :fillpreserve)
setcolor("black")
strokepath()
translate(50, 0)
end

scale(x, y) or scale(n) scales the current workspace by the specified amounts. Again, it's relative to the current scale, not to the document's original.

origin()
for i in range(0, step=30, length=6)
sethue(HSV(i, 1, 1)) # from Colors
circle(0, 0, 90, :fillpreserve)
setcolor("black")
strokepath()
scale(0.8, 0.8)
end

rotate() rotates the current workspace by the specifed amount about the current 0/0 point. It's relative to the previous rotation, not to the document's original.

origin()
for i in 1:8
randomhue()
squircle(Point(40, 0), 20, 30, :fillpreserve)
sethue("black")
strokepath()
rotate(pi/4)
end

To return home after many changes, you can use setmatrix([1, 0, 0, 1, 0, 0]) to reset the matrix to the default. origin() resets the matrix then moves the origin to the center of the page.

rescale() is a convenient utility function for linear interpolation, also called a "lerp".

scale(x, y)

Scale workspace by x and y.

Example:

scale(0.2, 0.3)
scale(f)

Scale workspace by f in both x and y.

rotate(a::Float64)

Rotate workspace by a radians clockwise (from positive x-axis to positive y-axis).

translate(point)
translate(x::Real, y::Real)

Translate the workspace to x and y or to pt.

Matrices and transformations

In Luxor, there's always a current matrix. It's a six element array:

$\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ \end{bmatrix}$

which is usually handled in Julia/Cairo/Luxor as a simple vector/array:

julia> getmatrix()
6-element Array{Float64,1}:
1.0
0.0
0.0
1.0
0.0
0.0

transform(a) transforms the current workspace by 'multiplying' the current matrix with matrix a. For example, transform([1, 0, xskew, 1, 50, 0]) skews the current matrix by xskew radians and moves it 50 in x and 0 in y.

function boxtext(p, t)
sethue("grey30")
box(p, 30, 50, :fill)
sethue("white")
textcentered(t, p)
end

for i in 0:5
xskew = tand(i * 5.0)
transform([1, 0, xskew, 1, 50, 0])
end

getmatrix() gets the current matrix, setmatrix(a) sets the matrix to array a.

getmatrix()

Get the current matrix. Returns an array of six float64 numbers:

• xx component of the affine transformation

• yx component of the affine transformation

• xy component of the affine transformation

• yy component of the affine transformation

• x0 translation component of the affine transformation

• y0 translation component of the affine transformation

Some basic matrix transforms:

• translate

transform([1, 0, 0, 1, dx, dy]) shifts by dx, dy

• scale

transform([fx 0 0 fy 0 0]) scales by fx and fy

• rotate

transform([cos(a), -sin(a), sin(a), cos(a), 0, 0]) rotates around to a radians

rotate around O: [c -s s c 0 0]
• shear

transform([1 0 a 1 0 0]) shears in x direction by a

• shear in y: [1 B 0 1 0 0]

• x-skew

transform([1, 0, tan(a), 1, 0, 0]) skews in x by a

• y-skew

transform([1, tan(a), 0, 1, 0, 0]) skews in y by a

• flip

transform([fx, 0, 0, fy, centerx * (1 - fx), centery * (fy-1)]) flips with center at centerx/centery

• reflect

transform([1 0 0 -1 0 0]) reflects in xaxis

transform([-1 0 0 1 0 0]) reflects in yaxis

When a drawing is first created, the matrix looks like this:

getmatrix() = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0]

When the origin is moved to 400/400, it looks like this:

getmatrix() = [1.0, 0.0, 0.0, 1.0, 400.0, 400.0]

To reset the matrix to the original:

setmatrix([1.0, 0.0, 0.0, 1.0, 0.0, 0.0])
setmatrix(m::AbstractArray)

Change the current matrix to matrix m. Use getmatrix() to get the current matrix.

transform(a::AbstractArray)

Modify the current matrix by multiplying it by matrix a.

For example, to skew the current state by 45 degrees in x and move by 20 in y direction:

transform([1, 0, tand(45), 1, 0, 20])

Use getmatrix() to get the current matrix.

crossproduct(p1::Point, p2::Point)

This is the perp dot product, really, not the crossproduct proper (which is 3D):

blendmatrix(b::Blend, m)

Set the matrix of a blend.

To apply a sequence of matrix transforms to a blend:

A = [1 0 0 1 0 0]
Aj = cairotojuliamatrix(A)
Sj = scalingmatrix(2, .2) * Aj
Tj = translationmatrix(10, 0) * Sj
A1 = juliatocairomatrix(Tj)
blendmatrix(b, As)
rotationmatrix(a)

Return a 3x3 Julia matrix that will apply a rotation through a radians.

scalingmatrix(sx, sy)

Return a 3x3 Julia matrix that will apply a scaling by sx and sy.

translationmatrix(x, y)

Return a 3x3 Julia matrix that will apply a translation in x and y.

Use the getscale(), gettranslation(), and getrotation() functions to find the current values of the current matrix. These can also find the values of arbitrary 3x3 matrices.

getscale(R::Matrix)
getscale()

Get the current scale of a Julia 3x3 matrix, or the current Luxor scale.

Returns a tuple of x and y values.

gettranslation(R::Matrix)
gettranslation()

Get the current translation of a Julia 3x3 matrix, or the current Luxor translation.

Returns a tuple of x and y values.

getrotation(R::Matrix)
getrotation()

Get the rotation of a Julia 3x3 matrix, or the current Luxor rotation.

$\begin{bmatrix} a & b & tx \\ c & d & ty \\ 0 & 0 & 1 \\ \end{bmatrix}$

The rotation angle is atan(-b, a) or atan(c, d).

You can convert between the 6-element and 3x3 versions of a transformation matrix using the functions cairotojuliamatrix() and juliatocairomatrix().

cairotojuliamatrix(c)

Return a 3x3 Julia matrix that's the equivalent of the six-element matrix in c.

juliatocairomatrix(c)

Return a six-element matrix that's the equivalent of the 3x3 Julia matrix in c.