<?xml version='1.0' encoding='UTF-8'?>

<?xml-stylesheet href="./_c74_tut.xsl" type="text/xsl"?>
<chapter name="Tutorial 39: Spatial Mapping">
<setdocpatch name="39jSpatialMapping" patch="39jSpatialMapping.maxpat"/>

<previous name="jitterchapter38">Basic Performance Setup</previous>
<next name="jitterchapter40">Drawing in OpenGL using jit.gl.sketch</next>
<parent name="jitindex">Jitter Tutorials</parent>



<h1>Tutorial 39: Spatial Mapping</h1>
<p>In this tutorial, we will look at ways to remap the spatial layout of cells in a Jitter matrix using a second matrix as a spatial map. Along the way, we&#x2019;ll investigate ways to generate complete maps through the interpolation of small datasets as well as ways to use mathematical expressions to fill a Jitter matrix with values.</p>

<p>Jitter matrices can be remapped spatially using lookup tables of arbitrary complexity. In <link type="tutorial" module="jit" name="jitterchapter12">Tutorial 12</link>, we learned that the <o>jit.charmap</o> object maps the cell values (or color information) of one Jitter matrix based on a lookup table provided by a second Jitter matrix. In a similar fashion, the <o>jit.repos</o> object takes a Jitter matrix containing spatial values that it then uses as a lookup table to rearrange the spatial positions of cells in another matrix.</p>

<bullet>Open the tutorial patch.</bullet>
<p>The tutorial patch shows the matrix output of a <o>jit.movie</o> object being processed by a new object, called <o>jit.repos</o>. The right inlet of <o>jit.repos</o> receives matrices from the output of a <o>jit.xfade</o> object fed by two named matrices, each of which has been given a name (<m>stretch</m> and <m>cartopol</m>) so that they share data with <o>jit.matrix</o> objects elsewhere in the patch. To the right of the main processing chain are two separate areas where we construct those two matrices, which will serve as our spatial maps for this tutorial.</p>

<bullet>Click the message box that reads <m>read colorswatch.pict</m> to load an image into the <o>jit.movie</o> object. Click the <o>toggle</o> labeled <i>Display</i> to start the <o>metro</o> object.</bullet>
<p>Notice that at this point, we only see a solid white frame appear in the <o>jit.pwindow</o> object at the bottom-left of the patch. This is because we haven&#x2019;t loaded a spatial map into our <o>jit.repos</o> object.</p>
<bullet>Open the <m>generate_stretch</m> subpatch.</bullet>
<bullet>Click the first box in the preset object in the area of the patch labeled <i>Generate &#x201C;stretch&#x201D; matrix</i>. You&#x2019;ll see the <o>pictslider</o> objects snap to various positions and a gradient progressing from magenta to green appear in the <o>jit.pwindow</o> at the bottom of that section.</bullet>
<p>We now see a normal looking image appear in the <o>jit.pwindow</o> at the bottom of our patch.</p>

<h2>The Wide World of Repos</h2>
<bullet>Open the <m>generate_stretch</m> subpatch.</bullet>
<bullet>Try manipulating the <o>pictslider</o> objects that set values in the <m>stretch</m> matrix. Notice that the effect is to stretch and twist our image.  By manipulating the <o>pictslider</o> objects one at a time, we can see that they distort different areas of the screen in a 3x3 grid. Clicking the first box in the <o>preset</o> object will return the image to normal.</bullet>
<illustration><img src="images/jitterchapter39a.png"/>Hall of mirrors.</illustration>
<p>The <o>pictslider</o> objects in our patch set individual cells in a 2-plane, 3x3 Jitter matrix of type <m>long</m>. This matrix is then upsampled with interpolation to a 320x240 matrix named <m>stretch</m>. This matrix contains the instructions by which <o>jit.repos</o> maps the cells in our incoming matrix. The interpolation here is necessary so that we can infer an entire 320x240 map from just nine specified coordinate points.</p>

<p>The spatial map provided to the <o>jit.repos</o> object in its right inlet contains a cell-by-cell description of which input cells are mapped to which output cells. The first plane (plane 0) in this matrix defines the x coordinates for the mapping; the second plane (plane 1) represents the y coordinates. For example, if the cell in the upper-left hand corner contains the values <m>50 75</m>, then the cell at coordinates <m>50 75</m> from the matrix coming into the left inlet of the <o>jit.repos</o> object will be placed in the upper left-hand corner of the output matrix.</p>

