<?xml version='1.0' encoding='UTF-8'?>

<?xml-stylesheet href="./_c74_tut.xsl" type="text/xsl"?>
<chapter name="Tutorial 15: Image Rotation">
<setdocpatch name="15jImageRotation" patch="15jImageRotation.maxpat"/>

<previous name="jitterchapter14">Matrix Positioning</previous>
<next name="jitterchapter16">Using Named Jitter Matrices</next>
<parent name="jitindex">Jitter Tutorials</parent>



<h1>Tutorial 15: Image Rotation</h1>
<h2>Rotating and Zooming with jit.rota</h2>
<p>Jitter provides an easy way to rotate and/or zoom an image with an object called <o>jit.rota</o>. Rotation and zoom are common and useful video effects, and by combining them in different ways you can also achieve a variety of kaleidoscopic effects. <o>jit.rota</o> takes a matrix of video data (or any other sort of image) in its inlet, and sends out a version that has been zoomed, rotated, and otherwise distorted based on the settings of the object's attributes.</p>

<bullet>Open the tutorial patch. The QuickTime video <i>dishes.mov</i> is read into the <o>jit.movie</o> object automatically by a <m>bang</m> from <o>loadbang</o>. To see the video, click on the <i>Display</i> <o>toggle</o> to start the <o>metro</o>.</bullet>
<p>The video is a three-second left-to-right camera pan over a set of dishes. However, the <m>loop</m> attribute of the <o>jit.movie</o> object has been initialized to <m>2</m>, so the movie loops back and forth, giving the illusion of a back-and-forth pan.</p>

<div>
<techdetail>Note: Many attributes of Jitter objects use only the arguments 1 and 0 to mean "on" and "off", so it's reasonable to assume that the <m>loop</m> attribute of <o>jit.movie</o> is the same. While it's true that <m>loop 0</m> turns looping off and <m>loop 1</m> turns it on, <m>loop 2</m> causes the video to play forward and then play backward when it reaches the <m>loopend</m> point, rather than leaping to the <m>loopstart</m> point.</techdetail>
</div><br/>
<p>The <m>theta</m> attribute of <o>jit.rota</o> determines the angle of rotation around a central <i>anchor point</i>.</p>

<bullet>Drag on the <i>Rotation Angle</i> <o>number</o> box to rotate the video. Positive (or increasing) values cause counter-clockwise rotation, and negative (or decreasing) values cause clockwise rotation. The angle of rotation&#x2014;a.k.a. the angle  (<i>theta</i>)&#x2014;is stated in <i>radians</i>. A value of 0&#x2014;or any multiple of 2&#x3C0; (i.e., 6.283185)&#x2014;is the normal upright positioning. A value of &#x3C0; (i.e. 3.141593)&#x2014;or any odd multiple of &#x3C0;&#x2014;is the fully upside-down position. Experiment until you understand the relationship between the <m>theta</m> values and the behavior of <o>jit.rota</o>.</bullet>

<div>
<techdetail>Technical Detail: <o>jit.rota</o> does a lot of internal calculation using trigonometry to determine how to rotate the image. If you're not a trigonometry buff, you might not be used to thinking of angles in terms of radians. In everyday conversation we more commonly use <i>degrees</i>, with a full rotation being 360&#xB0;. In trigonometry, it's more common to use <i>radians</i>, where a full rotation equals 2&#x3C0; radians. That's because a circle with a radius of 1 has a circumference of exactly 2&#x3C0;, so you can refer to an angle by referencing the point where it would intersect the unit circle. (For example if you started at a point on the unit circle and traveled a distance of exactly &#x3C0;/2 around the circumference, you would end up at a 90&#xB0; angle&#x2014;i.e. an angle of  &#x3C0;/2 radians&#x2014;from where you started, in reference to the circle's center.)<br/>

Also, in trigonometry we consider a positive change in angle to be a counter-clockwise rotation around the unit circle, whereas in everyday life you might more commonly think of a clockwise motion as being intuitively "positive" or "increasing" (like the passage of time).<br/>

So, to convert a clockwise rotation in degrees into the  same rotation in radians, you would need to multiply the degree angle by 2&#x3C0;, then divide by -360.</techdetail>
</div>

