UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

1,228 lines (633 loc) 36 kB
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="en"> <head> <meta charset="utf-8"/> <title>Getting Started with Processing</title> <script type="text/javascript"> if(!window.atlasplugins) window.atlasplugins = {} atlasplugins.codeMirrorSettings = { theme: "3024-day" } </script> <link href="http://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css"> </link> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"> </script> <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> </script> <link rel="stylesheet" type="text/css" href="theme/html/html.css"/> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.4.0/codemirror.min.css"/> <link rel="stylesheet" type="text/css" href="https://d2uogd9jz9k9zm.cloudfront.net/processingjs-0.0.3.css"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js" type="text/javascript"> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.4.0/codemirror.min.js" type="text/javascript"> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.5.0/mode/clike/clike.min.js" type="text/javascript"> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js" type="text/javascript"> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/processing.js/1.4.8/processing.min.js" type="text/javascript"> </script> <script src="https://d2uogd9jz9k9zm.cloudfront.net/processingjs-0.0.3.min.js" type="text/javascript"> </script> <script type="text/javascript"> $(function() { // Make all non runnable examples readonly codemirrors $('pre:not(*[data-executable], .CodeMirror pre)').each(function(i, el) { var textArea = $('<textarea>' + el.innerHTML + '</textarea>') $(el).replaceWith(textArea); var editor = CodeMirror.fromTextArea(textArea[0], { mode: "text/x-java", theme: "3024-day", readOnly: true, viewportMargin: 0 }); }); }); </script> <style type="text/css"> /* This needs to come last to set auto height, and html.css is always included first */ .CodeMirror { height: auto; font-size: 14px; padding: 15px; border-radius: 5px; } .CodeMirror-scroll { overflow-y: hidden; overflow-x: auto; height: auto; } </style> <script type="text/javascript" src="theme/html/epub.js"> </script> <script type="text/javascript" src="theme/html/reader.js"> </script> </head> <body data-type="book" id="viewer"> <section data-type="chapter" data-pdf-bookmark="Chapter 7. Motion" id="motion"> <h1>Motion</h1> <p>Like a flip book, animation on screen is created by drawing an image, then drawing a slightly different image, then another, and so on. The illusion of fluid motion is created by <em>persistence of vision</em>. When a set of similar images is presented at a fast enough rate, our brains translate these images into motion.</p> <section data-type="sect1" data-pdf-bookmark="PROD: Insert Section Title Here" id="example_7-1_colon_see_the_frame"> <h1>PROD: Insert Section Title Here</h1> <section data-type="sect2" data-pdf-bookmark="Example 7-1: See the Frame Rate" id="example_7-1_colon_see_the_frame-id1"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_01">Example 7-1: See the Frame Rate</a></h2> <p>To create smooth motion, Processing tries to run the code inside <em>draw()</em> at 60 frames each second. To confirm the frame rate, run this program and watch the values print to the Console. The <em>frameRate</em> variable keeps track of the program’s speed.</p> <pre data-type="programlisting">void draw() { println(frameRate); }</pre> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-2: Set the Frame Rate" id="example_7-2_colon_set_the_frame_rate"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_02">Example 7-2: Set the Frame Rate</a></h2> <p>The <em>frameRate()</em> function changes the speed at which the program runs. To see the result, uncomment different versions of <em>frameRate()</em> in this example:</p> <pre data-type="programlisting">void setup() { frameRate(30); // Thirty frames each second //frameRate(12); // Twelve frames each second //frameRate(2); // Two frames each second //frameRate(0.5); // One frame every two seconds } void draw() { println(frameRate); }</pre> <div data-type="note"> <p>Processing <em>tries</em> to run the code at 60 frames each second, but if it takes longer than 1/60th of a second to run the <em>draw()</em> method, then the frame rate will decrease. The <em>frameRate()</em> function specifies only the maximum frame rate, and the actual frame rate for any program depends on the computer that is running the code.</p> </div> </section> </section> <section data-type="sect1" data-pdf-bookmark="Speed and Direction" id="speed_and_direction"> <h1>Speed and Direction</h1> <p>To create fluid motion examples, we use a data type called <em>float</em>. This type of variable stores numbers with decimal places, which provide more resolution for working with motion. For instance, when using <em>int</em> s, the slowest you can move each frame is one pixel at a time (1, 2, 3, 4, . . .), but with <em>float</em> s, you can move as slowly as you want (1.01, 1.01, 1.02, 1.03, . . .).</p> <section data-type="sect2" data-pdf-bookmark="Example 7-3: Move a Shape" id="example_7-3_colon_move_a_shape"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_03">Example 7-3: Move a Shape</a></h2> <p>The following example moves a shape from left to right by updating the <em>x</em> variable:</p> <figure> <img src="images/Ex_07_03.png" alt="Ex 07 03"/> <figcaption/> </figure> <pre data-type="programlisting">int radius = 40; float x = -radius; float speed = 0.5; void setup() { size(240, 120); smooth(); ellipseMode(RADIUS); } void draw() { background(0); x += speed; // Increase the value of x arc(x, 60, radius, radius, 0.52, 5.76); }</pre> <p>When you run this code, you’ll notice the shape moves off the right of the screen when the value of the <em>x</em> variable is greater than the width of the window. The value of <em>x</em> continues to increase, but the shape is no longer visible.</p> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-4: Wrap Around" id="example_7-4_colon_wrap_around"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_04">Example 7-4: Wrap Around</a></h2> <p>There are many alternatives to this behavior, which you can choose from according to your preference. First, we’ll extend the code to show how to move the shape back to the left edge of the screen after it disappears off the right. In this case, picture the screen as a flattened cylinder, with the shape moving around the outside to return to its starting point:</p> <figure> <img src="images/Ex_07_04.png" alt="Ex 07 04"/> <figcaption/> </figure> <pre data-type="programlisting">int radius = 40; float x = -radius; float speed = 0.5; void setup() { size(240, 120); smooth(); ellipseMode(RADIUS); } void draw() { background(0); x += speed; // Increase the value of x if (x &amp;gt; width+radius) { // If the shape is off screen, x = -radius; // move to the left edge } arc(x, 60, radius, radius, 0.52, 5.76); }</pre> <p>On each trip through <em>draw()</em>, the code tests to see if the value of <em>x</em> has increased beyond the width of the screen (plus the radius of the shape). If it has, we set the value of <em>x</em> to a negative value, so that as it continues to increase, it will enter the screen from the left. See <a data-type="xref" href="#testing_for_the_left_edge_of_the_window">Figure 7-1</a> for a diagram of how it works.</p> <figure id="testing_for_the_left_edge_of_the_window"> <img src="images/Fig_07_01.png" alt="Fig 07 01"/> <figcaption>Testing for the left edge of the window.</figcaption> </figure> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-5: Bounce Off the Wall" id="example_7-5_colon_bounce_off_the_wall"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_05">Example 7-5: Bounce Off the Wall</a></h2> <p>In this example, we’ll extend <a data-type="xref" href="#example_7-3_colon_move_a_shape"><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_03">Example 7-3: Move a Shape</a></a> to have the shape change directions when it hits an edge, instead of wrapping around to the left. To make this happen, we add a new variable to store the direction of the shape. A direction value of 1 moves the shape to the right, and a value of −1 moves the shape to the left:</p> <figure> <img src="images/Ex_07_05.png" alt="Ex 07 05"/> <figcaption/> </figure> <pre data-type="programlisting">int radius = 40; float x = 110; float speed = 0.5; int direction = 1; void setup() { size(240, 120); smooth(); ellipseMode(RADIUS); } void draw() { background(0); x += speed * direction; if ((x &amp;gt; width-radius) || (x &amp;lt; radius)) { direction = -direction; // Flip direction } if (direction == 1) { arc(x, 60, radius, radius, 0.52, 5.76); // Face right } else { arc(x, 60, radius, radius, 3.67, 8.9); // Face left } }</pre> <p>When the shape reaches an edge, this code flips the shape’s direction by changing the sign of the <em>direction</em> variable. For example, if the <em>direction</em> variable is positive when the shape reaches an edge, the code flips it to negative.</p> </section> </section> <section data-type="sect1" data-pdf-bookmark="Tweening" id="tweening"> <h1>Tweening</h1> <p>Sometimes you want to animate a shape to go from one point on screen to another. With a few lines of code, you can set up the start position and the stop position, then calculate the in-between (<em>tween</em>) positions at each frame.</p> <section data-type="sect2" data-pdf-bookmark="Example 7-6: Calculate Tween Positions" id="example_7-6_colon_calculate_tween_positi"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_06">Example 7-6: Calculate Tween Positions</a></h2> <p>To make this example code modular, we’ve created a group of variables at the top. Run the code a few times and change the values to see how this code can move a shape from any location to any other at a range of speeds. Change the <em>step</em> variable to alter the speed:</p> <figure> <img src="images/Ex_07_06.png" alt="Ex 07 06"/> <figcaption/> </figure> <pre data-type="programlisting">int startX = 20; // Initial x-coordinate int stopX = 160; // Final x-coordinate int startY = 30; // Initial y-coordinate int stopY = 80; // Final y-coordinate float x = startX; // Current x-coordinate float y = startY; // Current y-coordinate float step = 0.005; // Size of each step (0.0 to 1.0) float pct = 0.0; // Percentage traveled (0.0 to 1.0) void setup() { size(240, 120); smooth(); } void draw() { background(0); if (pct &amp;lt; 1.0) { x = startX + ((stopX-startX) * pct); y = startY + ((stopY-startY) * pct); pct += step; } ellipse(x, y, 20, 20); }</pre> </section> </section> <section data-type="sect1" data-pdf-bookmark="Random" id="random"> <h1>Random</h1> <p>Unlike the smooth, linear motion common to computer graphics, motion in the physical world is usually idiosyncratic. For instance, think of a leaf floating to the ground, or an ant crawling over rough terrain. We can simulate the unpredictable qualities of the world by generating random numbers. The <em>random()</em> function calculates these values; we can set a range to tune the amount of disarray in a program.</p> <section data-type="sect2" data-pdf-bookmark="Example 7-7: Generate Random Values" id="example_7-7_colon_generate_random_values"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_07">Example 7-7: Generate Random Values</a></h2> <p>The following short example prints random values to the Console, with the range limited by the position of the mouse. The <em>random()</em> function always returns a floating-point value, so be sure the variable on the left side of the assignment operator (=) is a <em>float</em> as it is here:</p> <pre data-type="programlisting">void draw() { float r = random(0, mouseX); println(r); }</pre> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-8: Draw Randomly" id="example_7-8_colon_draw_randomly"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_08">Example 7-8: Draw Randomly</a></h2> <p>The following example builds on <a data-type="xref" href="#example_7-7_colon_generate_random_values"><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_07">Example 7-7: Generate Random Values</a></a>; it uses the values from <em>random()</em> to change the position of lines on screen. When the mouse is at the left of the screen, the change is small; as it moves to the right, the values from <em>random()</em> increase and the movement becomes more exaggerated. Because the <em>random()</em> function is inside the <em>for</em> loop, a new random value is calculated for each point of every line:</p> <figure> <img src="images/Ex_07_08.png" alt="Ex 07 08"/> <figcaption/> </figure> <pre data-type="programlisting">void setup() { size(240, 120); smooth(); } void draw() { background(204); for (int x = 20; x &amp;lt; width; x += 20) { float mx = mouseX / 10; float offsetA = random(-mx, mx); float offsetB = random(-mx, mx); line(x + offsetA, 20, x - offsetB, 100); } }</pre> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-9: Move Shapes Randomly" id="example_7-9_colon_move_shapes_randomly"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_09">Example 7-9: Move Shapes Randomly</a></h2> <p>When used to move shapes around on screen, random values can generate images that are more natural in appearance. In the following example, the position of the circle is modified by random values on each trip through <em>draw()</em>. Because the <em>background()</em> function is not used, past locations are traced:</p> <figure> <img src="images/Ex_07_09.png" alt="Ex 07 09"/> <figcaption/> </figure> <pre data-type="programlisting">float speed = 2.5; int diameter = 20; float x; float y; void setup() { size(240, 120); smooth(); x = width/2; y = height/2; } void draw() { x += random(-speed, speed); y += random(-speed, speed); ellipse(x, y, diameter, diameter); }</pre> <p>If you watch this example long enough, you may see the circle leave the window and come back. This is left to chance, but we could add a few <em>if</em> structures or use the <em>constrain()</em> function to keep the circle from leaving the screen. The <em>constrain()</em> function limits a value to a specific range, which can be used to keep <em>x</em> and <em>y</em> within the boundaries of the display window. By replacing the <em>draw()</em> in the preceding code with the following, you’ll ensure that the ellipse will remain on the screen:</p> <pre data-type="programlisting">void draw() { x += random(-speed, speed); y += random(-speed, speed); x = constrain(x, 0, width); y = constrain(y, 0, height); ellipse(x, y, diameter, diameter); }</pre> <div data-type="note"> <p>The <em>randomSeed()</em> function can be used to force <em>random()</em> to produce the same sequence of numbers each time a program is run. This is described further in the Processing Reference.</p> </div> </section> </section> <section data-type="sect1" data-pdf-bookmark="Timers" id="timers"> <h1>Timers</h1> <p>Every Processing program counts the amount of time that has passed since it was started. It counts in milliseconds (thousandths of a second), so after 1 second, the counter is at 1,000; after 5 seconds, it’s at 5,000; and after 1 minute, it’s at 60,000. We can use this counter to trigger animations at specific times. The <em>millis()</em> function returns this counter value.</p> <section data-type="sect2" data-pdf-bookmark="Example 7-10: Time Passes" id="example_7-10_colon_time_passes"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_10">Example 7-10: Time Passes</a></h2> <p>You can watch the time pass when you run this program:</p> <pre data-type="programlisting">void draw() { int timer = millis(); println(timer); }</pre> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-11: Triggering Timed Events" id="example_7-11_colon_triggering_timed_even"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_11">Example 7-11: Triggering Timed Events</a></h2> <p>When paired with an <em>if</em> block, the values from <em>millis()</em> can be used to sequence animation and events within a program. For instance, after two seconds have elapsed, the code inside the <em>if</em> block can trigger a change. In this example, variables called <em>time1</em> and <em>time2</em> determine when to change the value of the <em>x</em> variable:</p> <pre data-type="programlisting">int time1 = 2000; int time2 = 4000; float x = 0; void setup() { size(480, 120); smooth(); } void draw() { int currentTime = millis(); background(204); if (currentTime &amp;gt; time2) { x -= 0.5; } else if (currentTime &amp;gt; time1) { x += 2; } ellipse(x, 60, 90, 90); }</pre> </section> </section> <section data-type="sect1" data-pdf-bookmark="Circular" id="circular"> <h1>Circular</h1> <p>If you’re a trigonometry ace, you already know how amazing the sine and cosine functions are. If you’re not, we hope the next examples will trigger your interest. We won’t discuss the math in detail here, but we’ll show a few applications to generate fluid motion.</p> <p><a data-type="xref" href="#sine_and_cosine_values">Figure 7-2</a> shows a visualization of sine wave values and how they relate to angles. At the top and bottom of the wave, notice how the rate of change (the change on the vertical axis) slows down, stops, then switches direction. It’s this quality of the curve that generates interesting motion.</p> <p>The <em>sin()</em> and <em>cos()</em> functions in Processing return values between −1 and 1 for the sine or cosine of the specified angle. Like <em>arc()</em>, the angles must be given in radian values (see <a data-type="xref" href="ch03.html#example_3-7_colon_draw_part_of_an_ellips"><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_03_07">Example 3-7: Draw Part of an Ellipse</a></a> and <a data-type="xref" href="ch03.html#example_3-8_colon_draw_with_degrees"><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_03_08">Example 3-8: Draw with Degrees</a></a> for a reminder of how radians work). To be useful for drawing, the <em>float</em> values returned by <em>sin()</em> and <em>cos()</em> are usually multiplied by a larger value.</p> <figure id="sine_and_cosine_values"> <img src="images/Fig_07_02.png" alt="Fig 07 02"/> <figcaption>Sine and cosine values.</figcaption> </figure> <section data-type="sect2" data-pdf-bookmark="Example 7-12: Sine Wave Values" id="example_7-12_colon_sine_wave_values"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_12">Example 7-12: Sine Wave Values</a></h2> <p>This example shows how values for <em>sin()</em> cycle from −1 to 1 as the angle increases. With the <em>map()</em> function, the <em>sinval</em> variable is converted from this range to values from 0 and 255. This new value is used to set the background color of the window:</p> <pre data-type="programlisting">float angle = 0.0; void draw() { float sinval = sin(angle); println(sinval); float gray = map(sinval, −1, 1, 0, 255); background(gray); angle += 0.1; }</pre> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-13: Sine Wave Movement" id="example_7-13_colon_sine_wave_movement"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_13">Example 7-13: Sine Wave Movement</a></h2> <p>This example shows how these values can be converted into movement:</p> <figure> <img src="images/Ex_07_13.png" alt="Ex 07 13"/> <figcaption/> </figure> <pre data-type="programlisting">float angle = 0.0; float offset = 60; float scalar = 40; float speed = 0.05; void setup() { size(240, 120); smooth(); } void draw() { background(0); float y1 = offset + sin(angle) * scalar; float y2 = offset + sin(angle + 0.4) * scalar; float y3 = offset + sin(angle + 0.8) * scalar; ellipse( 80, y1, 40, 40); ellipse(120, y2, 40, 40); ellipse(160, y3, 40, 40); angle += speed; }</pre> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-14: Circular Motion" id="example_7-14_colon_circular_motion"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_14">Example 7-14: Circular Motion</a></h2> <p>When <em>sin()</em> and <em>cos()</em> are used together, they can produce circular motion. The <em>cos()</em> values provide the x-coordinates, and the <em>sin()</em> values the y-coordinates. Both are multiplied by a variable named <em>scalar</em> to change the radius of the movement and summed with an offset value to set the center of the circular motion:</p> <figure> <img src="images/Ex_07_14.png" alt="Ex 07 14"/> <figcaption/> </figure> <pre data-type="programlisting">float angle = 0.0; float offset = 60; float scalar = 30; float speed = 0.05; void setup() { size(120, 120); smooth(); } void draw() { float x = offset + cos(angle) * scalar; float y = offset + sin(angle) * scalar; ellipse( x, y, 40, 40); angle += speed; }</pre> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-15: Spirals" id="example_7-15_colon_spirals"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_15">Example 7-15: Spirals</a></h2> <p>A slight change made to increase the <em>scalar</em> value at each frame produces a spiral, rather than a circle:</p> <figure> <img src="images/Ex_07_15.png" alt="Ex 07 15"/> <figcaption/> </figure> <pre data-type="programlisting">float angle = 0.0; float offset = 60; float scalar = 2; float speed = 0.05; void setup() { size(120, 120); fill(0); smooth(); } void draw() { float x = offset + cos(angle) * scalar; float y = offset + sin(angle) * scalar; ellipse( x, y, 2, 2); angle += speed; scalar += speed; }</pre> </section> </section> <section data-type="sect1" data-pdf-bookmark="Translate, Rotate, Scale" id="translate_comma_rotate_comma_scale"> <h1>Translate, Rotate, Scale</h1> <p>Changing the screen coordinates is an alternative technique to create motion. For instance, 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 on screen is the same. By modifying the default coordinate system, we can create different <em>transformations</em> including translation, rotation, and scaling. <a data-type="xref" href="#translating_comma_rotating_comma_and_sca">Figure 7-3</a> demonstrates this graphically.</p> <figure id="translating_comma_rotating_comma_and_sca"> <img src="images/Fig_07_03.png" alt="Fig 07 03"/> <figcaption>Translating, rotating, and scaling the coordinates.</figcaption> </figure> <p>Working with transformations can be tricky, but the <em>translate()</em> function is the most straightforward, so we’ll start with it. This function can shift the coordinate system left, right, up, and down with its two parameters.</p> <section data-type="sect2" data-pdf-bookmark="Example 7-16: Translating Location" id="example_7-16_colon_translating_location"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_16">Example 7-16: Translating Location</a></h2> <p>In this example, notice that each rectangle is drawn at coordinate (0,0), but they are moved around on the screen, because they are affected by <em>translate()</em>:</p> <figure> <img src="images/Ex_07_16.png" alt="Ex 07 16"/> <figcaption/> </figure> <pre data-type="programlisting">void setup() { size(120, 120); } void draw() { translate(mouseX, mouseY); rect(0, 0, 30, 30); }</pre> <p>The <em>translate()</em> function sets the (0,0) coordinate of the screen to the mouse location. In the next line, the <em>rect()</em> drawn at the new (0,0) is in fact drawn at the mouse location.</p> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-17: Multiple Translations" id="example_7-17_colon_multiple_translations"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_17">Example 7-17: Multiple Translations</a></h2> <p>After a transformation is made, it is applied to all subsequent drawing functions. Notice what happens when a second <em>translate</em> command is added to control a second rectangle:</p> <figure> <img src="images/Ex_07_17.png" alt="Ex 07 17"/> <figcaption/> </figure> <pre data-type="programlisting">void setup() { size(120, 120); } void draw() { translate(mouseX, mouseY); rect(0, 0, 30, 30); translate(35, 10); rect(0, 0, 15, 15); }</pre> <p>The smaller rectangle was translated the amount of <em>mouseX</em> + 35 and <em>mouseY</em> + 10.</p> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-18: Isolating Transformations" id="example_7-18_colon_isolating_transformat"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_18">Example 7-18: Isolating Transformations</a></h2> <p>To isolate the effects of a transformation so they don’t affect later commands, use the <em>pushMatrix()</em> and <em>popMatrix()</em> functions. When the <em>pushMatrix()</em> function is run, it saves a copy of the current coordinate system and then restores that system after <em>popMatrix()</em>:</p> <figure> <img src="images/Ex_07_18.png" alt="Ex 07 18"/> <figcaption/> </figure> <pre data-type="programlisting">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); }</pre> <p>In this example, the smaller rectangle always draws in the upper-left corner because the <em>translate(mouseX, mouseY)</em> is cancelled by the <em>popMatrix()</em>.</p> <div data-type="note"> <p>The <em>pushMatrix()</em> and <em>popMatrix()</em> functions are always used in pairs. For every <em>pushMatrix()</em>, you need to have a matching <em>popMatrix()</em>.</p> </div> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-19: Rotation" id="example_7-19_colon_rotation"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_19">Example 7-19: Rotation</a></h2> <p>The <em>rotate()</em> 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 <em>origin</em>. To spin a shape around its center point, first use <em>translate()</em> to move to the location where you’d like the shape, then call <em>rotate()</em>, and then draw the shape with its center at coordinate (0,0):</p> <figure> <img src="images/Ex_07_19.png" alt="Ex 07 19"/> <figcaption/> </figure> <pre data-type="programlisting">float angle = 0.0; void setup() { size(120, 120); smooth(); } void draw() { translate(mouseX, mouseY); rotate(angle); rect(−15, −15, 30, 30); angle += 0.1; }</pre> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-20: Combining Transformations" id="example_7-20_colon_combining_transformat"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_20">Example 7-20: Combining Transformations</a></h2> <p>When <em>translate()</em> and <em>rotate()</em> are combined, the order in which they appear affects the result. The following example is identical to <a data-type="xref" href="#example_7-19_colon_rotation"><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_19">Example 7-19: Rotation</a></a>, except that <em>translate()</em> and <em>rotate()</em> are reversed. The shape now rotates around the upper-left corner of the display window, with the distance from the corner set by <em>translate</em>():</p> <figure> <img src="images/Ex_07_20.png" alt="Ex 07 20"/> <figcaption/> </figure> <pre data-type="programlisting">float angle = 0.0; void setup() { size(120, 120); smooth(); } void draw() { rotate(angle); translate(mouseX, mouseY); rect(−15, −15, 30, 30); angle += 0.1; }</pre> <div data-type="note"> <p>You can also use the <em>rectMode()</em>, <em>ellipseMode()</em>, <em>imageMode()</em>, and <em>shapeMode()</em> functions to make it easier to draw shapes from their center.</p> </div> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-21: Scaling" id="example_7-21_colon_scaling"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_21">Example 7-21: Scaling</a></h2> <p>The <em>scale()</em> function stretches the coordinates on the screen. Like <em>rotate()</em>, it transforms from the origin. Therefore, as with <em>rotate()</em>, to scale a shape from its center, translate to its location, scale, and then draw with the center at coordinate (0,0):</p> <figure> <img src="images/Ex_07_21.png" alt="Ex 07 21"/> <figcaption/> </figure> <pre data-type="programlisting">float angle = 0.0; void setup() { size(120, 120); smooth(); } void draw() { translate(mouseX, mouseY); scale(sin(angle) + 2); rect(−15, −15, 30, 30); angle += 0.1; }</pre> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-22: Keeping Strokes Consistent" id="example_7-22_colon_keeping_strokes_consi"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_22">Example 7-22: Keeping Strokes Consistent</a></h2> <p>From the thick lines in <a data-type="xref" href="#example_7-21_colon_scaling"><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_21">Example 7-21: Scaling</a></a>, you can see how the <em>scale()</em> function affects the stroke weight. To maintain a consistent stroke weight as a shape scales, divide the desired stroke weight by the scalar value:</p> <pre data-type="programlisting">float angle = 0.0; void setup() { size(120, 120); smooth(); } void draw() { translate(mouseX, mouseY); float scalar = sin(angle) + 2; scale(scalar); strokeWeight(1.0 / scalar); rect(−15, −15, 30, 30); angle += 0.1; }</pre> </section> <section data-type="sect2" data-pdf-bookmark="Example 7-23: An Articulating Arm" id="example_7-23_colon_an_articulating_arm"> <h2><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Ex_07_23">Example 7-23: An Articulating Arm</a></h2> <p>In this final and longest transformation example, we’ve put together a series of <em>translate()</em> and <em>rotate()</em> functions to create a linked arm that bends back and forth. Each <em>translate()</em> further moves the position of the lines, and each <em>rotate()</em> adds to the previous rotation to bend more:</p> <figure> <img src="images/Ex_07_23.png" alt="Ex 07 23"/> <figcaption/> </figure> <pre data-type="programlisting">float angle = 0.0; float angleDirection = 1; float speed = 0.005; void setup() { size(120, 120); smooth(); } 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 &amp;gt; QUARTER_PI) || (angle &amp;lt; 0)) { angleDirection *= −1; } }</pre> <p>Here, we don’t use a <em>pushMatrix()</em> or <em>popMatrix()</em>, because we want the transformations to <em>propagate</em>—for each transformation to build on the last. The coordinate system is automatically reset to the default when <em>draw()</em> begins each frame.</p> </section> </section> <section data-type="sect1" data-pdf-bookmark="Robot 5: Motion" id="robot_5_colon_motion"> <h1><a href="http://examples.oreilly.com/0636920000570/interactive_examples/index.php?example=Robot5_Motion">Robot 5: Motion</a></h1> <figure> <img src="images/NRobot_Motion.png" alt="NRobot Motion"/> <figcaption/> </figure> <p>In this example, the techniques for random and circular motion are applied to the robot. The <em>background()</em> was removed to make it easier to see how the robot’s position and body change.</p> <p>At each frame, a random number between −4 and 4 is added to the x-coordinate, and a random number between −1 and 1 is added to the y-coordinate. This causes the robot to move more from left to right than top to bottom. Numbers calculated from the <em>sin()</em> function change the height of the neck so it oscillates between 50 and 110 pixels high:</p> <pre data-type="programlisting">float x = 180; // X-coordinate float y = 400; // Y-coordinate float bodyHeight = 153; // Body height float neckHeight = 56; // Neck height float radius = 45; // Head radius float angle = 0.0; // Angle for motion void setup() { size(360, 480); smooth(); ellipseMode(RADIUS); background(204); } void draw() { // Change position by a small random amount x += random(-4, 4); y += random(-1, 1); // Change height of neck neckHeight = 80 + sin(angle) * 30; angle += 0.05; // Adjust the height of the head float ny = y - bodyHeight - neckHeight - radius; // Neck stroke(102); line(x+2, y-bodyHeight, x+2, ny); line(x+12, y-bodyHeight, x+12, ny); line(x+22, y-bodyHeight, x+22, ny); // Antennae line(x+12, ny, x-18, ny-43); line(x+12, ny, x+42, ny-99); line(x+12, ny, x+78, ny+15); // Body noStroke(); fill(102); ellipse(x, y-33, 33, 33); fill(0); rect(x-45, y-bodyHeight, 90, bodyHeight-33); fill(102); rect(x-45, y-bodyHeight+17, 90, 6); // Head fill(0); ellipse(x+12, ny, radius, radius); fill(255); ellipse(x+24, ny-6, 14, 14); fill(0); ellipse(x+24, ny-6, 3, 3); }</pre> </section> </section> </body> </html>