<p>The following example will give us a better sense of what <o>jit.repos</o> is doing with its instructions:</p>

<illustration><img src="images/jitterchapter39b.png"/>A simple spatial map.</illustration>
<p>In this example, we have a simple single-plane 5x5 matrix filled with ascending numbers being remapped by a spatial map. Notice the correlation between the values in the spatial map and where the corresponding cells in the output matrix get their original values from in the input matrix. For example, the cell in the lower-left of the spatial map matrix reads <m>4 0</m>. The cell at coordinate {4, 0} in the incoming matrix is set to value <m>5</m>. As you can see, the cell in the lower-left of the output matrix contains that value as well. </p>

<p>Similarly, you can see that the cells comprising the middle row of the output matrix are all set to the same value (<m>19</m>). This is because the corresponding cells in the spatial map are all set to the same value (<m>3 3</m>). Cell {3, 3} in the input matrix contains the value <m>19</m>; that value is then placed into that entire row in the output matrix.</p>

<p>In our tutorial patch, our spatial map needs to be able to contain values in a range that represents the full size (or <m>dim</m>) of the matrix to be processed. To this end, the <o>pictslider</o> ranges are set to 320 x 240.  To handle numbers of this size, we need to use a matrix of type <m>long</m> for our spatial map. If it was of type <m>char</m>, we would be constrained to values in the range of 0 to 255, limiting what cells we can choose from in our spatial map. Note that in order to correctly view these <m>long</m> matrices in <o>jit.pwindow</o> objects we use the <o>jit.clip</o> object to constrain the values in the matrix before viewing to the same range as <m>char</m> matrices.</p>

<div>
<techdetail><b>Technical Note:</b> The default behavior of <o>jit.repos</o> is to interpret the spatial map as a matrix of absolute coordinates, i.e. a literal specification of which input cells map to which output cells. The <m>mode</m> attribute of <o>jit.repos</o>, when set to 1, places the object in relative mode, wherein the spatial map contains coordinates relative to their normal position. For example, if cell {15, 10} in the spatial map contained the values <m>-3 7</m>, then a <o>jit.repos</o> object working in relative mode would set the value of cell {15, 10} in the output matrix to the contents of cell {12, 17} in the input matrix (i.e. {15-3, 10+7}).</techdetail>
</div>
<bullet>Play around some more with the <o>pictslider</o> objects to get a better feel for how <o>jit.repos</o> is working. Notice that if you set an entire row (or column) of <o>pictslider</o> objects to approximately the same position you can &#x201C;stretch&#x201D; an area of the image to cover an entire horizontal or vertical band in the output image. If you set the <o>pictslider</o> objects in the left columns to values normally contained in the right columns (and vice versa), you can flip the image on its horizontal axis. By doing this only part-way, or by positioning the slider values somewhere in between where they sit on the &#x201C;normal&#x201D; map (as set by the <o>preset</o> object) you can achieve the types of spatial distortion commonplace in funhouse mirrors, where a kink in the glass bends the reflected light to produce a spatial exaggeration.</bullet>

<h2>Spatial Expressions</h2>
<p>Now that we&#x2019;ve experimented with designing spatial maps by hand, as it were, we can look at some interesting ways to generate them algorithmically.</p>
<bullet>Open the <m>generate_cartopol</m> subpatch.</bullet>
<bullet>Click the <o>button</o> labeled <i>Generate</i>. Set the <o>number</o> box labeled <i>Crossfade</i> (attached to the <o>trigger</o> object in the main part of the patch) to <m>1.0</m>.</bullet>

<illustration><img src="images/jitterchapter39c.png"/>Lollipop space.</illustration>

<p>The <m>cartopol</m> spatial map has been set by a mathematical expression operated on the output of the <o>jit.gencoord</o> object by the <o>jit.expr</o> object. The results of this expression, performed on <m>float32</m> Jitter matrices, are then copied into a <m>long</m> matrix that serves as our spatial map. This spatial map causes <o>jit.repos</o> to spatially remap our incoming matrix from Cartesian to polar space, so that the leftmost edge of our incoming matrix becomes the center of the outgoing matrix, wrapping in a spiral pattern out to the final ring, which represents the rightmost edge of the input matrix. The <m>boundmode</m> attribute of <o>jit.repos</o> tells the object to wrap coordinates beyond that range back inwards, so that the final edge of our output image begins to fold in again towards the left side of the input image.</p>