<h2>Automated Rotation</h2>
<p>Besides rotating the image by hand, you can also write an automated process in Max that will supply continually changing rotation angles. In the previous chapter we wrote a subpatch called <i>rotate</i> that used the <o>line</o> object to increase the angle of hue rotation continually from 0&#xB0; to 360&#xB0;. In this chapter we do something similar, but this time we use the <m>bang</m> from the <o>metro</o> that's displaying the movie to increase the angle of rotation. To keep it "user-friendly" we show the user <i>degrees</i> of angle rotation rather than radians (we convert degrees to radians inside the subpatch), and we also display the rotation speed as "rotations per second."</p>

<bullet>In the <o>number</o> box labeled <i>Degrees per bang</i>, enter the number <m>6</m>. This will cause the rotation angle to increase by 6 degrees with each <m>bang</m> from the <o>metro</o>. Since the <o>metro</o> sends out a <m>bang</m> 20 times per second (once every 50 ms), we know that we can calculate the number of rotations per second by the formula <i>d*20/360</i>&#x2014;that is, <i>d/18</i>&#x2014;where <i>d</i> is the degrees of angle increase per <m>bang</m>. Now click on the <o>toggle</o> marked <i>On/Off</i> to begin the automated rotation.</bullet><br/>

<bullet>Double click on the <o>patcher</o> <m>rotate</m> object to see the contents of the subpatch.</bullet>

<illustration><img src="images/jitterchapter15a.png"/>Automated rotation in the [rotate] subpatch</illustration>

<p>We convert what the user specifies as degrees per <m>bang</m> into an amount in radians, by multiplying the degrees by 2&#x3C0; and dividing by &#x2013;360. (See the Technical Detail sidebar above.) When a <m>bang</m> comes in the left inlet, if rotation is turned on then the <m>bang</m> gets passed through and it causes an increase of angle rotation to be added into the <o>accum</o> object. Note that a negative degrees per <m>bang</m> amount works fine, too, and causes a counter-clockwise rotation of the image. When the total rotation angle exceeds 2&#x3C0; (or -2&#x3C0;), a <o>split</o> sends the value to an <o>expr</o> that uses a modulo operation to bring it back into range (resetting the value in the <o>accum</o> object) before sending it to the <o>outlet</o>. When rotation gets turned off, we detect that fact with a <link name="select" type="refpage">sel</link> <m>0</m> object, and reset the <m>theta</m> angle to <m>0</m>.</p>

<bullet>Close the subpatch window. Click on the <i>On/Off</i> <o>toggle</o> to stop the automated rotation.</bullet>
<h3>Zoom In or Out</h3>
<p>The other main feature of <o>jit.rota</o> is its zooming capability. The amount of zoom is determined by <o>jit.rota</o>'s <m>zoom_x</m> and <m>zoom_y</m> attributes. These permit you to zoom in or out in the horizontal and vertical dimensions independently; or you can zoom both dimensions simultaneously by changing both attributes at once.</p>

<bullet>Drag on the <o>number</o> box labeled <i>Zoom</i> to zoom in and out. Values greater than 1 expand the image (zoom in), and values less than 1 shrink the image (zoom out). You can change the zoom of the <i>x</i> and <i>y</i> dimensions independently by entering values directly into the <m>x</m> and <m>y</m> <o>number</o> boxes. (Negative zoom values flip the image as well as resize it.)</bullet>
<p>When we zoom in on the image&#x2014;say, with a zoom value of 2&#x2014;we still retain reasonably good image quality because we've turned <o>jit.rota</o>'s <m>interp</m> attribute on with an <m>interp 1</m> message. If you turn <m>interp</m> off, you will get pixelation when you zoom in. When you're zooming out, <m>interp</m> has no appreciable effect, so it's pretty much a waste of the computer's time. (See <link type="tutorial" module="jit" name="jitterchapter14">Jitter Tutorial 14</link>  for a discussion of pixelation and interpolation.) However, interpolation does improve the look of rotated images, even when they've been shrunk by zooming out.</p>

