All pastes #2102328 Raw Edit

Stuff

public text v1 · immutable
#2102328 ·published 2012-01-12 00:00 UTC
rendered paste body

//<!DOCTYPE HTML>

//<html>

//<head>

//	<title>JavaScript $N Multistroke Recognizer</title>

//	<!--[if IE]><script src="excanvas.js"></script><![endif]-->

//	<script type="text/javascript" src="canvas.text.js"></script>

//	<script type="text/javascript" src="./faces/gentilis-normal-normal.js"></script>

//	<script type="text/javascript" src="ndollar.js"></script>

//	<script type="text/javascript"> // This is for mouse and button events

		//

		// Startup

		//

		
var _isDown, _points, _strokes, _r, _g, _rc; // global variables

function loadbang()

		{

			this.onselectstart = function() { return false; }

			this.onmousedown = function() { return false; }

			_points = new Array(); // point array for current stroke

			_strokes = new Array(); // array of point arrays

		//	_r = new NDollarRecognizer(this.getElementById('useBoundedRotationInvariance').checked);



/*			var canvas = document.getElementById('myCanvas');			_g = canvas.getContext('2d');

			_g.lineWidth = 3;

			_g.font = "16px Gentilis";
*/



			_rc = getCanvasRect; // canvas rect on page

//			_g.fillStyle = "rgb(255,255,136)";

//			_g.fillRect(0, 0, _rc.width, 20);


			post (_rc)
			

		}


function getCanvasRect(x,y,w,h)

		{

			_isDown = false;

			var w = w

			var h = h



			var cx = x

			var cy = y

/*			while (canvas.offsetParent != null)

			{

				canvas = canvas.offsetParent;

				cx += canvas.offsetLeft;

				cy += canvas.offsetTop;

			}
*/
		//	return {x: cx, y: cy, width: w, height: h};

			post ("stuff")

		}

/*		function getScrollY()

		{

			var scrollY = 0;

			if (typeof(document.body.parentElement) != 'undefined')

			{

				scrollY = document.body.parentElement.scrollTop; // IE

			}

			else if (typeof(window.pageYOffset) != 'undefined')

			{

				scrollY = window.pageYOffset; // FF

			}

			return scrollY;

		}

*/



//

// Checkbox option for using limited rotation invariance requires rebuilding the recognizer.

//


function confirmRebuild()

	{

		/*if (confirm("Changing this option will discard any user-defined multistrokes you may have made."))

		{*/

		//		_r = new NDollarRecognizer(document.getElementById('useBoundedRotationInvariance').checked);

		/*	}

		 else

			{

				var chk = document.getElementById('useBoundedRotationInvariance');

			chk.checked = !chk.checked; // undo click
			
			}

			*/

			

	   post ("recognizer has been made")
	}

		//

		// Mouse Events

		//

function mouseDownEvent(x, y, button)

	{

//	if (button <= 1)

//		{

	x -= _rc.x;

	y -= _rc.y;

	if (_points.length == 0)

			{

				_strokes.length = 0;

				_g.clearRect(0, 0, _rc.width, _rc.height);

			}

	_points.length = 1; // clear

	_points[0] = new Point(x, y);

				post("Recording stroke #" + (_strokes.length + 1) + "...");

				var clr = "rgb(" + rand(0,200) + "," + rand(0,200) + "," + rand(0,200) + ")";

				_g.strokeStyle = clr;

				_g.fillStyle = clr;

				_g.fillRect(x - 4, y - 3, 9, 9);

				_isDown = true;

/*			}

			else if (button == 2)

			{

				post("Recognizing gesture...");

			}
*/

	}


function mouseMoveEvent(x, y, button)

	{

			if (_isDown)

			{

				x -= _rc.x;

				y -= _rc.y - getScrollY();

				_points[_points.length] = new Point(x, y); // append

				drawConnectedPoint(_points.length - 2, _points.length - 1);

			}

		}

		function mouseUpEvent(x, y, button)

		{

			if (button <= 1)

			{

				if (_isDown)

				{

					_isDown = false;

					_strokes[_strokes.length] = _points.slice(); // add new copy to set

					post("Stroke #" + _strokes.length + " recorded.");

				}

			}

			else if (button == 2) // segmentation with right-click

			{

				if (_strokes.length > 1 || (_strokes.length == 1 && _strokes[0].length >= 10))

				{

					var result = _r.Recognize(_strokes, document.getElementById('useBoundedRotationInvariance').checked, document.getElementById('requireSameNoOfStrokes').checked, document.getElementById('useProtractor').checked);

					post("Result: " + result.Name + " (" + round(result.Score,2) + ").");

				}

				else

				{

					post("Too little input made. Please try again.");

				}

				_points.length = 0; // clear and signal to clear strokes on next mousedown

			}

		}

		function drawConnectedPoint(from, to)

		{

			_g.beginPath();

			_g.moveTo(_points[from].X, _points[from].Y);

			_g.lineTo(_points[to].X, _points[to].Y);

			_g.closePath();

			_g.stroke();

		}

