Skip to content

Commit 21ab6b2

Browse files
Merge pull request #157 from jeremydouglass/fix_pop_matrix
Fix push_matrix, drop pop_matrix and pop_style
2 parents 9338c1a + 390589f commit 21ab6b2

File tree

4 files changed

+117
-145
lines changed

4 files changed

+117
-145
lines changed

docs/tutorials/2D transformations.rst

+99-119
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,16 @@ Here is code that draws the rectangle in red by changing its coordinates, then d
5555
5656
# draw a translucent blue rectangle by translating the grid
5757
fill(0, 0, 255, 128)
58-
push_matrix()
59-
translate(60, 80)
60-
rect((20, 20), 40, 40)
61-
pop_matrix()
58+
with push_matrix():
59+
translate(60, 80)
60+
rect((20, 20), 40, 40)
6261
6362
if __name__ == '__main__':
6463
run()
6564
66-
Let’s look at the translation code in more detail. ``push_matrix()`` is a built-in function that saves the current position of the coordinate system. The ``translate(60, 80)`` moves the coordinate system 60 units right and 80 units down. The ``rect((20, 20), 40, 40)`` draws the rectangle at the same place it was originally. Remember, the things you draw don’t move—the grid moves instead. Finally, ``pop_matrix()`` restores the coordinate system to the way it was before you did the translate.
65+
Let’s look at the translation code in more detail. ``push_matrix()`` is a built-in function that saves the current position of the coordinate system. The ``translate(60, 80)`` moves the coordinate system 60 units right and 80 units down. The ``rect((20, 20), 40, 40)`` draws the rectangle at the same place it was originally. Remember, the things you draw don’t move—the grid moves instead. Finally, when the ``with`` context ends, it "pops" and restores the coordinate system to the way it was before you did the translate.
6766

68-
Yes, you could have done a ``translate(-60, -80)`` to move the grid back to its original position. However, when you start doing more sophisticated operations with the coordinate system, it’s easier to use ``push_matrix()`` and ``pop_matrix()`` to save and restore the status rather than having to undo all your operations. Later on in this tutorial, you will find out why those functions seem to have such strange names.
67+
Yes, you could have done a ``translate(-60, -80)`` to move the grid back to its original position. However, when you start doing more sophisticated operations with the coordinate system, it’s easier to use ``with push_matrix():`` to save and then later restore the status rather than having to undo all your operations. Later on in this tutorial, you will find out why those functions seem to have such strange names.
6968