<h2>Beyond the Edge</h2>
<bullet>Set the zoom of both dimensions to some small value, such as <m>0.25</m>.</bullet><br/>
<p>When the image does not fill the entire display area because of shrinking or rotation, <o>jit.rota</o> has to decide what to do with the rest of the matrix that lies outside the image area. At present <o>jit.rota</o> is setting all the cell values outside the image area to 0, making them all black. The way that <o>jit.rota</o> handles the cells that lie outside the image boundaries is determined by its <m>boundmode</m> attribute. The different available <m>boundmode</m> settings are presented in the popup menu labeled <i>Space outside the image</i> in the upper-right corner of the patch. We initialized the <m>boundmode</m> value to <m>1</m>, which instructs <o>jit.rota</o> to clear all the outlying cells. Here is the meaning of each of the <m>boundmode</m> settings:</p>

<p><m>0</m> <i>Ignore:</i> Leave all outlying cells unchanged from their previous values.</p>
<p><m>1</m> <i>Clear:</i> Set all outlying cell values to 0.</p>
<p><m>2</m> <i>Wrap:</i> Begin the image again, as many times as necessary to fill the matrix.</p>
<p><m>3</m> <i>Clip:</i> For all the outlying cells, continue to use the values of the boundary cells of the image.</p>
<p><m>4</m> <i>Fold:</i> Repeat the image, flipped back in the opposite direction.</p><br/>
<bullet>For special effects when the image is zoomed out, try setting the <m>boundmode</m> attribute to <m>2</m> (wrap) for a "Warhol" duplicate image effect, or <m>4</m> (fold) for a kaleidoscope effect.</bullet>
<bullet>Now try turning the automated rotation back on, to combine rotation and zoom, and modify the different parameters (<i>Degrees per bang</i>, <i>Zoom</i>, and <i>Space outside the image</i>).</bullet>
<bullet>When you have finished experimenting, turn off the automated rotation and return the zoom attributes (<m>zoom_x</m> and <m>zoom_y</m>) to <m>1</m>.</bullet>


<h2>Some Adjustments&#x2014;Anchor Point and Offset</h2>
<p>Up to now we've been using the center of the image as the <i>anchor point</i> of the rotation. However, you can choose any point for this. The center of rotation is set with the <m>anchor_x</m> and <m>anchor_y</m> attributes. Right now those attributes are set to <m>160</m> and <m>120</m> (half of the image dimensions), but you can change them in the <o>number</o> boxes labeled <i>Anchor point</i>.</p>

<bullet>Try different anchor points, and drag on the <i>Rotation Angle</i> <o>number</o> box to see the effect. Some anchor point settings you might want to try are <i>0,0</i> or <i>40,30</i> or <i>160,&#x2013;120</i> or <i>320,240</i>. You might want to set the <m>boundmode</m> attribute to <m>1</m> so that you can see the effects of different rotations more clearly. Note that the <m>anchor_x</m> and <m>anchor_y</m> values are specified relative to the upper-left corner of the matrix, but they may exceed the bounds of the matrix's dimensions.</bullet><br/>
<p>In addition, you can move the image to a different location in the output matrix after zooming and rotation take place, using the <m>offset_x</m> and <m>offset_y</m> attributes.</p>

<bullet>To see this most effectively, first click on the <o>message</o> box above the <o>pvar</o> object in the lower right corner of the patch. This will set the rotation angle, boundary mode, zoom, and anchor points back to the settings we used at the outset of this chapter. (We have given names to the relevant user interface objects so that we can communicate with them via <o>pvar</o>.) Now set the <i>Zoom</i> number box to some value between 0 and 1, to zoom out on the image.</bullet><br/>
<bullet>Use the <i>Location offset</i> <o>number</o> boxes to move the image around by changing the <m>offset_x</m> and <m>offset_y</m> values. Try this in conjunction with <m>boundmode 4</m>, to see its utility in the "kaleidoscope" mode.</bullet>
<bullet>When you have finished, reset the <i>Location offset</i> values to <m>0</m>.</bullet>

<h2>Mouse control of rotation</h2>
<p>We've devised one more way for you to rotate the image.</p>

<bullet>Click in the <o>jit.pwindow</o> display object and, with the mouse button held down, drag in a small circular motion around the center of the object.</bullet>

<p><o>jit.pwindow</o> tracks your mouse movements and, as long the mouse button is down, it sends coordinate information (and other mouse information) out its right outlet in the form of <m>mouse</m> messages. The first two arguments of the <m>mouse</m> message are the <i>x</i> and <i>y</i> coordinates of the mouse, relative to the upper left corner of <o>jit.pwindow</o>. We use those coordinates to calculate the angle of the mouse relative to the center of the <o>jit.pwindow</o>, and we send that angle to <o>jit.rota</o> as the argument to the <m>theta</m> attribute.</p>