/*		function drawText(str)

		{

			_g.fillStyle = "rgb(255,255,136)";

			_g.fillRect(0, 0, _rc.width, 20);

			_g.fillStyle = "rgb(0,0,255)";

			_g.fillText(str, 1, 14);

		}

		function rand(low, high)

		{

			return Math.floor((high - low + 1) * Math.random()) + low;

		}

		function round(n, d) // round 'n' to 'd' decimals

		{

			d = Math.pow(10, d);

			return Math.round(n * d) / d

		}

		//

		// Multistroke Adding and Clearing

		//

		function onClickAddExisting()

		{

			if (_strokes.length > 0)

			{

				if (_strokes.length < 5 || confirm("With " + _strokes.length + " component strokes, it will take a few moments to add this gesture. Proceed?"))

				{

					var multistrokes = document.getElementById('multistrokes');

					var name = multistrokes[multistrokes.selectedIndex].value;

					var num = _r.AddMultistroke(name, document.getElementById('useBoundedRotationInvariance').checked, _strokes);

					drawText("\"" + name + "\" added. Number of \"" + name + "\"s defined: " + num + ".");

					_points.length = 0; // clear and signal to clear strokes on next mousedown

				}

			}

		}

		function onClickAddCustom()

		{

			var name = document.getElementById('custom').value;

			if (_strokes.length > 0 && name.length > 0)

			{

				if (_strokes.length < 5 || confirm("With " + _strokes.length + " component strokes, it will take a few moments to add this gesture. Proceed?"))

				{

					var num = _r.AddMultistroke(name, document.getElementById('useBoundedRotationInvariance').checked, _strokes);

					drawText("\"" + name + "\" added. Number of \"" + name + "\"s defined: " + num + ".");

					_points.length = 0; // clear and signal to clear strokes on next mousedown

				}

			}

		}

		function onClickCustom()

		{

			document.onselectstart = function() { return true; }

			document.onmousedown = function() { return true; }

			document.getElementById('custom').select();

			document.onselectstart = function() { return false; }

			document.onmousedown = function() { return false; }

		}

		function onClickDelete()

		{

			var num = _r.DeleteUserMultistrokes(); // deletes any user-defined templates

			alert("All user-defined multistrokes have been deleted. Only the 1 predefined multistroke gesture remains for each of the " + num + " gesture types.");

			_points.length = 0; // clear and signal to clear strokes on next mousedown

		}

		function onClickClearStrokes()

		{

			_points.length = 0;

			_strokes.length = 0;

			_g.clearRect(0, 0, _rc.width, _rc.height);

			drawText("Canvas cleared.");

		}

/*	</script>

</head>



<body onload="onLoadEvent()">



<!-- Title and affiliation -->

<p><span style="font-size:18pt; font-weight:bold">$N Multistroke Recognizer in JavaScript</span><br/>

<span style="font-size:10pt"><i>Lisa Anthony</i><sup>1</sup> and <i>Jacob O. Wobbrock</i><sup>2</sup><br />

<sup>1</sup><i>University of Maryland&mdash;Baltimore County</i> &nbsp; and &nbsp; <sup>2</sup><i>University of Washington</i></span></p>



<!-- Short description -->

<p style="margin-left:2em; margin-right:2em; margin-top:-10pt; margin-bottom:0pt; font-size:10pt">

	This page implements the "$N Multistroke Recognizer" that is based upon the

	<a href="index.html" target="_top">$1 Unistroke Recognizer</a>.

	Upon loading this page, only <u>one</u> multistroke is defined for each gesture below, but $N

	automatically generalizes each example to encompass all possible stroke orderings and directions.

	This means that you can make and define multistrokes using any stroke

	order and direction you wish, provided you begin at either endpoint of each component stroke.

	By default, multistrokes are regarded as fully rotation, scale, and position invariant, but a checkbox option

	allows you to limit rotation invariance (see below). Also, you can define your own multistrokes using the

	buttons beneath the canvas. See our

	<a href="http://faculty.washington.edu/wobbrock/pubs/gi-10.2.pdf" target="_blank"><i>Graphics Interface 2010</i> paper (PDF)</a>,

	<a href="./limits/index.html" target="_top">limitations of this recognizer</a> or a

	<a href="ndollar.pdf" target="_top">detailed pseudocode listing</a>.

</p>



<!-- Gesture image and canvas -->

<table border="0" width="100%" cellspacing="10">

	<tr>

		<td valign="top">

			<p><img src="multistrokes.gif"></p>

			<p>

				<form style="font-size:10pt">

					<input type="radio" name="search" id="useGSS" checked>

						<span style="background-color:#ffff88;font-weight:bold;font-style:italic">Use Golden Section Search.</span>

						The original $N algorithm uses Golden Section Search to find the best angular alignment between the inputted

						multistroke and template multistrokes. It is a fast iterative search algorithm.

					</input><br style="margin-bottom:6pt" />

					<input type="radio" name="search" id="useProtractor">

						<span style="background-color:#ffff88;font-weight:bold;font-style:italic">Use Protractor.</span>

						Yang Li published an improvement to $1, on which $N is based, called

						<a href="http://www.yangl.org/pdf/protractor-chi2010.pdf" target="_blank"><i>Protractor</i></a>,

						which avoids the iterative Golden Section Search and instead uses a closed-form formula based on cosine distances,

						making Protractor considerably faster.

					</input><br style="margin-bottom:6pt" />

					<input type="checkbox" id="useBoundedRotationInvariance" onclick="confirmRebuild()">

						<span style="background-color:#ffff88;font-weight:bold;font-style:italic">Use bounded rotation invariance.</span>

						Do not use full rotation invariance, but instead require gestures to be

						drawn within +/- 45 degrees of the orientation of the template. This can be used to disambiguate, e.g., the "H" and "I"

						gestures, or the line and exclamation gestures, since they differ mainly by orientation.

					 </input><br style="margin-bottom:6pt" />

					<input type="checkbox" id="requireSameNoOfStrokes">

						<span style="background-color:#ffff88;font-weight:bold;font-style:italic">Require same # of strokes.</span>

						Require the candidate and template to have the same number of component strokes.

						This option speeds recognition but reduces articulation flexibility. For example, the "N" template above was made

						with 3 strokes. If this option is checked, a 1- or 2-stroke "N" will not be allowed to match it. If

						this option is <u>not</u> checked, such gestures will be allowed to match. If you want to have this option checked

						but still want to allow for this kind of flexibility, you can simply define your own separate templates named "N"

						with 1 and/or 2 strokes.

					</input>

				 </form>

			 </p>

		</td>

		<td valign="top" width="50%">

			<table border="0" width="100%">

				<tr>

					<td align="left" style="font-size:10pt; margin-top:0pt; margin-bottom:6pt; padding-right:4px">

						<em>Make strokes on this canvas. <b>Recognition happens when you <u>right-click</u> the canvas.</b>

						If a misrecognition occurs, simply add the misrecognized multistroke as an example

						of the intended type, or try different checkbox options.</em>

					</td>

					<td align="right"><input type="button" value=" Clear  " onclick="onClickClearStrokes()"></td>

				</tr>

			</table>



			<canvas id="myCanvas" width="600" height="400" style="background-color:#dddddd"

					onmousedown="mouseDownEvent(event.clientX, event.clientY, event.button)"

					onmousemove="mouseMoveEvent(event.clientX, event.clientY, event.button)"

					onmouseup="mouseUpEvent(event.clientX, event.clientY, event.button)"

					oncontextmenu="return false;">



				<span style="background-color:#ffff88;">The &lt;canvas&gt; element is not supported by this browser.</span>



			</canvas>



			<!-- Editing area below stroking canvas area -->

			<table border="0" width="100%">

				<tr>

					<td>Add last multistroke as example of existing type:</td>

					<td align="right">

						<select id="multistrokes" style="width:156px" onkeypress="if (event.keyCode == 13) onClickAddExisting()">

							<option selected value="T">T</option>

							<option value="N">N</option>

							<option value="D">D</option>

							<option value="P">P</option>

							<option value="X">X</option>

							<option value="H">H</option>

							<option value="I">I</option>

							<option value="exclamation">exclamation</option>

							<option value="line">line</option>

							<option value="five-point star">five-point star</option>

							<option value="null">null</option>

							<option value="arrowhead">arrowhead</option>

							<option value="pitchfork">pitchfork</option>

							<option value="six-point star">six-point star</option>

							<option value="asterisk">asterisk</option>

							<option value="half-note">half-note</option>

						</select>

					</td>

					<td><input type="button" value="  Add   " onclick="onClickAddExisting()"></td>

				</tr>

				<tr>

					<td>Add last multistroke as example of custom type:</td>

					<td align="right"><input type="text" id="custom" style="width:150px" value="Type name here..." onclick="onClickCustom()" onkeypress="if (event.keyCode == 13) onClickAddCustom()"></td>

					<td><input type="button" value="  Add   " onclick="onClickAddCustom()"></td>

				</tr>

				<tr>

					<td>Delete all user-defined examples:</td>

					<td align="right">&nbsp;</td>

					<td><input type="button" value="Delete" onclick="onClickDelete()"></td>

				</tr>

			</table>

			<!-- End of editing area below stroking canvas area -->



		</td>

	</tr>

</table>







<table border="0" width="100%">

	<tr>

		<td valign="top" width="50%"><!-- Links and downloads -->

			<h3>$N Links and Downloads</h3>

			<ul>

				<li><a href="http://faculty.washington.edu/wobbrock/pubs/gi-10.2.pdf" target="_top">Our GI 2010 paper (PDF)</a></li>

				<li><a href="http://www.yangl.org/pdf/protractor-chi2010.pdf" target="_blank">Li's CHI 2010 Protractor note (PDF)</a></li>

				<li><a href="ndollar.js" target="_top">The $N Recognizer used on this page (JavaScript)</a></li>

				<li><a href="ndollar.pdf" target="_top">Pseudocode listing of $N</a></li>

				<li><a href="http://www.yangl.org/protractor/protractor.pdf" target="_top">Pseudocode listing of Protractor</a></li>

				<li><a href="ndollar.zip" target="_top">The $N Recognizer (C#)</a></li>

				<li><a href="./limits/index.html" target="_top">Limitations of $N</a></li>

				<li><a href="mailto:Jacob O. Wobbrock &lt;wobbrock@u.washington.edu&gt;?subject=From the $N recognizer page">Questions? Contact Prof. Jacob O. Wobbrock</a>

			</ul>

			<h3>References</h3>

				<span style="font-size:10pt;padding-right:8em">

				Anthony, L. and Wobbrock, J.O. (2010). <a href="http://faculty.washington.edu/wobbrock/pubs/gi-10.2.pdf" target="_top"><b>A lightweight multistroke recognizer for user interface prototypes.</b></a> Proceedings of Graphics Interface (GI '10). Ottawa, Ontario, Canada (May 31-June 2, 2010). Toronto, Ontario: Canadian Information Processing Society, pp. 245-252.

				</span><br/><br/>

				<span style="font-size:10pt;padding-right:8em">

				Li, Y. (2010). <a href="http://www.yangl.org/pdf/protractor-chi2010.pdf" target="_top"><b>Protractor: A fast and accurate gesture recognizer.</b></a> Proceedings of the ACM Conference on Human Factors in Computing Systems (CHI '10). Atlanta, Georgia (April 10-15, 2010). New York: ACM Press, pp. 2169-2172.

				</span><br/><br/>

				<span style="font-size:10pt;padding-right:8em">

				Wobbrock, J.O., Wilson, A.D. and Li, Y. (2007). <a href="http://faculty.washington.edu/wobbrock/pubs/uist-07.1.pdf" target="_top"><b>Gestures without libraries, toolkits or training: A $1 recognizer for user interface prototypes.</b></a> Proceedings of the ACM Symposium on User Interface Software and Technology (UIST '07). Newport, Rhode Island (October 7-10, 2007). New York: ACM Press, pp. 159-168.

				</span>

		</td>

		<td valign="top">

			<h3>$N Implementations by Others</h3>

			<ul>

				<li><a href="http://alphacount.wordpress.com/" target="_blank">AlphaCount iPhone app</a> by Olivier Brand

				<li><a href="https://github.com/britg/MultistrokeGestureRecognizer-iOS" target="_blank">MultistrokeGestureRecognizer-iOS</a> by <a href="http://britg.com/" target="_blank">Brit Gardner</a>

			</ul>

		</td>

	</tr>

</table>


</body>

</html>
*/
//}