7069
What’s the Advantage?
7170
=====================
@@ -100,12 +99,11 @@ Compare that to the version of the function that uses translate(). In this case,
10099
.. code:: python
101100
102101
def house(x, y):
103-
push_matrix()
104-
translate(x, y)
105-
triangle((15, 0), (0, 15), (30, 15))
106-
rect((0, 15), 30, 30)
107-
rect((12, 30), 10, 15)
108-
pop_matrix()
102+
with push_matrix():
103+
translate(x, y)
104+
triangle((15, 0), (0, 15), (30, 15))
105+
rect((0, 15), 30, 30)
106+
rect((12, 30), 10, 15)
109107
110108
Rotation
111109
========
@@ -134,11 +132,10 @@ Since most people think in degrees, Processing has a built-in ``radians()`` func
134132
no_stroke()
135133
rect((40, 40), 40, 40)
136134
137-
push_matrix()
138-
rotate(radians(45))
139-
fill(0)
140-
rect((40, 40), 40, 40)
141-
pop_matrix()
135+
with push_matrix():
136+
rotate(radians(45))
137+
fill(0)
138+
rect((40, 40), 40, 40)
142139
143140
if __name__ == '__main__':
144141
run()
@@ -179,17 +176,16 @@ And here is the code and its result, without the grid marks.
179176
no_stroke()
180177
rect((40, 40), 40, 40)
181178
182-
push_matrix()
183-
# move the origin to the pivot point
184-
translate(40, 40)
179+
with push_matrix():
180+
# move the origin to the pivot point
181+
translate(40, 40)
185182
186-
# then pivot the grid
187-
rotate(radians(45))
183+
# then pivot the grid
184+
rotate(radians(45))
188185
189-
# and draw the square at the origin
190-
fill(0)
191-
rect((0, 0), 40, 40)
192-
pop_matrix()
186+
# and draw the square at the origin
187+
fill(0)
188+
rect((0, 0), 40, 40)
193189
194190
if __name__ == '__main__':
195191
run()
@@ -214,11 +210,10 @@ And here is a program that generates a wheel of colors by using rotation. The sc
214210
frame_count * 5 % 255,
215211
frame_count * 7 % 255)
216212
217-
push_matrix()
218-
translate(100, 100)
219-
rotate(radians(frame_count * 2 % 360))
220-
rect((0, 0), 80, 20)
221-
pop_matrix()
213+
with push_matrix():
214+
translate(100, 100)
215+
rotate(radians(frame_count * 2 % 360))
216+
rect((0, 0), 80, 20)
222217
223218
if __name__ == '__main__':
224219
run()
@@ -244,10 +239,9 @@ The final coordinate system transformation is scaling, which changes the size of
244239
rect((20, 20), 40, 40)
245240
246241
stroke(0)
247-
push_matrix()
248-
scale(2.0)
249-
rect((20, 20), 40, 40)
250-
pop_matrix()
242+
with push_matrix():
243+
scale(2.0)
244+
rect((20, 20), 40, 40)
251245
252246
if __name__ == '__main__':
253247
run()
@@ -279,36 +273,34 @@ When you do multiple transformations, the order makes a difference. A rotation f
279273
line((0, 0), (200, 0)) # draw axes
280274
line((0, 0), (0, 200))
281275
282-
push_matrix()
283-
fill(255, 0, 0) # red square
284-
rotate(radians(30))
285-
translate(70, 70)
286-
scale(2.0)
287-
rect((0, 0), 20, 20)
288-
pop_matrix()
289-
290-
push_matrix()
291-
fill(255) # white square
292-
translate(70, 70)
293-
rotate(radians(30))
294-
scale(2.0)
295-
rect((0, 0), 20, 20)
296-
pop_matrix()
276+
with push_matrix():
277+
fill(255, 0, 0) # red square
278+
rotate(radians(30))
279+
translate(70, 70)
280+
scale(2.0)
281+
rect((0, 0), 20, 20)
282+
283+
with push_matrix():
284+
fill(255) # white square
285+
translate(70, 70)
286+
rotate(radians(30))
287+
scale(2.0)
288+
rect((0, 0), 20, 20)
297289
298290
if __name__ == '__main__':
299291
run()
300292
301293
The Transformation Matrix
302294
=========================
303295

