<?xml version="1.0" encoding="ISO-8859-1" ?>
<rss version="2.0">  <channel>
    <title>Cyan Canyon</title>
    <link>http://www.cyancanyon.com</link>
    <description>Cyan Canyon News, Updates, Bias</description>
    <language>en-us</language>
    <lastBuildDate>Sat, 02 Aug 2008 00:00:00</lastBuildDate>
	  <item>
      <title>FooBrain Help Files</title>
      <link>http://www.cyancanyon.com/news/?id=14</link>
      <description><![CDATA[<p>The next big thing on Cyan Canyon is <a href="http://www.cyancanyon.com/software/foobrain/">FooBrain</a>, the application planner/version tracker/bug tracker/dependency tracker for pocket PCs. I've been using variations of this program (I used to call it AppTrack) for the past three years or so. There are way too many ideas in my head and not enough time to deal with them all, but I keep track of them anyway as various programs fade in and out of my interest. I also use it to keep tabs on my projects at work so right now I think I have nearly 100 applications, maybe 300 versions and I'd guess upwards of 2500 change notes that need to be organized. FooBrain handles it perfectly (of course, if it didn't I would fix it, now wouldn't I?).</p><p>It hasn't been released yet but I did upload the <a href="http://www.cyancanyon.com/software/foobrain/">help files</a>. Not that they would mean much without the application or even screenshots yet (thats coming next) but I figured someone out there might want a peek.</p>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=14</guid>
      <pubDate>Sat, 02 Aug 2008 00:00:00</pubDate>
    </item>
	  <item>
      <title>Skipping the Mac Trash</title>
      <link>http://www.cyancanyon.com/news/?id=13</link>
      <description><![CDATA[<p>As much as I think that Mac OS X is a generally superior operating system, there is a thing or two that I gripe about. One is the Windows luxury of being able to yank out my USB key at any random time and without any prerequisite action, but I'm not going to talk about that one today. Another is the lack of a skip-the-trash delete command similar to shift-delete in Windows.</p><p>I know its easy to send something to the trash and then empty the trash and people have even automated this, but its not what I'm looking for. I want to completely delete a file without also deleting everything that just happened to be in the trash too. I also want to do it with a keyboard shortcut, but I haven't gotten there yet.</p><p>Here is my dock...</p><img alt="dock with toilet next to trash" title="toilet icon" src="http://www.cyancanyon.com/news/images/docktoilet.png"><p>Oh yeah, theres a toilet on my dock. Drag a file to the trash and it gets marked for deletion. Drag it to the toilet, and you get...</p><img alt="delete notification" title="delete notification" src="http://www.cyancanyon.com/news/images/toiletconfirm.png"><p>This is done with some AppleScript which calls some shell script together with configurable folder actions.</p><p>NOTE: This is all done on OS X Tiger, please let me know if there are caveats for (Snow)?Leopard.</p><p>The UI is just a folder where the icon has been set to the toilet pic. Then it is dragged down into the dock for easy access.</p><p>Next there is the AppleScript. This routine gets the list of files in a directory. For each file, it checks to make sure its not the .DS_Store or the icon file and if not, calls the shell command to delete it.</p><code><span>on adding folder items to this_folder after receiving these_items</span><br>&nbsp;&nbsp;<span>set the item_count to the count of these_items</span><br><br>&nbsp;&nbsp;<span class="comment">--confirm the deletion</span><br>&nbsp;&nbsp;<span>set the_choice to display dialog (item_count as string) &amp; " items are set to be deleted." buttons {"Delete", "Move to Trash"} default button 1</span><br>&nbsp;&nbsp;<span>set the_result to button returned of the_choice</span><br>&nbsp;&nbsp;<span>if the the_result is equal to "Delete" then</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>repeat with filename in these_items</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--don't process the .DS_Store or the icon file</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if ((filename as string) ends with ":.DS_Store") or ((filename as string) ends with ":Icon") or ((filename as string) ends with ":Icon ") then</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--do nothing</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>else</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>try</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--the delimiter is a colon, so we have to replace it with a slash so the shell command can read it</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set tid to text item delimiters</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set new_filename to missing value</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set text item delimiters to {":"}</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set fnstring to text items of (filename as text)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set text item delimiters to {"/"}</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if last item of fnstring is equal to "" then</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set new_filename to "/" &amp; ((items 2 thru -2 of fnstring) as string)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>else</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set new_filename to "/" &amp; ((items 2 thru -1 of fnstring) as string)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>end if</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set text item delimiters to tid</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>on error msg</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>display dialog msg</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>end try</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--call the shell command</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--the 'r' flag indicates that if the pathname points to a directory, it should be deleted as well as all files and folders it contains</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--the 'f' flag indicates that the deletion is forced, nonexistent files are ignored and there are no prompts</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>do shell script "rm -rf \"" &amp; new_filename &amp; "\""</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>end if</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>end repeat</span><br>&nbsp;&nbsp;<span>else</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--If you changed your mind, move it to the trash where it will be recoverable</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>tell application "Finder" to delete these_items</span><br>&nbsp;&nbsp;<span>end if</span><br><span>end adding folder items to</span></code><p>And then we just attach the script as a folder action to the toilet folder. Then, any time files are added to the folder (for example, by dragging them onto the dock icon) the script is fired off and the files deleted.</p>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=13</guid>
      <pubDate>Tue, 10 Jun 2008 00:00:00</pubDate>
    </item>
	  <item>
      <title>Great Circle Distances</title>
      <link>http://www.cyancanyon.com/news/?id=12</link>
      <description><![CDATA[<p>Point to point distances are needed all throughout applications we've written, most notably, Smache. So getting accurate distance values was obviously very important. Unfortunately, the pythagorean theorem doesn't cut it when we're looking for distances across the surface of a planet, so a slightly more complicated function had to be used.</p><p>What we wanted was the great circle distance. Great circles are lines that encircle a sphere leaving equal-sized halves on either side. Any two points on a sphere can be connected with one and only one great circle, except for the cases where the points are exactly opposite each other or exactly the same in which infinite great circles can pass through the two points. To come up with the distance between two points along their respective great circle, we need a little trig and the <a href="http://en.wikipedia.org/wiki/Haversine_formula">haversine formula</a>.</p><p>NOTE: Smache was written in .NET with C# so my examples are also, but I included the javascript representation too because, well, javascript rocks.</p><p>The input parameters to the distance calculation function are the latitude and longitude for point A and the latitude and longitude for point B. We also needed a radius constant which was set at 6372 kilometers, widely agreed upon as the average radius of the earth. Note that because of the irregular shape of the earth, the resulting distance may be off by as much as half a percent.</p><code><span>const double EARTH_RADIUS = 6372;</span><br><span>double GreatCircleDistance(double latA, double longA, double latB, double longB) { }</span></code><p>The haversine formula expects values to be in radians rather than degrees, so the first task is to make that conversion, simply dividing by 180 over pi.</p><code><span>double latARad = latA / (180 / Math.PI);</span><br><span>double longARad = longA / (180 / Math.PI);</span><br><span>double latBRad = latB / (180 / Math.PI);</span><br><span>double longBRad = longB / (180 / Math.PI);</span></code><p>The formula will also use delta values, so in the interest of comprehension we'll create those variables as well.</p><code><span>double latDelta = latBRad - latARad;</span><br><span>double longDelta = longBRad - longARad;</span><br></code><p>Next is where the haversine comes in. In order to eliminate duplicate equations I've split the formula into two statements.</p><code><span>double haversineA = Math.Pow(Math.Sin(latDelta / 2), 2)   Math.Cos(latARad) * Math.Cos(latBRad) * Math.Pow(Math.Sin(longDelta / 2), 2);</span><br><span>double haversineB = 2 * Math.Atan2(Math.Sqrt(haversineA), Math.Sqrt(1 - haversineA));</span><br></code><p>Finally we simply need to multiply by the radius to get the final distance.</p><code><span>double finalDistance = haversineB * EARTH_RADIUS;</span></code><p>So the function ends up like so...</p><code><span>const double EARTH_RADIUS = 6372;</span><br><br><span>double GreatCircleDistance(double latA, double longA, double latB, double longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double latARad = latA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double longARad = longA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double latBRad = latB / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double longBRad = longB / (180 / Math.PI);</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double latDelta = latBRad - latARad;</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double longDelta = longBRad - longARad;</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double haversineA = Math.Pow(Math.Sin(latDelta / 2), 2)   Math.Cos(latARad) * Math.Cos(latBRad) * Math.Pow(Math.Sin(longDelta / 2), 2);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double haversineB = 2 * Math.Atan2(Math.Sqrt(haversineA), Math.Sqrt(1 - haversineA));</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double finalDistance = haversineB * EARTH_RADIUS;</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;return finalDistance;</span><br><span>}</span></code><p>or in javascript...</p><code><span>var EARTH_RADIUS = 6372;</span><br><br><span>function GreatCircleDistance(latA, longA, latB, longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var latARad = latA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var longARad = longA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var latBRad = latB / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var longBRad = longB / (180 / Math.PI);</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var latDelta = latBRad - latARad;</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var longDelta = longBRad - longARad;</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var haversineA = Math.pow(Math.sin(latDelta / 2), 2)   Math.cos(latARad) * Math.cos(latBRad) * Math.pow(Math.sin(longDelta / 2), 2);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var haversineB = 2 * Math.atan2(Math.sqrt(haversineA), Math.sqrt(1 - haversineA));</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var finalDistance = haversineB * EARTH_RADIUS;</span><br><br> <span>&nbsp;&nbsp;&nbsp;&nbsp;return finalDistance;</span><br><span>}</span></code><br><p>And if you're into one-liners...</p><code><span>double GreatCircleDistance(double latA, double longA, double latB, double longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;return 6372 * 2 * Math.Atan2(Math.Sqrt(Math.Pow(Math.Sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2)   Math.Cos((latA / (180 / Math.PI))) * Math.Cos((latB / (180 / Math.PI))) * Math.Pow(Math.Sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2)), Math.Sqrt(1 - (Math.Pow(Math.Sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2)   Math.Cos((latA / (180 / Math.PI))) * Math.Cos((latB / (180 / Math.PI))) * Math.Pow(Math.Sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2))));</span><br><span>}</span><br><br><span>function GreatCircleDistance(latA, longA, latB, longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;return 6372 * 2 * Math.atan2(Math.sqrt(Math.pow(Math.sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2)   Math.cos((latA / (180 / Math.PI))) * Math.cos((latB / (180 / Math.PI))) * Math.pow(Math.sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2)), Math.sqrt(1 - (Math.pow(Math.sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2)   Math.cos((latA / (180 / Math.PI))) * Math.cos((latB / (180 / Math.PI))) * Math.pow(Math.sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2))));</span><br><span>}</span></code>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=12</guid>
      <pubDate>Sat, 07 Jun 2008 00:00:00</pubDate>
    </item>
  </channel>
</rss>