<p>The <o>jit.gencoord</o> object outputs a simple Cartesian map of floating-point values; when you multiply the values in the matrix by the <m>dim</m> of the matrix, you can easily get coordinates which literally correspond to the locations of the cells, as shown in this illustration:</p>

<illustration><img src="images/jitterchapter39d.png"/>Normal Cartesian map.</illustration>
<p>The <m>scale</m> and <m>offset</m> attributes of <o>jit.gencoord</o> allow us to manipulate the range of values put out by the object, saving us the step of using multiple <o>jit.op</o> objects to get our Cartesian map into the range we want. For our equation to work, we want our starting map to be in the range of {-1, 1} across both dimensions (hence we multiply the normal Cartesian space by 2 and offset it by -1).</p>

<p>The <o>jit.expr</o> object takes the incoming matrix provided by <o>jit.gencoord</o> and performs a mathematical expression on it, much as the Max <o>vexpr</o> object works on lists of numbers. The <m>expr</m> attribute of the <o>jit.expr</o> object defines the expression to use. The <m>in[i].p[j]</m> notation tells <o>jit.expr</o> that we want to operate on the values contained in plane <i>j</i> of the matrix arriving at inlet <i>i</i>. The <m>dim[i]</m> keyword returns the size of the dimension <i>i</i> in the matrix. In our case, the notation <m>dim[0]</m> would resolve to 320; <m>dim[1]</m> would resolve to 240.</p>

<p>Note that there are actually two mathematical expressions defined in the <o>message</o> box setting the <m>expr</m> attribute. The first defines the expression to use for plane 0 (the <i>x</i> coordinates of our spatial map), the second sets the expression for plane 1 (the <i>y</i> coordinates).</p>

<p>Placed in a more legible notation, the <o>jit.expr</o> expression for plane 0:</p>

<p><m>hypot(in[0].p[0]*in[1]\,in[0].p[1]*in[2])*dim[0]</m></p>

<p>works out to something like this, if x is the value in plane 0 of the input, y is the value in plane 1 of the input, and <i>a</i> and <i>b</i> are the values in the <o>number</o> box objects attached to the second and third inlets of the <o>jit.expr</o> object:</p>

<p>sqrt(ax<sup>2</sup>+by<sup>2</sup>)*320</p>

<p>Similarly, the expression for plane 1:</p>

<p><m>(((atan2(in[0].p[1]*in[2]\,in[0].p[0]*in[1])/PI)*0.5)+0.5)*dim[1]</m></p>

<p>works out to something like this:</p>

<p>(((atan(by/ax)/)/2)+0.5)*240</p>

<p>Therefore, we can see that we are filling the x (plane 0) values of our spatial map with numbers corresponding to the length of the hypotenuse of our coordinates (i.e. their distance from the center of the map), while we set the y (plane 1) values to represent those values&#x2019; angle (&#xD8;) around the origin. </p>

<bullet>In the lower-right section of the tutorial patch, change the <o>number</o> box objects attached to the <o>jit.expr</o> object. Note that they allow you to stretch and shrink the spatial map along the <i>x</i> and <i>y</i> axes.</bullet>
<p>The jit.expr object can take Max numerical values (<m>int</m> or <m>float</m>) in its inlets in lieu of matrices. Thus the <m>in[1]</m> and <m>in[2]</m> notation in the <m>expr</m> attribute refers to the numbers arriving at the middle and right inlets of the object. </p>