304-
Every time you do a rotation, translation, or scaling, the information required to do the transformation is accumulated into a table of numbers. This table, or matrix has only a few rows and columns, yet, through the miracle of mathematics, it contains all the information needed to do any series of transformations. And that’s why the ``push_matrix()` and ``pop_matrix()`` have that word in their name.
296+
Every time you do a rotation, translation, or scaling, the information required to do the transformation is accumulated into a table of numbers. This table, or matrix has only a few rows and columns, yet, through the miracle of mathematics, it contains all the information needed to do any series of transformations. And that’s why ``push_matrix()`` has that word in their name.
305297

306-
Push and Pop
298+
Push (and Pop)
307299
============
308300

309-
What about the push and pop part of the names? These come from a computer concept known as a stack, which works like a spring-loaded tray dispenser in a cafeteria. When someone returns a tray to the stack, its weight pushes the platform down. When someone needs a tray, he takes it from the top of the stack, and the remaining trays pop up a little bit.
301+
What about the push part of the name? It comes from a computer concept known as a stack, which works like a spring-loaded tray dispenser in a cafeteria. When someone returns a tray to the stack, its weight pushes the platform down. When someone needs a tray, he takes it from the top of the stack, and the remaining trays pop up a little bit.
310302

311-
In a similar manner, ``push_matrix()`` puts the current status of the coordinate system at the top of a memory area, and ``pop_matrix()`` pulls that status back out. The preceding example used ``push_matrix()`` and ``pop_matrix()`` to make sure that the coordinate system was “clean” before each part of the drawing. In all of the other examples, the calls to those two functions weren’t really necessary, but it doesn’t hurt anything to save and restore the grid status.
303+
In a similar manner, ``push_matrix()`` puts the current status of the coordinate system at the top of a memory area. When the `with` exits the context automatically "pops" and pulls that status back out. The preceding example used ``push_matrix()`` to make sure that the coordinate system was “clean” before each part of the drawing. In all of the other examples, the calls to those two functions weren’t really necessary, but it doesn’t hurt anything to save and restore the grid status.
312304

313305
Note: in Processing, the coordinate system is restored to its original state (origin at the upper left of the window, no rotation, and no scaling) every time that the ``draw()`` function is executed.
314306

@@ -381,16 +373,14 @@ Now, separate the code for drawing the left and right arms, and move the center
381373
ellipse((47, 12), 12, 12) # right eye
382374
383375
def drawLeftArm():
384-
push_matrix()
385-
translate(12, 32)
386-
rect((-12, 0), 12, 37)
387-
pop_matrix()
376+
with push_matrix():
377+
translate(12, 32)
378+
rect((-12, 0), 12, 37)
388379
389380
def drawRightArm():
390-
push_matrix()
391-
translate(66, 32)
392-
rect((0, 0), 12, 37)
393-
pop_matrix()
381+
with push_matrix():
382+
translate(66, 32)
383+
rect((0, 0), 12, 37)
394384
395385
Now test to see if the arms rotate properly. Rather than attempt a full animation, we will just rotate the left side arm 135 degrees and the right side arm -45 degrees as a test. Here is the code that needs to be added, and the result. The left side arm is cut off because of the window boundaries, but we’ll fix that in the final animation.
396386

@@ -400,18 +390,16 @@ Now test to see if the arms rotate properly. Rather than attempt a full animatio
400390
.. code:: python
401391
402392
def drawLeftArm():
403-
push_matrix()
404-
translate(12, 32)
405-
rotate(radians(135))
406-
rect((-12, 0), 12, 37)
407-
pop_matrix()
393+
with push_matrix():
394+
translate(12, 32)
395+
rotate(radians(135))
396+
rect((-12, 0), 12, 37)z
408397
409398
def drawRightArm():
410-
push_matrix()
411-
translate(66, 32)
412-
rotate(radians(-45))
413-
rect((0, 0), 12, 37)
414-
pop_matrix()
399+
with push_matrix():
400+
translate(66, 32)
401+
rotate(radians(-45))
402+
rect((0, 0), 12, 37)
415403
416404
Now we complete the program by putting in the animation. The left arm has to rotate from 0° to 135° and back. Since the arm-waving is symmetric, the right-arm angle will always be the negative value of the left-arm angle. To make things simple, we will go in increments of 5 degrees.
417405

@@ -431,18 +419,16 @@ Now we complete the program by putting in the animation. The left arm has to rot
431419
global armAngle, angleChange, ANGLE_LIMIT
432420
background(255)
433421
434-
push_matrix()
435-
translate(50, 50) # place robot so arms are always on screen
436-
drawRobot()
437-
armAngle += angleChange
438-
439-
# if the arm has moved past its limit,
440-
# reverse direction and set within limits.
441-
if armAngle > ANGLE_LIMIT or armAngle < 0:
442-
angleChange = -angleChange
443-
armAngle += angleChange
422+
with push_matrix():
423+
translate(50, 50) # place robot so arms are always on screen
424+
drawRobot()
425+
armAngle += angleChange
444426
445-
pop_matrix()
427+
# if the arm has moved past its limit,
428+
# reverse direction and set within limits.
429+
if armAngle > ANGLE_LIMIT or armAngle < 0:
430+
angleChange = -angleChange
431+
armAngle += angleChange
446432
447433
def drawRobot():
448434
no_stroke()
@@ -461,18 +447,16 @@ Now we complete the program by putting in the animation. The left arm has to rot
461447
ellipse((47, 12), 12, 12) # right eye
462448
463449
def drawLeftArm():
464-
push_matrix()
465-
translate(12, 32)
466-
rotate(radians(armAngle))
467-
rect((-12, 0), 12, 37)
468-
pop_matrix()
450+
with push_matrix():
451+
translate(12, 32)
452+
rotate(radians(armAngle))
453+
rect((-12, 0), 12, 37)
469454
470455
def drawRightArm():
471-
push_matrix()
472-
translate(66, 32)
473-
rotate(radians(-armAngle))
474-
rect((0, 0), 12, 37)
475-
pop_matrix()
456+
with push_matrix():
457+
translate(66, 32)
458+
rotate(radians(-armAngle))
459+
rect((0, 0), 12, 37)
476460
477461
if __name__ == '__main__':
478462
run()
@@ -505,11 +489,10 @@ Because this is a new concept, rather than integrate it into the robot program,
505489
angle = atan2(mouse_y - 100, mouse_x - 100)
506490
507491
background(255)
508-
push_matrix()
509-
translate(100, 100)
510-
rotate(angle)
511-
rect((0, 0), 50, 10)
512-
pop_matrix()
492+
with push_matrix():
493+
translate(100, 100)
494+
rotate(angle)
495+
rect((0, 0), 50, 10)
513496
514497
if __name__ == '__main__':
515498
run()
@@ -549,37 +532,34 @@ At this point, we can write the final version of the arm-tracking program. We st
549532
550533
background(255)
551534
552-
push_matrix()
553-
translate(ROBOT_X, ROBOT_Y) # place robot so arms are always on screen
554-
if mouse_is_pressed:
555-
mX = mouse_x - ROBOT_X
556-
mY = mouse_y - ROBOT_Y
535+
with push_matrix():
536+
translate(ROBOT_X, ROBOT_Y) # place robot with arms always on screen
537+
if mouse_is_pressed:
538+
mX = mouse_x - ROBOT_X
539+
mY = mouse_y - ROBOT_Y
557540
558-
if mx < MIDPOINT_X: # left side of robot
559-
leftArmAngle = atan2(mY - PIVOT_Y, mX - LEFT_PIVOT_X) - HALF_PI
560-
else:
561-
rightArmAngle = atan2(mY - PIVOT_Y, mX - RIGHT_PIVOT_X) - HALF_PI
541+
if mx < MIDPOINT_X: # left side of robot
542+
leftArmAngle = atan2(mY - PIVOT_Y, mX - LEFT_PIVOT_X) - HALF_PI
543+
else:
544+
rightArmAngle = atan2(mY - PIVOT_Y, mX - RIGHT_PIVOT_X) - HALF_PI
562545
563546
564-
drawRobot()
565-
pop_matrix()
547+
drawRobot()
566548
567549
The ``drawRobot()`` function remains unchanged, but a minor change to ``drawLeftArm()`` and ``drawRightArm()`` is now necessary. Because ``leftArmAngle`` and ``rightArmAngle`` are now computed in radians, the functions don’t have to do any conversion. The changes to the two functions are in bold.
568550

569551
.. code:: python
570552
571553
def drawLeftArm():
572554
global leftArmAngle
573-
push_matrix()
574-
translate(12, 32)
575-
rotate(leftArmAngle)
576-
rect((-12, 0), 12, 37)
577-
pop_matrix()
555+
with push_matrix():
556+
translate(12, 32)
557+
rotate(leftArmAngle)
558+
rect((-12, 0), 12, 37)
578559
579560
def drawRightArm():
580561
global rightArmAngle
581-
push_matrix()
582-
translate(66, 32)
583-
rotate(rightArmAngle)
584-
rect((0, 0), 12, 37)
585-
pop_matrix()
562+
with push_matrix():
563+
translate(66, 32)
564+
rotate(rightArmAngle)
565+
rect((0, 0), 12, 37)

docs/tutorials/strings and drawing text.rst

+17-19
Original file line numberDiff line numberDiff line change
@@ -538,19 +538,18 @@ The character by character method also allows us to display text along a curve.
538538
# Angle in radians is the arclength divided by the radius
539539
theta = arclength / r
540540
541-
push_matrix()
542-
# Polar to cartesian coordinate conversion
543-
translate(r*cos(theta), r*sin(theta))
541+
with push_matrix():
542+
# Polar to cartesian coordinate conversion
543+
translate(r*cos(theta), r*sin(theta))
544544
545-
# Rotate the box
546-
rotate(theta)
545+
# Rotate the box
546+
rotate(theta)
547547
548-
# Display the box
549-
fill(0, 100)
548+
# Display the box
549+
fill(0, 100)
550550
551-
rect_mode("CENTER")
552-
rect((0,0),w,h)
553-
pop_matrix()
551+
rect_mode("CENTER")
552+
rect((0,0),w,h)
554553
555554
# Move halfway again
556555
arclength += w/2
@@ -619,18 +618,17 @@ What we need to do is replace each box with a character from a String that fits
619618
# Starting on the left side of the circle by adding PI
620619
theta = PI + arclength / r
621620
622-
push_matrix()
623-
# Polar to cartesian coordinate conversion
624-
translate(r*cos(theta), r*sin(theta))
621+
with push_matrix():
622+
# Polar to cartesian coordinate conversion
623+
translate(r*cos(theta), r*sin(theta))
625624
626-
# Rotate the box
627-
rotate(theta)
625+
# Rotate the box
626+
rotate(theta)
628627
629-
# Display the box
630-
fill(0, 100)
628+
# Display the box
629+
fill(0, 100)
631630
632-
text(currentChar, (0,0))
633-
pop_matrix()
631+
text(currentChar, (0,0))
634632
635633
# Move halfway again
636634
arclength += w/2

0 commit comments

Comments
 (0)