<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">

<channel>
	<title>Tapdancing Goats</title>
	
	<link>http://www.tapdancinggoats.com</link>
	<description>A parfait of physics, Linux, LaTeX, and coding tips large and small.</description>
	<lastBuildDate>Sat, 26 May 2012 14:26:25 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Tapdancinggoats" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="tapdancinggoats" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Hear How You Sound to Others</title>
		<link>http://www.tapdancinggoats.com/hear-how-you-sound-to-others.htm</link>
		<comments>http://www.tapdancinggoats.com/hear-how-you-sound-to-others.htm#comments</comments>
		<pubDate>Sat, 26 May 2012 14:26:25 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Links]]></category>
		<category><![CDATA[sound]]></category>
		<category><![CDATA[voice]]></category>

		<guid isPermaLink="false">http://www.tapdancinggoats.com/?p=780</guid>
		<description><![CDATA[Are you surprised at how you sound when you listen to a recording of your voice? I always thought that we sound different to ourselves because your voice vibrates through your skull to your own eardrums, which gives you a unique experience of your voice. It&#8217;s kind of nice to think that every time you [...]]]></description>
			<content:encoded><![CDATA[<iframe width="480" height="360" src="http://www.youtube.com/embed/eWLWOSgYlG8" frameborder="0" allowfullscreen></iframe>

<p>Are you surprised at how you sound when you listen to a recording of your voice?  I always thought that we sound different to ourselves because your voice vibrates through your skull to your own eardrums, which gives you a unique experience of your voice.  It&#8217;s kind of nice to think that every time you speak you&#8217;re giving yourself a private concert, but for effective communication you need to know how others hear you.</p>

<p>Vocal Coach Chris Beatty&#8217;s video shows one way to hear how you sound to others.  He explains that part of the effect is due to sound travelling around your face to your ears.  Blocking this sound lets you hear how your voice changes as it travels through the room.  I gave it a try, and I was surprised at how well it works.</p>

<p>I have a deep voice, and people often have trouble understanding me.  I&#8217;ve always wondered if I sound louder to myself than others, and if the distinct frequencies of my voice get muddled as they bounce around a room.  When I used this technique and blocked my voice with some magazines, it sounded like my volume dropped to about 30%.  I couldn&#8217;t believe it!  I used to do theatre,<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup> so I know I can speak loudly enough to be clearly heard across an auditorium.  Because of this, I try to keep my volume down so that people don&#8217;t think I&#8217;m shouting, but I think I went too far.  Now, using this technique to hear how I sound to others for feedback I&#8217;ll be able to adjust how I speak to be more clearly understood.</p>

<p>via <a href="http://lifehacker.com/5910119/learn-how-other-people-hear-your-voice-with-two-folders">LifeHacker</a></p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:1">
<p>Yes, I was a stage ac-tor, I trod the boards&#160;<a href="#fnref:1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=sDkxN7lVTlc:sgJFdyWupvQ:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=sDkxN7lVTlc:sgJFdyWupvQ:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=sDkxN7lVTlc:sgJFdyWupvQ:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=sDkxN7lVTlc:sgJFdyWupvQ:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=sDkxN7lVTlc:sgJFdyWupvQ:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=sDkxN7lVTlc:sgJFdyWupvQ:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=sDkxN7lVTlc:sgJFdyWupvQ:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=sDkxN7lVTlc:sgJFdyWupvQ:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/Tapdancinggoats/~4/sDkxN7lVTlc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.tapdancinggoats.com/hear-how-you-sound-to-others.htm/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Flash Game Monstrosi Stacks</title>
		<link>http://www.tapdancinggoats.com/flash-game-monstrosi-stacks.htm</link>
		<comments>http://www.tapdancinggoats.com/flash-game-monstrosi-stacks.htm#comments</comments>
		<pubDate>Sat, 26 May 2012 06:23:06 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Flash]]></category>
		<category><![CDATA[Games]]></category>
		<category><![CDATA[flash game]]></category>
		<category><![CDATA[monstrosi]]></category>

		<guid isPermaLink="false">http://www.tapdancinggoats.com/?p=777</guid>
		<description><![CDATA[I released a new Flash game, Monstrosi Stacks. It&#8217;s a take on the match-3 genre where you drag whole columns instead of swapping pieces. It features cute little monster-things I&#8217;m calling Monstrosi. There are some more games in the works that will feature the Monstrosi, so I hope to build them into something of a [...]]]></description>
			<content:encoded><![CDATA[<p>I released a new Flash game, <a href="http://studios.clockworkmagpie.com/monstrosi_stacks">Monstrosi Stacks</a>.  It&#8217;s a take on the match-3 genre where you drag whole columns instead of swapping pieces.  It features cute little monster-things I&#8217;m calling Monstrosi.  There are some more games in the works that will feature the Monstrosi, so I hope to build them into something of a brand that will support secondary products.</p>

<p>It would really help me out if you go rate <a href="http://www.newgrounds.com/portal/view/596142">Monstrosi Stacks</a> on Newgrounds.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=GxxMHDH9uBY:9KZVAt5zwn0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=GxxMHDH9uBY:9KZVAt5zwn0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=GxxMHDH9uBY:9KZVAt5zwn0:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=GxxMHDH9uBY:9KZVAt5zwn0:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=GxxMHDH9uBY:9KZVAt5zwn0:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=GxxMHDH9uBY:9KZVAt5zwn0:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=GxxMHDH9uBY:9KZVAt5zwn0:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=GxxMHDH9uBY:9KZVAt5zwn0:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/Tapdancinggoats/~4/GxxMHDH9uBY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.tapdancinggoats.com/flash-game-monstrosi-stacks.htm/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>LaTeX Coffee Stains</title>
		<link>http://www.tapdancinggoats.com/latex-coffee-stains.htm</link>
		<comments>http://www.tapdancinggoats.com/latex-coffee-stains.htm#comments</comments>
		<pubDate>Wed, 25 Jan 2012 03:14:39 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[LaTeX]]></category>
		<category><![CDATA[Links]]></category>
		<category><![CDATA[coffee]]></category>
		<category><![CDATA[silly]]></category>

		<guid isPermaLink="false">http://www.tapdancinggoats.com/?p=758</guid>
		<description><![CDATA[LaTeX Coffee Stains is a new LaTeX style by Hanno Rein that will add coffee stains to LaTeX documents, in case you&#8217;re too hip to do it the old fashioned way.]]></description>
			<content:encoded><![CDATA[<p><a href="http://hanno-rein.de/archives/349">LaTeX Coffee Stains</a> is a new LaTeX style by Hanno Rein that will add coffee stains to LaTeX documents, in case you&#8217;re too hip to do it the old fashioned way.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=aJcszqA7fJE:ej6NXk-MHDA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=aJcszqA7fJE:ej6NXk-MHDA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=aJcszqA7fJE:ej6NXk-MHDA:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=aJcszqA7fJE:ej6NXk-MHDA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=aJcszqA7fJE:ej6NXk-MHDA:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=aJcszqA7fJE:ej6NXk-MHDA:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=aJcszqA7fJE:ej6NXk-MHDA:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=aJcszqA7fJE:ej6NXk-MHDA:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/Tapdancinggoats/~4/aJcszqA7fJE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.tapdancinggoats.com/latex-coffee-stains.htm/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Remember the Reason for the Season</title>
		<link>http://www.tapdancinggoats.com/remember-the-reason-for-the-season.htm</link>
		<comments>http://www.tapdancinggoats.com/remember-the-reason-for-the-season.htm#comments</comments>
		<pubDate>Fri, 23 Dec 2011 10:01:33 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Humor]]></category>
		<category><![CDATA[axial tilt]]></category>
		<category><![CDATA[christmas]]></category>
		<category><![CDATA[earth]]></category>
		<category><![CDATA[holidays]]></category>
		<category><![CDATA[pun]]></category>

		<guid isPermaLink="false">http://www.tapdancinggoats.com/?p=746</guid>
		<description />
			<content:encoded><![CDATA[<p><a href="http://www.tapdancinggoats.com/wp-content/uploads/2011/12/season.jpg"><img src="http://www.tapdancinggoats.com/wp-content/uploads/2011/12/season.jpg" alt="" title="Remember the Reason for the Season" width="572" height="607" class="aligncenter size-full wp-image-752" /></a></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=7bRORYUqbtA:tYHSvPWR4fg:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=7bRORYUqbtA:tYHSvPWR4fg:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=7bRORYUqbtA:tYHSvPWR4fg:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=7bRORYUqbtA:tYHSvPWR4fg:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=7bRORYUqbtA:tYHSvPWR4fg:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=7bRORYUqbtA:tYHSvPWR4fg:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=7bRORYUqbtA:tYHSvPWR4fg:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=7bRORYUqbtA:tYHSvPWR4fg:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/Tapdancinggoats/~4/7bRORYUqbtA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.tapdancinggoats.com/remember-the-reason-for-the-season.htm/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Puzzle 1</title>
		<link>http://www.tapdancinggoats.com/puzzle-1.htm</link>
		<comments>http://www.tapdancinggoats.com/puzzle-1.htm#comments</comments>
		<pubDate>Sat, 22 Oct 2011 08:13:12 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Puzzles]]></category>

		<guid isPermaLink="false">http://www.tapdancinggoats.com/?p=740</guid>
		<description><![CDATA[Rules and hints: The solution is one word. Post a comment with your answer. I will moderate comments so incorrect solutions will not appear. Feel free to put a link to your profile or contact information in the comment with your solution, so people that are impressed with your puzzle skillz can find you. Don&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>Rules and hints:</p>

<ul>
<li>The solution is one word.</li>
<li>Post a comment with your answer.  I will moderate comments so incorrect solutions will not appear.</li>
<li>Feel free to put a link to your profile or contact information in the comment with your solution, so people that are impressed with your puzzle skillz can find you.</li>
<li>Don&#8217;t submit more than one solution per day or you will be disqualified.</li>
<li>If I get more than one solution within a few days, I will post them all.</li>
<li>After a solution is posted, comments will be closed.</li>
</ul>

<p><a href="http://www.tapdancinggoats.com/wp-content/uploads/2011/10/puzzle1.png"><img src="http://www.tapdancinggoats.com/wp-content/uploads/2011/10/puzzle1.png" alt="" title="puzzle1" width="320" height="512" class="aligncenter size-full wp-image-741" /></a></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=ZkBLCPKu678:YaQMZl1C4kU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=ZkBLCPKu678:YaQMZl1C4kU:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=ZkBLCPKu678:YaQMZl1C4kU:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=ZkBLCPKu678:YaQMZl1C4kU:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=ZkBLCPKu678:YaQMZl1C4kU:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=ZkBLCPKu678:YaQMZl1C4kU:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=ZkBLCPKu678:YaQMZl1C4kU:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=ZkBLCPKu678:YaQMZl1C4kU:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/Tapdancinggoats/~4/ZkBLCPKu678" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.tapdancinggoats.com/puzzle-1.htm/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>So You Occupied Wall Street?  Now What?</title>
		<link>http://www.tapdancinggoats.com/so-you-occupied-wall-street-now-what.htm</link>
		<comments>http://www.tapdancinggoats.com/so-you-occupied-wall-street-now-what.htm#comments</comments>
		<pubDate>Sat, 01 Oct 2011 21:37:07 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Politics]]></category>
		<category><![CDATA[civil resistance]]></category>
		<category><![CDATA[occupy wall street]]></category>
		<category><![CDATA[protest]]></category>

		<guid isPermaLink="false">http://www.tapdancinggoats.com/?p=731</guid>
		<description><![CDATA[Wall Street is occupied! Okay. Now what? As far as I can tell, this protest crystallized around anger directed at the people who got away with stealing billions from the rest of us. Lamenting the past is not the organizing principle for a political movement. This is a candlelight vigil for our economy. What do [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Wall Street is occupied!</strong></p>

<p>Okay.  Now what?</p>

<p>As far as I can tell, this protest crystallized around anger directed at the people who got away with stealing billions from the rest of us.  Lamenting the past is not the organizing principle for a political movement.  This is a candlelight vigil for our economy.</p>

<h3>What do we want?</h3>

<p>If you think that getting mad as hell is all it takes to effect change, you need to go back and finish watching Network.  Anger isn&#8217;t enough.  We saw the same thing during <a href="http://www.guardian.co.uk/media/2010/dec/08/mastercard-hackers-wikileaks-revenge">the Mastercard hack in Dec. 2010</a>.  What did that achieve?  Nothing.  They had no demands and weren&#8217;t asking the public to help.  If you don&#8217;t have a clear goal you&#8217;re not a protester, you&#8217;re a vandal.  A protest that can effect change needs to have a stated, achievable goal, and a clear call to action.</p>

<p>A clear goal gives a protest a finish line, and it makes the opposition responsible for the costs.  The message of a protest shouldn&#8217;t be, &#8220;We&#8217;re going to destroy your business,&#8221; but rather, &#8220;You can stop this by making a simple change.&#8221;  A protest without a goal is just a public party, and the opposition has no choice but to wait it out.</p>

<h3>At least they&#8217;re doing something?</h3>

<p>Sure, maybe it&#8217;s not well organized, but at least they&#8217;re not just sitting on their butts doing nothing, right?  Really?  That&#8217;s the rock the vote argument, that every eligible person should vote regardless of how well they understand the issues.  Just be heard; it doesn&#8217;t matter what you say.</p>

<p>This protest is going to be completely ineffective.  This isn&#8217;t elementary school, there are no points for participation.  This is going to demoralize people that might have participated in an effective civil action in the future.  Worthless protests are a pressure release valve to prevent effective civil disobedience.</p>

<p>Don&#8217;t just do anything.  Save your energy until you have a clear goal, and a real action that will put pressure on your opposition.  Without those things you aren&#8217;t holding a protest.  You&#8217;re just throwing a party.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=xr7eUv4Fi-U:9eh-H6JXPqA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=xr7eUv4Fi-U:9eh-H6JXPqA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=xr7eUv4Fi-U:9eh-H6JXPqA:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=xr7eUv4Fi-U:9eh-H6JXPqA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=xr7eUv4Fi-U:9eh-H6JXPqA:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=xr7eUv4Fi-U:9eh-H6JXPqA:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=xr7eUv4Fi-U:9eh-H6JXPqA:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=xr7eUv4Fi-U:9eh-H6JXPqA:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/Tapdancinggoats/~4/xr7eUv4Fi-U" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.tapdancinggoats.com/so-you-occupied-wall-street-now-what.htm/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Graham Scan in Haskell</title>
		<link>http://www.tapdancinggoats.com/graham-scan-in-haskell.htm</link>
		<comments>http://www.tapdancinggoats.com/graham-scan-in-haskell.htm#comments</comments>
		<pubDate>Wed, 07 Sep 2011 03:57:04 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[algorithm]]></category>
		<category><![CDATA[convex hull]]></category>
		<category><![CDATA[Graham scan]]></category>
		<category><![CDATA[haskell]]></category>

		<guid isPermaLink="false">http://www.tapdancinggoats.com/?p=719</guid>
		<description><![CDATA[This article follows how I developed an implementation of the Graham scan algorithm in Haskell, missteps and all. I think it&#8217;s valuable to see the process that others use, especially in a &#8220;weird&#8221; language like Haskell. At times, it can seem like most of what you&#8217;ve learned about developing software doesn&#8217;t apply to Haskell, but [...]]]></description>
			<content:encoded><![CDATA[<p>This article follows how I developed an implementation of the <a href="http://en.wikipedia.org/wiki/Graham_scan">Graham scan</a> algorithm in Haskell,
missteps and all.  I think it&#8217;s valuable to see the process that others use, especially in a &#8220;weird&#8221; language like Haskell.  At times, it can seem like most of what you&#8217;ve learned about developing software doesn&#8217;t apply to Haskell, but I think that&#8217;s just because the language allows so much of the scaffolding to be cut away once the software is finished.  Reading Haskell code written by the masters can feel like looking at the Sistine chapel and wondering where they got such long paint brushes.  This will be a step-by-step implementation as I developed it.  I&#8217;m still learning too, so don&#8217;t take this as an example of excellent Haskell style.</p>

<p><span id="more-719"></span></p>

<p>The <a href="http://en.wikipedia.org/wiki/Graham_scan">Graham scan</a> algorithm finds the convex hull of a set of planar points in three
steps.  First, we find the lower-left point in the set (call it <strong>P</strong>).  Second, we
sort the set based on the angle each point makes with <strong>P</strong> and the
x-axis.  Third, we go through the sorted list and keep each point
which makes a &#8220;left turn.&#8221;</p>

<h3>Data Types</h3>

<p>Even before we start, there are some obvious types we need.  We&#8217;ll
need a type to store points.  This could just be a tuple, but I think
the code will be easier to understand if the point type has
accessors.  I&#8217;ll define <code>Point</code> as</p>

<div class="dean_ch" style="white-space: wrap;"><br />
<span class="kw1">data</span> Point a = Point <span class="br0">&#123;</span>px :: a, py :: a<span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">deriving</span> <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#t:Show"><span class="kw4">Show</span></a><br />
&nbsp;</div>

<p>I parameterized the number type, so our algorithm can be as general as
possible.  I think we&#8217;ll want a type to represent the result of the
turn calculation too, so define a turn data type:</p>

<div class="dean_ch" style="white-space: wrap;"><br />
<span class="kw1">data</span> Turn = Left | Right | Colinear<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">deriving</span> <span class="br0">&#40;</span><a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#t:Show"><span class="kw4">Show</span></a>, <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#t:Eq"><span class="kw4">Eq</span></a><span class="br0">&#41;</span><br />
&nbsp;</div>

<p>Since I&#8217;m redefining <code>Left</code> and <code>Right</code> here, I have to hide <code>Either</code>
with <code>import Prelude hiding (Either(..))</code>.</p>

<h3>Stubs</h3>

<p>I want to start capturing what I know about the Graham scan function
in code.  First, it will take a set of points and return the points of
the convex hull, so</p>

<div class="dean_ch" style="white-space: wrap;"><br />
grahamScan :: <span class="br0">&#91;</span>Point a<span class="br0">&#93;</span> -&gt; <span class="br0">&#91;</span>Point a<span class="br0">&#93;</span><br />
&nbsp;</div>

<p>Next, the algorithm doesn&#8217;t work right if there are fewer than three
points, so it will generate an error in that case.  There are many ways to handle errors in Haskell.  Some common
methods include putting the function in the Error monad, returning an
Either or Maybe, or returning an empty list.  I&#8217;ll explore other methods in future articles, but since this function is
for experimentation I&#8217;m going to just raise an error.  I&#8217;m not even
going to bother with a detailed error report, so the function will start
like this.</p>

<div class="dean_ch" style="white-space: wrap;"><br />
grahamScan points<br />
&nbsp; &nbsp; | <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:length"><span class="kw3">length</span></a> points &lt; <span class="nu0">3</span> = <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:error"><span class="kw3">error</span></a> <span class="st0">&quot;Degenerate&quot;</span><br />
&nbsp; &nbsp; | <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:otherwise"><span class="kw3">otherwise</span></a> &nbsp; &nbsp; &nbsp; &nbsp; = &#8230;<br />
&nbsp;</div>

<p>Now that I know we have valid input, I can get to the meat of the
algorithm.  First we find the lower left point and sort the point list
with it.  I&#8217;m not sure how to find the lower left point, so I&#8217;ll just defer that to a function called <code>findFirstPoint</code>.  I&#8217;ll use the functions <a href="http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:compare"><code>compare</code></a> and <a href="http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-Function.html#v:on"><code>on</code></a> with a new function <code>angle</code> to create a function that will compare points by their angle.  This new comparator is handed to <a href="http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-List.html#v:sortBy"><code>sortBy</code></a> to sort the list.  For now, I set <code>findFirstPoint</code> and <code>angle</code> to <code>undefined</code> so
the code will compile.  Compile early and often; it helps to keep
mistakes from compounding and mutating.</p>

<div class="dean_ch" style="white-space: wrap;"><br />
<span class="kw1">let</span> <span class="br0">&#40;</span>firstPoint:rest<span class="br0">&#41;</span> = findFirstPoint points<br />
&nbsp; &nbsp; sortedRest = sortBy <span class="br0">&#40;</span><a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:compare"><span class="kw3">compare</span></a> `on` <span class="br0">&#40;</span>angle firstPoint<span class="br0">&#41;</span><span class="br0">&#41;</span> rest<br />
&nbsp;</div>

<p>The third step in the algorithm is a bit tricky.  We need to look at
the next three points in the set and decide whether or not to keep the
second point, and the last two points have to be tested along with the first point.
Since our input list is not circular, there has to be special handling
for the end of the list.</p>

<div class="dean_ch" style="white-space: wrap;"><br />
loop <span class="br0">&#40;</span>a:b:<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> = <span class="kw1">case</span> turn a b firstPoint <span class="kw1">of</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Left -&gt; b : <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _ &nbsp; &nbsp;-&gt; <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
loop <span class="br0">&#40;</span>a:b:c:ps<span class="br0">&#41;</span> = <span class="kw1">case</span> turn a b c <span class="kw1">of</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Left -&gt; b : loop <span class="br0">&#40;</span>b:c:ps<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _ &nbsp; &nbsp;-&gt; loop <span class="br0">&#40;</span>a:c:ps<span class="br0">&#41;</span><br />
&nbsp;</div>

<p>Isn&#8217;t that lovely?  Haskell is so expressive, the code basically reads
like the English description: &#8220;Take the next three points in the list,
and if they make a left turn keep the middle point.  If there are only
two points left, tack on the first point.&#8221;  All that remains is to
start the list with the first point and append the rest of the hull.
Here is the complete body of the function.</p>

<div class="dean_ch" style="white-space: wrap;"><br />
grahamScan points<br />
&nbsp; &nbsp; | <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:length"><span class="kw3">length</span></a> points &lt; <span class="nu0">3</span> = <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:error"><span class="kw3">error</span></a> <span class="st0">&quot;Degenerate&quot;</span><br />
&nbsp; &nbsp; | <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:otherwise"><span class="kw3">otherwise</span></a> &nbsp; &nbsp; &nbsp; &nbsp; = <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">let</span> <span class="br0">&#40;</span>firstPoint:rest<span class="br0">&#41;</span> = findFirstPoint points<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sortedRest = sortBy <span class="br0">&#40;</span><a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:compare"><span class="kw3">compare</span></a> `on` <span class="br0">&#40;</span>angle firstPoint<span class="br0">&#41;</span><span class="br0">&#41;</span> rest<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; loop <span class="br0">&#40;</span>a:b:<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> = <span class="kw1">case</span> turn a b firstPoint <span class="kw1">of</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Left -&gt; b : <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _ &nbsp; &nbsp;-&gt; <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; loop <span class="br0">&#40;</span>a:b:c:ps<span class="br0">&#41;</span> = <span class="kw1">case</span> turn a b c <span class="kw1">of</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Left -&gt; b : loop <span class="br0">&#40;</span>b:c:ps<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _ &nbsp; &nbsp;-&gt; loop <span class="br0">&#40;</span>a:c:ps<span class="br0">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">in</span> firstPoint : loop <span class="br0">&#40;</span>firstPoint:sortedRest<span class="br0">&#41;</span><br />
&nbsp;</div>

<h3>Support</h3>

<p>Now to define the helper functions I stubbed out before.  The <code>angle</code>
function is supposed to calculate the angle between ba and the x-axis, but it&#8217;s really calculating the cosine of that angle.  Don&#8217;t tell anybody.</p>

<div class="dean_ch" style="white-space: wrap;"><br />
angle a b = dx / len <span class="kw1">where</span><br />
&nbsp; &nbsp; dx = px a &#8211; px b<br />
&nbsp; &nbsp; dy = py a &#8211; py b<br />
&nbsp; &nbsp; len = <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:sqrt"><span class="kw3">sqrt</span></a> <span class="br0">&#40;</span>dx * dx + dy * dy<span class="br0">&#41;</span><br />
&nbsp;</div>

<p>Now the compiler says that the type parameter in our <code>Point</code> has to be
in the <code>Floating</code> class for <code>sqrt</code>.  Add that to the type declaration
of <code>grahamScan</code>.</p>

<p>The function <code>turn</code> is also simple.  If the cross product of the two
line segments is greater than zero then it is a left turn, while less than zero
is a right turn. Equal to zero is colinear, which can go either way
depending on what you want from your convex hull. I report &#8216;em all and
let the caller sort it out.</p>

<div class="dean_ch" style="white-space: wrap;"><br />
turn a b c = <span class="kw1">case</span> <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:compare"><span class="kw3">compare</span></a> cross <span class="nu0">0</span> <span class="kw1">of</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;GT -&gt; Left<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;EQ -&gt; Colinear<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;LT -&gt; Right<br />
&nbsp; &nbsp; <span class="kw1">where</span><br />
&nbsp; &nbsp; &nbsp; cross = x1 * y2 &#8211; x2 * y1<br />
&nbsp; &nbsp; &nbsp; x1 = px b &#8211; px a<br />
&nbsp; &nbsp; &nbsp; y1 = py b &#8211; py a<br />
&nbsp; &nbsp; &nbsp; x2 = px c &#8211; px b<br />
&nbsp; &nbsp; &nbsp; y2 = py c &#8211; py b<br />
&nbsp;</div>

<p>I used <code>compare</code> here so I could use <code>case</code> instead of nested if
statements, or a function with guards.  I don&#8217;t know what the
performance differences are, I just think the code is cleaner like
this.</p>

<p>Since this function compares the values of the point coordinates,
we&#8217;ll need to add the <code>Ord</code> class to the <code>Point</code> parameter.  That goes
on <code>grahamScan</code>, which now reads</p>

<div class="dean_ch" style="white-space: wrap;"><br />
grahamScan :: <span class="br0">&#40;</span><a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#t:Floating"><span class="kw4">Floating</span></a> a, <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#t:Ord"><span class="kw4">Ord</span></a> a<span class="br0">&#41;</span> =&gt; <span class="br0">&#91;</span>Point a<span class="br0">&#93;</span> -&gt; <span class="br0">&#91;</span>Point a<span class="br0">&#93;</span><br />
&nbsp;</div>

<p>The last function, <code>firstPoint</code>, is a little tricky.  You could define
it using two passes, like this:</p>

<div class="dean_ch" style="white-space: wrap;"><br />
findFirstPoint points =<br />
&nbsp; <span class="kw1">let</span> firstPoint = minimumBy lowerLeftCmp points<br />
&nbsp; &nbsp; &nbsp; rest = <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:filter"><span class="kw3">filter</span></a> <span class="br0">&#40;</span>/= firstPoint<span class="br0">&#41;</span> points<br />
&nbsp; <span class="kw1">in</span> firstPoint:rest<br />
&nbsp;</div>

<p>However, I chose to do it in one pass by moving points from the input
list to an output list.  The lower-left point at each step is held
back and prepended to the output at the end.</p>

<div class="dean_ch" style="white-space: wrap;"><br />
findFirstPoint points<br />
&nbsp; &nbsp; | <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:null"><span class="kw3">null</span></a> points = <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:error"><span class="kw3">error</span></a> <span class="st0">&quot;Null points&quot;</span><br />
&nbsp; &nbsp; | <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:otherwise"><span class="kw3">otherwise</span></a> &nbsp; = loop points <span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="kw1">where</span><br />
&nbsp; &nbsp; loop <span class="br0">&#40;</span>a:<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> ps = a:ps<br />
&nbsp; &nbsp; loop <span class="br0">&#40;</span>a:b:rest<span class="br0">&#41;</span> ps =<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>py a &lt; py b<span class="br0">&#41;</span> || <span class="br0">&#40;</span>py a == py b &amp;&amp; px a &lt; px b<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">then</span> loop <span class="br0">&#40;</span>a:rest<span class="br0">&#41;</span> <span class="br0">&#40;</span>b:ps<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> loop <span class="br0">&#40;</span>b:rest<span class="br0">&#41;</span> <span class="br0">&#40;</span>a:ps<span class="br0">&#41;</span><br />
&nbsp;</div>

<p>And that&#8217;s it for a working implementation.  I also added a
convenience function for converting tuples to Points:
<code>point = uncurry Point</code>.</p>

<h3>Refinement</h3>

<p>Now we can test this out in <code>ghci</code>:</p>

<div class="dean_ch" style="white-space: wrap;"><br />
*GrahamScan&gt; grahamScan . <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:map"><span class="kw3">map</span></a> point $ <span class="br0">&#91;</span><span class="br0">&#40;</span><span class="nu0">2</span>, <span class="nu0">3</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">1</span>, <span class="nu0">1</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">-1</span>, <span class="nu0">0</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">1</span>, <span class="nu0">-3</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#93;</span><br />
<span class="br0">&#91;</span>Point <span class="br0">&#123;</span>px = <span class="nu0">1.0</span>, py = <span class="nu0">-3.0</span><span class="br0">&#125;</span>,Point <span class="br0">&#123;</span>px = <span class="nu0">2.0</span>, py = <span class="nu0">3.0</span><span class="br0">&#125;</span>,Point <span class="br0">&#123;</span>px = <span class="nu0">-1.0</span>, py = <span class="nu0">0.0</span><span class="br0">&#125;</span><span class="br0">&#93;</span><br />
*GrahamScan&gt; grahamScan . <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:map"><span class="kw3">map</span></a> point $ <span class="br0">&#91;</span><span class="br0">&#40;</span><span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">0.5</span>, <span class="nu0">0.5</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">1</span>, <span class="nu0">1</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">0</span>, <span class="nu0">2</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">-1</span>, <span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#93;</span><br />
<span class="br0">&#91;</span>Point <span class="br0">&#123;</span>px = <span class="nu0">0.0</span>, py = <span class="nu0">0.0</span><span class="br0">&#125;</span>,Point <span class="br0">&#123;</span>px = <span class="nu0">0.5</span>, py = <span class="nu0">0.5</span><span class="br0">&#125;</span>,Point <span class="br0">&#123;</span>px = <span class="nu0">0.0</span>, py = <span class="nu0">2.0</span><span class="br0">&#125;</span>,Point <span class="br0">&#123;</span>px = <span class="nu0">-1.0</span>, py = <span class="nu0">1.0</span><span class="br0">&#125;</span><span class="br0">&#93;</span><br />
&nbsp;</div>

<p>Uh oh, there&#8217;s a bug.  The convex hull in the last test should have
included <code>(1, 1)</code> but not <code>(0.5, 0.5)</code>.  The problem is that they are
colinear with the lowest point <code>(0, 0)</code>, so they have the same angle.
They will be sorted in an arbitrary order, but whichever one ends up
first will be rejected.  To fix this, we&#8217;ll need to add the distance
to the lower-left point in the angle comparison.  If two points have
the same angle, the point that is closer should come first in the
list.  That way, it will be rejected.</p>

<p>To do this, I&#8217;ll change <code>angle</code> to return a tuple with the angle and
the length.  When comparing tuples, the elements <code>n</code> are only compared
if the elements <code>n-1</code> are equal, which is exactly what we want.  So,
with the subdefinitions the same, angle becomes:</p>

<div class="dean_ch" style="white-space: wrap;"><br />
angle a b = <span class="br0">&#40;</span>dx / len, len<span class="br0">&#41;</span><br />
&nbsp;</div>

<p>When I did this, I realized that I could use a tuple in the
<code>findFirstPoint</code> comparison too.  The expression:</p>

<div class="dean_ch" style="white-space: wrap;"><br />
<span class="br0">&#40;</span>py a &lt; py b<span class="br0">&#41;</span> || <span class="br0">&#40;</span>py a == py b &amp;&amp; px a &lt; px b<span class="br0">&#41;</span><br />
&nbsp;</div>

<p>is equivalent to</p>

<div class="dean_ch" style="white-space: wrap;"><br />
<span class="br0">&#40;</span>py a, px a<span class="br0">&#41;</span> &lt; <span class="br0">&#40;</span>py b, px b<span class="br0">&#41;</span><br />
&nbsp;</div>

<p>I think the second is easier to read, and due to laziness the
x-coordinates aren&#8217;t even calculated unless the y-coordinates are
equal.</p>

<h3>Final Thoughts</h3>

<p>So that&#8217;s it really.  You can get the final source <a href="https://github.com/shadwstalkr/GrahamScanDemo">here</a>.  I
would be interested to see someone tune the performance on this.  It
scales about linearly,<sup id="fnref:timing"><a href="#fn:timing" rel="footnote">1</a></sup> which is expected since the algorithm is
O(n&nbsp;log&nbsp;n), but I think that there&#8217;s plenty of room for
improvement.  If you have some suggestions, fork <a href="https://github.com/shadwstalkr/GrahamScanDemo">the repository at Github</a> and show me what you can do.</p>

<p>Haskell&#8217;s expressive power made the implementation of this algorithm fairly easy.  There are several different ways to do it, and I&#8217;ll explore some of the options in future articles.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:timing">
<p>These are timings for the calculation of the convex hull of sets of
random vertices.  The timings were performed on a 2.1 GHz Intel Core 2
Duo.
<table>
<thead>
<tr>
  <th>Input Vertices</th>
  <th>Time (s)</th>
</tr>
</thead>
<tbody>
<tr>
  <td>100</td>
  <td>0.013</td>
</tr>
<tr>
  <td>200</td>
  <td>0.021</td>
</tr>
<tr>
  <td>500</td>
  <td>0.037</td>
</tr>
<tr>
  <td>10000</td>
  <td>0.70</td>
</tr>
<tr>
  <td>20000</td>
  <td>1.4</td>
</tr>
<tr>
  <td>50000</td>
  <td>3.5</td>
</tr>
<tr>
  <td>100000</td>
  <td>7.0</td>
</tr>
<tr>
  <td>200000</td>
  <td>14.3</td>
</tr>
<tr>
  <td>500000</td>
  <td>37</td>
</tr>
</tbody>
</table>&#160;<a href="#fnref:timing" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=Kb-rpg29WaQ:JG4L23SXVhk:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=Kb-rpg29WaQ:JG4L23SXVhk:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=Kb-rpg29WaQ:JG4L23SXVhk:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=Kb-rpg29WaQ:JG4L23SXVhk:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=Kb-rpg29WaQ:JG4L23SXVhk:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=Kb-rpg29WaQ:JG4L23SXVhk:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=Kb-rpg29WaQ:JG4L23SXVhk:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=Kb-rpg29WaQ:JG4L23SXVhk:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/Tapdancinggoats/~4/Kb-rpg29WaQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.tapdancinggoats.com/graham-scan-in-haskell.htm/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Double Pendulum Simulation</title>
		<link>http://www.tapdancinggoats.com/double-pendulum-simulation.htm</link>
		<comments>http://www.tapdancinggoats.com/double-pendulum-simulation.htm#comments</comments>
		<pubDate>Thu, 18 Aug 2011 07:53:27 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Flash]]></category>
		<category><![CDATA[Physics]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[double pendula]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[pendulum]]></category>

		<guid isPermaLink="false">http://www.tapdancinggoats.com/?p=714</guid>
		<description><![CDATA[I&#8217;ve been playing around with some of the new features in HTML5, particularly to see how the canvas stacks up to Flash. One of the things I wanted to test was javascript performance, so I ported this Flash toy I wrote a few years ago. It&#8217;s a physical simulation of a double pendulum system. It&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.tapdancinggoats.com/double-pendula"><img src="http://www.tapdancinggoats.com/wp-content/uploads/2011/08/pendula.png" alt="" title="Double Pendula" width="421" height="392" class="alignright size-full wp-image-715" /></a></p>

<p>I&#8217;ve been playing around with some of the new features in HTML5, particularly to see how the canvas stacks up to Flash.  One of the things I wanted to test was javascript performance, so I ported this Flash toy I wrote a few years ago.  It&#8217;s a physical simulation of a double pendulum system.  It&#8217;s interactive, and it can export the line drawing it produces as a PNG.</p>

<p>How did canvas+JS do?  The export was a lot easier: I had to write a PNG encoder in Actionscript for the original version!  Pretty much everything else was harder.  Canvas has features similar to Flash 5, and I missed modern Flash&#8217;s rich standard library.  CSS layout is still somewhat inferior to Flex for GUI design, as the layout options are less flexible.</p>

<p>One of the appeals of canvas is mobile support, but I was disappointed by the performance on my Motorola Droid.  Just clearing the background on a canvas larger than 500&#215;200 took the frame rate to single digits, and I couldn&#8217;t find a reliable way to make the canvas fill the screen if other elements were present (I didn&#8217;t look too hard, since a canvas that large was unusable).  The javascript performance was fine, it was just the drawing that caused problems.  Let me know if you get better results on different hardware, I&#8217;d love to know that this can work better.</p>

<p>Overall, canvas shows promise, but I don&#8217;t think it&#8217;s ready to replace Flash for complex graphical applications.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=Z_m0E46HkDU:05TEXw3Wh1o:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=Z_m0E46HkDU:05TEXw3Wh1o:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=Z_m0E46HkDU:05TEXw3Wh1o:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=Z_m0E46HkDU:05TEXw3Wh1o:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=Z_m0E46HkDU:05TEXw3Wh1o:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=Z_m0E46HkDU:05TEXw3Wh1o:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=Z_m0E46HkDU:05TEXw3Wh1o:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=Z_m0E46HkDU:05TEXw3Wh1o:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/Tapdancinggoats/~4/Z_m0E46HkDU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.tapdancinggoats.com/double-pendulum-simulation.htm/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apocalypse</title>
		<link>http://www.tapdancinggoats.com/apocalypse.htm</link>
		<comments>http://www.tapdancinggoats.com/apocalypse.htm#comments</comments>
		<pubDate>Thu, 18 Aug 2011 03:03:29 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Politics]]></category>

		<guid isPermaLink="false">http://www.tapdancinggoats.com/?p=705</guid>
		<description><![CDATA[Why do people speak of the apocalypse as a discrete, identifiable event? We will not stare in horror as the sky splits open, pouring hordes upon us. Do you think the Romans or the Chinese recognized the beginning of the end of their empire? History suggests that they didn&#8217;t even recognize the middle of the [...]]]></description>
			<content:encoded><![CDATA[<p>Why do people speak of the apocalypse as a discrete, identifiable event?  We will not stare in horror as the sky splits open, pouring hordes upon us.  Do you think the Romans or the Chinese recognized the beginning of the end of their empire?  History suggests that they didn&#8217;t even recognize the middle of the end.  Look around.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=TneVQrsnrXo:OK_LDWX5kYo:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=TneVQrsnrXo:OK_LDWX5kYo:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=TneVQrsnrXo:OK_LDWX5kYo:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=TneVQrsnrXo:OK_LDWX5kYo:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=TneVQrsnrXo:OK_LDWX5kYo:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=TneVQrsnrXo:OK_LDWX5kYo:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=TneVQrsnrXo:OK_LDWX5kYo:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=TneVQrsnrXo:OK_LDWX5kYo:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/Tapdancinggoats/~4/TneVQrsnrXo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.tapdancinggoats.com/apocalypse.htm/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows vs. Unix File System Semantics – Conifer Systems</title>
		<link>http://www.tapdancinggoats.com/windows-vs-unix-file-system-semantics-conifer-systems.htm</link>
		<comments>http://www.tapdancinggoats.com/windows-vs-unix-file-system-semantics-conifer-systems.htm#comments</comments>
		<pubDate>Fri, 15 Jul 2011 01:43:25 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Links]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[file system]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[win32]]></category>

		<guid isPermaLink="false">http://www.tapdancinggoats.com/?p=701</guid>
		<description><![CDATA[This is a great summary of the low-level API differences between Windows and Linux file systems. Windows vs. Unix File System Semantics &#8211; Conifer Systems.]]></description>
			<content:encoded><![CDATA[<p>This is a great summary of the low-level API differences between Windows and Linux file systems.</p>

<p><a href="http://www.conifersystems.com/2008/10/21/windows-vs-unix-file-system-semantics/">Windows vs. Unix File System Semantics &#8211; Conifer Systems</a>.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=WrWFR2boPd8:BKTsoXQva_c:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=WrWFR2boPd8:BKTsoXQva_c:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=WrWFR2boPd8:BKTsoXQva_c:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=WrWFR2boPd8:BKTsoXQva_c:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=WrWFR2boPd8:BKTsoXQva_c:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=WrWFR2boPd8:BKTsoXQva_c:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Tapdancinggoats?a=WrWFR2boPd8:BKTsoXQva_c:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/Tapdancinggoats?i=WrWFR2boPd8:BKTsoXQva_c:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/Tapdancinggoats/~4/WrWFR2boPd8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.tapdancinggoats.com/windows-vs-unix-file-system-semantics-conifer-systems.htm/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

