Another technique for positioning and moving things on screen is to change the screen coordinate system. For example, you can move a shape 50 pixels to the right, or you can move the location of coordinate (0,0) 50 pixels to the right—the visual result is the same.
By modifying the default coordinate system, we can create different transformations including translation, rotation, and scaling.
Working with transformations can be tricky, but the translate()
function is the most straightforward, so we’ll start with that. As Figure 6-1 shows, this function can shift the coordinate system left, right, up, and down.
In this example, notice that the rectangle is drawn at coordinate (0,0), but it is moved around on the screen, because it is affected by translate()
:
void
setup
()
{
size
(
120
,
120
);
}
void
draw
()
{
translate
(
mouseX
,
mouseY
);
rect
(
0
,
0
,
30
,
30
);
}
The translate()
function sets the (0,0) coordinate of the screen to the mouse location (mouseX
and mouseY
). Each time the draw()
block repeats, the rect()
is drawn at the new origin, derived from the current mouse location.
After a transformation is made, it is applied to all drawing functions that follow. Notice what happens when a second translate
function is added to control a second rectangle:
void
setup
()
{
size
(
120
,
120
);
}
void
draw
()
{
translate
(
mouseX
,
mouseY
);
rect
(
0
,
0
,
30
,
30
);
translate
(
35
,
10
);
rect
(
0
,
0
,
15
,
15
);
}
The values for the translate()
functions are added together. The smaller rectangle was translated the amount of mouseX
+ 35 and mouseY
+ 10. The x and y coordinates for both rectangles are (0,0), but the translate()
functions move them to other positions on screen.
However, even though the transformations accumulate within the draw()
block, they are reset each time draw()
starts again at the top.
The rotate()
function rotates the coordinate system. It has one parameter, which is the angle (in radians) to rotate. It always rotates relative to (0,0), known as rotating around the origin. Figure 3-2 in Example 3-7 shows the radians angle values. Figure 6-2 shows the difference between rotating with positive and negative numbers.
To rotate a shape, first define the rotation angle with rotate()
, then draw the shape. In this sketch, the amount to rotate (mouseX / 100.0
) will be between 0 and 1.2 to define the rotation angle because mouseX
will be between 0 and 120, the width of the Display Window specified with the size()
function. Note that you should divide by 100.0 not 100, because of how numbers work in Processing (see “Making Variables”).
void
setup
()
{
size
(
120
,
120
);
}
void
draw
()
{
rotate
(
mouseX
/
100.0
);
rect
(
40
,
30
,
160
,
20
);
}
To rotate a shape around its own center, it must be drawn with coordinate (0,0) in the middle. In this example, because the shape is 160 wide and 20 high as defined in rect()
, it is drawn at the coordinate (–80, –10) to place (0,0) at the center of the shape:
void
setup
()
{
size
(
120
,
120
);
}
void
draw
()
{
rotate
(
mouseX
/
100.0
);
rect
(-
80
,
-
10
,
160
,
20
);
}
The previous pair of examples showed how to rotate around coordinate (0,0), but what about other possibilities? You can use the translate()
and rotate()
functions for more control. When they are combined, the order in which they appear affects the result. If the coordinate system is first moved and then rotated, that is different than first rotating the coordinate system, then moving it.
To spin a shape around its center point at a place on screen away from the origin, first use translate()
to move to the location where you’d like the shape, then call rotate()
, and then draw the shape with its center at coordinate (0,0):
float
angle
=
0
;
void
setup
()
{
size
(
120
,
120
);
}
void
draw
()
{
translate
(
mouseX
,
mouseY
);
rotate
(
angle
);
rect
(-
15
,
-
15
,
30
,
30
);
angle
+=
0.1
;
}
The following example is identical to Example 6-5, except that translate()
and rotate()
are reversed. The shape now rotates around the upper-left corner of the Display Window, with the distance from the corner set by translate()
:
float
angle
=
0.0
;
void
setup
()
{
size
(
120
,
120
);
}
void
draw
()
{
rotate
(
angle
);
translate
(
mouseX
,
mouseY
);
rect
(-
15
,
-
15
,
30
,
30
);
angle
+=
0.1
;
}
Another option is to use the rectMode()
, ellipseMode()
, imageMode()
, and shapeMode()
functions, which make it easier to draw shapes from their center. You can read about these functions in the Processing Reference.
In this example, we’ve put together a series of translate()
and rotate()
functions to create a linked arm that bends back and forth. Each translate()
further moves the position of the lines, and each rotate()
adds to the previous rotation to bend more:
float
angle
=
0.0
;
float
angleDirection
=
1
;
float
speed
=
0.005
;
void
setup
()
{
size
(
120
,
120
);
}
void
draw
()
{
background
(
204
);
translate
(
20
,
25
);
// Move to start position
rotate
(
angle
);
strokeWeight
(
12
);
line
(
0
,
0
,
40
,
0
);
translate
(
40
,
0
);
// Move to next joint
rotate
(
angle
*
2.0
);
strokeWeight
(
6
);
line
(
0
,
0
,
30
,
0
);
translate
(
30
,
0
);
// Move to next joint
rotate
(
angle
*
2.5
);
strokeWeight
(
3
);
line
(
0
,
0
,
20
,
0
);
angle
+=
speed
*
angleDirection
;
if
((
angle
>
QUARTER_PI
)
||
(
angle
<
0
))
{
angleDirection
=
-
angleDirection
;
}
}
The angle
variable grows from 0 to QUARTER_PI
(one quarter of the value of pi), then decreases until it is less than zero, then the cycle repeats. The value of the angleDirection
variable is always 1 or –1 to make the value of angle
correspondingly increase or decrease.
The scale()
function stretches the coordinates on the screen. Because the coordinates expand or contract as the scale changes, everything drawn to the Display Window increases or decreases in dimension. Use scale(1.5)
to make everything 150% of their original size, or scale(3)
to make them three times larger. Using scale(1)
would have no effect, because everything would remain 100% of the original. To make things half their size, use scale(0.5)
.
Like rotate()
, the scale()
function transforms from the origin. Therefore, as with rotate()
, to scale a shape from its center, translate to its location, scale, and then draw with the center at coordinate (0,0):
void
setup
()
{
size
(
120
,
120
);
}
void
draw
()
{
translate
(
mouseX
,
mouseY
);
scale
(
mouseX
/
60.0
);
rect
(-
15
,
-
15
,
30
,
30
);
}
From the thick lines in Example 6-8, you can see how the scale()
function affects the stroke weight. To maintain a consistent stroke weight as a shape scales, divide the desired stroke weight by the scalar value:
void
setup
()
{
size
(
120
,
120
);
}
void
draw
()
{
translate
(
mouseX
,
mouseY
);
float
scalar
=
mouseX
/
60.0
;
scale
(
scalar
);
strokeWeight
(
1.0
/
scalar
);
rect
(-
15
,
-
15
,
30
,
30
);
}
To isolate the effects of a transformation so they don’t affect later commands, use the pushMatrix()
and popMatrix()
functions. When pushMatrix()
is run, it saves a copy of the current coordinate system and then restores that system after popMatrix()
. This is useful when transformations are needed for one shape, but not wanted for another.
In this example, the smaller rectangle always draws in the same position because the translate(mouseX, mouseY)
is cancelled by the popMatrix()
:
void
setup
()
{
size
(
120
,
120
);
}
void
draw
()
{
pushMatrix
();
translate
(
mouseX
,
mouseY
);
rect
(
0
,
0
,
30
,
30
);
popMatrix
();
translate
(
35
,
10
);
rect
(
0
,
0
,
15
,
15
);
}
The pushMatrix()
and popMatrix()
functions are always used in pairs. For every pushMatrix()
, you need to have a matching popMatrix()
.
The translate()
, rotate()
, and scale()
functions are all utilized in this modified robot sketch. In relation to “Robot 3: Response”, translate()
is used to make the code easier to read. Here, notice how the x
value no longer needs to be added to each drawing function because translate()
moves everything.
Similarly, the scale()
function is used to set the dimensions for the entire robot. When the mouse is not pressed, the size is set to 60%, and when it is pressed, it goes to 100% in relation to the original coordinates.
The rotate()
function is used within a loop to draw a line, rotate it a little, then draw a second line, then rotate a little more, and so on until the loop has drawn 30 lines half-way around a circle to style a lovely head of robot hair:
float
x
=
60
;
//
x
coordinate
float
y
=
440
;
//
y
coordinate
int
radius
=
45
;
// Head radius
int
bodyHeight
=
180
;
// Body height
int
neckHeight
=
40
;
// Neck height
float
easing
=
0.04
;
void
setup
(
)
{
size
(
360
,
480
)
;
ellipseMode
(
RADIUS
)
;
}
void
draw
(
)
{
strokeWeight
(
2
)
;
float
neckY
=
-
1
*
(
bodyHeight
+
neckHeight
+
radius
)
;
background
(
0
,
153
,
204
)
;
translate
(
mouseX
,
y
)
;
// Move all to (mouseX, y)
if
(
mousePressed
)
{
scale
(
1.0
)
;
}
else
{
scale
(
0.6
)
;
// 60% size when mouse is pressed
}
// Body
noStroke
(
)
;
fill
(
255
,
204
,
0
)
;
ellipse
(
0
,
-
33
,
33
,
33
)
;
fill
(
0
)
;
rect
(
-
45
,
-
bodyHeight
,
90
,
bodyHeight
-
33
)
;
// Neck
stroke
(
255
)
;
line
(
12
,
-
bodyHeight
,
12
,
neckY
)
;
// Hair
pushMatrix
(
)
;
translate
(
12
,
neckY
)
;
float
angle
=
-
PI
/
30.0
;
for
(
int
i
=
0
;
i
<
=
30
;
i
+
+
)
{
line
(
80
,
0
,
0
,
0
)
;
rotate
(
angle
)
;
}
popMatrix
(
)
;
// Head
noStroke
(
)
;
fill
(
0
)
;
ellipse
(
12
,
neckY
,
radius
,
radius
)
;
fill
(
255
)
;
ellipse
(
24
,
neckY
-
6
,
14
,
14
)
;
fill
(
0
)
;
ellipse
(
24
,
neckY
-
6
,
3
,
3
)
;
}