<div>
<techdetail><b>Technical Note:</b> The <o>jit.expr</o> object parses the mathematical expressions contained in the <m>expr</m> attribute as individual strings (one per plane). As a result, each individual expression should be contained in quotes and commas need to be escaped by backslash (\) characters. This is to prevent Max from atomizing the expression into a list separated by spaces or a sequence of messages separated by commas. We can gain some insight into how <o>jit.expr</o> parses its expressions by turning on the <m>verbose</m> attribute to the object (as we do in this tutorial). When an <m>expr</m> attribute is evaluated, the object prints the expression&#x2019;s evaluation hierarchy to the Max Console so we can see exactly how the different parts of the expression are tokenized and interpreted.</techdetail>
</div>
<bullet>Open the <o>patcher</o> object labeled <m>steps</m>. Click through the different expressions contained in the <o>message</o> boxes, each of which shows a simpler stage of how we computed the <m>cartopol</m> map. The <m>norm[i]</m> and <m>snorm[i]</m> notation essentially replicates the function of <o>jit.gencoord</o> here, generating a map of Cartesian values (<m>0</m> to <m>1</m>) or signed Cartesian values (<m>-1</m> to <m>1</m>) across the specified dimension.</bullet>
<illustration><img src="images/jitterchapter39e.png"/>Different stages of the Cartesian-to-polar mapping: (a) normal map, (b) normal map scaled and offset to a signed {-1, 1} range, (c) hypotenuse only, (d) theta only</illustration>
<p>An important thing to notice with this subpatch is that the <o>message</o> box objects are directly connected to the <o>jit.matrix</o> object in the main patch. The <m>exprfill</m> message to <o>jit.matrix</o> allows us to fill a Jitter matrix with values from a mathematical expression without having to resort to using <o>jit.expr</o> as a separate object. The syntax for the <m>exprfill</m> method is slightly different from the <m>expr</m> attribute to <o>jit.expr</o>: the first argument to <m>exprfill</m> is the <m>plane</m> to fill with values, so two <m>exprfill</m> messages (separated by commas) are necessary to generate the spatial map) using this method.</p>

<h2>Still Just Matrices</h2>
<bullet>Slowly change the <o>number</o> box attached to the <o>trigger</o> object on the left of the patch from <m>1</m> to <m>0</m> and back again.</bullet>
<illustration><img src="images/jitterchapter39f.png"/>A partial cross-fade of the two spatial maps used in this tutorial.</illustration>
<p>The number box controls the <m>xfade</m> attribute of the <o>jit.xfade</o> object directly attached to the <o>jit.repos</o> object at the heart of our processing chain. As a result, we can smoothly transition from our manually defined spatial map (the <m>stretch</m> matrix) to our algorithmically defined one (the <m>cartopol</m> matrix). The ability to use Jitter processing objects to composite different spatial maps adds another layer of potential flexibility. Because the two matrices containing our maps have the same typology (<m>type</m>, <m>dim</m>, and <m>planecount</m>) they can be easily combined to make an infinite variety of composites.</p>

<h2>Summary</h2>
<p>The <o>jit.repos</o> object processes an input matrix based on a spatial map encoded as a matrix sent to its second inlet. The default behavior is for <o>jit.repos</o> to process matrices spatially using absolute coordinates, such that the cell values in the second matrix correspond to coordinates in the input matrix whose cell values are copied into the corresponding position in the output matrix.</p>

<p>Spatial maps can be created by setting cells explicitly in a matrix and, if desired, upsampling with interpolation from a smaller initial matrix. Maps can also be created by generating matrices through algorithmic means, such as processing a &#x201C;normal&#x201D; Cartesian map generated by a <o>jit.gencoord</o> object through a mathematical expression evaluated by a <o>jit.expr</o> object. The <o>jit.expr</o> object takes strings of mathematical expressions, one per plane, and parses them using keywords to represent incoming cell values as well as other useful information, such as the size of the incoming matrix. If you want to fill a Jitter matrix with the results of a mathematical expression directly, you can use the <m>exprfill</m> message to a <o>jit.matrix</o> object to accomplish the same results without having to use a separate <o>jit.expr</o> object.</p>

<p>The <o>jit.repos</o> object can be used with a wide variety of spatial maps for different uses in image and data processing. The &#x2018;jitter-examples/video/spatial&#x2019; folder of the Max &#x2018;examples&#x2019; folder contains a number of patches that illustrate the possibilities of <o>jit.repos</o>.</p>

	<seealsolist>
		<seealso name="jit.cellblock">Two-dimensional storage and viewing</seealso>
		<seealso name="jit.charmap">256 point input to output map</seealso>
		<seealso name="jit.clip">Limit data to the range [min,max]</seealso>
		<seealso name="jit.expr">Evaluate expressions</seealso>
		<seealso name="jit.gencoord">Evaluates a procedural basis function graph</seealso>
		<seealso name="jit.matrix">The Jitter Matrix!</seealso>
		<seealso name="jit.pwindow">In-Patcher Window</seealso>
		<seealso name="jit.movie">Play or edit a movie</seealso>
		<seealso name="jit.repos">Reposition spatially</seealso>
		<seealso name="jit.xfade">Crossfade between 2 matrices</seealso>
	</seealsolist>
	</chapter>