<illustration><img src="images/jitterchapter15b.png"/>You can use the mouse location in <o>jit.pwindow</o> as control information</illustration>
<div>
<techdetail>Technical Detail: Do you really want to know how we did that calculation? If so, read on.<br/>
If we think of the center point of the <o>jit.pwindow</o> as the origin point <i>0,0</i>, and we think of the current mouse location relative to that as being a point along a circle around the origin, then we can describe a right triangle based on those two points. By taking the arctangent of the mouse's coordinates <i>y/x</i>, we get the angle of the mouse relative to the center of <o>jit.pwindow</o>.<br/>

<br/><illustration><img src="images/jitterchapter15c.png"/></illustration><br/><br/>

So we take the incoming <i>x</i> and <i>y</i> coordinates, and the first thing we do is convert them so that they're relative to the center of the <o>jit.pwindow</o>. We do that by subtracting 160 from the <i>x</i> dimension coordinate (so the x values will now go from -160 to 160) and multiplying the <i>y</i> coordinate by &#x2013;1 (so values will increase as we go up, instead of down) then adding 119.5 to it. (If we added exactly 120, then every time we had a <i>y</i> coordinate of 120 from <o>jit.pwindow</o> we'd be trying to divide by 0 in <o>expr</o>, which is an undefined mathematical operation.) Once we have converted the <i>x</i> and <i>y</i> coordinates, we take the arctangent of <i>y/x</i> to get the angle in radians, then multiply that angle value by -1 to make clockwise rotation of the mouse cause clockwise rotation of the image.<br/><br/>
This method only works within a 180&#xB0; span, because the arctangent function can't tell the difference between a mouse location and its opposite point on the circle. (The calculation of <i>y/x</i> will be the same for both points.) So, every time the <i>y</i> coordinate of the mouse goes into the bottom half of the <o>jit.pwindow</o>, we add an offset of -&#x3C0; to the theta angle to distinguish those locations from their counterparts on the opposite side. (That's the last part of the expression.)<br/><br/>
Note that this expression only works relative to the point <i>160,120</i> in the <o>jit.pwindow</o>. If we wanted to make an expression that works for the central point of <i>any</i> size <o>jit.pwindow</o>, we'd need to get the <o>jit.pwindow</o>'s dimensions with a <m>getsize</m> message, and use the <m>size</m> values as variables in our expression. As the math books say, "We'll leave that as an exercise for the reader."</techdetail>
</div>
<h2>Summary</h2>
<p>The <o>jit.rota</o> object provides an easy way to rotate an image with its <m>theta</m> attribute, specifying an angle of rotation, in radians. It also provides an easy way to zoom in and out on an image with its <m>zoom_x</m> and <m>zoom_y</m> attributes. You can change the central point of the rotation with the <m>anchor_x</m> and <m>anchor_y</m> attributes, and you can move the resulting image in the output matrix with the <m>offset_x</m> and <m>offset_y</m> attributes. You can change the way that <o>jit.rota</o> treats the matrix cells that lie outside of the resulting image with the <m>boundmode</m> attribute. Using all of these capabilities in combination, you can get image-duplication and kaleidoscope effects in addition to simple zoom and rotation.</p>

<p>Zooming and rotation involve some rather intensive internal calculation by <o>jit.rota</o>, so these operations make substantial demands on the computer's processor. There are additional attributes, not covered in this tutorial, that give you access to virtually every coefficient in the rotation formula, presenting you even more possibilities for distorting and rotating the image. These are shown in <i>jit.rota reference</i>.</p>

<p>To manage the control of so many attributes at once, you can devise automated Max processes to generate attribute values, and/or interactive controls to change the values with gestures.</p>

	<seealsolist>
		<seealso name="accum">Store, add to, and multiply a number</seealso>
		<seealso name="expr">Evaluate a mathematical expression</seealso>
		<seealso name="if">Conditional statement in if/then/else form</seealso>
		<seealso name="jit.pwindow">In-Patcher Window</seealso>
		<seealso name="jit.movie">Play or edit a movie</seealso>
		<seealso name="jit.rota">Scale/rotate in 2D</seealso>
		<seealso name="split">Look for a range of numbers</seealso>
	</seealsolist>
	</chapter>
