http://www.gasi.ch/blog/RTFM / Daniel Gasienica2018-07-02T13:24:27+00:00Daniel Gasienicadaniel@gasienica.chhttp://www.gasi.ch/blog/practical-functional-programming-2PFP: Sorting Things Out2018-06-29T00:00:00+00:00<p>Welcome to Part Two of <em>Practical Functional Programming (PFP)</em>. To learn more about the origin of this series, read the Introduction: <a href="/blog/practical-functional-programming">Practical Functional Programming: Prelude</a>.</p>
<hr />
<section class="problem">
<h2 id="problem">Problem</h2>
<p>You add a new feature to your app and other parts break.</p>
</section>
<h2 id="example">Example</h2>
<blockquote>
<h3 id="code-typescript-good">Code: TypeScript (good)</h3>
<p>We are building a World Cup app. The app needs to show a team leaderboard based on the number of titles they’ve won. We have an unsorted list of World Cup winners, so we write a function to sort the teams by <code class="highlighter-rouge">numWorldCupTitles</code> in descending order:</p>
<figure class="highlight"><pre><code class="language-typescript" data-lang="typescript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="code"><pre><span class="kr">interface</span> <span class="nx">Team</span> <span class="p">{</span>
<span class="nl">numWorldCupTitles</span><span class="p">:</span> <span class="nx">number</span><span class="p">;</span>
<span class="nl">country</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">teams</span><span class="p">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">Team</span><span class="o">></span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Italy"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"France"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Uruguay"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Spain"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Argentina"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Germany"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"England"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Brazil"</span> <span class="p">}</span>
<span class="p">];</span>
<span class="kd">const</span> <span class="nx">sortByNumTitles</span> <span class="o">=</span> <span class="p">(</span><span class="nx">teams</span><span class="p">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">Team</span><span class="o">></span><span class="p">):</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">Team</span><span class="o">></span> <span class="o">=></span>
<span class="nx">teams</span><span class="p">.</span><span class="nx">sort</span><span class="p">(</span>
<span class="p">(</span><span class="nx">a</span><span class="p">:</span> <span class="nx">Team</span><span class="p">,</span> <span class="nx">b</span><span class="p">:</span> <span class="nx">Team</span><span class="p">)</span> <span class="o">=></span>
<span class="nx">b</span><span class="p">.</span><span class="nx">numWorldCupTitles</span> <span class="o">-</span> <span class="nx">a</span><span class="p">.</span><span class="nx">numWorldCupTitles</span>
<span class="p">);</span>
<span class="kd">const</span> <span class="nx">teamsByNumTitles</span> <span class="o">=</span> <span class="nx">sortByNumTitles</span><span class="p">(</span><span class="nx">teams</span><span class="p">);</span></pre></td></tr></tbody></table></code></pre></figure>
<p>Somewhere else in the app, we’d like to show the top team. Since we already have the teams sorted by title wins, we pick the first team from the list…</p>
<figure class="highlight"><pre><code class="language-typescript" data-lang="typescript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">26
27
28
29
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">topTeam</span> <span class="o">=</span> <span class="nx">teamsByNumTitles</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span>
<span class="s2">`Top team: </span><span class="p">${</span><span class="nx">topTeam</span><span class="p">.</span><span class="nx">country</span><span class="p">}</span><span class="s2"> (</span><span class="p">${</span><span class="nx">topTeam</span><span class="p">.</span><span class="nx">numWorldCupTitles</span><span class="p">}</span><span class="s2">)`</span>
<span class="p">);</span></pre></td></tr></tbody></table></code></pre></figure>
<p>…and get the correct result:</p>
<!-- prettier-ignore-start -->
<div class="correct highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Top team: Brazil (5)
</code></pre></div> </div>
<!-- prettier-ignore-end -->
</blockquote>
<p>Later on, a section of our app needs to show the list of World Cup winners in alphabetical order:</p>
<blockquote>
<h3 id="code-typescript-bad">Code: TypeScript (bad)</h3>
<p>We write a function to sort the teams by <code class="highlighter-rouge">country</code>:</p>
<figure class="highlight"><pre><code class="language-typescript" data-lang="typescript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">23
24
25
26
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">sortByCountry</span> <span class="o">=</span> <span class="p">(</span><span class="nx">teams</span><span class="p">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">Team</span><span class="o">></span><span class="p">)</span> <span class="o">=></span>
<span class="nx">teams</span><span class="p">.</span><span class="nx">sort</span><span class="p">((</span><span class="nx">a</span><span class="p">:</span> <span class="nx">Team</span><span class="p">,</span> <span class="nx">b</span><span class="p">:</span> <span class="nx">Team</span><span class="p">)</span> <span class="o">=></span>
<span class="nx">a</span><span class="p">.</span><span class="nx">country</span><span class="p">.</span><span class="nx">localeCompare</span><span class="p">(</span><span class="nx">b</span><span class="p">.</span><span class="nx">country</span><span class="p">)</span>
<span class="p">);</span></pre></td></tr></tbody></table></code></pre></figure>
<p>We pass the <code class="highlighter-rouge">teams</code> constant into <code class="highlighter-rouge">sortByCountry</code> and store it in <code class="highlighter-rouge">const teamsByCountry</code> next to the previously set <code class="highlighter-rouge">const teamsByTitles</code>:</p>
<figure class="highlight"><pre><code class="language-typescript" data-lang="typescript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">28
29
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">teamsByNumTitles</span> <span class="o">=</span> <span class="nx">sortByNumTitles</span><span class="p">(</span><span class="nx">teams</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">teamsByCountry</span> <span class="o">=</span> <span class="nx">sortByCountry</span><span class="p">(</span><span class="nx">teams</span><span class="p">);</span></pre></td></tr></tbody></table></code></pre></figure>
<p>However, when we run the program right after adding the new code above, without making any other changes, our top team has changed and is wrong:</p>
<!-- prettier-ignore-start -->
<div class="incorrect highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Top team: Argentina (2)
</code></pre></div> </div>
<!-- prettier-ignore-end -->
</blockquote>
<h2 id="cause">Cause</h2>
<p>Many operations in JavaScript mutate their arguments, among them <code class="highlighter-rouge">Array::sort</code>, <code class="highlighter-rouge">Object.assign</code>, <code class="highlighter-rouge">delete</code>, <code class="highlighter-rouge">Date::setDate</code>, etc. <em>Note: There are exceptions such as <code class="highlighter-rouge">Array::map</code>, <code class="highlighter-rouge">Array::filter</code>, <code class="highlighter-rouge">Math.abs</code>, <code class="highlighter-rouge">String::toLowerCase</code>, etc. (the last two because <code class="highlighter-rouge">Number</code> and <code class="highlighter-rouge">String</code> are immutable)</em></p>
<p>In our case, we pass in <code class="highlighter-rouge">const teams</code> to both <code class="highlighter-rouge">sortByNumTitles</code> and <code class="highlighter-rouge">sortByCountry</code> which use <code class="highlighter-rouge">Array::sort</code> under the hood and therefore mutate <code class="highlighter-rouge">teams</code>, despite it being declared as <code class="highlighter-rouge">const</code>.</p>
<p>Ultimately, this means even when we only introduce new code without changing existing one, we can run into regressions due to side-effects of mutation.</p>
<blockquote>
<h2 id="background-const">Background: <code class="highlighter-rouge">const</code></h2>
<p>In JavaScript, <code class="highlighter-rouge">const</code> declarations can be misleading because they only guarantee that they are never reassigned, not that their underlying data is left untouched:</p>
<figure class="highlight"><pre><code class="language-typescript" data-lang="typescript"><span class="kd">const</span> <span class="nx">numbers</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">];</span>
<span class="c1">// OK: Mutation</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"Reversed numbers:"</span><span class="p">,</span> <span class="nx">numbers</span><span class="p">.</span><span class="nx">reverse</span><span class="p">());</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"Original numbers:"</span><span class="p">,</span> <span class="nx">numbers</span><span class="p">);</span>
<span class="c1">// Reversed numbers: [3, 2, 1]</span>
<span class="c1">// Original numbers: [3, 2, 1] // Unexpected!</span>
<span class="c1">// Not OK: Reassignment</span>
<span class="nx">numbers</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">];</span>
<span class="c1">// TypeError: Assignment to constant variable.</span></code></pre></figure>
</blockquote>
<h2 id="solution">Solution</h2>
<p>What if we eliminated mutation as a core mechanism in the language we use? What if there was no difference between immutable primitive types such as <code class="highlighter-rouge">string</code>, <code class="highlighter-rouge">number</code>, and <code class="highlighter-rouge">boolean</code> and mutable ones such as <code class="highlighter-rouge">Array</code>, <code class="highlighter-rouge">Object</code>, etc.?</p>
<p>That’s exactly what PureScript and most other functional programming languages do.</p>
<p>Let’s see what that looks like in practice:</p>
<blockquote>
<h3 id="code-purescript-good">Code: PureScript (good)</h3>
<p>We start out with the same functionality as the first TypeScript example:</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
</pre></td><td class="code"><pre><span class="kr">type</span> <span class="kt">Team</span> <span class="o">=</span> <span class="p">{</span> <span class="n">numWorldCupTitles</span> <span class="o">::</span> <span class="kt">Int</span><span class="p">,</span> <span class="n">country</span> <span class="o">::</span> <span class="kt">String</span> <span class="p">}</span>
<span class="n">teams</span> <span class="o">::</span> <span class="kt">Array</span> <span class="kt">Team</span>
<span class="n">teams</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Italy"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"France"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Uruguay"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Spain"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Argentina"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Germany"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"England"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">5</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Brazil"</span> <span class="p">}</span>
<span class="p">]</span>
<span class="n">sortByNumTitles</span> <span class="o">::</span> <span class="kt">Array</span> <span class="kt">Team</span> <span class="o">-></span> <span class="kt">Array</span> <span class="kt">Team</span>
<span class="n">sortByNumTitles</span> <span class="n">ts</span> <span class="o">=</span> <span class="kt">Array</span><span class="o">.</span><span class="n">sortBy</span>
<span class="p">(</span><span class="nf">\</span><span class="n">a</span> <span class="n">b</span> <span class="o">-></span> <span class="n">compare</span> <span class="n">b</span><span class="o">.</span><span class="n">numWorldCupTitles</span> <span class="n">a</span><span class="o">.</span><span class="n">numWorldCupTitles</span><span class="p">)</span> <span class="n">ts</span>
<span class="n">main</span> <span class="o">::</span> <span class="n">forall</span> <span class="n">eff</span><span class="o">.</span> <span class="kt">Eff</span> <span class="p">(</span><span class="n">console</span> <span class="o">::</span> <span class="kt">CONSOLE</span> <span class="o">|</span> <span class="n">eff</span><span class="p">)</span> <span class="kt">Unit</span>
<span class="n">main</span> <span class="o">=</span> <span class="kr">do</span>
<span class="kr">let</span> <span class="n">teamsByNumTitles</span> <span class="o">=</span> <span class="n">sortByNumTitles</span> <span class="n">teams</span>
<span class="n">maybeTopTeam</span> <span class="o">=</span> <span class="n">teamsByNumTitles</span> <span class="o">!!</span> <span class="mi">0</span>
<span class="kr">case</span> <span class="n">maybeTopTeam</span> <span class="kr">of</span>
<span class="kt">Just</span> <span class="n">topTeam</span> <span class="o">-></span>
<span class="kt">Console</span><span class="o">.</span><span class="n">log</span> <span class="p">(</span><span class="s">"Top team: "</span> <span class="o"><></span> <span class="n">topTeam</span><span class="o">.</span><span class="n">country</span> <span class="o"><></span>
<span class="s">" ("</span> <span class="o"><></span> <span class="n">show</span> <span class="n">topTeam</span><span class="o">.</span><span class="n">numWorldCupTitles</span> <span class="o"><></span> <span class="s">")"</span><span class="p">)</span>
<span class="kt">Nothing</span> <span class="o">-></span> <span class="kt">Console</span><span class="o">.</span><span class="n">log</span> <span class="s">"No top team found."</span></pre></td></tr></tbody></table></code></pre></figure>
<p><em>Note: The PureScript’s <code class="highlighter-rouge">array !! index</code> translates to <code class="highlighter-rouge">array[index]</code> in JavaScript. Similarly to <code class="highlighter-rouge">Array.find</code> from Part One, it returns <code class="highlighter-rouge">Maybe a</code> to indicate that there is no result (<code class="highlighter-rouge">Nothing</code>) when we access an out of bounds index. That’s why we are reminded to handle both, the <code class="highlighter-rouge">Just a</code> and <code class="highlighter-rouge">Nothing</code>, cases.</em></p>
<p>Running the PureScript program above logs the expected result:</p>
<!-- prettier-ignore-start -->
<div class="correct highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Top team: Brazil (5)
</code></pre></div> </div>
<!-- prettier-ignore-end -->
</blockquote>
<p>Next, we expand the program to create an alphabetical list of all World Cup winners just like we did with TypeScript.</p>
<blockquote>
<h3 id="code-purescript-still-good">Code: PureScript (still good)</h3>
<p>We add the function to sort teams alphabetically by country:</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">31
32
33
</pre></td><td class="code"><pre><span class="n">sortByCountry</span> <span class="o">::</span> <span class="kt">Array</span> <span class="kt">Team</span> <span class="o">-></span> <span class="kt">Array</span> <span class="kt">Team</span>
<span class="n">sortByCountry</span> <span class="n">ts</span> <span class="o">=</span> <span class="kt">Array</span><span class="o">.</span><span class="n">sortBy</span>
<span class="p">(</span><span class="nf">\</span><span class="n">a</span> <span class="n">b</span> <span class="o">-></span> <span class="n">compare</span> <span class="n">a</span><span class="o">.</span><span class="n">country</span> <span class="n">b</span><span class="o">.</span><span class="n">country</span><span class="p">)</span> <span class="n">ts</span></pre></td></tr></tbody></table></code></pre></figure>
<p>Then we introduce the <code class="highlighter-rouge">teamsByCountry</code> constant:</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">37
38
</pre></td><td class="code"><pre> <span class="kr">let</span> <span class="n">teamsByNumTitles</span> <span class="o">=</span> <span class="n">sortByNumTitles</span> <span class="n">teams</span>
<span class="n">teamsByCountry</span> <span class="o">=</span> <span class="n">sortByCountry</span> <span class="n">teams</span></pre></td></tr></tbody></table></code></pre></figure>
<p>We run the program and still get the expected result as <code class="highlighter-rouge">teams</code> was not mutated by <code class="highlighter-rouge">Array.sortBy</code>:</p>
<!-- prettier-ignore-start -->
<div class="success highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Top team: Brazil (5)
</code></pre></div> </div>
<!-- prettier-ignore-end -->
</blockquote>
<section class="conclusion">
<h2 id="conclusion">Conclusion</h2>
<p>Abandon the distinction between values and references and treat everything as immutable values.</p>
<p>Embracing a functional programming language such as PureScript will automatically guide you to <a href="https://blog.codinghorror.com/falling-into-the-pit-of-success/">The Pit of Success</a>, where every value is immutable by default and functions return immutable data.</p>
<p>This means adding new code or changing existing one will not introduce regressions caused by mutation related side-effects.</p>
</section>
<hr />
<p>Enjoyed this post? <a href="https://twitter.com/gasi">Follow me</a> on Twitter <a href="https://twitter.com/gasi">@gasi</a> to learn when the next one is out.</p>
<hr />
<h3 id="notes">Notes</h3>
<ul>
<li>In JavaScript and TypeScript, we can prevent the error above by first manually creating a shallow copy of the input array using <code class="highlighter-rouge">Array::slice</code> before calling <code class="highlighter-rouge">Array::sort</code> on it, but this takes some experience and diligence to do every time:</li>
</ul>
<blockquote>
<h3 id="code-typescript-fixed">Code: TypeScript (fixed)</h3>
<figure class="highlight"><pre><code class="language-typescript" data-lang="typescript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">sortByNumTitles</span> <span class="o">=</span> <span class="p">(</span><span class="nx">teams</span><span class="p">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">Team</span><span class="o">></span><span class="p">):</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">Team</span><span class="o">></span> <span class="o">=></span>
<span class="nx">teams</span><span class="p">.</span><span class="nx">slice</span><span class="p">().</span><span class="nx">sort</span><span class="p">(</span>
<span class="c1">// ^^^^^^^</span>
<span class="p">(</span><span class="nx">a</span><span class="p">:</span> <span class="nx">Team</span><span class="p">,</span> <span class="nx">b</span><span class="p">:</span> <span class="nx">Team</span><span class="p">)</span> <span class="o">=></span>
<span class="nx">b</span><span class="p">.</span><span class="nx">numWorldCupTitles</span> <span class="o">-</span> <span class="nx">a</span><span class="p">.</span><span class="nx">numWorldCupTitles</span>
<span class="p">);</span>
<span class="kd">const</span> <span class="nx">sortByCountry</span> <span class="o">=</span> <span class="p">(</span><span class="nx">teams</span><span class="p">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">Team</span><span class="o">></span><span class="p">)</span> <span class="o">=></span>
<span class="nx">teams</span><span class="p">.</span><span class="nx">slice</span><span class="p">().</span><span class="nx">sort</span><span class="p">((</span><span class="nx">a</span><span class="p">:</span> <span class="nx">Team</span><span class="p">,</span> <span class="nx">b</span><span class="p">:</span> <span class="nx">Team</span><span class="p">)</span> <span class="o">=></span>
<span class="c1">// ^^^^^^^</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">country</span><span class="p">.</span><span class="nx">localeCompare</span><span class="p">(</span><span class="nx">b</span><span class="p">.</span><span class="nx">country</span><span class="p">)</span>
<span class="p">);</span>
<span class="kd">const</span> <span class="nx">teamsByNumTitles</span> <span class="o">=</span> <span class="nx">sortByNumTitles</span><span class="p">(</span><span class="nx">teams</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">teamsByCountry</span> <span class="o">=</span> <span class="nx">sortByCountry</span><span class="p">(</span><span class="nx">teams</span><span class="p">);</span></pre></td></tr></tbody></table></code></pre></figure>
<p>With this change, the correct result is logged:</p>
<!-- prettier-ignore-start -->
<div class="success highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Top team: Brazil (5)
</code></pre></div> </div>
<!-- prettier-ignore-end -->
</blockquote>
<ul>
<li>An amazing side-effect (no pun intended) of treating everything as value is that PureScript avoids some famous <a href="https://www.destroyallsoftware.com/talks/wat">JavaScript <em>Wat</em> moments</a>:</li>
</ul>
<blockquote>
<h3 id="code-javascript">Code: JavaScript</h3>
<p>Due to the difference between value types (<code class="highlighter-rouge">string</code>, <code class="highlighter-rouge">number</code>, <code class="highlighter-rouge">boolean</code>, etc.) and reference types (<code class="highlighter-rouge">Array</code>, <code class="highlighter-rouge">Object</code>, etc.), equality in JavaScript is not always intuitive:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">$</span> <span class="nx">node</span>
<span class="c1">// Equality works as expected…</span>
<span class="o">></span> <span class="mi">1</span> <span class="o">===</span> <span class="mi">1</span>
<span class="kc">true</span>
<span class="o">></span> <span class="kc">true</span> <span class="o">===</span> <span class="kc">true</span>
<span class="kc">true</span>
<span class="o">></span> <span class="s2">"hello"</span> <span class="o">===</span> <span class="s2">"hello"</span>
<span class="kc">true</span>
<span class="c1">// …until it doesn’t:</span>
<span class="o">></span> <span class="p">[]</span> <span class="o">===</span> <span class="p">[]</span>
<span class="kc">false</span>
<span class="o">></span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="o">===</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span>
<span class="kc">false</span>
<span class="o">></span> <span class="p">{}</span> <span class="o">===</span> <span class="p">{}</span>
<span class="kc">false</span>
<span class="o">></span> <span class="p">{</span><span class="na">a</span><span class="p">:</span> <span class="s2">"b"</span><span class="p">}</span> <span class="o">===</span> <span class="p">{</span><span class="na">a</span><span class="p">:</span> <span class="s2">"b"</span><span class="p">}</span>
<span class="kc">false</span>
</code></pre></div> </div>
</blockquote>
<blockquote>
<h3 id="code-purescript-012">Code: PureScript 0.12</h3>
<p>PureScript, treating all types as values, makes equality intuitive.</p>
<p><em>Note: PureScript doesn’t implicitly coerce types and therefore only needs a single equality operator, namely <code class="highlighter-rouge">==</code>.</em></p>
<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">$</span> <span class="n">pulp</span> <span class="n">repl</span>
<span class="o">></span> <span class="mi">1</span> <span class="o">==</span> <span class="mi">1</span>
<span class="n">true</span>
<span class="o">></span> <span class="n">true</span> <span class="o">==</span> <span class="n">true</span>
<span class="n">true</span>
<span class="o">></span> <span class="s">"hello"</span> <span class="o">==</span> <span class="s">"hello"</span>
<span class="n">true</span>
<span class="o">></span> <span class="kt">[]</span> <span class="o">==</span> <span class="kt">[]</span> <span class="o">::</span> <span class="kt">Array</span> <span class="kt">Number</span>
<span class="n">true</span>
<span class="o">></span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span>
<span class="n">true</span>
<span class="o">></span> <span class="p">{}</span> <span class="o">==</span> <span class="p">{}</span>
<span class="n">true</span>
<span class="o">></span> <span class="p">{</span><span class="n">a</span><span class="o">:</span> <span class="s">"b"</span><span class="p">}</span> <span class="o">==</span> <span class="p">{</span><span class="n">a</span><span class="o">:</span> <span class="s">"b"</span><span class="p">}</span>
<span class="n">true</span>
</code></pre></div> </div>
</blockquote>
http://www.gasi.ch/blog/practical-functional-programming-1PFP: The Billion Dollar Mistake2018-06-29T00:00:00+00:00<p>Welcome to Part One of <em>Practical Functional Programming (PFP)</em>. To learn more about the origin of this series, read the Introduction: <a href="/blog/practical-functional-programming">Practical Functional Programming: Prelude</a>.</p>
<hr />
<p>To get warmed up, let’s talk about one of the classic problems of programming.</p>
<section class="problem">
<h2 id="problem">Problem</h2>
<p>Your app has unexpected runtime errors due to <code class="highlighter-rouge">null</code> (or <code class="highlighter-rouge">undefined</code>.)</p>
</section>
<!-- prettier-ignore-start -->
<blockquote>
<h3 id="background-history">Background: History</h3>
<p><a href="https://en.wikipedia.org/wiki/Tony_Hoare">Tony Hoare</a> famously called <code class="highlighter-rouge">null</code> references his <a href="https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare">Billion Dollar Mistake</a>. In addition to <code class="highlighter-rouge">null</code>, in JavaScript we have <code class="highlighter-rouge">undefined</code> as in the dreaded</p>
<div class="error highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'undefined' is not a function
</code></pre></div> </div>
<!-- prettier-ignore-end -->
</blockquote>
<p>PureScript doesn’t have runtime errors caused by <code class="highlighter-rouge">null</code> references. Let’s see why that is.</p>
<h2 id="example">Example</h2>
<blockquote>
<h3 id="code-javascript-broken">Code: JavaScript (broken)</h3>
<p>When we try to <code class="highlighter-rouge">find</code> an element in an array that doesn’t exist we get <code class="highlighter-rouge">undefined</code> back. Accessing <code class="highlighter-rouge">numWorldCupTitles</code> on <code class="highlighter-rouge">undefined</code> (or <code class="highlighter-rouge">null</code>) throws a runtime error (line 15):</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">teams</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Brazil"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Germany"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Italy"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Uruguay"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Argentina"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"England"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"Spain"</span> <span class="p">},</span>
<span class="p">{</span> <span class="na">numWorldCupTitles</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">country</span><span class="p">:</span> <span class="s2">"France"</span> <span class="p">}</span>
<span class="p">];</span>
<span class="kd">const</span> <span class="nx">switzerland</span> <span class="o">=</span> <span class="nx">teams</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span>
<span class="nx">team</span> <span class="o">=></span> <span class="nx">team</span><span class="p">.</span><span class="nx">country</span> <span class="o">===</span> <span class="s2">"Switzerland"</span>
<span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"Switzerland: "</span> <span class="o">+</span> <span class="nx">switzerland</span><span class="p">.</span><span class="nx">numWorldCupTitles</span><span class="p">);</span>
<span class="c1">// TypeError: Cannot read property 'numWorldCupTitles'</span>
<span class="c1">// of undefined</span></pre></td></tr></tbody></table></code></pre></figure>
</blockquote>
<p>Unfortunately, we won’t know that until we run that particular line of code, either manually or by writing a test first.</p>
<h2 id="cause">Cause</h2>
<p>Accessing properties on <code class="highlighter-rouge">null</code> or <code class="highlighter-rouge">undefined</code> throws a runtime error.</p>
<h2 id="solution">Solution</h2>
<p>What if we ditched <code class="highlighter-rouge">null</code> as a first-class language feature? Or never even introduce it in the first place? That’s exactly what PureScript does.</p>
<blockquote>
<h3 id="code-purescript-broken">Code: PureScript (broken)</h3>
<p>This PureScript program is equivalent to the JavaScript above:</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="code"><pre><span class="n">teams</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">5</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Brazil"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Germany"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Italy"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Uruguay"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Argentina"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"England"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Spain"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"France"</span> <span class="p">}</span>
<span class="p">]</span>
<span class="n">main</span> <span class="o">=</span> <span class="kr">do</span>
<span class="kr">let</span> <span class="n">switzerland</span> <span class="o">=</span> <span class="kt">Array</span><span class="o">.</span><span class="n">find</span> <span class="p">(</span><span class="nf">\</span><span class="n">team</span> <span class="o">-></span>
<span class="n">team</span><span class="o">.</span><span class="n">country</span> <span class="o">==</span> <span class="s">"Switzerland"</span><span class="p">)</span> <span class="n">teams</span>
<span class="kt">Console</span><span class="o">.</span><span class="n">log</span> <span class="p">(</span><span class="s">"Switzerland: "</span> <span class="o"><></span>
<span class="n">show</span> <span class="n">switzerland</span><span class="o">.</span><span class="n">numWorldCupTitles</span><span class="p">)</span></pre></td></tr></tbody></table></code></pre></figure>
<p>Before we can run a PureScript program, it first gets checked by the compiler. The compiler will refuse to compile the program and return the following error:</p>
<!-- prettier-ignore-start -->
<div class="error highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Could not match type
Record
with type
Maybe
</code></pre></div> </div>
<!-- prettier-ignore-end -->
</blockquote>
<p>Above, <code class="highlighter-rouge">Record</code>—think of it as <code class="highlighter-rouge">Object</code> in JavaScript—refers to one of our array entries. We tried to access <code class="highlighter-rouge">numWorldCupTitles</code> on <code class="highlighter-rouge">Record</code> but <code class="highlighter-rouge">Array.find</code> returned <code class="highlighter-rouge">Maybe Record</code> which doesn’t have such a field. The reason we get <code class="highlighter-rouge">Maybe Record</code> instead of <code class="highlighter-rouge">Record</code> is because under the hood, PureScript’s <code class="highlighter-rouge">Array.find</code> has the following type (slightly simplified):</p>
<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Array</span><span class="o">.</span><span class="n">find</span> <span class="o">::</span> <span class="n">forall</span> <span class="n">a</span><span class="o">.</span> <span class="p">(</span><span class="n">a</span> <span class="o">-></span> <span class="kt">Boolean</span><span class="p">)</span>
<span class="o">-></span> <span class="kt">Array</span> <span class="n">a</span>
<span class="o">-></span> <span class="kt">Maybe</span> <span class="n">a</span>
<span class="c1">-- ^^^^^^^</span>
</code></pre></div></div>
<p>We can ignore the beginning and just focus on the bit after the last arrow. That marks the function’s return type: <code class="highlighter-rouge">Maybe a</code>.</p>
<p>The equivalent in TypeScript is:</p>
<!-- prettier-ignore-start -->
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">type</span> <span class="nx">find</span><span class="o"><</span><span class="nx">A</span><span class="o">></span> <span class="o">=</span> <span class="p">(</span><span class="nx">predicate</span><span class="p">:</span> <span class="p">(</span><span class="nx">element</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=></span> <span class="kr">boolean</span><span class="p">)</span>
<span class="o">=></span> <span class="p">(</span><span class="nx">array</span><span class="p">:</span> <span class="nb">Array</span><span class="o"><</span><span class="nx">A</span><span class="o">></span><span class="p">)</span>
<span class="o">=></span> <span class="nx">Maybe</span><span class="o"><</span><span class="nx">A</span><span class="o">></span><span class="p">;</span>
<span class="c1">// ^^^^^^^^</span>
</code></pre></div></div>
<!-- prettier-ignore-end -->
<h3 id="what-is-maybe">What is <code class="highlighter-rouge">Maybe</code>?</h3>
<p><code class="highlighter-rouge">Maybe</code> is a data type to describe whether a value is present or not. Here’s how it’s defined in PureScript:</p>
<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cd">-- | The `Maybe` type is used to represent optional values and</span>
<span class="cd">-- | can be seen as something like a type-safe `null`, where</span>
<span class="cd">-- | `Nothing` is `null` and `Just x` is the non-null value `x`.</span>
<span class="kr">data</span> <span class="kt">Maybe</span> <span class="n">a</span> <span class="o">=</span> <span class="kt">Nothing</span> <span class="o">|</span> <span class="kt">Just</span> <span class="n">a</span>
</code></pre></div></div>
<p>The <code class="highlighter-rouge">a</code> parameter in <code class="highlighter-rouge">Maybe a</code> can refer to any type, e.g. a built-in <code class="highlighter-rouge">String</code>, <code class="highlighter-rouge">Boolean</code>, <code class="highlighter-rouge">Number</code> type, or your own custom <code class="highlighter-rouge">WorldCupTeam</code> type. If the syntax is unfamiliar to you, a very literal interpretation of the above in TypeScript is:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">type</span> <span class="nx">Maybe</span><span class="o"><</span><span class="nx">A</span><span class="o">></span> <span class="o">=</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="s2">"Nothing"</span> <span class="p">}</span> <span class="o">|</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="s2">"Just"</span><span class="p">;</span> <span class="nl">value</span><span class="p">:</span> <span class="nx">A</span> <span class="p">};</span>
</code></pre></div></div>
<p>So what’s special about <code class="highlighter-rouge">Maybe</code>? Well, nothing (no pun intended) really, except that it forces you to be explicit about which values are always required…</p>
<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">name</span> <span class="o">::</span> <span class="kt">String</span>
<span class="n">birthYear</span> <span class="o">::</span> <span class="kt">Number</span>
</code></pre></div></div>
<p>…versus ones that are optional:</p>
<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">streetName</span> <span class="o">::</span> <span class="kt">Maybe</span> <span class="kt">String</span>
<span class="n">annualSalary</span> <span class="o">::</span> <span class="kt">Maybe</span> <span class="kt">Number</span>
</code></pre></div></div>
<p>Based on this, the compiler will let you know if you didn’t handle both cases, <code class="highlighter-rouge">Just</code> and <code class="highlighter-rouge">Nothing</code>. In the example above, we could fix it as follows:</p>
<blockquote>
<h3 id="code-purescript-fixed">Code: PureScript (fixed)</h3>
<p>By adding a <code class="highlighter-rouge">case</code> expression, we can independently handle <code class="highlighter-rouge">Just</code> and <code class="highlighter-rouge">Nothing</code>:</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">20
21
22
23
24
25
26
27
28
29
</pre></td><td class="code"><pre><span class="n">main</span> <span class="o">=</span> <span class="kr">do</span>
<span class="kr">let</span> <span class="n">maybeSwitzerland</span> <span class="o">=</span> <span class="kt">Array</span><span class="o">.</span><span class="n">find</span> <span class="p">(</span><span class="nf">\</span><span class="n">team</span> <span class="o">-></span>
<span class="n">team</span><span class="o">.</span><span class="n">country</span> <span class="o">==</span> <span class="s">"Switzerland"</span><span class="p">)</span> <span class="n">teams</span>
<span class="kr">case</span> <span class="n">maybeSwitzerland</span> <span class="kr">of</span>
<span class="kt">Just</span> <span class="n">switzerland</span> <span class="o">-></span>
<span class="kt">Console</span><span class="o">.</span><span class="n">log</span> <span class="p">(</span>
<span class="s">"Switzerland: "</span> <span class="o"><></span> <span class="n">show</span> <span class="n">switzerland</span><span class="o">.</span><span class="n">numWorldCupTitles</span><span class="p">)</span>
<span class="kr">_</span> <span class="o">-></span> <span class="kt">Console</span><span class="o">.</span><span class="n">log</span> <span class="s">"Switzerland has never won a World Cup."</span>
<span class="c1">-- Switzerland has never won a World Cup.</span></pre></td></tr></tbody></table></code></pre></figure>
<p>If the value of <code class="highlighter-rouge">maybeSwitzerland</code> matches the pattern <code class="highlighter-rouge">Just switzerland</code>, we extract the <code class="highlighter-rouge">switzerland</code> value (a <code class="highlighter-rouge">Record</code>) and log its <code class="highlighter-rouge">numWorldCupTitles</code> value. Otherwise, we log an alternate message.</p>
</blockquote>
<section class="conclusion">
<h2 id="conclusion">Conclusion</h2>
<p>Unhandled <code class="highlighter-rouge">null</code>s and <code class="highlighter-rouge">undefined</code>s can cause unexpected runtime errors.</p>
<p>By adopting language with a sufficiently expressive type system such as PureScript, you can explicitly model the presence and absence of values and enforce handling of all cases while avoiding the problems of <code class="highlighter-rouge">null</code>.</p>
</section>
<hr />
<p>Enjoyed this post? <a href="https://twitter.com/gasi">Follow me</a> on Twitter <a href="https://twitter.com/gasi">@gasi</a> to learn when the next one is out.</p>
<hr />
<h3 id="notes">Notes</h3>
<ul>
<li>
<p>You may think: Doesn’t TypeScript alleviate this problem with <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#null--and-undefined-aware-types"><em>strict null checks</em></a> (<code class="highlighter-rouge">--strictNullChecks</code> compiler option)? You’re right.</p>
<p>However, please keep in mind that this required the TypeScript team to update the compiler and if you were a <a href="https://blogs.msdn.microsoft.com/typescript/2014/04/02/announcing-typescript-1-0/">TypeScript 1.0</a> (released in April 2014) user, you would have had to wait almost two and a half years until <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html">TypeScript 2.0</a> (released in September 2016) to leverage this. Due to its design, PureScript supported this basically from day one.</p>
<p>Future posts will have more examples—<code class="highlighter-rouge">async</code> / <code class="highlighter-rouge">await</code> among others—of how a simple core language with custom operators and a powerful type system allows PureScript developers to solve many issues themselves that require JavaScript or TypeScript developers to wait for their respective compiler—Babel or <code class="highlighter-rouge">tsc</code>—to support.</p>
</li>
<li>
<p>Have you noticed anything strange about the PureScript code listing above, besides the maybe unfamiliar syntax? It has no explicit type definitions. Odd for a post about the power of types, no? Indeed.</p>
<p>One of the many cool things about PureScript (and Haskell) is that it can fully infer all the types in your program. But since types are useful to see when sharing code with your coworkers (or yourself in three months from now), not writing type definitions on top-level definitions results in a compiler warning. Therefore, here’s the whole listing with types added and zero compile warnings:</p>
</li>
</ul>
<blockquote>
<h3 id="code-purescript-full-listing">Code: PureScript (full listing)</h3>
<p>Note the explicitly added type signatures for top-level definitions on lines 11, 13, and 26:</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
</pre></td><td class="code"><pre><span class="kr">module</span> <span class="nn">PFP1WorldCup3</span> <span class="kr">where</span>
<span class="kr">import</span> <span class="nn">Prelude</span>
<span class="kr">import</span> <span class="nn">Control.Monad.Eff</span> <span class="p">(</span><span class="kt">Eff</span><span class="p">)</span>
<span class="kr">import</span> <span class="nn">Control.Monad.Eff.Console</span> <span class="p">(</span><span class="kt">CONSOLE</span><span class="p">)</span>
<span class="kr">import</span> <span class="nn">Control.Monad.Eff.Console</span> <span class="k">as</span> <span class="n">Console</span>
<span class="kr">import</span> <span class="nn">Data.Array</span> <span class="k">as</span> <span class="n">Array</span>
<span class="kr">import</span> <span class="nn">Data.Maybe</span> <span class="p">(</span><span class="kt">Maybe</span><span class="p">(</span><span class="o">..</span><span class="p">))</span>
<span class="kr">type</span> <span class="kt">Team</span> <span class="o">=</span> <span class="p">{</span> <span class="n">numWorldCupTitles</span> <span class="o">::</span> <span class="kt">Int</span><span class="p">,</span> <span class="n">country</span> <span class="o">::</span> <span class="kt">String</span> <span class="p">}</span>
<span class="c1">-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</span>
<span class="n">teams</span> <span class="o">::</span> <span class="kt">Array</span> <span class="kt">Team</span>
<span class="c1">-- ^^^^^^^^^^^^^^^^</span>
<span class="n">teams</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">5</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Brazil"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Germany"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Italy"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Uruguay"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Argentina"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"England"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"Spain"</span> <span class="p">},</span>
<span class="p">{</span> <span class="n">numWorldCupTitles</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">country</span><span class="o">:</span> <span class="s">"France"</span> <span class="p">}</span>
<span class="p">]</span>
<span class="n">main</span> <span class="o">::</span> <span class="n">forall</span> <span class="n">eff</span><span class="o">.</span> <span class="kt">Eff</span> <span class="p">(</span><span class="n">console</span> <span class="o">::</span> <span class="kt">CONSOLE</span> <span class="o">|</span> <span class="n">eff</span><span class="p">)</span> <span class="kt">Unit</span>
<span class="c1">-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</span>
<span class="n">main</span> <span class="o">=</span> <span class="kr">do</span>
<span class="kr">let</span> <span class="n">maybeSwitzerland</span> <span class="o">=</span> <span class="kt">Array</span><span class="o">.</span><span class="n">find</span> <span class="p">(</span><span class="nf">\</span><span class="n">team</span> <span class="o">-></span>
<span class="n">team</span><span class="o">.</span><span class="n">country</span> <span class="o">==</span> <span class="s">"Switzerland"</span><span class="p">)</span> <span class="n">teams</span>
<span class="kr">case</span> <span class="n">maybeSwitzerland</span> <span class="kr">of</span>
<span class="kt">Just</span> <span class="n">switzerland</span> <span class="o">-></span>
<span class="kt">Console</span><span class="o">.</span><span class="n">log</span> <span class="p">(</span>
<span class="s">"Switzerland: "</span> <span class="o"><></span> <span class="n">show</span> <span class="n">switzerland</span><span class="o">.</span><span class="n">numWorldCupTitles</span><span class="p">)</span>
<span class="kr">_</span> <span class="o">-></span> <span class="kt">Console</span><span class="o">.</span><span class="n">log</span> <span class="s">"Switzerland has never won a World Cup."</span>
<span class="c1">-- Switzerland has never won a World Cup.</span></pre></td></tr></tbody></table></code></pre></figure>
</blockquote>
<hr />
<p><em>Thanks to Aseem, Boris, Gerd, Matt, Shaniece, and Stephanie for reading drafts of this.</em></p>
http://www.gasi.ch/blog/practical-functional-programmingPractical Functional Programming: Prelude2018-06-28T00:00:00+00:00<section class="lede">
<p>This series is a <em>practical</em> guide to applying techniques from functional programming (FP) to:</p>
<ul>
<li>Avoid common errors</li>
<li>Translate your designs into code using types</li>
<li>Construct software that can evolve</li>
</ul>
<blockquote>
<h2 id="tldr">tl;dr</h2>
<p>Want to jump straight in? These posts have been published so far:</p>
<ul>
<li><a href="/blog/practical-functional-programming-1/">PFP: The Billion Dollar Mistake</a></li>
<li><a href="/blog/practical-functional-programming-2/">PFP: Sorting Things Out</a></li>
</ul>
</blockquote>
</section>
<h2 id="you">You</h2>
<p>Are you a web developer using JavaScript—maybe even <a href="https://www.typescriptlang.org/">TypeScript</a> or <a href="https://flow.org/en/">Flow</a>—and feel like you still too often run into problems with…</p>
<ul>
<li>…unexpected runtime errors?</li>
<li>…gnarly parts of code that become harder to maintain over time?</li>
<li>…new features breaking existing parts of your app?</li>
</ul>
<p>Or: Have you grasped the basics of functional programming and are wondering how to apply that knowledge in real-world situations?</p>
<p>If you’ve answered <em>yes</em> to any of these, keep on reading.</p>
<h2 id="me">Me</h2>
<p>I am a developer with 10 years of experience writing software professionally and 20 years of doing it for fun.</p>
<p>In all those years, I’ve used many different programming languages. Out of all of them, functional programming languages broadened my perspective the most. Learning and using them made me a more skilled and confident developer.</p>
<h2 id="journey">Journey</h2>
<p>It was the Saturday after Thanksgiving in 2015. Ahead of me was a 31 hour train ride back home, from Santa Barbara, CA, to Seattle, WA, on the <em>Coast Starlight</em>. Once comfortable in my seat, I decided it was time to finally learn Haskell and discover what FP is all about. I’ve attempted it twice before: First in 2008 when I <a href="/blog/functional-actionscript-1/">was</a> <a href="/blog/functional-actionscript-2/">originally</a> <a href="/blog/functional-actionscript-3/">introduced</a> to Haskell in my <em><a href="http://archiv.infsec.ethz.ch/education/ss08/fmfp/index.html">Formal Methods and Functional Programming</a></em> class at ETH. The second time was in 2009, when I tried to write a <a href="https://github.com/gasi/deepzoom.hs">DeepZoom image tiler in Haskell</a>. On neither occasion did it click for me. But I knew this time it would!</p>
<p>What did I do differently? I picked a project that was both real, unlike toy problems I’ve attempted in the past, and one I understood well — two big boosts for my motivation. My choice: <a href="http://zoomhub.net/">ZoomHub</a>, a side project that a few friends from Microsoft and I started.</p>
<p>ZoomHub is a place to share and view high-resolution images. It provides a small REST API for developers to publish their own images. We wrote it in CoffeeScript on top of Node.js, a stack we were familiar with from our day jobs. After a while, life got in the way and my friends’ priorities changed. I still wanted to see the project through but it was hard to invest large chunks of my free time because I didn’t learn anything new technically. This is what made the project a suitable starting ground for learning Haskell.</p>
<p>What began on the train with a port of the REST API from Node.js to Haskell, using the novel <a href="http://haskell-servant.readthedocs.io/en/stable/">servant</a> library, reached its first milestone just a few months later when in April, 2016, I shipped the Haskell implementation of <a href="http://zoomhub.net/">ZoomHub</a> to production. Today, ZoomHub serves thousands of page views a month and I can sleep soundly as it rarely has any issues.</p>
<h2 id="motivation">Motivation</h2>
<p>Learning functional programming has been a very rewarding journey. But I’d lie if I didn’t mention that the road was paved with frustration and late nights deciphering dense error messages. What kept me going was a strong belief that there must a more sustainable and enjoyable way to write large programs than imperative and object-oriented languages offered. While not a silver bullet by any means, I found a lot of good in functional programming.</p>
<p>But if all you know is that FP languages have weird syntax and use intimidating math terms, you might ask yourself: What’s the point of leaving behind what I’m comfortable with and going through the struggle of learning this new way of doing things? That’s what this series is about. Presenting short, but realistic, examples of the benefits you gain by adopting FP and types.</p>
<p>We’ll learn why the combination of features such as immutability, algebraic data types (ADT), value types, purity and effects, etc. results in a whole that is greater than the sum of its parts. I will show how each one can be used to solve a particular problem we have in modern web development using JavaScript.</p>
<p>Beginners can find many <a href="#resources">valuable resources</a> about the basics of FP and learning a particular language. Likewise, for experts, there are many discussions about advanced topics such as monad transformers, type-level programming, and category theory. However, they can be intimidating and, more importantly, distracting for someone new to FP as they are—based on personal experience—not required to build useful applications.</p>
<p>This observation is touched upon in <a href="https://patrickmn.com/software/the-haskell-pyramid/">The Haskell Pyramid</a>: Learning enough Haskell to be productive takes far less effort than people imagine. Though if you follow Haskell experts on social media, it seems daunting to even begin the journey.</p>
<p>Wanting to become productive myself, I struggled to find good articles about intermediate level topics on types and functional programming and how they can be effectively applied in real-world situations. This reminds me of when I first learned OOP. Most examples were about <code class="highlighter-rouge">Ball</code> and <code class="highlighter-rouge">Ball::bounce()</code>, or <code class="highlighter-rouge">Animal</code>, <code class="highlighter-rouge">Dog</code>, <code class="highlighter-rouge">Cat</code>, and <code class="highlighter-rouge">Animal::sayHello()</code>, whereas I was looking to figure out how OOP could help me separate my data fetching from my presentation logic.</p>
<p>My goal for this series is to bridge that gap and show that it doesn’t take much to become productive with functional programming and using types as a tool to build more robust applications.</p>
<p>So far, I’ve shared my passion for functional programming and type-driven development with my past three teams:</p>
<ul>
<li><strong>FiftyThree:</strong> <em><a href="/publications/functional-programming-fun-profit-daniel-gasienica.pdf">Functional Programming for Fun & Profit</a></em></li>
<li><strong>Shutterstock:</strong> <em>Type-Driven Development</em>. Internally presented a case study about six bugs we’ve encountered in production and how using a functional programming language and types could have prevented them and made our app more maintainable.</li>
<li><strong>Signal:</strong> Implemented proof of concept for integrating <a href="https://github.com/gasi/Signal-Desktop/pull/2">PureScript with Signal Desktop</a>. It demonstrates how explicitly defining your domain types, e.g. <code class="highlighter-rouge">Conversation</code>, <code class="highlighter-rouge">Message</code>, <code class="highlighter-rouge">Attachment</code>, etc., can help you better understand your app’s behavior and how to safely extend it.</li>
</ul>
<p><em>Practical Functional Programming</em> is about sharing what I learned more broadly.</p>
<h2 id="structure">Structure</h2>
<p>Each post in this series will consist of a description of a <em>Problem</em>, an <em>Example</em>, a <em>Cause</em> where helpful, and then a <em>Solution</em> to the problem using techniques from functional programming. Finally, we’ll wrap it up with a <em>Conclusion</em>.</p>
<p>All posts aim to stand on their own, meaning you can jump between the various parts of the series based on your interest.</p>
<h2 id="code">Code</h2>
<p>To make it more approachable for web developers, I will write examples in <a href="http://www.purescript.org/">PureScript</a> and compare them to JavaScript or TypeScript, to make the connection to something you may already be familiar with.</p>
<p><a href="http://www.purescript.org/">PureScript</a> is a purely functional, strongly-typed language that compiles to JavaScript. Having the benefit of being newer, PureScript addresses some of the idiosyncrasies of Haskell. Not only can it be compiled to JavaScript, it also plays nicely with existing JavaScript. This makes it an exceptional tool for web developers to build large and reliable applications.</p>
<section class="conclusion">
<h2 id="next-steps">Next Steps</h2>
<p>Dive into Part One: <a href="/blog/practical-functional-programming-1/">Practical Functional Programming: The Billion Dollar Mistake</a>.</p>
<p><em>Optional: If you want to actively follow along, set up your environment by following the <a href="https://github.com/purescript/documentation/blob/master/guides/Getting-Started.md">Getting Started with PureScript</a> instructions. Learn the basics of the language by reading the excellent <a href="https://leanpub.com/purescript/read">PureScript by Example</a> by <a href="https://twitter.com/paf31">Phil Freeman</a>, its creator.</em></p>
</section>
<hr />
<p><em>Thanks to Aseem, Boris, Gerd, Matt, Shaniece, and Stephanie for reading drafts of this.</em></p>
<hr />
<p><a name="resources"></a></p>
<h3 id="resources">Resources</h3>
<ul>
<li><a href="http://haskellbook.com/">Haskell Book</a></li>
<li><a href="http://learnyouahaskell.com/">Learn You a Haskell for Great Good!</a></li>
<li><a href="https://leanpub.com/purescript/read">PureScript by Example</a></li>
</ul>
http://www.gasi.ch/blog/thingdomThe Thingdom2011-09-12T00:00:00+00:00<p>Together with <a href="http://aseemk.com/">Aseem</a>, I am proud to finally share what we’ve been
working on for the greater part of this year.</p>
<h2 id="background">Background</h2>
<p>How come there’s a place where you can <a href="http://twitter.com/gasi">follow your interests</a>,
<a href="http://facebook.com/daniel.gasienica">connect with your friends & family</a>, <a href="http://flickr.com/photos/gasi">share your photos</a>,
<a href="http://www.last.fm/user/follkommen">share the music you like</a>, <a href="http://www.gasi.ch/blog/">share your thoughts</a>, but none
where you can <em>share the things you have and want</em>?</p>
<p>Well, there is now. Enter <a href="http://www.thethingdom.com/">The Thingdom</a>.</p>
<h2 id="what-is-the-thingdom">What is The Thingdom?</h2>
<p>The Thingdom is a place where I can share <a href="http://thethingdom.com/gasi/photography">my photography gear</a>, <a href="http://www.thethingdom.com/gasi/movies-tv">my movie
collection</a>, <a href="http://www.thethingdom.com/gasi/books">my book shelf</a>, the <a href="http://www.thethingdom.com/182-Nilfisk-Backuum">ultimate vacuum cleaner</a>,
find out <a href="http://www.thethingdom.com/tg/wants">what my brother wants</a> for his birthday, or what <a href="http://www.thethingdom.com/gz">my</a>
<a href="http://www.thethingdom.com/424f">friends</a> think about <a href="http://www.thethingdom.com/2118-Deus-Ex-Human-Revolution#discussion">a particular video game</a>.</p>
<p>I can’t wait to see what you are going to use it for!</p>
<h2 id="call-to-action">Call to Action</h2>
<p>Go have a look around, sign up, <a href="http://www.thethingdom.com/gasi">follow me</a>, <a href="mailto:feedback@thethingdom.com">let us
know what you think</a> and spread the word!</p>
<h3 id="thanks">Thanks</h3>
<p class="footnote">I’d like to say thanks to our friends and
family for the support and encouragement during these exciting and sometimes
difficult times. Please believe me when I say <a href="http://www.thethingdom.com/">The Thingdom</a> wouldn’t
be what it is now without the invaluable & critical feedback from our guinea
pigs, eh, beta users, with special shout-out to <a href="http://www.thethingdom.com/424f">Boris</a>,
<a href="http://www.thethingdom.com/gz">Gerd</a> & <a href="http://www.thethingdom.com/frida">Frida</a>.</p>
<p class="footnote">Last but not least, thanks to my dear friend
and co-founder <a href="http://aseemk.com/">Aseem</a>, who trusted me to join him me on this insane
rollercoaster ride and for being there with me on all the ups & downs of our
journey! I love you, buddy! :)</p>
http://www.gasi.ch/blog/live-labsLive Labs2010-11-01T00:00:00+00:00<h2 id="remember-remember-the-fifth-of-november">Remember, Remember the Fifth of November</h2>
<p>Live Labs is closing its doors on Friday, November 5, 2010. In acknowledgement
of the innovative work of the people who worked here, I’d like to highlight
once more the biggest achievements from the past five years:</p>
<h1 id="seadragon">Seadragon</h1>
<iframe class="youtube-player" type="text/html" width="500" height="400" src="http://www.youtube.com/embed/0ra5tp7K--I" frameborder="0">
</iframe>
<h1 id="photosynth">Photosynth</h1>
<iframe class="youtube-player" type="text/html" width="500" height="306" src="http://www.youtube.com/embed/M-8k8GEGZPM" frameborder="0">
</iframe>
<h1 id="pivot">Pivot</h1>
<iframe class="youtube-player" type="text/html" width="500" height="400" src="http://www.youtube.com/embed/BZuFUZpEZ-A" frameborder="0">
</iframe>
<h2 id="post-mortem">Post Mortem</h2>
<p>The spirit of Live Labs will live on. It will live on in the people who shaped
its history over the past five years and in all the projects that found new homes:
<a href="http://zoom.it">Zoom.it</a>, <a href="http://www.silverlight.net/learn/pivotviewer/">PivotViewer</a>, <a href="http://www.microsoft.com/silverlight/deep-zoom/">Deep Zoom</a>,
<a href="http://seadragon.com/developer/ajax/">Seadragon Ajax</a> and <a href="http://photosynth.net/">Photosynth</a>.</p>
<p><em>Thank you for five amazing years of ideas, inspiration, enthusiasm & support!</em></p>
http://www.gasi.ch/blog/zoom-it-apiZoom.it API2010-08-10T00:00:00+00:00<p>In <a href="http://www.reddit.com/r/technology/comments/cykxj/microsoft_launches_zoomit_free_service_for">case</a> <a href="http://blogs.msdn.com/b/stevecla01/archive/2010/08/05/microsoft-s-live-labs-launches-zoom-it.aspx">you</a> <a href="http://news.ycombinator.com/item?id=1582203">haven’t</a> <a href="http://www.readwriteweb.com/archives/microsoft_introduces_social_lightbox_zoomit_from_live_labs_and_silverlight.php">heard</a>, <a href="http://livelabs.com/">Microsoft Live Labs</a>
has (re-)launched <a href="http://zoom.it/">Zoom.it</a> (formerly <a href="http://seadragon.com/">Seadragon.com</a>), a
free (high-resolution) image sharing service. If you’re a developer, the most
important aspect of the announcement is the <a href="http://api.zoom.it/">Zoom.it API</a>.</p>
<h2 id="why-its-a-big-deal">Why It’s a Big Deal</h2>
<p>Basically, the <a href="http://api.zoom.it/">Zoom.it API</a> lets you convert any image — PDF or
even page — on the web into a <a href="/blog/inside-deep-zoom-1/">multiscale representation</a>, specifically
a <a href="http://msdn.microsoft.com/en-us/library/cc645077(VS.95).aspx">Deep Zoom image</a> or <acronym title="Deep Zoom Image">DZI</acronym>.
Apart from sharing it as the default zooming embed, you can also incorporate
this multiscale resource into your application or web site, using any of the
existing platforms such as <a href="http://www.microsoft.com/silverlight/deep-zoom/">Deep Zoom</a> (Silverlight & Windows Phone 7),
<a href="http://seadragon.com/developer/ajax/">Seadragon Ajax</a> (JavaScript), <a href="http://www.getpivot.com/">Pivot</a> (WPF &
Silverlight), <a href="http://itunes.apple.com/us/app/seadragon-mobile/id299655981?mt=8">Seadragon Mobile</a> (iOS) and <a href="http://openzoom.org">OpenZoom</a> (Flash), all connected through a common, open and
<a href="http://msdn.microsoft.com/en-us/library/cc645077(VS.95).aspx">documented file format</a>.</p>
<p>Finally, this could bring true <em>image streaming</em>, very much like audio & video
streaming, to the web. Personally, I wish that the <a href="http://api.zoom.it/">Zoom.it service</a>
will do the same for high-resolution image sharing as <a href="http://www.youtube.com/watch?v=oHg5SJYRHA0">YouTube</a>
did for video sharing, even if it’s on a smaller scale.<br />
Similarly, it could potentially establish itself as a <a href="http://techcrunch.com/2008/03/12/youtube-the-platform/">platform</a>,
bringing some of <a href="/blog/inline-multiscale-image-replacement/">the benefits I’ve talked about previously</a> to every
author on the web, namely the power & simplicity of publishing the highest
quality image you’ve got, irrespective of file size or target device.</p>
<blockquote class="info">
<h2 id="web-apis-a-brief-introduction">Web APIs: A Brief Introduction</h2>
<p>If you’d like to learn more about this topic, check out the brownbag presentation
I gave at Seadragon last year:<br />
<a href="/publications/web-apis-daniel-gasienica.pdf">Web APIs, Mashups & REST (PDF)</a></p>
</blockquote>
<h2 id="contribution">Contribution</h2>
<p>While unfortunately not currently being a part of the team behind <a href="http://zoom.it/">Zoom.it</a>,
I’d still like to share with you a small contribution of mine:
The <a href="http://openzoom.org/zoomit-as3-sdk/">Zoom.it ActionScript 3 SDK</a> which I’ve implemented over
the course of last weekend, taking advantage of the fantastic
<a href="http://zoom.it/pages/api/">Zoom.it API documentation</a>. It will hopefully fit right in
there along with the <a href="http://zoom.it/pages/api/libraries/">official Zoom.it API helper libraries</a>
for .NET, Silverlight, Windows Phone 7 and the inherent
<a href="http://zoom.it/pages/api/quickstarts/javascript">JavaScript support</a>, e.g. using <a href="http://jquery.com">jQuery</a>.</p>
<p>The <a href="http://openzoom.org/zoomit-as3-sdk/">Zoom.it ActionScript 3 SDK</a> lets you build exciting new applications using <a href="http://adobe.com/flashplatform">Adobe Flash, Flex and AIR</a> on top of the <a href="http://api.zoom.it">Zoom.it service</a>, optionally combining it with the <a href="http://openzoom.org">OpenZoom SDK</a> for super-smooth rendering.</p>
<p>I’d like to point out that the <a href="http://openzoom.org/zoomit-as3-sdk/">Zoom.it ActionScript 3 SDK</a> wouldn’t have been viable if it weren’t for Live Labs’ generous support of very liberal <code>clientaccesspolicy.xml</code> and <code>crossdomain.xml</code> files on <a href="http://api.zoom.it/clientaccesspolicy.xml">all</a> <a href="http://cache.zoom.it/clientaccesspolicy.xml">relevant</a> <a href="http://api.zoom.it/crossdomain.xml">API</a> <a href="http://cache.zoom.it/crossdomain.xml">endpoints</a>, enabling an equal experience for the most commonly used client technologies. Enjoy.</p>
<blockquote class="flash">
<h2 id="zoomit-actionscript-3-sdk">Zoom.it ActionScript 3 SDK</h2>
<p><a href="http://openzoom.org/zoomit-as3-sdk/download/latest/zip">Download Zoom.it ActionScript 3 SDK 0.8 (32KB)</a><br />
<a style="font-weight:normal" href="http://github.com/openzoom/zoomit-as3-sdk">Browse Source</a><br />
<a style="font-weight:normal" href="http://docs.openzoom.org/zoomit-as3-sdk/">Read Documentation & Examples</a><br />
<strong>License:</strong> <a style="font-weight:normal" href="http://www.apache.org/licenses/LICENSE-2.0.html">Apache License, Version 2.0</a></p>
</blockquote>
<blockquote class="info">
<h2>Demo</h2>
<p class="footnote">Browse the front pages of the <a href="http://nytimes.com">New York Times</a>, between August 9 and August 15, depending on your time zone. The widget utilizes the <a href="http://api.zoom.it">Zoom.it API</a> to dynamically create the corresponding multiscale image of the front page and renders it using the OpenZoom SDK.</p>
<a href="/blog/examples/zoom-it-api/" title="Zoom.it NY Times Demo by Daniel Gasienica, on Flickr"><img style="border: 2px solid #999999" src="http://farm5.static.flickr.com/4102/4877662142_f21494697c.jpg" width="500" height="300" alt="Zoom.it NY Times Demo" /></a>
<a href="/blog/examples/zoom-it-api/">View Demo</a> | <a href="/blog/examples/zoom-it-api/source/">View Source</a>
</blockquote>
<h3>Acknowledgements</h3>
<p class="footnote">As an intern on the Seadragon team at Microsoft Live Labs, I was involved in the early design of the <a href="http://api.zoom.it">Zoom.it API</a> (formerly Seadragon.com API) but my deep respect and gratitude go out to the team — <a href="http://twitter.com/aseemk">Aseem</a>, <a href="http://twitter.com/golds711">Goldie</a>, <a href="http://twitter.com/kpsin">Karan</a>, Jyoti, Jesse <em>et al.</em> — who executed on this idea and delivered this truly elegant first embodiment of it.</p>
http://www.gasi.ch/blog/joining-microsoftJoining Microsoft2009-09-09T00:00:00+00:00<p>I am thrilled to announce that I’ll be joining the <a href="http://seadragon.com">Seadragon team</a> at <a href="http://livelabs.com">Microsoft Live Labs</a> as a <em>research intern</em>. Ever since I first watched <a href="http://www.ted.com/index.php/talks/blaise_aguera_y_arcas_demos_photosynth.html">Blaise Agüera y Arcas present Seadragon & Photosynth</a> at <a href="http://ted.com">TED</a>, I knew the Seadragon team was at the cutting edge of changing the way we will interact with vast amounts of visual information in the future.</p>
<blockquote class="flash">
<h2 id="all-good-things-come-to-an-end">All Good Things Come To An End</h2>
<p>I’ve had a blast and learned a lot during my internship at <a href="http://seadragon.com/">Seadragon</a>.
More importantly, I was fortunate enough to meet and spend time with many great
people whom I dearly miss now.</p>
<p><em>The internship ended at the beginning of January and I’ve returned to
Switzerland in February.</em></p>
</blockquote>
<p><a href="http://seadragon.com/">
<img src="http://farm3.static.flickr.com/2617/3898566366_c287c95d04_o.png" width="500" height="320" alt="" />
</a></p>
<p>Getting the chance to be a part of this group of extremely visionary &
talented people is a feeling I cannot describe. I am looking forward to finally
meet some of the people I’ve had contact with over the last months in person,
among them <a href="http://twitter.com/iangilman">Ian</a>, <a href="http://twitter.com/aseemk">Aseem</a>,
<a href="http://twitter.com/benvanik">Ben</a>, <a href="http://twitter.com/mr_yuk">Kevin</a>, <a href="http://blogs.msdn.com/lutzg">Lutz</a>,
<a href="http://twitter.com/modeless">James</a>, <a href="http://twitter.com/billcrow">Bill</a>,
<a href="http://twitter.com/jaysenior">Jay</a>, and all those I haven’t got to know so far.</p>
<p>They are the brilliant minds behind products & projects such as
<a href="http://seadragon.com/developer/silverlight/">Silverlight Deep Zoom</a>, <a href="http://seadragon.com/developer/ajax/">Seadragon Ajax</a>, <a href="http://itunes.apple.com/us/app/seadragon-mobile/id299655981?mt=8">Seadragon Mobile</a>,
<a href="http://seadragon.com/">Seadragon.com</a>, <a href="http://photosynth.net/">Photosynth</a>,
<a href="http://en.wikipedia.org/wiki/JPEG_XR"><strike>WMP</strike> <strike>HD Photo</strike> JPEG XR</a>,
<a href="http://infinitecanvas.appjet.net/">Infinite Canvas</a>, <a href="http://gimmeshiny.com/">Gimme Shiny!</a> and <a href="http://code.google.com/p/pspplayer/">PSP Player</a>.</p>
<p>Personally I see working at Microsoft as a great opportunity to work on
<a href="/blog/zoomable-user-interfaces/">all</a> <a href="/blog/tandem/">the</a> <a href="http://openzoom.org/">things</a> <a href="/blog/openzoom-description-format/">I’m</a> <a href="http://tandem.gasi.ch/">passionate</a>
<a href="/blog/gigapan-mobile/">about</a> and potentially reach millions of people around the world.</p>
<p>Stay tuned as I’ll share my stories about settling down in the <a href="seattle-maps">Seattle area</a>
and hopefully a bit about the stuff I’ll be working on!</p>
<p>Yours,<br />
Daniel</p>
<h3 id="ps-openzoom">P.S. OpenZoom</h3>
<p class="footnote">As of next week active development of the <a href="/blog/openzoom-sdk/">OpenZoom SDK</a>
on my part will be put on hold for the duration of my internship. I consider the
features and components already included (<a href="http://www.openzoom.org/sdk/download/latest/zip">0.4.2.1 release</a>)
fairly stable and as many people have shown it’s very well suited to be used
in real-world projects. Apart from that, I’ll still try my best to answer questions
and help out on the <a href="http://www.openzoom.org/go/community">OpenZoom community</a>
whenever possible.</p>
<p class="footnote">I’d like to point that I made sure that the status of the
<a href="http://openzoom.org/">OpenZoom</a> project itself is not in any way affected by this announcement.
It’s still open source and free. Actually, I’d like to encourage other people to
<a href="http://github.com/openzoom/sdk">fork it</a> and continue where I left off.</p>
http://www.gasi.ch/blog/openzoom-sdkOpenZoom SDK2009-08-17T00:00:00+00:00<p>After many months of hard, fun, frustrating & rewarding work, I am happy to announce the first public release of the <strong>OpenZoom SDK</strong>.</p>
<h2>What Is the OpenZoom SDK?</h2>
<p>The <strong>OpenZoom SDK</strong> is a <em>free & open source toolkit</em> for delivering <em>high-resolution images</em> and <em>Zoomable User Interfaces (ZUIs)</em> to the web and desktop. It is built on top of the <a href="http://www.adobe.com/flashplatform/">Adobe Flash Platform</a> which means you can use it in Flash, Flex, ActionScript & AIR projects equally well.</p>
<h2>Showcase</h2>
<blockquote class="info">
<h2>Gigapixel Photography</h2>
<p><a href="http://gigapixelphotography.com" title="Gigapixel Photography Powered By OpenZoom"><img style="border: 2px solid #CCC;" src="http://farm3.static.flickr.com/2460/3828368948_3729196555.jpg" width="500" height="300" alt="Gigapixel Photography Powered By OpenZoom" /></a>
<a href="http://gigapixelphotography.com/">GigapixelPhotography.com</a> — Dugg almost 5000 times on <a href="http://digg.com/arts_culture/Extreme_Gigapixel_Photo_Look_into_everyone_s_apartment">digg.com</a>.</p>
<p class="footnote">Copyright 2009, <a href="http://gigapixelphotography.com/">Gigapixel Photography</a></p>
</blockquote>
<blockquote class="info">
<h2>Alba Water</h2>
<p><a href="http://www.albawater.com.vn" title="Alba Water Powered By OpenZoom"><img style="border: 2px solid #CCC;" src="http://farm3.static.flickr.com/2480/3828349350_532a27d90a.jpg" width="500" height="300" alt="Alba Water Powered By OpenZoom" /></a>
<a href="http://albawater.com.vn/">AlbaWater.com.vn</a> — Stunning microsite for Alba Water.</p>
<p class="footnote">Copyright 2009, <a href="http://albawater.com.vn/">Alba Water</a></p>
</blockquote>
<blockquote class="info">
<h2>Is This Your Luggage?</h2>
<p><a href="/blog/openzoom-is-this-your-luggage/" title="Is This Your Luggage? Powered By OpenZoom"><img style="border: 2px solid #CCC;" src="http://farm4.static.flickr.com/3594/3474363339_fafdee1f23.jpg" width="500" height="347" alt="Is This Your Luggage? Powered By OpenZoom" /></a>
Remix of the famous <a href="http://isthisyourluggage.com/">IsThisYourLuggage.com</a>. Powered by OpenZoom.</p>
<p class="footnote">Copyright 2009, <a href="http://isthisyourluggage.com/">IsThisYourLuggage.com</a></p>
</blockquote>
<h2>Development</h2>
<p>While the development of the <strong>OpenZoom SDK</strong> originally started in <a href="http://subversion.tigris.org/">Subversion</a> on <a href="http://open-zoom.googlecode.com/">Google Code</a><sup><a href="#cite-1">1</a></sup>, I have meanwhile successfully and happily migrated it to <a href="http://git-scm.com/">Git</a> and <a href="http://github.com/openzoom/sdk">GitHub</a>.</p>
<p>Having the <strong>OpenZoom SDK</strong> hosted on <a href="http://github.com/openzoom/sdk">GitHub</a> means you can <a href="http://github.com/openzoom/sdk">browse the history</a>, <a href="http://github.com/openzoom/sdk/fork">fork the project</a>, <a href="http://github.com/openzoom/sdk/toggle_watch">watch its progress</a>, <a href="http://github.com/openzoom/sdk">download tags & branches</a> or just quietly enjoy the power & simplicity of the <a href="http://git-scm.com/">Git version control system</a>.</p>
<h2>Community</h2>
<p>The <strong>OpenZoom</strong> project has a thriving community over at <a href="http://openzoom.org/go/community">Get Satisfaction</a> with <strong>82+ topics posted</strong>, <strong>95+ people participating</strong> and <em>me answering questions and addressing bugs in matters of hours</em>.</p>
<p>Unless there’s something confidential about your project or you plan to ask me for my bank account for donations, I promise you’ll get a quicker answer on <a href="http://openzoom.org/go/community">Get Satisfaction</a> than by <a href="mailto:daniel@gasienica.ch">emailing</a> me.</p>
<h2>License</h2>
<p>In order to encourage a broad (ab)use of the <strong>OpenZoom SDK</strong>, I’ve added two additional licenses under which you can use it. The <strong>OpenZoom SDK</strong> is now licensed under <a href="http://www.mozilla.org/MPL/MPL-1.1.txt">MPL 1.1</a>/<a href="http://www.gnu.org/licenses/gpl-3.0.txt">GPL 3</a>/<a href="http://www.gnu.org/licenses/lgpl-3.0.txt">LGPL 3</a>.</p>
<p>This licensing model was adopted from the famous <a href="http://mozilla.org/">Mozilla foundation</a> and their products I love so much: <strong>Firefox</strong> and <strong>Thunderbird</strong>.</p>
<p>Although I’m not a big fan of lawyers either, I strongly recommend you carefully read the licensing terms of the available licenses, choose the one that suits you & your project best and consult with a lawyer if you have questions. <a href="http://www.youtube.com/watch?v=xoqUwyHseg4">You should never assume anything</a>.</p>
<p>With the current licensing model I want to encourage all kinds of projects & products powered by the <strong>OpenZoom SDK</strong> while ensuring the constant evolution of <strong>OpenZoom SDK</strong>, including third-party improvements.</p>
<p>Should your project require a different kind of licensing scheme, please contact me at <a href="mailto:daniel@gasienica.ch">daniel@gasienica.ch</a>.</p>
<h3>Attribution</h3>
<p>Besides publishing any source code as required by the license you chose, please attribute your use of the <strong>OpenZoom SDK</strong> by creating a context menu entry with the caption <code>About OpenZoom...</code> linked to <a href="http://openzoom.org/">http://openzoom.org/</a>. Thanks.</p>
<blockquote class="flash">
<h2 id="download--documentation">Download & Documentation</h2>
<p>Get the latest <strong>OpenZoom SDK</strong>, including source code, documentation, SWC
library and 14 examples for Flash CS3, Flash CS4, Flex & ActionScript projects.</p>
<p><a href="http://openzoom.org/sdk/download/latest/zip/">Download OpenZoom SDK (ZIP)</a></p>
<p>After you’ve downloaded the SDK, please read the <a href="http://www.openzoom.org/sdk/api/">OpenZoom SDK API documentation</a>.</p>
</blockquote>
<h3>Follow</h3>
<p>Keep up with the development progress of the <strong>OpenZoom SDK</strong> through <a href="http://twitter.com/OpenZoom">Twitter</a> and <a href="http://www.facebook.com/pages/OpenZoom/53991200768">Facebook</a>:</p>
<ul>
<li><a href="http://twitter.com/OpenZoom">Follow @OpenZoom on Twitter</a></li>
<li><a href="http://www.facebook.com/pages/OpenZoom/53991200768">Become a Fan of OpenZoom</a></li>
</ul>
<blockquote class="request">
<h2>Donations</h2>
<p>Support the future development of the <strong>OpenZoom SDK</strong> and other <strong>OpenZoom</strong> projects with a donation:</p>
<div align="center">
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHJwYJKoZIhvcNAQcEoIIHGDCCBxQCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYBlJrLVQNzJkEvF2/Dw0d+6SarPhzRcYMKtMBiYY6a8vD7sAsaCZuIkUoTww7twJLSa/c0RwjBMbEJaYMJg/FJqFE9JfrLvIMVdpD+whoWVtsI6gr/t8BNzTjaCwpnkJ7HE6leyobVci9mmva1dKYc8sQqUCX0q8lGnQrvUFDwVbTELMAkGBSsOAwIaBQAwgaQGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQImEOH7e5jr6OAgYC/791t/6xdPhTlFyXafRF+YhpfBm76KXTwYIZ++mGZjxM5BthNF3x+62CybTtxokjv9Fky6plGQgyxh0v/ap4mt7fITwKHg2zHu5yXtTUG4a8Nv8wr1H5BYcGi/uNnzb77fKLPNf84/vtrtXPtLS5Js6y1MvHcUi8J5igkUxFdbaCCA4cwggODMIIC7KADAgECAgEAMA0GCSqGSIb3DQEBBQUAMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTAeFw0wNDAyMTMxMDEzMTVaFw0zNTAyMTMxMDEzMTVaMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwUdO3fxEzEtcnI7ZKZL412XvZPugoni7i7D7prCe0AtaHTc97CYgm7NsAtJyxNLixmhLV8pyIEaiHXWAh8fPKW+R017+EmXrr9EaquPmsVvTywAAE1PMNOKqo2kl4Gxiz9zZqIajOm1fZGWcGS0f5JQ2kBqNbvbg2/Za+GJ/qwUCAwEAAaOB7jCB6zAdBgNVHQ4EFgQUlp98u8ZvF71ZP1LXChvsENZklGswgbsGA1UdIwSBszCBsIAUlp98u8ZvF71ZP1LXChvsENZklGuhgZSkgZEwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAgV86VpqAWuXvX6Oro4qJ1tYVIT5DgWpE692Ag422H7yRIr/9j/iKG4Thia/Oflx4TdL+IFJBAyPK9v6zZNZtBgPBynXb048hsP16l2vi0k5Q2JKiPDsEfBhGI+HnxLXEaUWAcVfCsQFvd2A1sxRr67ip5y2wwBelUecP3AjJ+YcxggGaMIIBlgIBATCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTA5MDgxNzAxMDUwMVowIwYJKoZIhvcNAQkEMRYEFNO8OQvBCvGKQlGkfb8BZoAFdiZ8MA0GCSqGSIb3DQEBAQUABIGAPXFB11JQ9VrfWXu+yD0uWu7q5wTCijEaw0qYxrcI8bDta+AZDKXHNxFESkMws9Tz0+agNi7ChJpcWDXyniCI5J3eAnIix4eQUUKPftUCpOzjbXt6NMco4Jo74S16Gkz0C0t8nGki0OkPx609IIWdHfxJUnqw1EPE/++zXE6eeTI=-----END PKCS7-----" />
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" />
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />
</form>
</div>
</blockquote>
<h3>Stats</h3>
<script type="text/javascript" src="http://www.ohloh.net/p/22041/widgets/project_basic_stats.js"></script>
<h3>Footnote</h3>
<p class="footnote"><strike><a name="cite-1">[1]</a> Thanks, Google, for not ever replying to my two emails and one tweet regarding my deep wish to get the much deserved <strong>openzoom</strong> project handle which is reserved by a #$*&#@ project on SourceForge (link purposely not included) which hasn't even had a commit in four years.</strike></p>
<p><br /></p>
http://www.gasi.ch/blog/openzoom-is-this-your-luggageOpenZoom, Is This Your Luggage?2009-04-26T00:00:00+00:00<p>Lately I’ve been working a lot on very technical stuff around <a href="http://www.openzoom.org/">OpenZoom</a>.
I desperately needed a break. Then I stumbled upon <a href="http://www.isthisyourluggage.com/">Is This Your Luggage</a>
and I instantaneously fell in love with the site. But then I asked myself…</p>
<p><q>Wouldn’t this be even more awesome using zooming?</q></p>
<p>Here we are, a couple of hours later. I’ve ripped apart the <a href="http://www.isthisyourluggage.com/">original site</a>
and put it together again but this time in a zooming environment.
One nice side-effect has been that I’ve found a couple of rough edges in the
<a href="http://www.openzoom.org/sdk/api">OpenZoom SDK API</a> that I was able to improve due this quick real-world
check. Alright, enough talked, check it out!</p>
<blockquote class="info">
<h2 id="remix-is-this-your-luggage">Remix: Is This Your Luggage</h2>
<p><a href="/blog/examples/openzoom-is-this-your-luggage/" title="OpenZoom, Is This Your Luggage?">
<img style="border: 3px solid #CCC" src="http://farm4.static.flickr.com/3594/3474363339_fafdee1f23.jpg" width="500" height="347" alt="OpenZoom, Is This Your Luggage?" />
</a>
<a href="/blog/examples/openzoom-is-this-your-luggage/">View Showcase</a> | <a href="http://www.isthisyourluggage.com/">View Original</a> | <a href="https://github.com/openzoom/sdk/tree/master/examples/flex/isthisyourluggage">View Source</a></p>
<p><em>Disclaimer: This is nothing but a showcase for what you can do with zooming &
OpenZoom. If you liked this, please give proper respect to the author and
<a href="http://www.isthisyourluggage.com/">visit the original site</a>.</em></p>
</blockquote>
<h3>P.S.</h3>
<p>This is supposed to demonstrate a bit what kind of experiences you can build
with the <a href="http://www.openzoom.org/go/code">OpenZoom SDK</a>. It doesn’t always have to be high-resolution images
and a boring layout. Hope you enjoy it. ;)
<br /></p>
<p class="footnote">
<strong>Copyright:</strong> All Artwork & Content Copyright 2009, <a href="http://www.isthisyourluggage.com/">Is This Your Luggage</a>.
</p>
http://www.gasi.ch/blog/inline-multiscale-image-replacementInline Multiscale Image Replacement2009-04-08T00:00:00+00:00<p><q>How will we view and publish images<br />on the web in five years?</q>
Today I’d like to share with you my vision of how we could improve the publication and viewing of high-resolution images on the web. Before discussing what this vision entails, I’d really like you to explore it yourself.</p>
<h1>Scenarios</h1>
<p>The following are three real-world scenarios for publishing high-resolution images on the web: <strong>news</strong>, <strong>blogs</strong> and <strong>photo sharing</strong>. Explore any one or all of these scenarios and interact with the images within. <em>Come on, let’s get your mouse wheel spinnin’!</em> :)</p>
<blockquote class="info">
<h2 id="news-the-new-york-times">News: The New York Times</h2>
<p><a href="/blog/examples/2009/04/08/inline-multiscale-image-replacement/nytimes/" title="Inline Multiscale Image Replacement"><img src="http://farm4.static.flickr.com/3299/3419715880_dfac0333de.jpg" width="500" height="500" alt="Inline Multiscale Image Replacement" /></a>
<a href="/blog/examples/2009/04/08/inline-multiscale-image-replacement/nytimes/">View Demo</a> | <a href="http://www.nytimes.com/2007/08/12/world/asia/12afghan.html">View Original Page</a></p>
<p class="footnote"><em>Disclaimer: Since a high-resolution version of the original image was not available to me, I replaced it with another one from the Department of Defense which was taken in the same region, called Zabul Province.</em></p>
</blockquote>
<blockquote class="info">
<h2 id="blogs-information-architects">Blogs: Information Architects</h2>
<p><a href="/blog/examples/2009/04/08/inline-multiscale-image-replacement/ia/" title="Inline Multiscale Image Replacement"><img src="http://farm4.static.flickr.com/3651/3418905983_0cd00199f6.jpg" width="500" height="500" alt="Inline Multiscale Image Replacement" /></a>
<a href="/blog/examples/2009/04/08/inline-multiscale-image-replacement/ia/">View Demo</a> | <a href="http://informationarchitects.jp/web-trend-map-4-final-beta/">View Original Page</a></p>
</blockquote>
<blockquote class="info">
<h2 id="photo-sharing-flickr">Photo Sharing: Flickr</h2>
<p><a href="/blog/examples/2009/04/08/inline-multiscale-image-replacement/flickr/" title="Inline Multiscale Image Replacement"><img src="http://farm4.static.flickr.com/3408/3419715614_95204d3df9.jpg" width="500" height="500" alt="Inline Multiscale Image Replacement" /></a>
<a href="/blog/examples/2009/04/08/inline-multiscale-image-replacement/flickr/">View Demo</a> | <a href="http://www.flickr.com/photos/oliveralex/524852348/">View Original Page</a></p>
<p class="footnote"><em>Disclaimer: Due to conflicts, the original JavaScript that was included in the page was removed for this demo.</em></p>
</blockquote>
<h1>Vision</h1>
<p>The solution for publishing high-resolution images on the web I set out to develop had to have the following qualities:</p>
<h2>For Users</h2>
<ul>
<li>The user <strong>shall not pay</strong>, in terms of time or bandwidth, for large <strong>images she's not interested in</strong>.</li>
<li>The solution shall <strong>degrade gracefully</strong>, e.g. fall back to <em>status quo</em>, for users that do not meet the technical requirements.</li>
<li>The solution shall offer <strong>a vastly enhanced experience for viewing high-resolution images on the web</strong>, including <strong>full screen support</strong> while <strong>retaining</strong> as many of the <strong>standard interactions</strong> with images as possible.</li>
<li>The user shall <strong>not be taken out of the context</strong> she was working in.</li>
</ul>
<h2>For Publishers</h2>
<ul>
<li>The solution shall offer <strong>simple publishing of high-resolution images</strong> and turn their <strong>exploration</strong> into an <strong>awesome experience</strong>!</li>
<li>The solution shall be as much <strong>backwards- & forwards-compatible</strong> as possible.</li>
</ul>
<h1>Solution</h1>
<p>The solution I developed, based on something I started to call <strong>Inline Multiscale Image Replacement</strong>, is a combination of three different technologies from the <a href="http://openzoom.org/">OpenZoom</a> project, together known as <strong>OpenZoom Endo</strong>:</p>
<ul>
<li><strong>OpenZoom Caral</strong>: A <a href="http://python.org/">Python</a> tool to batch convert images into a multiscale image format based on the <a href="/blog/openzoom-description-format/">OpenZoom Description Format</a> and <a href="http://msdn.microsoft.com/en-us/library/cc645077(VS.95).aspx">Microsoft Deep Zoom</a>.</li>
<li><strong>OpenZoom Nano</strong>: A light-weight multiscale image viewer running in the Adobe Flash Player and built with the <a href="http://openzoom.org/">OpenZoom SDK</a>.</li>
<li><strong>OpenZoom Endo</strong>: A script performing progressive enhancement on new and existing HTML pages. It is written in JavaScript and packaged as <a href="http://jquery.com/">jQuery</a> plugin.</li>
</ul>
<blockquote class="info">
<h2 id="features">Features</h2>
<p><img src="http://farm4.static.flickr.com/3613/3419742376_15aaf47058.jpg" width="500" height="500" alt="Inline Multiscale Image Replacement" /></p>
<ul>
<li><strong>Simple publishing of high-resolution images on the web</strong>, even on existing pages.</li>
<li><strong>Continue to use interactions your already familiar with</strong> such as <em>Save Image As</em> and <em>View Image</em>. (Screenshot)</li>
<li><strong>Progressively enhance your browsing experience</strong>, with graceful fallback for those of you with browsers that are not JavaScript or Flash enabled.</li>
<li><strong>Never download more data than you currently look at.</strong></li>
<li>Take advantage of your entire screen real estate by using the <strong>full screen mode</strong>.</li>
<li>Explore the full glory of <strong>high-resolution images without</strong> ever <strong>leaving their page</strong>.</li>
<li><strong>Publish images in different sizes</strong> from the same source image.</li>
<li><strong>Free!</strong> <em>as in beer and freedom!</em><br />Released under the GPLv3 open source license.</li>
</ul>
</blockquote>
<h3 id="known-issues">Known Issues</h3>
<p>Of course, as with any <em>fresh-out-of-the-oven</em> technology there a couple of
things that don’t work as intended. Here’s a list of <em>known issues</em>:</p>
<ul>
<li>Image replacement can be delayed by large pages, resulting in a visible page flicker.</li>
<li>At this point, OpenZoom Endo does not pass the W3C validator due to the custom attribute <em>(XHTML namespaces & custom DTDs, anyone?)</em></li>
<li>Ideally, the viewer would initially feature some visual cues that convey the enhanced functionality. Add some simple controls to that. <em>Designer, anyone?</em></li>
<li>Script sometimes conflicts with existing JavaScript within the same page.</li>
<li>Multiple replacements of images cause performance problem related to Flash Player plugin instantiation.</li>
<li>Images sometimes fail to load due to plugin activation issues.</li>
</ul>
<blockquote class="info">
<h2 id="walkabout">Walkabout</h2>
<p>Let me quickly guide you through the basic process of publishing a high-resolution image on your web page:</p>
<ol>
<li>
<p>First, use Python and the <strong>OpenZoom Caral</strong> library to convert your image
into a multiscale image pyramid and optionally define additional sizes of
your image you’d like to publish or offer for download:</p>
<figure class="highlight"><pre><code class="language-py" data-lang="py"><span class="kn">import</span> <span class="nn">openzoom</span>
<span class="n">creator</span> <span class="o">=</span> <span class="n">openzoom</span><span class="o">.</span><span class="n">ImageCreator</span><span class="p">()</span>
<span class="n">creator</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s">"awesome.jpg"</span><span class="p">,</span> <span class="s">"awesome"</span><span class="p">,</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">600</span><span class="p">,</span> <span class="mi">1920</span><span class="p">])</span></code></pre></figure>
</li>
<li>
<p>Reference jQuery library, e.g. using Google’s CDN and the <strong>OpenZoom Endo</strong>
script in your HTML page:</p>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="o"><</span><span class="nx">script</span> <span class="nx">type</span><span class="o">=</span><span class="s2">"text/javascript"</span>
<span class="nx">src</span><span class="o">=</span><span class="s2">"http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"</span><span class="o">><</span><span class="sr">/script</span><span class="err">>
</span><span class="o"><</span><span class="nx">script</span> <span class="nx">type</span><span class="o">=</span><span class="s2">"text/javascript"</span>
<span class="nx">src</span><span class="o">=</span><span class="s2">"openzoom-min.js"</span><span class="o">><</span><span class="sr">/script</span><span class="err">>
</span> </code></pre></figure>
</li>
<li>
<p>Place the <strong>OpenZoom Nano</strong> viewer SWF (<code class="highlighter-rouge">OpenZoomViewer.swf</code>) file into the
same directory as your page.</p>
</li>
<li>
<p>Add the image to your HTML page and annotate it with the special
<code class="highlighter-rouge">openzoom:source</code> attribute:</p>
<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><img</span> <span class="na">src=</span><span class="s">"awesome/awesome-600x320.jpg"</span> <span class="na">width=</span><span class="s">"600"</span> <span class="na">height=</span><span class="s">"320"</span>
<span class="na">openzoom:source=</span><span class="s">"awesome/image.xml"</span><span class="nt">/></span></code></pre></figure>
</li>
<li>
<p>Enjoy hassle-free high-resolution imagery on the web!</p>
</li>
</ol>
</blockquote>
<blockquote class="flash">
<h2 id="download--source">Download & Source</h2>
<ul>
<li>Download <a href="http://open-zoom.googlecode.com/files/openzoom-endo-0.4.zip">OpenZoom Endo (808kb)</a> from Google Code.</li>
<li>Have a look at the <a href="http://code.google.com/p/open-zoom/source/browse/trunk/projects/endo/trunk?r=316#trunk/src">source code</a> on the OpenZoom Google Code repository.</li>
</ul>
</blockquote>
<!--
# You
*I want your feedback!* Let me know if my idea works for you. If it doesn't,
why not? How could things be improved? *I am listening*. If you're a JavaScript
hacker, take apart my code and share with me what could be done better.
If you're a designer, consider contributing to a better interface for the viewer
for an even better user experience. If you're an iPhone user, let me know if my
solution breaks when you check out one of the previous demos. If you think
this is *b#$&#($@*, then share your vision of the future of high-resolution
images on the web with me!
As always, if you have feedback, questions or want to start a dialogue, feel
free to leave a comment down below, head over to the awesome
[OpenZoom Get Satisfaction site](http://getsatisfaction.com/openzoom/) or
follow me on Twitter: [@OpenZoom](http://twitter.com/OpenZoom) and
[@gasi](http://twitter.com/gasi). In case you find a bug, please file a bug
report in the
[OpenZoom Google Code Bug System](http://code.google.com/p/open-zoom/issues/).
-->
<h2 id="epilogue">Epilogue</h2>
<p>With this and my other work on the OpenZoom project, I want to explore new ways
of interacting with visual information on the web. Join me and let’s explore how
the future of browsers with the appropriate image formats that support
high-definition images could look & feel like!</p>
http://www.gasi.ch/blog/gigapan-mobileGigaPan Mobile: GigaPan on iPhone & iPod touch2009-02-23T00:00:00+00:00<p>Want to find your friend on the <a href="http://gigapan-mobile.appspot.com/gigapan/17217">gigapixel Obama Inauguration panorama</a> no matter where you are right now? With GigaPan Mobile you can. GigaPan Mobile brings the full range of currently more than 14’000 gigapans from <a href="http://gigapan.org/">GigaPan.org</a> directly to your <a href="http://www.apple.com/iphone/">iPhone</a> and <a href="http://www.apple.com/ipodtouch/">iPod touch</a> through <a href="http://livelabs.com/seadragon-mobile/">Seadragon Mobile</a> from Microsoft.</p>
<blockquote class="flash">
<h2 id="gigapan-mobile-20">GigaPan Mobile 2.0</h2>
<p>GigaPan Mobile 2.0 is here and there’s no need to add RSS feeds to your
Seadragon Mobile, although you still can. Just grab your iPhone, launch Safari
and head over to <a href="http://gigapan-mobile.appspot.com/">GigaPan Mobile</a> and start enjoying all 20’000+ gigapans in
full glory.</p>
</blockquote>
<h2>Quick Setup</h2>
<ol>
<li>Get an <a href="http://www.apple.com/iphone/">iPhone</a>/<a href="http://www.apple.com/ipodtouch/">iPod touch</a>.</li>
<li>Install <a href="http://www.itunes.com/app/seadragonmobile">Seadragon Mobile</a>.</li>
<li>Add the <a href="http://gigapan-mobile.appspot.com/feed">GigaPan Mobile RSS</a> feed to Seadragon Mobile.</li>
</ol>
<blockquote class="info">
<h2>GigaPan Mobile RSS Feed</h2>
<a href="http://gigapan-mobile.appspot.com/feed"><code>http://gigapan-mobile.appspot.com/feed</code></a>
</blockquote>
<blockquote class="info">
<h2>Setup</h2>
<ol>
<li><a href="http://www.itunes.com/app/seadragonmobile">Install Seadragon Mobile</a> from the <a href="http://www.apple.com/iphone/appstore/">Apple AppStore</a>.</li>
<li>
Launch <em>Seadragon Mobile</em>…
<div align="center">
<img src="http://farm4.static.flickr.com/3611/3301180401_e694bc4bb8_o.png" width="320" height="480" alt="GigaPan Mobile" />
<img src="http://farm4.static.flickr.com/3304/3301180493_ef17b8e4cc_o.png" width="320" height="480" alt="GigaPan Mobile" />
</div>
</li>
<li>Tap the <em>+ button</em> in the lower right corner to add custom content…
<div align="center">
<img src="http://farm4.static.flickr.com/3344/3302011366_5df1739e0c_o.png" width="320" height="480" alt="GigaPan Mobile" />
</div>
</li>
<li>Choose <em>RSS feed</em>…
<div align="center">
<img src="http://farm4.static.flickr.com/3297/3301180645_336012e2c2_o.png" width="320" height="480" alt="GigaPan Mobile" />
</div>
</li>
<li>Enter the URL for the <em>GigaPan Mobile</em> feed from above and tap <em>Done</em>.
<div align="center">
<img src="http://farm4.static.flickr.com/3336/3302011582_6a6ba7c0fe_o.png" width="320" height="480" alt="GigaPan Mobile" />
</div>
</li>
</ol>
</blockquote>
<blockquote class="info">
<h2>Browse GigaPan Mobile</h2>
<ul>
<li>Go to the newly added <em>GigaPan</em> feed in <em>Seadragon Mobile</em>…
<div align="center">
<img src="http://farm4.static.flickr.com/3625/3302111798_a46b8883ef_o.png" width="320" height="480" alt="GigaPan Mobile" />
<img src="http://farm4.static.flickr.com/3394/3302011676_3c64efb817_o.png" width="320" height="480" alt="GigaPan Mobile" />
</div></li>
<li>…and explore the details of <a href="http://www.gigapan.org/viewGigapan.php?id=17217">one</a> of the 10 most popular gigapans from <a href="http://gigapan.org/">GigaPan.org</a> with just the tips of your fingers.
<div align="center">
<img src="http://farm4.static.flickr.com/3442/3301181091_dca901cb05_o.png" width="320" height="480" alt="GigaPan Mobile" />
<img src="http://farm4.static.flickr.com/3625/3302012476_04dd55b8fa_o.png" width="480" height="320" alt="GigaPan Mobile" />
</div>
</li>
</ul>
<p class="footnote">Photo <a href="http://gigapan-mobile.appspot.com/gigapan/17217">President Barack Obama's Inaugural Address</a> © 2009, <a href="http://davidbergman.net">David Bergman</a></p>
</blockquote>
<h3>Inside GigaPan Mobile</h3>
<p>GigaPan Mobile was built using <a href="http://python.org/">Python</a>, <a href="http://code.google.com/appengine/">Google App Engine</a> and knowledge acquired through the <a href="http://openzoom.org/">OpenZoom project</a>.</p>
<h3>20'000+ Gigapans (Update – 04.08.2009)</h3>
<p>To see one of more than 20’000 gigapans with Seadragon Mobile, simply add the following link as <em>Deep Zoom Content</em>, where <strong><ID></strong> is the gigapan ID number:</p>
<blockquote class="info">
<h2>Custom GigaPan Content for Seadragon Mobile</h2>
<code>http://gigapan-mobile.appspot.com/gigapan/<strong><ID></strong>.dzi</code>
</blockquote>
<p><strong>Example:</strong> Obama Inaugural Address<br /><a href="http://gigapan-mobile.appspot.com/gigapan/17217.dzi">http://gigapan-mobile.appspot.com/gigapan/17217.dzi</a></p>
http://www.gasi.ch/blog/gigapan-desktopGigaPan Desktop2009-02-20T00:00:00+00:00<p>Ever wanted to check out those <em>super-high-resolution</em> images from
<a href="http://gigapan.org">GigaPan.org</a> in fullscreen? Now you can with GigaPan Desktop. Last week I’ve
built a desktop application for viewing GigaPan images in fullscreen using
<a href="http://www.adobe.com/products/air/">Adobe AIR</a> and the
<a href="http://openzoom.org/">OpenZoom SDK</a>.</p>
<p>You can choose gigapans from a list of the most recent ones or simply by typing
in the ID number of the gigapan. The application runs on Windows, Mac OS X and
Linux.</p>
<!-- class="info" -->
<blockquote>
<h2 id="gigapan-desktop">GigaPan Desktop</h2>
<p><a href="http://openzoom.org/go/gigapan" title="GigaPan Desktop by Daniel Gasienica"><img src="http://farm4.static.flickr.com/3454/3294731601_887f8aa409.jpg" width="500" height="324" alt="GigaPan Desktop" /></a></p>
<h3 id="keyboard-shortcuts">Keyboard Shortcuts</h3>
<ul>
<li>Toggle Fullscreen: <strong>F</strong></li>
<li>Show All: <strong>H</strong></li>
<li>Pan: <strong>W, S, A, D</strong> or <strong>↑, ↓, ←, →</strong></li>
<li>Zoom: <strong>I, O</strong> or <strong>+, ‒</strong></li>
</ul>
<h3 id="download--source">Download & Source</h3>
<p><a href="http://openzoom.org/go/gigapan">Download</a> | <a href="http://code.google.com/p/open-zoom/source/browse/openzoom/gigapan-desktop/trunk/?r=358">View Source</a></p>
</blockquote>
<h3 id="feedback">Feedback</h3>
<p>Please share your feedback and feature requests on our
<a href="http://community.openzoom.org">community</a>.</p>
http://www.gasi.ch/blog/openzoom-visualization-z-orderOpenZoom Visualization: Z-Order2009-01-04T00:00:00+00:00<p>This weekend I’ve been playing around with a couple of things related to the
OpenZoom project. The following is a small but beautiful example I wanted to
share with you: A visualization of the <a href="http://en.wikipedia.org/wiki/Z-order_(curve)">Z-order (Morton-order)</a>
curve in a zoomable environment using the OpenZoom framework. Behind the
scenes there are some real gems:
<a href="https://github.com/openzoom/sdk/blob/9cc5a61330be1448b8e2eb93645d2ca7d4e15dc4/src/org/openzoom/flash/utils/MortonOrder.as">Bit Twiddling Hacks</a>, <a href="http://en.wikipedia.org/wiki/Z-order_(curve)">Mathematics</a>,
<a href="http://getsatisfaction.com/livelabs/topics/vector_graphics_in_seadragon_also_see_zoomism_com">Vectors in OpenZoom</a>. Enjoy…</p>
<blockquote class="info">
<h2 id="demo-z-order-visualization">Demo: Z-Order Visualization</h2>
<p><a href="/blog/examples/2009/01/04/z-order-visualization/" title="Z-Order Visualization">
<img src="http://farm4.static.flickr.com/3261/3166780093_7b2d2eedf8.jpg" width="500" height="279" alt="Z-Order" />
</a></p>
<p><a href="/blog/examples/2009/01/04/z-order-visualization/">View Demo</a> | <a href="/blog/examples/2009/01/04/z-order-visualization/source/">View Source</a> | <a href="https://github.com/openzoom/sdk/blob/9cc5a61330be1448b8e2eb93645d2ca7d4e15dc4/src/org/openzoom/flash/utils/MortonOrder.as">View MortonOrder Class Source</a></p>
</blockquote>
http://www.gasi.ch/blog/pimp-your-photoshop-zoomify-with-openzoomPimp Your Photoshop Zoomify with OpenZoom2008-12-24T00:00:00+00:00<p>Part of my passion for <a href="http://openzoom.org/">OpenZoom</a> is the promotion of the beauty of large photos on the web and giving people the right tools to publish them. Enabling people to publish their images means building tools that support and enhance the workflow they're already familiar with.</p>
<p>You might know that since version CS3 <a href="http://www.adobe.com/products/photoshop/photoshop/">Adobe Photoshop</a> has a great built-in feature for <a href="http://www.zoomify.com/photoshop.htm">exporting high-resolution images with Zoomify</a>. The feature works really great. Although it took my machine a couple of minutes, I've managed to export a beautiful <em>86400x43200 pixel</em> version of the <a href="http://visibleearth.nasa.gov/view_set.php?categoryID=2355">NASA Blue Marble</a> images. The feature would be perfect if it wasn't for the <em>not so perfect</em>, <em>not so smooth</em> <a href="http://www.zoomify.com/">Zoomify</a> viewer that is used to view the exported image.</p>
<h2>Scratching Your Own Itch</h2>
<p>Therefore, while enjoying the calm of the holidays, I sat down and developed an <a href="http://openzoom.org/">OpenZoom</a> viewer that you can use as an drop-in replacement for the built-in Zoomify viewer in Adobe Photoshop.</p>
<blockquote class="info">
<h2>Demo: OpenZoom Viewer for Adobe Photoshop</h2>
New and improved viewer built with the <a href="http://openzoom.org/">OpenZoom SDK</a>:
<a href="/blog/examples/2008/12/23/photoshop-openzoom-viewer/openzoom/" title="OpenZoom Viewer for Adobe Photoshop"><img src="http://farm3.static.flickr.com/2134/2041196959_db9cc76bbc.jpg" width="500" height="375" alt="" /></a>
<a href="/blog/examples/2008/12/23/photoshop-openzoom-viewer/openzoom/">View Demo</a> | <a href="http://github.com/openzoom/nano/raw/1b0dbf94c5083c07afdd0dc134ac2acfd267b277/src/OpenZoomViewer.as">View Source</a>
<h2>Features</h2>
<ul>
<li><strong>Fullscreen support</strong></li>
<li><strong>Mouse navigation</strong> (<strong>click</strong> to <em>zoom in</em> and <strong>Shift-click</strong> to <em>zoom out</em>)</li>
<li><strong>Mouse wheel support</strong> in all browsers (<strong>scroll</strong> to <em>zoom</em>)</li>
<li><strong>Keyboard navigation</strong> (<em>Pan</em> with <strong>W, S, A, D</strong> or <strong>arrow keys</strong>, <em>Zoom</em> with <strong>+ / –</strong> or <strong>I and O</strong>, <em>Show All</em> with <strong>H</strong>, <em>Fullscreen</em> with <strong>F</strong>.)</li>
<li><strong>Context Menu</strong> support</li>
<li><strong>Standards-compliant</strong> Flash embed through <a href="http://swfobject.googlecode.com/">SWFObject</a></li>
<li><strong>6 templates</strong> — <em>Normal / Fullscreen: black, gray and white.</em></li>
<li><strong>Just 40KB</strong> — <em>100% Flash, no Flex.</em></li>
<li><strong>100% Open Source.</strong> No branding.</li>
</ul>
</blockquote>
<blockquote class="info">
<h2>Demo: Zoomify Viewer for Adobe Photoshop</h2>
For comparison, this is the old Zoomify viewer in Photoshop:
<a href="/blog/examples/2008/12/23/photoshop-openzoom-viewer/zoomify/" title="Zoomify Viewer for Adobe Photoshop"><img src="http://farm4.static.flickr.com/3119/3131151323_30940a5f34.jpg" width="500" height="355" alt="Zoomify Viewer" /></a>
<a href="/blog/examples/2008/12/23/photoshop-openzoom-viewer/zoomify/">View Demo</a>
</blockquote>
<blockquote class="info">
<h2>Download</h2>
<ol>
<li><a href="http://openzoom.org/tango/download/latest/zip/">Download OpenZoom Viewer Templates <br />for Adobe Photoshop CS3/CS4 (ZIP - 86KB)</a>.</li>
<li>For installation and tutorials, read the instructions in <a href="http://github.com/openzoom/tango/raw/master/README.txt">README.txt</a></li>
<li><em>Enjoy and let me know what you come up with!</em></li>
</ol>
<h3>Update — March 25, 2009</h3>
The OpenZoom Viewer Templates received an update in the form of a <strong>0.3 release</strong> and are now known as <strong>OpenZoom Tango</strong>.
<h3>Update — August 9, 2009</h3>
With the <strong>0.5.2 release</strong>, <strong>OpenZoom Tango</strong> has moved from <a href="http://open-zoom.googlecode.com/">Google Code</a> to <a href="http://github.com/openzoom/tango/">GitHub</a>. Additionally, <strong>OpenZoom Tango</strong> is now licensed under the <a href="http://www.mozilla.org/MPL/MPL-1.1.txt">MPL 1.1</a>/<a href="http://www.gnu.org/licenses/gpl-3.0.txt">GPL 3</a>/<a href="http://www.gnu.org/licenses/lgpl-3.0.txt">LGPL 3</a> licenses.
<em>P.S. To get started, check out this great tutorial called <a href="http://www.photoshopessentials.com/photo-editing/zoomify/">Zoomify High Resolution Images With Photoshop CS3</a>.</em>
</blockquote>
http://www.gasi.ch/blog/openzoom-description-formatOpenZoom Description Format2008-12-19T00:00:00+00:00<p>When we look at <em>multiscale</em> or <em>multi-resolution</em> imaging in 2008,<sup><a href="#footnote-1-3">1</a></sup> we’re mostly looking at a pile of image files<sup><a href="#footnote-2">2</a></sup> (called <em>tiles</em>) that make up an image pyramid.<sup><a href="#footnote-1-3">3</a></sup> Typically, image file formats, e.g. JPEG and PNG, have stored their properties such as width and height inside the file. These formats acted not only as carrier of image data but also as container for the metadata associated with it. This is a manifestation of the <a href="http://blogs.msdn.com/pix/archive/2006/08/16/702780.aspx">The Truth Is in the File</a> paradigm.</p>
<blockquote class="error">
<h2>Deprecation Warning</h2>
The <strong>OpenZoom Description Format</strong> was just a proof-of-concept. For real-word applications, I highly recommend using the <a href="http://msdn.microsoft.com/en-us/library/cc645077(VS.95).aspx">Microsoft Deep Zoom file format</a> as it has the widest client & tooling support, is well-documented and can be used within collections.
</blockquote>
<h2>The Truth Is Out There</h2>
<p>Taking this paradigm into account, we suddenly encounter a problem with multiscale images. If the original image is exploded into many little pieces, where do we store its metadata? There are different solutions to this problem. For mapping sites such as Google Maps and Yahoo Maps it is probably sufficient to just hard-code the image pyramid properties and how to access the tiles directly inside the client. However, for general multiscale image viewing technologies such as <a href="http://www.zoomify.com/">Zoomify</a>, <a href="http://livelabs.com/blog/seadragon/silverlight-2-deep-zoom/">Deep Zoom</a> or <a href="http://openzoom.org/">OpenZoom</a> this is not an option since we don’t know the properties of the images until run-time. Again, there’s a simple and elegant solution to for this: <em>XML description files that carry the image metadata.</em></p>
<h2>Rumble In the Jungle</h2>
<p>In the following section I will first quickly present you the two dominant multiscale image description formats out there: <a href="http://www.zoomify.com/">Zoomify</a> and <a href="http://msdn.microsoft.com/en-us/library/cc645077(VS.95).aspx">Microsoft Deep Zoom</a>. After that, I will introduce you to a new description format I designed called <a href="http://openzoom.org/specs/">OpenZoom description format</a>.</p>
<p><em>Note: The following examples all describe a 10 megapixel JPEG image with the name <strong>bruges</strong>.</em></p>
<blockquote class="info">
<h2>Zoomify</h2>
<strong>Example</strong>
<pre lang="xml">
<IMAGE_PROPERTIES VERSION="1.8" WIDTH="3872" HEIGHT="2592" TILESIZE="256" NUMTILES="241" NUMIMAGES="1" />
</pre>
This Zoomify image has the following structure on the file system:
<strong>Descriptor</strong>
<code>bruges/ImageProperties.xml</code>
<code><strong>[filename]/ImageProperties.xml</strong></code>
<strong>Tiles</strong>
<code>bruges/TileGroup0/0-0-0.jpg</code>
<code>bruges/TileGroup0/1-0-0.jpg</code>
<code>bruges/TileGroup0/1-0-1.jpg</code>
<code>bruges/TileGroup0/1-1-0.jpg</code>
<code>bruges/TileGroup0/1-1-1.jpg</code>
<code>bruges/TileGroup0/2-0-0.jpg</code>
…
<code>bruges/TileGroup0/4-15-9.jpg</code>
<code><strong>[filename]/TileGroup[X]/[level]-[column]-[row].jpg</strong></code>
</blockquote>
<blockquote class="info">
<h2>Deep Zoom Image (DZI)</h2>
<strong>Example</strong>
<pre lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<Image xmlns="http://schemas.microsoft.com/deepzoom/2008" TileSize="256" Overlap="1" Format="jpg">
<Size Width="3872" Height="2592" />
</Image>
</pre>
This <a href="http://msdn.microsoft.com/en-us/library/cc645077(VS.95).aspx">Deep Zoom image (DZI)</a> has the following structure on the file system:
<strong>Descriptor</strong>
<code>bruges.xml</code>
<code><strong>[filename].[xml|dzi]</strong></code>
<strong>Tiles</strong>
<code>bruges_files/0/0_0.jpg</code>
<code>bruges_files/1/0_0.jpg</code>
<code>bruges_files/2/0_0.jpg</code>
…
<code>bruges_files/9/0_0.jpg</code>
<code>bruges_files/9/0_1.jpg</code>
<code>bruges_files/9/1_0.jpg</code>
<code>bruges_files/9/1_1.jpg</code>
…
<code>bruges_files/12/15_9.jpg</code>
<code><strong>[filename]_files/[level]/[column]-[row].[extension]</strong></code>
</blockquote>
<blockquote class="info">
<h2>OpenZoom Description Format</h2>
The following is actually a description of the Deep Zoom image we've looked at previously.
<strong>Descriptor</strong>
<code>bruges.xml</code>
<code><strong>[filename].xml</strong></code>
<strong>Tiles</strong>
<em>Wherever you wish…</em>
<strong>Example</strong>
<pre lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<image xmlns="http://ns.openzoom.org/openzoom/2008">
<pyramid width="131072" height="131072" type="image/jpeg" tileWidth="256" tileHeight="256" tileOverlap="0" origin="topLeft">
<level width="1" height="1" columns="1" rows="1">
<uri template="bruges_files/0/{column}_{row}.jpg" />
</level>
<level width="2" height="2" columns="1" rows="1">
<uri template="bruges_files/1/{column}_{row}.jpg" />
</level>
…
<level width="242" height="162" columns="1" rows="1">
<uri template="bruges_files/8/{column}_{row}.jpg" />
</level>
<level width="484" height="324" columns="2" rows="2">
<uri template="bruges_files/9/{column}_{row}.jpg" />
</level>
<level width="968" height="648" columns="4" rows="3">
<uri template="bruges_files/10/{column}_{row}.jpg" />
</level>
<level width="1936" height="1296" columns="8" rows="6">
<uri template="bruges_files/11/{column}_{row}.jpg" />
</level>
<level width="3872" height="2592" columns="16" rows="11">
<uri template="bruges_files/12/{column}_{row}.jpg" />
</level>
</pyramid>
</image>
</pre>
<a href="http://openzoom.org/specs/">OpenZoom Description Format XML Schema (Draft)</a>
</blockquote>
<h2>Not Invented Here</h2>
<p>Alright, we’ve seen examples of all three description formats for the same image. Before anything else, you might ask yourself: <em>Why the #&$@ another format?</em> Good attitude and glad you asked. Hopefully, I will be able to answer this question for most of you. If not, just leave me a comment, I’d be glad to discuss this further. Now, let’s compare these three formats by looking at where they shine but of course also at their shortcomings.</p>
<h3>Conciseness</h3>
<p>Obviously, <strong>Zoomify</strong> and <strong>Deep Zoom</strong> win big time here. Their description files have a <strong>couple of lines vs the 40+ lines</strong> of the <strong>OpenZoom</strong> descriptor which inherently is very verbose <em>— Ed.: Levels 2–7 omitted for esthetic reasons</em>. On the other hand, we should keep in mind that everything we see in the <strong>OpenZoom</strong> descriptor sample somehow has to be computed by the client for the other two formats. More on that later.</p>
<h3>Portability</h3>
<p>Not sure if portability is the right term, but let me explain what I mean: <em>How flexible is the format regarding the storage of the descriptor and its image tiles?</em> <strong>Deep Zoom</strong> is the most extreme case of the three where the <strong>descriptor file and the image tiles</strong> are <strong>strongly coupled</strong> through the original file name of your image. That means if you move your descriptor you always have to remember to move the image data folder as well. This could be considered risky as the two are not contained in one folder. <strong>Zoomify</strong> has the <strong>same limitation</strong> but at least the image data and its descriptor are both contained in the same folder that carries the name of the original image. <strong>OpenZoom</strong> is clearly <strong>the most portable</strong> of the three as it let’s you specify the descriptor file independently of the image tiles.</p>
<p><em><strong>Important Note:</strong> Both Microsoft and Zoomify offer an alternative storage method in the form of a single-file format. They are called <a href="http://www.zoomify.com/support.htm#a20061222_2108">Zoomify’s Pyramidal File Format (PFF)</a> and DZIZ (a <a href="http://en.wikipedia.org/wiki/ZIP">ZIP</a>-based container for DZI) which I’ve seen used by <a href="http://livelabs.com/photosynth/">Microsoft Photosynth</a>.</em></p>
<h3>Flexibility</h3>
<p><strong>Flexibility</strong> apparently was <strong>not a design goal of Microsoft or Zoomify</strong>. This is fine considering that the design of such a new format requires these kinds of trade-offs. Their assumption is that the descriptor file and the image tiles are strongly coupled and the latter are computed with a well-defined algorithm and stored in a fixed file hierarchy. Flexibility is <em>the</em> area where the <strong>OpenZoom description format</strong> shines. When I worked on the OpenZoom description format, I obviously followed the <a href="http://www.python.org/dev/peps/pep-0020/">Python Zen</a> which states <q>Explicit is better than implicit.</q> Although one drawback is the verbosity of the format, there are many advantages we can get from it. For example, when I worked on the <a href="http://openzoom.org/">OpenZoom framework</a>, I wanted to test it with some really large multiscale images that are out there. Well, what is the largest image out there that I know of? A map of the world, of course. The <a href="http://openstreetmap.org/">OpenStreetMap Project</a>, for example, features many, many gigapixels of image data. Fine, so how do I test the framework with a map? <a href="http://modestmaps.mapstraction.com/trac/browser/trunk/as2/lib/com/modestmaps/mapproviders/OpenStreetMapProvider.as?rev=192">Hard-code the URLs</a> somewhere? <em>No, no</em>. Let’s create a descriptor for it. <a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/source/images/openstreetmap.xml">So I did</a>. <a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/source/images/openstreetmap.xml">Grab it</a> and play with it with your copy of the <a href="http://code.google.com/p/open-zoom/">OpenZoom framework</a>. <em>Look Ma’, no code!</em></p>
<p>This example demonstrates one of the advantages of the format, namely your descriptor file does not have to be stored along with your image data. Just put your descriptor wherever you wish and point it to the image tiles.</p>
<h2>Features: OpenZoom Description Format</h2>
<p>The following section gives you a short summary of some of the features in the OpenZoom description format.</p>
<p><strong>Flexible Pyramid Layout</strong>
Behind both Zoomify and Deep Zoom, there are well-specified algorithms that create the image pyramid and define its properties. To get an idea of how the formats expand the information you previously saw in their descriptors, feel free to take a look at their implementation in <a href="http://open-zoom.googlecode.com/">OpenZoom</a>: <a href="http://code.google.com/p/open-zoom/source/browse/trunk/src/main/flash/org/openzoom/flash/descriptors/zoomify/ZoomifyDescriptor.as">ZoomifyDescriptor</a> and <a href="http://code.google.com/p/open-zoom/source/browse/trunk/src/main/flash/org/openzoom/flash/descriptors/deepzoom/DZIDescriptor.as">DZIDescriptor</a>.</p>
<p>The OpenZoom description format doesn’t require a particular layout of the image pyramid. One requirement would be that every level of the pyramid approximately has the same aspect ratio but I’ve even managed to work around that constraint. To give you an idea of how powerful this flexibility is, consider the following couple of facts:</p>
<ol>
<li>The OpenZoom description format can express both, the properties of a Deep Zoom image pyramid, as well as the one produced by Zoomify. Besides these, it supports the pyramids of <a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/source/images/openstreetmap.xml">OpenStreetMap</a>, Google Maps (<a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/source/images/google-maps-road.xml">road</a>, <a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/source/images/google-maps-terrain.xml">terrain</a> and <a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/source/images/google-maps-satellite.xml">satellite</a>) and many more.</li>
<li>Just like in Deep Zoom, you can specify <strong>tile overlap</strong><sup><a href="#footnote-5">5</a></sup> in the OpenZoom description format.</li>
<li>Unlike Deep Zoom or Zoomify, the OpenZoom description format also supports non-square image tiles by exposing a <strong>tileWidth</strong> as well as a <strong>tileHeight</strong> property. Deep Zoom and Zoomify obviously don't have to support this as they know that their algorithms don't produce non-square tiles. The OpenZoom format however, has to accomodate legacy multiscale image data that has non-square tiles.
<li>One thing that surprised me most is the fact that even images on Flickr which are stored in many different dimensions can be put into relationship of an image pyramid. The levels of a <a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/source/images/flickr.xml">Flickr image pyramid</a> are quite irregular compared to Deep Zoom and Zoomify as they are bounded by maximum sidelengths of <em>100, 240, 500, 1024</em> and <em>original</em>. Even though it isn't very efficient since Flickr doesn't support tiles, images from Flickr can be rendered as multiscale images inside <a href="http://openzoom.org/">OpenZoom</a>.</li>
</ol>
<strong>Important Note:</strong> <em>Deep Zoom features a powerful concept called <a href="http://msdn.microsoft.com/en-us/library/cc645077(VS.95).aspx#Sparse_Images">Sparse Images</a> not present in any other format known to me. <strike>However, I am considering to incorporate this feature into the OpenZoom description format at a later date.</strike></em>
<strong>Powerful Addressing Scheme</strong>
The description format again makes minimal assumptions about the location of the image tiles. Only the following two conditions have to be met: Tiles have to be addressable by their <em>column</em> and <em>row</em> in a <a href="http://en.wikipedia.org/wiki/Cartesian_coordinate_system">cartesian or rectangular coordinate system</a>. The client then simply applies string substitution to the reserved tokens <strong>{row}</strong> and <strong>{column}</strong> and replaces them with coordinates that have a range of <strong>[0, numRows)</strong> or <strong>[0, numColumns)</strong> respectively. The upper bounds <strong>numRows</strong> and <strong>numColumns</strong> are specified on the corresponding <strong>level</strong> element.
Important to note is that unlike Zoomify which only seems to support JPEG tiles anyway and Deep Zoom where the extension is specified in the descriptor, the OpenZoom description format makes no assumptions about the file extension of the tiles whatsoever. In the days where server-side scripts with a extensions like <em>.php</em> or <em>.cfm</em> serve us images, it would be negligent to rely on the file type extension. For the client to decide if it can render the images that are being served, the format features a <strong>type</strong> property on the <strong>pyramid</strong> element that specifies the mime type of the tiles.
<strong>Exceptions:</strong> <em>Obviously, no matter how powerful a design is, there are always things it can't handle. For the OpenZoom description format this means sources such as <a href="http://maps.live.com/">Microsoft's Virtual Earth</a> or the <a href="http://gigapan.org/">GigaPan</a> project which both feature a <a href="http://de.wikipedia.org/wiki/Quadtree">quadtree</a>-based addressing scheme. That the OpenZoom description format cannot describe these kinds of sources doesn't mean the OpenZoom framework can't render them. However, doing that involves some amount of code which in the case of OpenZoom would mean to implement the <a href="http://code.google.com/p/open-zoom/source/browse/trunk/src/main/flash/org/openzoom/flash/descriptors/IMultiScaleImageDescriptor.as">IMultiScaleImageDescriptor</a> interface. For Silverlight Deep Zoom that would be the abstract <a href="http://msdn.microsoft.com/en-us/library/system.windows.media.multiscaletilesource(VS.95).aspx">MultiScaleTileSource</a> class.</em>
<strong>Support for Multiple URLs</strong>
As you may know, most current browsers are <a href="http://www.openajax.org/runtime/wiki/The_Two_HTTP_Connection_Limit_Issue">limited to 2 concurrent requests per domain</a>. Therefore, the OpenZoom description format has support for defining multiple URLs for the same data. A client which supports the format is then able to concurrently fetch more than 2 image tiles at the same time. This technique is applied by most large map providers such as Google Maps and Microsoft Virtual Earth.
<strong>Example</strong>
<pre lang="xml">
…
<level index="11" width="524288" height="524288" columns="2048" rows="2048">
<uri template="http://t0.foo.com/11/{column}/{row}.png" />
<uri template="http://t1.foo.com/11/{column}/{row}.png" />
<uri template="http://t2.foo.com/11/{column}/{row}.png" />
<uri template="http://t3.foo.com/11/{column}/{row}.png" />
</level>
…
</pre>
<br />
<strong>Ease of Implementation</strong>
Since the OpenZoom description format is very explicit (and therefore verbose), implementing a client to read it is very, very simple. Unlike Deep Zoom and Zoomify where the client has to do a considerable amount of work to compute the properties of the image pyramid, with the OpenZoom description format this work basically boils down to mapping the properties of the descriptor into the internal representation of a multiscale image description.
In my opinion, the single biggest advantage of the OpenZoom description format is that a client that can read the format does not need to understand the algorithms that created the image pyramids which the format itself describes. This way we can totally decouple the producer of an image pyramid from its ultimate client.
If you are interested in getting an idea of how all of this works, I suggest you take a look at the following classes in the <a href="http://code.google.com/p/open-zoom/">OpenZoom source code repository</a>: <a href="http://code.google.com/p/open-zoom/source/browse/trunk/src/main/flash/org/openzoom/flash/descriptors/zoomify/ZoomifyDescriptor.as">ZoomifyDescriptor</a>, <a href="http://code.google.com/p/open-zoom/source/browse/trunk/src/main/flash/org/openzoom/flash/descriptors/deepzoom/DZIDescriptor.as">DZIDescriptor</a> and <a href="http://code.google.com/p/open-zoom/source/browse/trunk/src/main/flash/org/openzoom/flash/descriptors/openzoom/OpenZoomDescriptor.as">OpenZoomDescriptor</a>.
<h2>Conclusion</h2>
I hope this overview of the three multiscale image description formats gave you an idea on what problems each one of them is trying to solve and how well they succeed in doing that. When designing the <a href="http://openzoom.org/specs/">OpenZoom description format</a>, my intention was certainly not to create <em>yet another description format</em>. It simply tackles the issues of multiscale image formats from a different angle. Doing that, it turned out to be quite powerful in representing all kinds of multiscale images out there, including the two big ones: Deep Zoom and Zoomify. More importantly, the OpenZoom description format offers a way to describe a vast amount of all multiscale image out there under a single specification.
That being said, the <a href="http://openzoom.org/">OpenZoom framework</a> itself, which strives to be the most open, most flexible platform for multiscale images and Zoomable User Interfaces out there, obviously supports all of the formats discussed here equally well.
I hope you've enjoyed this <em>behind the scenes</em> of the OpenZoom description format. At some point, I will show you an idea I've been working that involves these multiscale image descriptors. Until then, have a look at the links in the <a href="#further-reading">Further Reading</a> section as there are some hidden gems.
<p class="footnote"><em>Disclaimer: All details of the OpenZoom description format are subject to change. Feature requests and opinions are welcome. As usual, feel free to leave a comment.</em></p>
<h3>Acknowledgement</h3>
At this point, I'd like to thank my buddy <a href="http://424f.com/blog/">Boris</a> who unbeknownst to him, through our many very valuable discussions, considerably shaped the current form of the <a href="http://openzoom.org/specs/">OpenZoom description format specification</a>. Believe me when I say that without him the format would have most certainly been <acronym title="Yet Another (Damn) Multiscale Image Description Format">YAMSIDF.</acronym>
<h3>Footnotes</h3>
<p class="footnote"><a name="footnote-1-3">[1 & 3]</a> If you'd like to get some more background on this topic, I wrote an <a href="/blog/inside-deep-zoom-1/">introduction to multiscale imaging</a> and another article about the <a href="/blog/inside-deep-zoom-2/">mathematical properties of an image pyramid</a> using Microsoft's Deep Zoom as an example.</p>
<p class="footnote"><a name="footnote-2">[2]</a> From my own experience, I know that there are unfortunately still people out there who think that there is some magic going on behind multiscale imaging. To set this straight, if you've used any of the following, <a href="http://maps.google.com/">Google Maps</a>, <a href="http://maps.yahoo.com/">Yahoo Maps</a>, <a href="http://maps.live.com/">Microsoft Virtual Earth</a>, <a href="http://livelabs.com/blog/seadragon/silverlight-2-deep-zoom/">Silverlight Deep Zoom</a>, <a href="http://livelabs.com/seadragon-ajax/">Seadragon AJAX</a>, <a href="http://livelabs.com/seadragon-mobile/">Seadragon Mobile</a> or <a href="http://zoomify.com/">Zoomify</a>,<sup><a href="#footnote-4">4</a></sup> you should know that all of them basically work the same, namely with <em>off the shelf</em> JPEG or PNG image files. These files are stored either on disk or in a database. Once requested, they are sent to and rendered on the client which in the previous examples is either the browser, the Flash or Silverlight plugin or the iPhone.
But you might ask: <em>What about JPEG 2000?</em> Indeed, there are some possible candidates for image file formats out there which would bring better support for multiscale imaging in the future. Two of them being <a href="http://en.wikipedia.org/wiki/JPEG_2000">JPEG 2000</a> and <a href="http://en.wikipedia.org/wiki/HD_Photo">HD Photo</a>. We won't see significant adoption of the first anytime soon because of <a href="http://en.wikipedia.org/wiki/JPEG_2000#Legal_issues">legal issues</a> such as <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=36351">this one</a>. <a href="http://blogs.msdn.com/billcrow/archive/2006/11/17/introducing-hd-photo.aspx">HD Photo originated at Microsoft</a> and is being considered as successor to the JPEG standard dubbed JPEG XR. Again, widespread use won't happen overnight.</p>
<p class="footnote"><a name="footnote-4">[4]</a> <em>By the way, <a href="http://openzoom.org/">OpenZoom</a> supports most of these out of the box.</em></p>
<p class="footnote"><a name="footnote-5">[5]</a> In <a href="/blog/inside-deep-zoom-2/">Inside Deep Zoom 2</a> I've explained the concept of <em>tile overlap</em>.</p>
<h3><a name="further-reading">Further Reading</a></h3>
<ul>
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/JPEG_2000">JPEG 2000</a></li>
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/HD_Photo">HD Photo</a></li>
<li><a href="http://blogs.msdn.com/billcrow/archive/2006/10/20/msu-evaluates-windows-media-photo-vs-jpeg-2000.aspx">HD Photo (formerly Windows Media Photo) vs JPEG 2000</a></li>
<li><a href="http://blogs.msdn.com/billcrow/archive/2006/11/20/photosynth.aspx">Photosynth</a> <em>—<a href="http://twitter.com/billcrow/">Bill Crow</a> gives us an excellent behind the scenes of an early preview of Photosynth and how it leverages HD Photo.</em></li>
<li>Microsoft MSDN: <a href="http://msdn.microsoft.com/en-us/library/cc645077(VS.95).aspx">Deep Zoom File Format Overview</a></li>
<li><a href="http://dragonosticism.wordpress.com/2008/12/11/deepzoomtoolsdll/">Dragonosticism: DeepZoomTools.dll</a> <em>— Develop your own tools for creating Deep Zoom images.</em></li>
<li><a href="http://blog.kapilt.com/2008/11/30/sharing-large-images-openlayers-gsiv-modestmaps-deepzoom-and-python/">Viewing Large Images - OpenLayers, GSIV, ModestMaps, DeepZoom, and Python</a> <em>— Create Deep Zoom Images on Windows, Mac and Linux with <a href="http://www.python.org/">Python</a> and <a href="http://www.pythonware.com/products/pil/">PIL</a>.</em></li>
<li><a href="http://8ninths.com/?p=487">Enter the Seadragon</a> <em>— Introduction to Seadragon and multiscale imaging.</em></li>
<li><a href="http://www.iangilman.com/blog/2008/12/seadragon-on-your-iphone.php">Seadragon On Your iPhone</a> <em>— The story of Seadragon Mobile by Ian Gilman, one of the Seadragon team members.</em></li>
<li><a href="http://blogs.msdn.com/lutzg/archive/2008/11/23/seadragon-ajax-and-deep-zoom.aspx">Seadragon AJAX & Deep Zoom</a> <em>— Comparison of the two implementations by Lutz Gerhard, a LiveLabs product manager.</em></li>
<li><a href="http://www.zoomify.com/express.htm">Zoomifyer EZ</a> <em>— Create Zoomify images for free on your Mac and PC.</em></li>
<li><a href="http://www.zoomify.com/photoshop.htm">Zoomify Photoshop</a> <em>— Create Zoomify images with <a href="http://www.adobe.com/products/photoshop/photoshop/">Adobe Photoshop</a>.</em></li>
</ul>
</li></ol>
http://www.gasi.ch/blog/flex-multiscaleimage-componentMultiScaleImage: Flex Deep Zoom Component2008-12-08T00:00:00+00:00<p>Ever since I published my proof of concept of <a href="/blog/inside-deep-zoom-3/">Deep Zoom in Flash</a>, many people from around the world got in touch with me and told me that they also wanted to play around with this technology. Therefore, for a couple of months now, I’ve been working on an <acronym title="Software Development Kit">SDK</acronym> for <acronym title="Zoomable User Interface">ZUIs</acronym> and multi-scale images for Flash called <a href="http://openzoom.org/">OpenZoom</a>. The SDK is still <em>work in progress</em> and I won’t focus on it today. However, I promise you it will definitely be topic of many posts in the future.</p>
<p>Today I’d like to share with you a first preview of the <a href="http://docs.openzoom.org/sdk/org/openzoom/flex/components/MultiScaleImage.html">MultiScaleImage component</a> that I’ve built on top of the <a href="http://openzoom.org/">OpenZoom SDK</a>.</p>
<blockquote class="flash">
<h2>Introducing the OpenZoom SDK</h2>
This stuff is really old and is only kept around here for archival purposes. Get the latest news and the first public release of the <a href="/blog/openzoom-sdk">OpenZoom SDK</a> in the <a href="/blog/openzoom-sdk">official announcement</a>.
</blockquote>
<h2>Nomen Est Omen</h2>
<p>MultiScaleImage? Sounds familiar? Well indeed, it is the same name Microsoft uses for their <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.multiscaleimage(VS.95).aspx">Deep Zoom Silverlight control</a>. In spirit, the <a href="http://docs.openzoom.org/sdk/org/openzoom/flex/components/MultiScaleImage.html">Flex MultiScaleImage component</a> I’ve built and its Silverlight counterpart are very close. How close? Well, let’s look at a fictional code listing:</p>
<blockquote class="info">
<h2>Code: Silverlight vs Flex</h2>
Microsoft Silverlight
<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><MultiScaleImage</span> <span class="na">x:Name=</span><span class="s">"image"</span> <span class="na">Source=</span><span class="s">"foo/bar.xml"</span><span class="nt">/></span></code></pre></figure>
Adobe Flex
<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><openzoom:MultiScaleImage</span> <span class="na">id=</span><span class="s">"image"</span> <span class="na">source=</span><span class="s">"foo/bar.xml"</span><span class="nt">/></span></code></pre></figure>
</blockquote>
<h2>Under the Hood</h2>
<p>As much as the two look alike from the outside, they differ very much under the hood. Microsoft’s MultiScaleImage control is a native control of the Silverlight runtime and therefore has a very efficient implementation. On the contrary, the <a href="http://docs.openzoom.org/sdk/org/openzoom/flex/components/MultiScaleImage.html">Flex MultiScaleImage</a> component is built on top of ActionScript 3. Nonetheless, I am very pleased with the performance, considering I haven’t spent much time on tuning it yet.</p>
<p>Even though I can praise Microsoft for their Deep Zoom implementation, I dare to say they didn’t do their homework on <acronym title="Application Programming Interface">API</acronym> design. Shortly after Deep Zoom was introduced to the public with the fantastic <a href="http://memorabilia.hardrock.com/">Hard Rock Memorabilia</a> showcase by <a href="http://www.vertigo.com/">Vertigo</a>, dozens of bloggers wrote posts about how to program the Silverlight MultiScaleImage control to create something that somewhat comes close to the Hard Rock site. It was hard because the <acronym title="Application Programming Interface">API</acronym> is cumbersome and confusing. I’d speculate this is one of the reasons we haven’t seen a lot more inspiring Deep Zoom work since.</p>
<p>Flexibility, a <a href="http://docs.openzoom.org/sdk/org/openzoom/flex/components/MultiScaleImage.html">powerful API</a> and ease of use were the top priorities for my implementation of the MultiScaleImage component for Flex. Whether I’ve achieved my goals, I am eagerly waiting to hear from you.</p>
<h2>Batteries Included</h2>
<p>As the title of the article suggests, the Flex MultiScaleImage component has built-in support for <a href="http://msdn.microsoft.com/en-us/library/cc645050(VS.95).aspx">Deep Zoom</a> images. But not only that, it also comes with native support for <a href="http://zoomify.com/">Zoomify</a> and <a href="http://openzoom.org/specs/">OpenZoom</a> images without you having to write one line of code. The latter is a new multi-scale image description format I’ve designed and will probably discuss another time.</p>
<p>To keep things clear, there is one thing that the Silverlight MultiScaleImage control supports that I do not (yet) support: Collections. Personally, I think this is one of the cases where it’s misleading that a MultiScaleImage (singular!) supports collections of images and suddenly has subimages. Therefore, once this functionality will be part of the OpenZoom SDK, it most certainly will have its own component.</p>
<p>In order to prevent a similiar incident to Silverlight’s introduction of their MultiScaleImage component where <a href="http://www.wintellect.com/CS/blogs/jprosise/archive/2008/03/18/mousewheel-zooms-in-silverlight-2-0.aspx">bloggers</a> <a href="http://www.hanselman.com/blog/TheWeeklySourceCode18DeepZoomSeadragonSilverlight2MultiScaleImageMouseWheelZoomingAndPanningEdition.aspx">around</a> <a href="http://silverlight.net/blogs/msnow/archive/2008/07/29/tip-of-the-day-23-how-to-capture-the-mouse-wheel-event.aspx">the</a> <a href="http://blogs.msdn.com/jaimer/archive/2008/03/31/a-deepzoom-primer-explained-and-coded.aspx">world</a> had to write how to add mouse and keyboard navigation to Deep Zoom, I’ve architected the Flex component in a way that has plug & play support for this kind of functionality which is a nice application of the <a href="http://en.wikipedia.org/wiki/Strategy_pattern">Strategy Pattern</a>.</p>
<h2>Getting Started</h2>
<p>I could go on and on about how the Flex MultiScaleImage component works but instead I’ve prepared three demos that let you interactively explore the three core concepts of the component, namely <a href="http://docs.openzoom.org/sdk/org/openzoom/flash/viewport/transformers/package-detail.html">transformers</a>, <a href="http://docs.openzoom.org/sdk/org/openzoom/flash/viewport/controllers/package-detail.html">controllers</a> and <a href="http://docs.openzoom.org/sdk/org/openzoom/flash/viewport/constraints/package-detail.html">constraints</a>.</p>
<blockquote class="info">
<h2>Demo #1: Transformers</h2>
The transformer controls the animation of the viewport. Currently, I've implemented one controller, <a href="http://docs.openzoom.org/sdk/org/openzoom/flash/viewport/transformers/TweenerTransformer.html">TweenerTransformer</a>, that is based on the fantastic <a href="http://tweener.googlecode.com/">Tweener</a> animation library. Check out the demo to see how flexible the architecture is and how easily you can customize the animation of MultiScaleImage. You want to know what's also great about transformers? If for whatever reason you don't need them, you don't pay a single kilobyte penalty for an animation library that is never used.
<a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/transformers/" title="Flex MultiScaleImage Transformers Demo"><img src="http://farm4.static.flickr.com/3159/3090848151_e68b462ebc.jpg" width="500" height="300" /></a>
<a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/transformers/">View Demo</a> | <a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/">View Source</a>
</blockquote>
<blockquote class="info">
<h2>Demo #2: Controllers</h2>
Controllers are the glue between user input and viewport control. The <a href="http://openzoom.org/">OpenZoom SDK</a> features two implementations of controllers at this point, namely a <a href="http://docs.openzoom.org/sdk/org/openzoom/flash/viewport/controllers/MouseController.html">MouseController</a> and a <a href="http://docs.openzoom.org/sdk/org/openzoom/flash/viewport/controllers/KeyboardController.html">KeyboardController</a>. Both have already built-in support for quite some customization but if you need more, feel free to implement your own controller based on <a href="http://docs.openzoom.org/sdk/org/openzoom/flash/viewport/IViewportController.html">IViewportController</a>.
<a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/controllers/" title="Flex MultiScaleImage Controllers Demo"><img src="http://farm4.static.flickr.com/3220/3090848513_70f2a74735.jpg" width="500" height="300" /></a>
<a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/controllers/">View Demo</a> | <a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/">View Source</a>
</blockquote>
<blockquote class="info">
<h2>Demo #3: Constraints</h2>
The constraint controls what states the viewport can reach. Don't want people to zoom out too much? Just add a <a href="http://docs.openzoom.org/sdk/org/openzoom/flash/viewport/constraints/ZoomConstraint.html">ZoomConstraint</a> and set minimum and maximum zoom. Don't want them to lose the scene out of sight? Add a <a href="http://docs.openzoom.org/sdk/org/openzoom/flash/viewport/constraints/VisibilityConstraint.html">VisibilityConstraint</a>. Doesn't the component only support a single constraint? Yes, but the architecture is flexible enough to allow you to combine constraints in a <a href="http://docs.openzoom.org/sdk/org/openzoom/flash/viewport/constraints/CompositeConstraint.html">CompositeConstraint</a> which is an application of the <a href="http://en.wikipedia.org/wiki/Composite_pattern">Composite Design Pattern</a>.
<a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/constraints/" title="Flex MultiScaleImage Constraints Demo"><img src="http://farm4.static.flickr.com/3270/3090847691_b2b3725fff.jpg" width="500" height="300" /></a>
<a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/constraints/">View Demo</a> | <a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/">View Source</a>
</blockquote>
<blockquote class="info">
<h2>Demo #3½: Out of the Box</h2>
After having experienced all the advanced features of the component, let's sit back and enjoy the elegance and purity of the MultiScaleImage component as it comes out of the box. In its plain form, a MultiScaleImage has only one purpose: Displaying images tack sharp no matter how big they appear on the screen. Try for yourself, click on the picture to go to the demo and there just keep on resizing your browser window and enjoy the beauty of simplicity how the image slowly appears sharper and sharper.
<a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/out-of-the-box/" title="Flex MultiScaleImage Out of the Box Demo" target="_blank"><img src="http://farm3.static.flickr.com/2074/1572001177_f2b1783b09.jpg" width="500" height="375" /></a>
<a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/out-of-the-box/">View Demo</a> | <a href="/blog/examples/2008/12/08/flex-multiscaleimage-component/source/">View Source</a>
</blockquote>
<h2>Download</h2>
<p>If you’d like to play around with this component, please <a href="http://openzoom.org/sdk/download/latest/zip/">download the OpenZoom SDK (ZIP)</a>. If you want to find out more, I recommend you to read the <a href="http://docs.openzoom.org/sdk/">OpenZoom SDK API documentation</a> and if you feel particularly adventureous, take a look at the <a href="http://openzoom.org/go/code/">source code.</a></p>
<p>In case you have questions, feedback or you’ve found a bug, feel free to leave a comment down below or file a bug report at the <a href="http://openzoom.org/go/issues/">OpenZoom Bug Tracking System</a>.</p>
<p>Today, I’ve barely scratched the surface of what’s possible with the OpenZoom Flex MultiScaleImage component. In case you have suggestions for topics you would like me to talk more about, again, just leave a comment and I’ll see what I can do.</p>
<p>I can’t wait to see what you create with it. If you have a demo with the MultiScaleImage component, post a comment with a link to your demo.</p>
<h3 style="font-size:100%">Disclaimer</h3>
<p><span style="font-size:85%">This is a preview release. Performance has not been optimized yet. All parts of the component, in particular the API are subject to change.</span></p>
<h3 style="font-size:100%">License</h3>
<p><span style="font-size:85%">The <a href="http://openzoom.org/">OpenZoom SDK</a> is licensed under the <a href="http://www.mozilla.org/MPL/MPL-1.1.html">MPL 1.1</a>/<a href="http://www.gnu.org/licenses/gpl.html">GPL 3</a>/<a href="http://www.gnu.org/licenses/lgpl.html">LGPL 3</a> trilicense.</span></p>
http://www.gasi.ch/blog/inside-deep-zoom-3Inside Deep Zoom – Part III: Deep Zoom in Flash2008-10-13T00:00:00+00:00<p>Welcome back to part three of <em>Inside Deep Zoom</em>. Well, I actually didn't plan to post this tonight but I just got home and found out that tomorrow, Microsoft will <a href="http://micromiel.com/2008/10/13/silverlight-2-ships/">officially ship Silverlight 2</a> along with its acclaimed Deep Zoom technology which…
<q>…enables unparalleled interactivity and navigation of ultra-high resolution imagery.</q></p>
<h2>History</h2>
<p>As the previous articles and the title of this one already hinted it, I did implement Deep Zoom in Adobe Flash. The viewer I’ve built reads standard, unmodified Deep Zoom images directly from Deep Zoom Composer. It turns out, the idea was born when I was doing my internship at <a href="http://zoomorama.com/">Zoomorama</a> and I was tasked to work on multi-scale image rendering. One night at my apartment, I was taking a closer look at Deep Zoom and realized that it works quite similarly and could be easily implemented on top of the work we did at Zoomorama. The very next day, I talked to our people about it and their reaction was very positive. Another twenty-four hours later, I had a working proof-of-concept for rendering Deep Zoom images inside our own viewer. At this point, I'd like to thank my incredible developer teammates David, Olivier & Eric for being part of making this possible.</p>
<h2>Deep Zoom in Flash</h2>
<p>What you are about to see, is one set of original Deep Zoom images (fresh out of Deep Zoom Composer) that runs both inside Flash as well as Silverlight. Going into all the implementation details and the implications of this will probably warrant another blog post. However, I think it's interesting to note that with the work I did, we now have a working solution for creating the same kind of experiences Deep Zoom enables on Microsoft Silverlight, but instead running on Adobe Flash.</p>
<p>What does this mean? First, the reach you get with Flash is massively bigger than what you get with Silverlight. Think about it, when Silverlight 2 ships tomorrow it's market share starts at basically zero. Considering that Flash player 9 has something close to 98% market penetration, this is a big deal.</p>
<p><em>On a side note, if I wouldn't be kept busy with all these ETH projects, Deep Zoom would have been out for weeks before the official launch of Silverlight 2 on a platform that is mature and has an order of magnitude higher reach.</em></p>
<p>Second, unlike Deep Zoom in Silverlight, Deep Zoom in Flash doesn't crash Firefox on my Mac. Enough said, see for yourself.</p>
<blockquote class="info">
<h2>Navigation</h2>
<strong>Click to zoom in</strong> and <strong>shift-click to zoom out</strong> on the picture. <strong>Drag & drop to pan</strong>. The Flash viewer supports <strong>fullscreen</strong> mode with the keyboard shortcut <strong>F</strong> or by right-clicking and choosing <em>Fullscreen</em> in the context menu. Other ways to navigate are also found in the context menu.
</blockquote>
<blockquote class="info">
<h2>Sample #1 – Mont Saint Michel</h2>
<a href="/blog/examples/2008/10/13/inside-deep-zoom/mont-saint-michel/flash/"><img src="http://farm4.static.flickr.com/3011/2933175032_8d570e7e51.jpg" width="500" height="334" alt="Mont Saint Michel" /></a>
<a href="/blog/examples/2008/10/13/inside-deep-zoom/mont-saint-michel/flash/">View in Flash</a> | <a href="/blog/examples/2008/10/13/inside-deep-zoom/mont-saint-michel/silverlight">View in Silverlight Deep Zoom</a> | <a href="/blog/examples/2008/10/13/inside-deep-zoom/mont-saint-michel/mont-saint-michel.jpg">View Original Image</a>
</blockquote>
<blockquote class="info">
<h2>Sample #2 – Billions</h2>
<a href="/blog/examples/2008/10/13/inside-deep-zoom/billions/flash/"><img src="http://farm1.static.flickr.com/200/441355880_94f96bcd5d.jpg" width="500" height="334" alt="Billions & Billions Served." /></a>
<a href="/blog/examples/2008/10/13/inside-deep-zoom/billions/flash/">View in Flash</a> | <a href="/blog/examples/2008/10/13/inside-deep-zoom/billions/silverlight">View in Silverlight Deep Zoom</a> | <a href="/blog/examples/2008/10/13/inside-deep-zoom/billions/billions.jpg">View Original Image</a>
</blockquote>
<blockquote class="info">
<h2>Sample #3 – Deux Femmes</h2>
<a href="/blog/examples/2008/10/13/inside-deep-zoom/deux-femmes/flash/"><img src="http://farm3.static.flickr.com/2084/2101923518_c021e4766f_b.jpg" width="500" height="746" alt="Deux Femmes" /></a>
<a href="/blog/examples/2008/10/13/inside-deep-zoom/deux-femmes/flash/">View in Flash</a> | <a href="/blog/examples/2008/10/13/inside-deep-zoom/deux-femmes/silverlight/">View in Silverlight Deep Zoom</a> | <a href="/blog/examples/2008/10/13/inside-deep-zoom/deux-femmes/deux-femmes.jpg">View Original Image</a>
</blockquote>
<h2>Addendum</h2>
<p>The Flash viewer for viewing Deep Zoom images is still work in progress and therefore you might experience certain jerkiness when navigating around. It is based on a codebase I wrote from the ground up. So far in this blog, I've not only written about things I experimented with but usually also released the source code that goes along with it. This should not be an exception, however, the code is not ready for public consumption and therefore I kindly ask you to keep an eye on this blog for future announcements.</p>
<p>Although I agree mostly with the statement that <a href="http://theflashblog.com/?p=351">Silverlight Deep Zoom is Nothing New for Flash</a> (one could go one step further by saying <em>Silverlight Deep Zoom is Nothing New</em>), I must give credits to the Seadragon team for their innovative & engaging implementation that revived the interest in zooming as a very powerful concept.</p>
<p>Even though the Adobe Flash Platform is still my preferred development platform, I am very happy about the competition Microsoft brought with Silverlight or Sun, Apple & Curl with their respective RIA technologies. I realized this more then ever when I compared Silverlight Deep Zoom to my own implementation on top of the Flash Player. Therefore, I'd like to take this opportunity to petition Adobe for the following two matters:</p>
<blockquote class="info">
<h2>Adobe Petition #1: <br />Native Mouse Wheel Support on Mac OS X</h2>
Adobe touts the Flash Player as leading example for a <em>cross-platform</em> development environment. According to this <a href="http://onflash.org/ted/2008/03/defining-cross-platform.php">article by Ted Patrick</a>, an Adobe Evangelist, <strong>cross-platform</strong> is defined as follows:
<strong>Across operating systems and web browsers:</strong>
<ul>
<li><strong>Identical APIs (classes, methods, properties, types, and return values)</strong></li>
<li><strong>Identical API behavior</strong></li>
<li><strong>Similar performance</strong></li>
<li><strong>Similar installation experience</strong></li>
</ul>
As long as we don't have multi-touch screens available everywhere, the mouse wheel or <em>scrolling</em> is one of the most powerful input gestures for Zoomable User Interfaces.
It's 2008 and the Flash Player still does not allow you to natively listen for mouse wheel events on the Macintosh. Adobe, please fix this <a href="http://www.imdb.com/title/tt0151804/quotes#qt0386876"><em>glitch</em></a>.
</blockquote>
<blockquote class="info">
<h2>Adobe Petition #2: <br />Support for a Background Thread</h2>
As you play around with the Deep Zoom samples in Flash you'll sometimes notice jerkiness in the zooming and panning while the movement should be very smooth. Although there is still a lot of room for optimization that can be done on my part, from my observations, the root cause that is responsible for this jerkiness is when the Flash Player is handling network traffic (when fetching tiles) and doing zooming or panning animations at the same time.
From my research I've gathered that Microsoft's Silverlight supports <a href="http://msdn.microsoft.com/en-us/library/cc221403(VS.95).aspx">Background Workers</a>, a way to run time-consuming tasks on a background thread. I believe a similar mechanism on the Flash Player would allow me to handle network traffic on a background thread while the UI smoothly animates. This is an advanced feature but the Flash Platform is slowly maturing and exposing such mechanisms to developers has to considered.
</blockquote>
<h3>Photography</h3>
<p>The photos <em><a href="http://flickr.com/photos/gasi/2933175032/">Mont Saint Michel</a></em>, <em><a href="http://flickr.com/photos/gasi/441355880/">Billions</a></em> & <em><a href="http://flickr.com/photos/gasi/2101923518/">Deux Femmes</a></em> were all taken by me. If you'd like to see more, I suggest you to check out <a href="http://flickr.com/photos/gasi/">my Flickr stream</a>. All three photos are published under a <a href="http://creativecommons.org/licenses/by-nc-nd/2.0/">Creative Commons Attribution-Noncommercial-No Derivative Works</a> license.</p>
http://www.gasi.ch/blog/inside-deep-zoom-2Inside Deep Zoom – Part II: Mathematical Analysis2008-10-11T00:00:00+00:00<p>Welcome to part two of <em>Inside Deep Zoom</em>. In <a href="/blog/inside-deep-zoom-1/">part one</a>, I talked about the very basic ideas behind Deep Zoom and other multi-scale image technologies. Today, I'd like to continue in a more <em>hands-on</em> fashion and show you how to calculate the properties of the Deep Zoom image pyramid.</p>
<h2>Anatomy of a Deep Zoom Image</h2>
<p>When you convert one of your images, let's say with <a href="http://www.microsoft.com/downloads/details.aspx?familyid=457b17b7-52bf-4bda-87a3-fa8a4673f8bf&displaylang=en">Deep Zoom Composer</a>, into a Deep Zoom image you get an output that looks something along the lines of this:</p>
<p><img src="http://farm4.static.flickr.com/3210/2931554184_d43347540c.jpg" width="500" height="407" alt="bruges" /></p>
<p>In your output folder there's a an XML file called the same as your source image and a folder with the same name and a <code>_files</code> suffix. The <em>image_files</em> folder contains the image data in different subfolders according to the levels of the image pyramid as the next screenshot illustrates:</p>
<p><img src="http://farm4.static.flickr.com/3237/2931572244_141d79c2cc.jpg" width="500" height="429" alt="bruges" /></p>
<p>The key to understanding how Deep Zoom generates this file structure and determines the number of levels, the size of the levels etc., is the XML descriptor file in the top-level folder.</p>
<h2>Deep Zoom Descriptor</h2>
<p>Let's work with this sample XML descriptor:</p>
<pre>
<?xml version="1.0" encoding="UTF-8"?>
<Image TileSize="256" Overlap="1" Format="png"
xmlns="http://schemas.microsoft.com/deepzoom/2008">
<Size Width="4224" Height="3168"/>
</Image>
</pre>
<p>It is remarkably brief but I will show you how you can find out everything you need to know about the pyramid of your image. First, we can see that we have information about the <em>size of the tiles</em> that make up our image pyramid; <em>overlap</em> is something I will talk about later and then we obviously have the <em>original dimensions</em> of the image in the <code><Size></code> element.</p>
<p>Basically, the Deep Zoom image converter generates an image pyramid by taking the original image, dividing its dimensions by two in every step and slicing it into tiles until it reaches the lowest pyramid level with a size of 1x1 pixels.</p>
<h2>Maximum Level</h2>
<p>The first thing we would like to know about the image pyramid is what its <em>maximum level</em> is. As you may have seen in the folder structure of our sample Deep Zoom image, its maximum level is thirteen. The question is:
<q>How many times can we divide the original size of an image by two until we end up with an 1x1 pixel image?</q></p>
<p>To answer this question we'll use logarithms. For example, how many times do you have to divide sixteen by two until you get one? The answer is four which is exactly</p>
<div align="center" style="margin-bottom: 10px;"><img src="http://www.codecogs.com/png.latex?\200dpi&space;\log_2{16}=4" alt="\200dpi \log_2{16}=4" /></div>
<p>For determining the maximum level of a Deep Zoom image we can use the following formula</p>
<div align="center" style="margin-bottom: 10px;"><img src="http://www.codecogs.com/png.latex?\200dpi&space;\lceil{\log_2{\max{\{width,&space;height\}}}}\rceil" alt="\200dpi \lceil{\log_2{\max{\{width, height\}}}}\rceil" /></div>
<p>First, we take the longer side of our original image and calculate the logarithm to the base two. This answer will most likely be a real number because our original image will most probably not have a power-of-two dimension. Since we want to know how many times we have to divide until we get a dimension of 1x1 we round up the result.</p>
<blockquote class="info">
<h2>Code: Maximum Level</h2>
This is the equivalent formula in code, in this case in ActionScript due to the nature of this blog, for determining the maximum level of a Deep Zoom image:
<pre lang="actionscript">
function getMaximumLevel( width : uint, height : uint ) : uint
{
return Math.ceil( Math.log( Math.max( width, height ))/Math.LN2 )
}
</pre>
The division by <code>Math.LN2</code> comes from the fact that ActionScript's built-in logarithm function does not allow us to specify the logarithm base. Therefore we can make use of the following identity
<div align="center"><img src="http://www.codecogs.com/png.latex?\large&space;\log_a{b}&space;=&space;\frac{\log{b}}{\log{a}}" alt="log_a{b} = \frac{\log{b}}{\log{a}}" /></div>
where <em>log</em> denotes the natural logarithm, accessible in ActionScript as <code>Math.log</code>.
</blockquote>
<h2>Pyramid Levels</h2>
<p>Now that we know the maximum level of our image pyramid we can go ahead and calculate the properties of our image pyramid levels with a simple loop.</p>
<blockquote class="info">
<h2>Code: Pyramid Levels</h2>
Since we already know how to compute the maximum level of a Deep Zoom image pyramid, we'll go ahead and compute the dimensions of each level going from the largest to the lowest like so:
<pre lang="actionscript">
function computeLevels( width : uint, height : uint,
tileSize : uint ) : void
{
var maxLevel : uint = getMaximumLevel( width, height )
var columns : uint
var rows : uint
for( var level : int = maxLevel; level >= 0; level-- )
{
// compute number of rows & columns
columns = Math.ceil( width / tileSize )
rows = Math.ceil( height / tileSize )
trace( "level " + level + " is " + width + " x " + height
+ " (" + columns + " columns, " + rows + " rows)" )
// compute dimensions of next level
width = Math.ceil( width / 2 )
height = Math.ceil( height / 2 )
// for bit-shift fans =)
// width = ( width + 1 ) >> 1
// height = ( height + 1 ) >> 1
}
}
</pre>
</blockquote>
<p>For our sample image we'll get the following trace output…</p>
<pre>
level 13 is 4224 x 3168 (17 columns, 13 rows)
level 12 is 2112 x 1584 (9 columns, 7 rows)
level 11 is 1056 x 792 (5 columns, 4 rows)
level 10 is 528 x 396 (3 columns, 2 rows)
level 9 is 264 x 198 (2 columns, 1 rows)
level 8 is 132 x 99 (1 columns, 1 rows)
level 7 is 66 x 50 (1 columns, 1 rows)
level 6 is 33 x 25 (1 columns, 1 rows)
level 5 is 17 x 13 (1 columns, 1 rows)
level 4 is 9 x 7 (1 columns, 1 rows)
level 3 is 5 x 4 (1 columns, 1 rows)
level 2 is 3 x 2 (1 columns, 1 rows)
level 1 is 2 x 1 (1 columns, 1 rows)
level 0 is 1 x 1 (1 columns, 1 rows)
</pre>
<p>…which is identical to the output we get from the <code>ImageTool.exe</code> dump:</p>
<p><img src="http://farm4.static.flickr.com/3190/2931817716_66211f0211_o.png" width="501" height="268" alt="bruges-dump" /></p>
<p><code>ImageTool.exe</code> is a handy command line tool for creating Deep Zoom images and for finding out more about their structure. You find it in your Deep Zoom Composer installation folder among other tools such as <code>SparseImageTool.exe</code> for creating Deep Zoom collections.</p>
<h2>Heavy Mathematics</h2>
<p>In hindsight, calling all of this a <em>mathematical analysis</em> might be an overstatement, I agree. <em>Nonetheless, I can imagine many of you are here because the title looked so delicious in your RSS reader.</em> However, I remember well, when I first looked into multi-scale imaging in general and Deep Zoom in particular, having no prior exposure to concepts such as image pyramids and tile overlap, I was very puzzled when I saw the Deep Zoom composer output such as:</p>
<p><img src="http://farm4.static.flickr.com/3237/2931572244_141d79c2cc.jpg" width="500" height="429" alt="bruges" /></p>
<p>I was asking myself: How are the levels computed? Why did the actual tile sizes (second column: <em>Abmessungen</em>) seemed to look so irregular? I knew I was not the only one when I found blog posts such as <a href="http://blogs.msdn.com/jaimer/archive/2008/03/31/a-deepzoom-primer-explained-and-coded.aspx">A deepzoom primer (explained and coded)</a> by <a href="http://blogs.msdn.com/jaimer/">Jaime Rodriguez</a>, a Microsoft blogger:
<q>A few of the docs I read said the tiles are 256x256, but from peeking through the files generated by the composer I am not convinced; I do know from reading through the internal stuff that there is some heavy math involved here, so I trust they tile for right size :).</q></p>
<p>As you will realize, nothing in here involves <em>heavy math</em>. Nevertheless, it does take some time to put all the pieces together. The information I present you here are things I learned from playing around with Deep Zoom and wished to be around back then. Although there exists an official <a href="http://msdn.microsoft.com/en-us/library/cc645077(VS.95).aspx">Microsoft Deep Zoom File Format Overview</a>, it does not nearly go into as much detail as you find here. Finally, as I couldn't find this background information, I've decided to write it up in this series called <em>Inside Deep Zoom</em> and share it with people interested in finding out more about this technology.</p>
<h2>258 x 25</h2>
<p>Alright, so let's find out what's behind the seemingly weird numbers that are produced by Deep Zoom Composer. It's actually very easy and has to do with something called <em>tile overlap:</em></p>
<p><img src="http://farm4.static.flickr.com/3025/2932771418_82ceddfa0b.jpg" width="500" height="375" alt="overlap" /></p>
<p>Have a look at the highlighted tile. The solid red outline is the actual tile at this position with a size that is defined in the XML descriptor. In our case this is 256 by 256 pixels. Now, what Deep Zoom Composer does is, it actually extends the dimensions of a single tile by adding some pixels of its neighboring tiles (dotted red outline.) How many pixels that is, is also defined in the XML descriptor as <code>Overlap</code> attribute of the <code>Image</code> element. By default, Deep Zoom Composer gives each tile an overlap of one pixel. If you use the command line tools <code>ImageTool.exe</code> or <code>SparseImageTool.exe</code>, both located in your Deep Zoom Composer installation folder, you can specify your own value for tile overlap as something between zero and ten pixels.</p>
<p>One thing I'd like you to notice is the special role of tiles in the corner as well as on the edges. Whereas regular tiles have overlap on all four sides, corner tiles only have overlap on two sides and analogously edge tiles on three sides. This explains all the seemingly strange numbers that come up when you look at the dimensions of the tiles of a certain level.</p>
<p>As to the question of why Deep Zoom uses tile overlap, I cannot conclusively answer it, as I've never developed for Microsoft Silverlight and don't know enough about its rendering engine. Having said that, one thing I've observed in Flash though is that when you reconstruct large images from much smaller image tiles, sometimes there are visible artifacts between the tiles which most probably stem from rounding errors in their positioning. This problem is greatly alleviated by using overlap between the tiles. Due to the similar nature of Flash & Silverlight, I can imagine a similar motive behind using tile overlap in Deep Zoom.</p>
<h2>Accessing & Positioning Tiles</h2>
<p>One of the last things I want to look at is how to access the right tiles…</p>
<blockquote class="info">
<h2>Code: Accessing Tiles</h2>
Getting the URL for a given tile is pretty straightforward when we have the URL to a Deep Zoom descriptor XML:
<pre lang="actionscript">
function getTileURL( level : uint, column : uint,
row : uint ) : String
{
// source: Path to the Deep Zoom image descriptor XML
// extension: Image format extension, e.g <Image … Format="png"/>
return source.substring( 0, source.length - 4 ) + "_files/"
+ String( level ) + "/" + String( column ) + "_"
+ String( row ) + "." + extension
}
</pre>
</blockquote>
<p>…and how to calculate their position within a certain pyramid level:</p>
<blockquote class="info">
<h2>Code: Positioning Tiles</h2>
The following function calculates the position of a tile within a image pyramid level taking into account the tile size as well as the tile overlap:
<pre lang="actionscript">
function getTilePosition( column : uint, row : uint ) : Point
{
var position : Point = new Point()
// tileSize: Dimensions of tile, e.g <Image … TileSize="256"/>
// tileOverlap: Overlap in pixels, e.g. <Image … Overlap="1"/>
var offsetX : uint = ( column == 0 ) ? 0 : tileOverlap
var offsetY : uint = ( row == 0 ) ? 0 : tileOverlap
position.x = ( column * tileSize ) - offsetX
position.y = ( row * tileSize ) - offsetY
return position
}
</pre>
</blockquote>
<h2>Redundant?</h2>
<p>Before finishing part two of <em>Inside Deep Zoom</em>, I'd like to talk about one more thing which does not really have to do with anything above but is nevertheless interesting, mathematical and perhaps even a little bit of a surprise.</p>
<p>Last time I introduced you to image pyramids, this time I showed you how Deep Zoom calculates & structures its pyramid. By now, I hope you've come to appreciate the benefits the image pyramid provides us with for efficiently viewing high-resolution images. The question now is:
<q>How much more space does it really take to store an image pyramid compared to simply storing the original sized image?</q></p>
<p>Although, again, the calculation is not very hard and is based on some simple principles, I was nevertheless surprised by its outcome. For the following calculation I will make some simplifying assumptions, such as taking out the impact of image compression and other factors on file size.</p>
<blockquote class="info">
<h2>Calculation: Redundancy of an Image Pyramid</h2>
<h3>Single Image</h3>
If we assume that the file size of an image is solely given by the image dimensions (<em>w</em> for width & <em>h</em> for height) we get the following result for a single image:
<img src="http://www.codecogs.com/png.latex?\large&space;filesize_{single}&space;=&space;w&space;\cdot&space;h&space;=&space;1&space;\cdot&space;1&space;=&space;1" alt="\large filesize_{single} = width \cdot height = 1 \cdot 1 = 1" />
<h3>Image Pyramid</h3>
For a Deep Zoom image which is power-of-two based we get the following:
<img src="http://www.codecogs.com/png.latex?\large&space;\begin{align*}&space;filesize_{pyramid}&space;&=&space;w&space;\cdot&space;h&space;+&space;\frac{w}{2}&space;\cdot&space;\frac{h}{2}&space;+&space;\frac{w}{4}&space;\cdot&space;\frac{h}{4}&space;+&space;\cdots&space;\\&space;&=&space;1&space;\cdot&space;1&space;+&space;\frac{1}{2}&space;\cdot&space;\frac{1}{2}&space;+&space;\frac{1}{4}&space;\cdot&space;\frac{1}{4}&space;+&space;\cdots&space;\\&space;&\leq&space;\sum_{i=0}^{\infty}&space;(\frac{1}{2}&space;\cdot&space;\frac{1}{2})^i&space;=&space;\sum_{i=0}^{\infty}&space;(\frac{1}{4})^i&space;\\&space;&\leq&space;\frac{1}{1-\frac{1}{4}}&space;=&space;\frac{1}{\frac{3}{4}}&space;=&space;\frac{4}{3}&space;=&space;1.333&space;\hdots&space;\end{align}" alt="\large \begin{align} filesize_{pyramid} &= width \cdot height + \frac{w}{2} \cdot \frac{h}{2} + \frac{w}{4} \cdot \frac{h}{4} + \cdots \\ &= 1 \cdot 1 + \frac{1}{2} \cdot \frac{1}{2} + \frac{1}{4} \cdot \frac{1}{4} + \cdots \\ &\leq \sum_{i=0}^{\infty} (\frac{1}{2} \cdot \frac{1}{2})^i = \sum_{i=0}^{\infty} (\frac{1}{4})^i \\ &\leq \frac{1}{1-\frac{1}{4}} = \frac{1}{\frac{3}{4}} = \frac{4}{3} = 1.333 \hdots \end{align}" />
To solve this equation I've used a <a href="http://en.wikipedia.org/wiki/Geometric_series">geometric series.</a>
</blockquote>
<p>In my opinion quite surprising, is the fact that storing an image pyramid of base two only requires 33% more space than simply storing the original image.</p>
<h2>Deeper and Deeper…</h2>
<p>After a rather basic introduction to multi-scale imaging in <a href="/blog/inside-deep-zoom-1/">part one</a> of <em>Inside Deep Zoom</em>, I hope you gained more insight in this second part and got a better understanding on what's going on behind the scenes. Be sure to come back for part three where I will wrap-up everything I talked about so far, show off some cool demos as well as give an outlook on where Deep Zoom & Co. could take us.</p>
<h3><a name="further-reading">Further Reading</a></h3>
<ul>
<li>Jaime Rodriguez:<a href="http://blogs.msdn.com/jaimer/archive/2008/03/31/a-deepzoom-primer-explained-and-coded.aspx">A Deep Zoom Primer (Explained and Coded)…</a></li>
<li>Microsoft: <a href="http://msdn.microsoft.com/en-us/library/cc645077(VS.95).aspx">Deep Zoom File Format Overview</a></li>
<li>Microsoft: <a href="http://msdn.microsoft.com/en-us/library/cc645050(VS.95).aspx">Deep Zoom</a></li>
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Geometric_series">Geometric Series</a></li>
<li>delicious: <a href="http://delicious.com/gasienica/deepzoom">Daniel Gasienica's bookmarks tagged Deep Zoom</a></li>
</ul>
http://www.gasi.ch/blog/inside-deep-zoom-1Inside Deep Zoom – Part I: Multiscale Imaging2008-10-01T00:00:00+00:00<p>In March 2007 Blaise Aguera y Arcas presented <a href="http://www.ted.com/index.php/talks/blaise_aguera_y_arcas_demos_photosynth.html">Seadragon & Photosynth</a> at <a href="http://ted.com/">TED</a> that created quite some buzz around the web. About a year later, in March 2008, Microsoft released Deep Zoom (formerly <a href="http://livelabs.com/seadragon/">Seadragon</a>) as a «killer feature» of their Silverlight 2 (Beta) launch at <a href="http://visitmix.com/2008/">Mix08</a>. Following this event, there was quite some <a href="http://theflashblog.com/?p=351">back</a> and <a href="http://weblogs.asp.net/jgalloway/archive/2008/03/21/why-silverlight-2-deep-zoom-really-is-something-new.aspx">forth</a> in the blogosphere (damn, I hate that word) about the true innovation behind Microsoft's Deep Zoom.</p>
<p>Today, I don't want to get into the same kind of discussion but rather start a series that will give you a «behind the scenes» of Microsoft's Deep Zoom and similar technologies.</p>
<p>This first part of «Inside Deep Zoom» introduces the main ideas & concepts behind Deep Zoom. In part two, I'll talk about some of the mathematics involved and finally, part three will feature a discussion of the possibilities of this kind of technology and a demo of something you probably haven't seen yet.</p>
<blockquote class="info">
<h2>Background</h2>
As part of my <a href="http://flickr.com/photos/gasi/collections/72157605647818072/">awesome internship</a> at <a href="http://zoomorama.com/">Zoomorama</a> in Paris, I was working on some amazing things (of which you'll hopefully hear soon) and in my spare time, I've decided to have a closer look at Deep Zoom (formerly <a href="http://livelabs.com/seadragon/">Seadragon</a>.) This is when I did a lot of research around this topic and where I had the idea for this series in which I wanted to share my knowledge.
</blockquote>
<h2>Introduction</h2>
<p>Let's begin with a quote from Blaise Aguera y Arcas demo of Seadragon at the TED conference<sup><a href="#ref-1">[1]</a></sup>:
<q>…the only thing that ought to limit the performance of a system like this one is the number of pixels on your screen at any given moment.</q></p>
<p>What is this supposed to mean? See, I have a 24" screen with a maximum resolution of 1920 x 1200 pixels. Now let's take a photo from my digital camera which shoots at 12 megapixel. The photo's size is typically 3872 x 2592 pixels. When I get the photo onto my computer, I roughly end with something that looks like this:</p>
<p><img src="http://farm4.static.flickr.com/3007/2895952539_6706a7d8e8.jpg" width="500" height="375" alt="Large image on small screen." /></p>
<p>No matter how I put it, I'll never be able to see the entire 12 megapixel photo at 100% magnification on my 2.3 megapixel screen. Although this might seem obvious, let's take the time and look at it from another angle: With this in mind we don't care anymore if an image has 10 megapixel (that is 10'000'000 pixels) or 10 gigapixel (10'000'000'000 pixels) since the number of pixels we can see at any moment is limited by the resolution of our screen. This again means, looking at a 10 megapixel image and 10 gigapixel image on the same computer screen should have the same performance. The same should hold for looking at the same two images on a mobile device such as the iPhone. However, important to note is that with reference to the quote above we <em>might</em> experience a performance difference between the two devices since they differ in the number of pixels they can display.</p>
<p>So how do we manage to make the performance of displaying image data independent of its resolution? This is where the concept of an image pyramid steps in.</p>
<h2>The Image Pyramid</h2>
<p>Deep Zoom, or for that matter any other similar technology such as <a href="http://zoomorama.com/">Zoomorama</a>, <a href="http://zoomify.com/">Zoomify</a>, <a href="http://maps.google.com/">Google Maps</a> etc., uses something called an <em>image pyramid</em> as a basic building block for displaying large images in an efficient way:</p>
<p><img src="http://farm4.static.flickr.com/3185/2895500066_4f063f8dcf_o.jpg" width="500" height="250" alt="Image Pyramid" /></p>
<p>The picture above illustrates the layout of such of an image pyramid. The two purposes of a typical image pyramid are to store an image of any size at many different resolutions (hence the term <em>multi-scale</em>) as well as these different resolutions sliced up in many parts, referred to as <em>tiles</em>.</p>
<p>Because the pyramid stores the original image (redundantly) at different resolutions we can display the resolution that is closest to the one we need and in a case where not the entire image fits on our screen, only the parts of the image (tiles) that are actually visible. Setting the parameter values for our pyramid such as number of levels and tile size allows us to control the required data transfer.</p>
<p>Image pyramids are the result of a <em>space vs. bandwidth</em> trade-off, often found in computer science. The image pyramid obviously has a bigger file size than its single image counterpart (for finding out how much exactly, be sure to come back for part two) but as you see in the illustration below, regarding bandwidth it's much more efficient at displaying high-resolution images where most parts of the image are typically not visible anyway (grey area):</p>
<p><img src="http://farm4.static.flickr.com/3110/2896812190_9ee246831d.jpg" width="500" height="375" alt="Multiscale Imaging 2" /></p>
<p>As you can see in the picture above, there is still more data loaded (colored area) than absolutely necessary to display everything that is visible on the screen. This is where the image pyramid parameters I mentioned before come into play: Tile size and number of levels determine the relationship between amount of storage, number of network connections and bandwidth required for displaying high-resolution images.</p>
<h2>Next</h2>
<p>Well, this was it for part one of <em>Inside Deep Zoom.</em> I hope you enjoyed this short introduction to image pyramids & multi-scale imaging. If you want to find out more, as usual, I've collected some links in the <a href="#further-reading">Further Reading</a> section. Other than that, be sure to come back, as the next part of this series – part two – will discuss the characteristics of the Deep Zoom image pyramid and I will show you some of the mathematics behind it.</p>
<h3><a name="further-reading">Further Reading</a></h3>
<ul>
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Pyramid_(image_processing)">Pyramid (image processing)</a></li>
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Gaussian_Pyramid">Gaussian Pyramid</a></li>
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Mipmap">Mipmap</a></li>
<li>Paper: <a href="http://web.mit.edu/persci/people/adelson/pub_pdfs/RCA84.pdf">Pyramid Methods in Image Processing</a> (PDF)</li>
<li>Video: <a href="http://www.youtube.com/watch?v=0ra5tp7K--I">Seadragon Tech Demo</a> (prior to Microsoft acquisition)</li>
</ul>
<h3><a name="references">References</a></h3>
<ul>
<li><a name="ref-1">[1]</a> <a href="http://www.ted.com/index.php/talks/blaise_aguera_y_arcas_demos_photosynth.html">Blaise Aguera y Arcas: Jaw-dropping Photosynth demo</a></li>
</ul>
http://www.gasi.ch/blog/photosynth-pepiniere-des-haiesPhotosynth: Pépinière des Haies2008-08-29T00:00:00+00:00<p>Ever since <a href="http://livelabs.com/">Microsoft Live Labs</a> released <a href="http://photosynth.net/">Photosynth</a>, I wanted to give this revolutionary technology a shot with <a href="http://photosynth.net/view.aspx?cid=F5735DFE-4D28-41FB-B6FA-CFF7775B9C46">my own photos.</a> Due to the tremendous popularity of the site, it took me four attempts to successfully publish <a href="http://photosynth.net/view.aspx?cid=F5735DFE-4D28-41FB-B6FA-CFF7775B9C46">my «synth.»</a> The <a href="http://photosynth.net/view.aspx?cid=F5735DFE-4D28-41FB-B6FA-CFF7775B9C46">«synth»</a> below represents the courtyard of the <a href="http://www.mairie20.paris.fr/mairie20/jsp/site/Portal.jsp?page_id=549">incubator</a> that our company <a href="http://zoomorama.com/">Zoomorama</a> is part of. I am pretty happy with the way it turned my 213 photos (515MB image date) into a 94% «synthy» collage:</p>
<iframe frameborder="0" src="http://photosynth.net/embed.aspx?cid=F5735DFE-4D28-41FB-B6FA-CFF7775B9C46" width="500" height="360"></iframe>
<p>Other than this, I hope that soon I'll be able to talk about the exciting things I've been working on during this summer at <a href="http://zoomorama.com/">Zoomorama</a>. Stay tuned.</p>
http://www.gasi.ch/blog/bonjour-parisBonjour, Paris2008-06-15T00:00:00+00:00<p><a href="http://www.flickr.com/photos/gasi/2578328227/" title="Eiffel & moi by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3042/2578328227_b9900c521c.jpg" width="210" height="280" alt="Eiffel & moi" align="left" style="margin-right:12px;margin-bottom:4px;" /></a>As I mentioned in one of my previous entries, there's something very special about this summer. I am doing an internship at <a href="http://zoomorama.com/">Zoomorama</a> in Paris. Looking back, it probably all came together trough <a href="http://tandem.gasi.ch/">my work on ZUIs</a> and <a href="/blog/">this blog</a>.</p>
<p>Last November someone left a comment on my blog (the first non-robotic one). This someone was <a href="http://www.zoomoblog.com/">Franklin Servan-Schreiber</a>, the founder of <a href="http://zoomorama.com/">Zoomorama</a>, a company exploring ZUIs for the web, and hence <em>my boss</em> in the meantime. Sometime in spring Franklin was doing business in Basel and asked me if he could come to Zürich in order to meet me. I agreed and we had lunch on a beautiful day in Zürich. When he was about to leave, I asked him out of curiosity if I could do my internship at Zoomorama and he said: <em>«Sure, join us in Paris.»</em></p>
<h2>TGV</h2>
<p>Last Tuesday I fulfilled one of my dreams when I entered the <em>TGV </em>to Paris. Imagine yourself travelling with <strong>more than 300km/h</strong> through the French countryside. This video will hopefully give you a little taste:</p>
<object type="application/x-shockwave-flash" width="500" height="375" data="http://www.flickr.com/apps/video/stewart.swf?v=49235" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
<param name="flashvars" value="intl_lang=en-us&photo_secret=65cb8b174b&photo_id=2578241597&show_info_box=true" /></param>
<param name="movie" value="http://www.flickr.com/apps/video/stewart.swf?v=49235" /></param>
<param name="bgcolor" value="#000000" /></param>
<param name="allowFullScreen" value="true" /></param>
<embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/video/stewart.swf?v=49235" bgcolor="#000000" allowfullscreen="true" flashvars="intl_lang=en-us&photo_secret=65cb8b174b&photo_id=2578241597&flickr_show_info_box=true" height="375" width="500" /></embed>
</object>
<h2>Paris L'Est</h2>
<p>I arrived at <em>Paris L'Est</em> where Franklin picked me up. We took the <em>métro</em> to the apartment I will stay at during the summer. Climbing the stairs of Paris' underground was somewhat exhausting but not a surprise considering that I couldn't let my favorite literature getting dusty at home. Besides some clothes, my toothbrush and lots of chocolate for the team, I brought the following books with me, <em>la crème de la crème</em> of my bookshelf so to speak:</p>
<ul>
<li><strong>Design Patterns</strong> by the <em>Gang of Four</em></li>
<li><strong>Refactoring</strong> by Martin Fowler</li>
<li><strong>About Face</strong> by Alan Cooper</li>
<li><strong>Head First Design Patterns</strong> by Elisabeth & Freeman, Bert Bates, and Kathy Sierra<li>
<li><strong>Patterns of Enterprise Application Architecture</strong> by Martin Fowler</li>
<li><strong>Making Things Move!</strong> by Keith Peters</li>
<li><strong>Essential ActionScript 3.0</strong> by Colin Moock</li>
<li><strong>The Mythical Man-Month: Essays on Software Engineering</strong> by Frederick P. Brooks</li>
</ul>
…and possibly a life-saver: a French-German dictionary.
<h2>Rue Bagnolet</h2>
Franklin presented me some options where I could stay during the summer. The most beautiful and closest to the office was an apartment at the student residence <em><a href="http://www.estudines.com/residence-logement-etudiant-paris-13-176.html">Les Estudines Clos Saint Germain</a></em>. Basically, I have a spacious room with a desk, a dining table, a comfortable bed, a little kitchen and a bathroom at the entry. The apartment is located in a very charming part of Paris — <em>XX<sup>e</sup> arrondissement</em> — the 20<sup>th</sup> arrondissement. There's a lot of life in the streets, to the office it's a five minute walk — <em>I love to sleep in</em> — and there are two <em>métro</em> stations nearby, <strong>Maraîchers</strong> and <strong>Alexandre Dumas.</strong>
Lucky me, the lady responsible for the residence was from Poland, so I was able to discuss all the paperwork for the apartment in Polish. The electricity was supposed to be turned on two days before my arrival. Unfortunately, it turned out that the electricians never showed up. Therefore, for the next two nights I was stuck with no light and no hot water. Franklin acted fast, called the company responsible for the mess and later, the two of us got myself a flashlight, an LED light for the bathroom and some candles. It's too hard describe the feeling of taking a cold shower in LED light… however, let me tell you, <em>it turned out to be a geek's secret fantasy…</em>
<a href="http://www.flickr.com/photos/gasi/2581799362/" title="Rue de Bagnolet by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3094/2581799362_13c84998ab.jpg" width="500" height="322" alt="Rue de Bagnolet" /></a>
<h2>Boulot</h2>
Since I needed some hardware to work, Franklin & I went to the city to get myself a brand-new <em>MacBook</em>. I picked up an <em>Apple</em> notebook since it's what I worked with most recently but more importantly so we can test our work not only on <em>Windows</em> but also on <em>Mac OS</em>.
<a href="http://www.flickr.com/photos/gasi/2578215497/" title="MacBook by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3122/2578215497_3fb894144e.jpg" width="500" height="375" alt="MacBook" /></a>
<h2>Zoomorama</h2>
Franklin warned me a few weeks before my arrival that my first day of work will be the attendance of his wedding. Glad to be part of it, I packed my suit & tie and left for Paris. Last Thursday, the first day of work has come. I got up, dressed up nicely and travelled all across the city to <em>La mairie de Neuilly.</em> Exactly, that's Nicolas Sarkozy's former workplace. It was a civil wedding ceremony, very short but very beautiful. Later, I got the chance to meet the entire team in very relaxing setting.
<a href="http://zoomorama.com/">Zoomorama </a>says <em>«hello»</em>, from left to right, these are <strong>Franklin</strong> (founder of Zoomorama), <strong>Anne-Céline</strong> (PR) and <strong>Eric</strong> (developer)
<a href="http://www.flickr.com/photos/gasi/2578281101/" title="Franklin, Anne-Céline & Eric by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3130/2578281101_2302d9e1a6.jpg" width="500" height="335" alt="Franklin, Anne-Céline & Eric" /></a>
Again, from left to right, these are <strong>Jean-Christophe</strong> (marketing), <strong>Olivier</strong> (developer) & <strong>David</strong> (my mentor & project manager)
<a href="http://www.flickr.com/photos/gasi/2578308049/" title="Jean-Christophe by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3117/2578308049_a29a88658a_m.jpg" width="161" height="240" alt="Jean-Christophe" /></a><a href="http://www.flickr.com/photos/gasi/2578241613/" title="Olivier by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3068/2578241613_f72043a9df_m.jpg" width="161" height="240" alt="Olivier" style="margin: 0 8px;" /></a><a href="http://www.flickr.com/photos/gasi/2578241617/" title="David by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3108/2578241617_719c9460ed_m.jpg" width="161" height="240" alt="David" /></a>
On the left is the street that leads to the office and to the right is my desk at Zoomorama's beautiful atelier.
<a href="http://www.flickr.com/photos/gasi/2578213619/" title="Rue des Haies by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3036/2578213619_0e1a886ef4_m.jpg" width="176" height="235" alt="Rue des Haies" style="margin-right: 10px;" /></a><a href="http://www.flickr.com/photos/gasi/2579152094/" title="Zoomorama by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3151/2579152094_97f52c028f.jpg" width="313" height="235" alt="Zoomorama" /></a>
<h2>A Bientôt</h2>
Love letters, money orders & food packages are most welcome at:
<blockquote>
<strong><em>Daniel Gasienica
Les Estudines
Porte 313
105 Rue de Bagnolet
75020 Paris
France</em></strong></blockquote>
Stay tuned, as I will show you more of my summer in Paris. As always, there's more to see on <a href="http://tandem.gasi.ch/#/photos/gasi">tandem</a>.
<em><strong>P.S.</strong> What can I say, the food is great & the girls are hot… ;-)</em>
<a href="http://www.flickr.com/photos/gasi/2578241631/" title="Gabrielle & Anne-Céline by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3263/2578241631_c77b58d7cd.jpg" style="margin-right: 6px;" width="329" height="220" alt="Gabrielle & Anne-Céline" /></a><a href="http://www.flickr.com/photos/gasi/2578325483/" title="Bon Appetit by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3142/2578325483_40c9956d43_m.jpg" width="165" height="220" alt="Bon Appetit" /></a>
<em>By the way, the girl on the left is <strong>Gabrielle</strong> from <a href="http://frenchfling.com/">FrenchFling.com</a> which is a great guide to Paris from an Australian runaway.</em>
</li></li></ul>
http://www.gasi.ch/blog/tandem-et-moitandem & moi2008-06-04T00:00:00+00:00<p>So far, the response to the first release of <a href="http://tandem.gasi.ch/">tandem</a> has been <a href="http://www.zoomoblog.com/zooming/2008/05/a-killer-piece.html">very</a> <a href="http://fr.techcrunch.com/2008/05/30/fr-tandem-une-superbe-interface-pour-naviguer-sur-flickr/">positive</a> and I couldn't be happier. Besides that, there are thousands of ideas spinning around in my head, waiting to be realized. In between bug fixing and prototyping new features, I love to play around with some of <a href="http://tandem.gasi.ch/">tandem's</a> various parameters.</p>
<p>Obviously, software in the real world is built around certain constraints. In <a href="http://tandem.gasi.ch/">tandem</a>, one of these constraints is, <em>at the moment at least</em>, the number of photos that can be displayed on screen. For the current release, I've came to limit this number to around three hundred — 363 to be precise. This is due to the fact that I haven't invested much time into memory management yet, a matter that turned out to be <a href="http://www.gskinner.com/blog/archives/2008/04/failure_to_unlo.html">one of Flash Player 9's dirty little secrets</a>.</p>
<p>Anyway, the real test for <a href="http://tandem.gasi.ch/">tandem</a>, to a certain point for selfish reasons, will always be <a href="http://flickr.com/photos/gasi">my personal Flickr account</a> which currently features <strong>6250 public photos</strong>. Needless to say, nobody who's right in his mind would attempt to load <em>6250 photos</em> into a Flash application, would she? Well, whatever you may think, I did. And, it was a lot of fun. However, see for yourself:</p>
<p><a href="http://farm4.static.flickr.com/3028/2550472403_716ffc95a5_o.jpg" title="tandem & moi by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3028/2550472403_247b2e941b.jpg" width="500" height="313" alt="tandem & moi" /></a></p>
<p><a href="http://farm4.static.flickr.com/3153/2551295664_0404381869_o.jpg" title="tandem & moi by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3153/2551295664_9812bb0704.jpg" width="500" height="313" alt="tandem & moi" /></a></p>
<p><a href="http://farm4.static.flickr.com/3050/2550476447_6fa510e38e_o.jpg" title="tandem & moi by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3050/2550476447_d318edfa4f.jpg" width="500" height="313" alt="tandem & moi" /></a></p>
<p><a href="http://farm4.static.flickr.com/3013/2551299250_d44362f919_o.jpg" title="tandem & moi by Daniel Gasienica, on Flickr"><img src="http://farm4.static.flickr.com/3013/2551299250_dc9be6a505.jpg" width="500" height="313" alt="tandem & moi" /></a></p>
<p><a href="http://farm3.static.flickr.com/2340/2551299928_79479d8c7c_o.jpg" title="tandem & moi by Daniel Gasienica, on Flickr"><img src="http://farm3.static.flickr.com/2340/2551299928_419dc4cf50.jpg" width="500" height="313" alt="tandem & moi" /></a></p>
<p>By the way, this fun little experiment only used up little more than 1.2 GB of memory. Gotta love this little quote by <a href="http://en.wikipedia.org/wiki/C._Northcote_Parkinson">Parkinson</a>:
<q>Software expands to fill the available memory.</q></p>
<p><em><strong>P.S.</strong> Since <a href="/blog/hello-tandem/#comments">someone asked already</a>, the closest to setting up a feed for releases, was setting up a <strong>tandem commits mailing list</strong> at <a href="http://groups.google.com/group/tandem-commits">http://groups.google.com/group/tandem-commits</a>.</em></p>
http://www.gasi.ch/blog/hello-tandemHello, tandem.2008-05-30T00:00:00+00:00<p>I've been thinking & writing <a href="/blog/zui-the-next-step/">a</a> <a href="/blog/zooming-in-flash-flex/">lot</a> <a href="/blog/deep-zoom-microsofts-take-on-bringing-zooming-to-the-web/">about</a> <a href="/blog/zoomable-user-interfaces/">Zoomable User Interfaces</a> and now and then I mentioned <a href="/blog/tandem/">tandem</a>, the project I've been working on & off, exploring the different concepts behind ZUIs.</p>
<p>Well, finally, after the last few weeks of hard work <em>(and a lot of fun)</em>, I'd like to present you on where I am today:</p>
<p><a href="http://tandem.gasi.ch/"><img src="http://farm3.static.flickr.com/2264/2534729779_d1a950c768.jpg" alt="Try tandem" width="500" height="330" /></a>
<em>Try it out for yourself at <a href="http://tandem.gasi.ch/">http://tandem.gasi.ch/</a></em></p>
<h2>RTFM</h2>
<p><a href="http://tandem.gasi.ch/">tandem</a> is very easy to use. Simply use your mouse wheel to zoom in and out on the photos and click & drag to move around. However, in my opinion, there's even a cooler way to zoom around and that is by using your keyboard. Be sure to click inside your browser first and then use the <strong>arrow keys</strong> or <strong>W</strong>, <strong>A</strong>, <strong>S</strong>, <strong>D</strong> to move around and the <strong>+</strong> & <strong>–</strong> keys on your Numpad or alternatively <strong>I</strong> & <strong>O</strong> to zoom in and out. And yeah, if this is to slow for you, simply hold down the <strong>Shift</strong> key while pressing the other keys. Dive in to see a photo in better quality and if you like it, an innocent double click will take you to the original on <a href="http://flickr.com/">Flickr</a> so you can manifest your appreciation for the photo and the artist in a comment.</p>
<h2>You</h2>
<p>Although I love <a href="http://tandem.gasi.ch/">tandem</a>, it is really <em>not</em> about me. If you have a <a href="http://flickr.com/">Flickr</a> account (otherwise hurry up and <a href="http://flickr.com/signup/">get one</a>), <em>you</em> can use <a href="http://tandem.gasi.ch/">tandem</a> to explore <em>your own</em> photos in a new way . Simply type <a href="http://tandem.gasi.ch/#/">http://tandem.gasi.ch/#/</a> and then add your personal Flickr address to view your own photos. For example, my personal address is either <a href="http://tandem.gasi.ch/#/photos/72389028@N00/">http://tandem.gasi.ch/#/photos/72389028@N00/</a> or the much nicer shortcut <a href="http://tandem.gasi.ch/#/gasi/">http://tandem.gasi.ch/#/gasi/</a>.</p>
<p>Check out some of my favorite streams:
<a href="http://tandem.gasi.ch/#/norablue/">http://tandem.gasi.ch/#/norablue/</a>
<a href="http://tandem.gasi.ch/#/tg81/">http://tandem.gasi.ch/#/tg81/</a>
<a href="http://tandem.gasi.ch/#/darkshapesprowl/">http://tandem.gasi.ch/#/darkshapesprowl/</a>
<a href="http://tandem.gasi.ch/#/patric_shaw/">http://tandem.gasi.ch/#/patric_shaw/</a></p>
<h2>Open</h2>
<p>To me, personally, <a href="http://tandem.gasi.ch/">tandem</a> is much more than a fun way to browse through photos. Since I started exploring this idea almost a year ago — <em>can't believe it's been so long</em> — I have learned a lot and I mean <em>a lot</em>. Two complete rewrites later, namely <a href="http://tandem.gasi.ch/1">this</a> and <a href="http://tandem.gasi.ch/">that one</a>, I have come to a point where I am more or less happy with the architectural foundation of the application — <em>which is more than an aspiring Computer Scientist could ask for</em> — and therefore I am making <a href="http://tandem.gasi.ch/">tandem</a> <a href="http://code.google.com/p/tandem/">open source</a> as of today.</p>
<p>I've made this decision early on when I saw my fascination for ZUIs grow stronger from day to day. It means several things to me. First, giving back to the community I've learned so much from, by giving interested individuals a view behind the scenes of how I approach development and to show that Flash applications aren't <a href="http://www.onflex.org/ted/2008/05/adobe-walled-garden-of-knowledge.php">dark magic</a>. Second, this is a longer-term commitment to the ideas behind this project. Where I am spending my summer has a lot to do with this, although believe me when I tell you that this deserves at least another whole post for itself. Third, it's my first step into the fascinating world of open source development.</p>
<h2>Roadmap</h2>
<p>What you <a href="http://tandem.gasi.ch/">see today</a> is not the end, it's just the beginning. I have a lot of ideas that I want to see realized in <a href="http://tandem.gasi.ch/">tandem</a> that, at the moment, are only sketches on paper lying around all over my room. Besides that, there's obviously a lot to fix around memory management <em>(I know, I am quite bad at cleaning up after myself)</em> as well as replacing all my favorite naïve implementations of certain algorithms & data structures. <em>Sorry, <a href="http://www.ti.inf.ethz.ch/people/widmayer.html">Prof. Dr. Widmayer</a>.</em></p>
<h2>Behind the Scenes</h2>
<p>Like I said, the application is <a href="http://tandem.gasi.ch/">online</a>, spread the word and let me know what you think. Then, the entire source code is available at <a href="http://code.google.com/p/tandem/">http://code.google.com/p/tandem/</a> under the <a href="http://www.fsf.org/licensing/licenses/agpl-3.0.html">GNU Affero General Public License Version 3</a> or at <a href="http://tandem.gasi.ch/source/">http://tandem.gasi.ch/source/</a> if you bother <em>checking it out</em>.
<em>Enjoy.</em></p>
<p><em><strong>Attention:</strong> If you appreciate the kind of experiences that <a href="http://tandem.gasi.ch/">tandem</a> and other Flash applications offer, please take a minute to <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=434914">vote on this bug</a> in the upcoming Firefox 3 that basically cripples keyboard support in Flash. Thanks.</em></p>
http://www.gasi.ch/blog/functional-actionscript-3Functional ActionScript – Part III2008-04-02T00:00:00+00:00<p>In <a href="/blog/functional-actionscript-part-1/">Part I</a> of <em>Functional ActionScript</em> I gave a short introduction to <a href="http://en.wikipedia.org/wiki/Functional_programming">functional programming</a> in ActionScript. Then, <a href="/blog/functional-actionscript-part-2/">Part II</a> discussed some functional <acronym title="Application Programming Interface">APIs</acronym> that ActionScript provides and gave an example for each one of them. This last part — Part III — of my series will be a little bit different. This time I would like to take the opportunity to share a small but fascinating example with you that will hopefully serve as an inspiration to look at functional programming more closely.</p>
<h2>Functional vs. Imperative</h2>
<p>Without going into much depth, I'd like to quickly discuss the main conceptual difference between <a href="http://en.wikipedia.org/wiki/Functional_programming">functional</a> and <a href="http://en.wikipedia.org/wiki/Imperative_programming">imperative programming</a> as it applies to the example in this article. In the course <a href="http://www.infsec.ethz.ch/education/ss08/fmfp">Formal Methods & Functional Programming</a> we were taught that often times
<q>functional vs. imperative</q> is actually about <q>what vs. how</q></p>
<p>Programming in an imperative way (i.e., in ActionScript, C, <a href="http://en.wikipedia.org/wiki/Eiffel_(programming_language)">Eiffel</a>, etc.) means that we're telling the machine each step it has to take to get to the result. In that case, it's all about <em>how</em> to do it to get there. On the other hand, if we program in a functional way, it's more about the <em>what</em> to do rather than <em>how</em> to do it to get to the result. Please keep this in the back of your head as you continue reading.</p>
<h2>Haskell</h2>
<p>In summer 2007, just before finishing my first year as undergraduate at ETH, an assistant gave us a sneak peek at the courses in the second year of the Computer Science curriculum. I remember, when we looked at the course <a href="http://www.infsec.ethz.ch/education/ss08/fmfp">Formal Methods & Functional Programming</a> he showed us the following example. I also remember that I was quite impressed.</p>
<blockquote class="info">
<h2>Example: QuickSort</h2>
Here we go: <a href="http://en.wikipedia.org/wiki/Quicksort">QuickSort</a> in <a href="http://en.wikipedia.org/wiki/Haskell_(programming_language)">Haskell</a>…
<pre lang="haskell">
qsort :: Ord a => [a] -> [a]
qsort [] = []
qsort (x:xs) = qsort [y | y <- xs, y < x] ++ [x] ++ qsort [y | y <- xs, y >= x]
</pre>
…compared to QuickSort in <a href="http://en.wikipedia.org/wiki/C_(programming_language)">C</a>.
<pre lang="c">
void qsort(int a[], int lo, int hi) {
{
int h, l, p, t;
if (lo < hi) {
l = lo;
h = hi;
p = a[hi];
do {
while ((l < h) && (a[l] <= p))
l = l+1;
while ((h > l) && (a[h] >= p))
h = h-1;
if (l < h) {
t = a[l];
a[l] = a[h];
a[h] = t;
}
} while (l < h);
t = a[l];
a[l] = a[hi];
a[hi] = t;
qsort( a, lo, l-1 );
qsort( a, l+1, hi );
}
}
</pre>
</blockquote>
<h2>Walk-Trough</h2>
<p>Let’s look at these three lines that reveal us quite a bit about Haskell:</p>
<pre lang="haskell" line="1">
qsort :: Ord a => [a] -> [a]
qsort [] = []
qsort (x:xs) = qsort [y | y <- xs, y < x] ++ [x] ++ qsort [y | y <- xs, y >= x]
</pre>
<p><em>Line 1:</em> This is the type declaration for the function <code>qsort</code>.
Haskell has a very sophisticated type system that goes beyond what we can observe here but this is interesting nonetheless.</p>
<p>First, since <code>qsort</code> a sorting algorithm it takes a list of <em>something</em> (where <code>a</code> is that <em>something</em> and the square brackets denote that it is a <em>list</em> of <em>something</em>) and again returns a list of that <em>something</em>. Compare this to ActionScript, for example. There you have to define explicit types such <code>Number</code>, <code>String</code>, etc. (if you want to take advantage of the <a href="http://en.wikipedia.org/wiki/Type_system#Static_typing">static type checking</a>) and therefore hypothetically have to write a separate sorting algorithm for each one of them, e.g. one that takes a list of <code>Number</code> and one that takes a list of <code>String</code>. Unlike ActionScript 3 (where it’s optional), Haskell is statically typed. To avoid the mess of having to write a sorting routine for every type, Haskell takes advantage of <a href="http://en.wikipedia.org/wiki/Polymorphism_(computer_science#Parametric_polymorphism">parametric polymorphism</a>. All you need to tell Haskell, is that this <em>something</em> called <code>a</code> has a <a href="http://en.wikipedia.org/wiki/Total_order">total order</a>, meaning you can compare two instances of <code>a</code> and determine which one is greater than the other. This is done with the constraint: <code>Ord a =></code></p>
<p><em>Line 2:</em> Here we merely say that if <code>qsort</code> gets an empty list it will return an empty list. This elegant notion of handling different inputs by listing the cases specifically takes advantage of Haskell’s <a href="http://www.haskell.org/tutorial/patterns.html">pattern matching</a> capabilities.</p>
<p><em>Line 3:</em> This is the line where all the <em>magic</em> happens. This is such a great example for the expressiveness of functional programming that I hope I can convey its meaning as clearly as possible.</p>
<p>First, <code>qsort</code> is called with a non-empty list (since the empty list case is covered in <em>line 2</em>) that is defined as <code>x:xs</code>. This notation basically means that the list is split into <code>x</code>, the first element of the list, called <em>head</em>, and <code>xs</code>, the rest of the list, called <em>tail</em>. Second, we select the head of the list <code>x</code> as <a href="http://en.wikipedia.org/wiki/Pivot_element">pivot</a>. Then we recursively call <code>qsort</code> with a new list that looks like this:</p>
<pre lang="haskell">
[y | y <- xs, y < x]
</pre>
<p>Read it as follows: <em>Take all <code>y</code>, where <code>y</code> is an element of <code>xs</code> (the tail or the rest of the list) and <code>y</code> is smaller (mathematically: less than) than the pivot <code>x</code>.</em></p>
<p>Just look at this as QuickSort’s <em>partitioning</em>. The code above actually could be rewritten like this:</p>
<pre lang="haskell">
(filter (< x) xs)
</pre>
<p>I prefer the former variant which is basically syntactic sugar and takes advantage of Haskell’s <a href="http://en.wikipedia.org/wiki/List_comprehension">list comprehension</a> feature. Coming to think of it, my preference is probably influenced by my <a href="http://www.ethz.ch/">ETH</a> background where we were indoctrinated with <a href="http://en.wikipedia.org/wiki/Set-builder_notation">set theory</a>. <em>Chuckle.</em></p>
<p>This was the hard part actually, as the rest works pretty much the same. We concatenate this left-hand side sub-list with the pivot…</p>
<pre lang="haskell">
… ++ [x] ++ …
</pre>
<p>…and then all the preceding with the right-hand side sub-list that is the sorted list of all elements that are greater or equal the pivot <code>x</code>:</p>
<pre lang="haskell">
qsort [y | y <- xs, y >= x]
</pre>
<p>…and we're done.</p>
<h2>Discussion</h2>
<p>If you compare the functional Haskell and the imperative C version of QuickSort you can hopefully see what I was trying to bring across regarding to the concept of <em>what</em> vs. <em>how</em>. Besides this, I can't think of anything to discuss at this point… well, maybe except for questions like <em>What about performance?</em> (which I hope to cover some time in the future) or <em>Is this even a real QuickSort?</em> (…if you ask <a href="http://en.wikipedia.org/wiki/C._A._R._Hoare">C. A. R. Hoare</a> the answer is probably <em>«No.»</em>)</p>
<p>It is important to note that this series (I dare not to say <em>life</em>) is not about <em>programming language X vs. programming language Y</em>. It's about learning different concepts or rather different ways of thinking and apply the best of them in our daily endeavours as programmers. At this point I'd like to thank my buddy <a href="http://424f.com/blog/">Boris</a> (who's busy working on his <a href="http://424f.com/blog/native-google-reader-app-on-your-iphone/">Google Reader App for the iPhone</a> as I am writing this) for the great discussions about software engineering & programming languages that sometimes keep us both awake until 3am. This exchange pushes me to think <em>outside the box</em> every day. Thanks.
<em>And yes, Boris, I will hopefully pick up Python sometime…</em></p>
<h2>One More Thing…</h2>
<p>What would this part of the series on <em>Functional ActionScript</em> be, if it wasn’t even related to ActionScript and I didn’t have the opportunity to show off a little bit?</p>
<blockquote class="info">
<h2>Example: Functional QuickSort in ActionScript</h2>
Here we go:
<pre lang="actionscript">
function quickSort( list : Array ) : Array
{
// non-recursive branch
if( list.length == 0 )
{
// return the empty array if we can't split it more
return []
}
else
{
// choose first element as pivot
var pivot : Number = list[ 0 ]
// slice of the pivot and keep them as rest
var rest : Array = list.slice( 1 )
return(
// sort all numbers…
quickSort(
// …that are less or equal than pivot…
rest.filter(
wrap( lessOrEqualThan( pivot ) )
)
)
// …concatenate them with the pivot…
.concat(
[ pivot ]
)
// …and concatenate all of the previous…
.concat(
// …with all sorted numbers…
quickSort(
// …that are greater than pivot.
rest.filter(
wrap( greaterThan( pivot ) )
)
)
)
)
}
}
function lessOrEqualThan( value : Number ) : Function
{
return(
function( x : Number ) : Boolean
{
return x <= value
}
)
}
function greaterThan( value : Number ) : Function
{
return(
function( x : Number ) : Boolean
{
return x > value
}
)
}
</pre>
<em>For the <a href="/blog/functional-actionscript-part-2/#wrap">definition of <code>wrap</code></a> check out <a href="/blog/functional-actionscript-part-2/#wrap">Functional ActionScript – Part II</a>.</em>
</blockquote>
<p>The example above (and the fact that I wrote it in a few minutes) made me realize for the first time the potential of ActionScript which allows us to write programs in a functional way. The QuickSort code in ActionScript is pretty much the equivalent of the Haskell code at the beginning. You can walk through it with the Haskell <em>Walk-Trough</em> and the comments will hopefully help you in case you get stuck.</p>
<p>But does this actually work? Let's see what happens when we take an unsorted list and run <code>quickSort</code> on it:</p>
<pre lang="actionscript">
var unsortedList : Array = [ 23, 15, 16, 42, 8, 4 ]
quickSort( unsortedList )
//? 4,8,15,16,23,42
</pre>
<p><em>Tada!</em> Works like a charm. Enjoy.</p>
<blockquote class="info">
<h2>Source</h2>
<a href="/blog/examples/2008/04/02/functional-actionscript-part-3/source/">View Source</a> | <a href="/blog/examples/2008/04/02/functional-actionscript-part-3/source/FunctionalActionScript3.zip">Download Source (ZIP, 3KB)</a>
</blockquote>
<h2>Epilogue</h2>
<p>Like I said at the beginning, this was the last part of my series on <em>Functional ActionScript</em>. I hope you enjoyed reading it and learned something new here and there. I definitely did. If would like to learn more about functional programming, I suggest you to check out <a href="http://haskell.org">Haskell</a> or look at some of the links I put together in <em>Further Reading</em>. As I will learn more about all of this myself, I hope I will come back to this really fascinating topic sometime in the future. Stay tuned.</p>
<h2>Further Reading</h2>
<ul>
<li>Haskell.org: <a href="http://www.haskell.org/haskellwiki/Introduction">Introduction to Haskell</a></li>
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Functional_programming">Functional Programming</a></li>
<li>The Effect Generator: <a href="http://effectgenerator.com/blog/?p=6">ActionScript for Functional Programmers</a></li>
<li>~eokyere: <a href="http://eokyere.blogspot.com/2007/09/higher-order-actionscript.html">higher order actionscript</a></li>
<li>Iconara: <a href="http://blog.iconara.net/2008/03/30/separating-event-handling-from-event-filtering/">Separating event handling from event filtering</a></li>
<li>Francis Cheng: <a href="http://blogs.adobe.com/fcheng/2008/01/proper_tail_calls_a_definition.html">Proper Tail Calls, a definition</a></li>
</ul>
<h3>References</h3>
<ul>
<li>Wikipedia (German): <a href="http://de.wikipedia.org/wiki/Haskell_(Programmiersprache)#QuickSort">QuickSort Haskell Example</a></li>
<li>Haskell.org: <a href="http://haskell.org/haskellwiki/Introduction#Quicksort_in_C">QuickSort C Example</a></li>
</ul>
http://www.gasi.ch/blog/functional-actionscript-2Functional ActionScript – Part II2008-03-30T00:00:00+00:00<p>Welcome to the second part of my series on <em>Functional ActionScript</em>. <a href="/blog/functional-actionscript-part-1/">Part I</a> was a brief introduction to some concepts of <a hef="http://en.wikipedia.org/wiki/Functional_programming">functional programming</a> in ActionScript. In this second part, I will present you some examples to ActionScript's built-in functional APIs on <code><a href="http://livedocs.adobe.com/flex/3/langref/Array.html">Array</a></code>. However, first I would like to introduce you to a neat little trick that will save us some typing and make our code more clear.</p>
<h2>Foreplay</h2>
<p>If you take a look at the <a href="http://livedocs.adobe.com/flex/3/langref/Array.html">documentation</a> of the following methods that we will discuss later (<code><a href="http://livedocs.adobe.com/flex/3/langref/Array.html#every()">every</a></code>, <code><a href="http://livedocs.adobe.com/flex/3/langref/Array.html#some()">some</a></code>, <code><a href="http://livedocs.adobe.com/flex/3/langref/Array.html#filter()">filter</a></code>, <code><a href="http://livedocs.adobe.com/flex/3/langref/Array.html#forEach()">forEach</a></code>, and <code><a href="http://livedocs.adobe.com/flex/3/langref/Array.html#map()">map</a></code>) you will notice that they all take a callback that, apart from the return type maybe, has a signature that looks like this:</p>
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">function</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">item</span><span class="o">:*</span><span class="p">,</span> <span class="nx">index</span><span class="o">:</span><span class="nx">int</span><span class="p">,</span> <span class="nx">array</span><span class="o">:</span><span class="nx">Array</span><span class="p">)</span><span class="o">:*</span></code></pre></figure>
<p><a name="wrap">
Being pragmatic, I rarely have use for the last two arguments, <code>index</code> and <code>array</code>. Therefore, I wrote myself a little wrapper function that looks like this:</a></p>
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">function</span> <span class="nx">wrap</span><span class="p">(</span><span class="nx">f</span><span class="o">:</span><span class="nb">Function</span><span class="p">)</span><span class="o">:</span><span class="nb">Function</span>
<span class="p">{</span>
<span class="k">return</span><span class="p">(</span>
<span class="kd">function</span><span class="p">(</span><span class="nx">x</span><span class="o">:*</span><span class="p">,</span> <span class="nx">index</span><span class="o">:</span><span class="nx">int</span><span class="p">,</span> <span class="nx">array</span><span class="o">:</span><span class="nx">Array</span><span class="p">)</span><span class="o">:*</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nx">f</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="p">}</span></code></pre></figure>
<p>Basically, it takes a simple function like:</p>
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">function</span> <span class="nx">even</span><span class="p">(</span><span class="nx">x</span><span class="o">:</span><span class="nx">int</span><span class="p">)</span><span class="o">:</span><span class="nx">Boolean</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nx">x</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span>
<span class="p">}</span></code></pre></figure>
<p>…and returns a function which conforms to the callback signature shown above. Another great example for the power of <a href="/blog/functional-actionscript-part-1/#higher-order-functions">higher-order functions</a>.</p>
<h2>The Party</h2>
<p>After having been introduced to friend number one, namely <code>map</code>, in <a href="/blog/functional-actionscript-part-1/">Part I</a>, I suggest we get to know some new friends but first a small convention:</p>
<blockquote class="info">
<h2>trace</h2>
I will use the following convention to denote trace output:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="c1">//?</span></code></pre></figure>
</blockquote>
<h2>Friend Number Two: every</h2>
<p>If you want to check if all the elements of an <code>Array</code> satisfy a certain condition, just write a test function and drop it into <code>Array.every</code>.</p>
<blockquote class="info">
<h2>Example: Everybody Even?</h2>
For example, let's see if all integer in <code>list</code> are even:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">var</span> <span class="nx">list</span><span class="o">:</span><span class="nx">Array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span></code></pre></figure>
First, we take the <code>even</code> function from above which takes an <code>int</code>, tests if it's even and returns the corresponding <code>Boolean</code>:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">function</span> <span class="nx">even</span><span class="p">(</span><span class="nx">x</span><span class="o">:</span><span class="nx">int</span><span class="p">)</span><span class="o">:</span><span class="nx">Boolean</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nx">x</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span>
<span class="p">}</span></code></pre></figure>
Then, wrap <code>even</code> with <code>wrap</code> — <em>doh!</em> — drop it into <code>Array.every</code> and see what happens:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="nx">list</span><span class="p">.</span><span class="nx">every</span><span class="p">(</span><span class="nx">wrap</span><span class="p">(</span><span class="nx">even</span><span class="p">))</span>
<span class="c1">//? false</span></code></pre></figure>
</blockquote>
<h2>Friend Number Three: some</h2>
<p><code>Array.some</code> works along the lines of <code>every</code> but returns true as soon as one of the elements passes the supplied test.</p>
<blockquote class="info">
<h2>Example: Anybody Odd?</h2>
In the following example, we check if any (meaning: one or more) of the elements in <code>list</code> is odd:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">var</span> <span class="nx">list</span><span class="o">:</span><span class="nx">Array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span></code></pre></figure>
Our test function:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">function</span> <span class="nx">odd</span><span class="p">(</span><span class="nx">x</span><span class="o">:</span><span class="nx">int</span><span class="p">)</span><span class="o">:</span><span class="nx">Boolean</span>
<span class="p">{</span>
<span class="k">return</span> <span class="o">!</span><span class="nx">even</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span>
<span class="p">}</span></code></pre></figure>
The test:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="nx">list</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="nx">wrap</span><span class="p">(</span><span class="nx">odd</span><span class="p">))</span>
<span class="c1">//? true</span></code></pre></figure>
</blockquote>
<h2>Friend Number Four: filter</h2>
<p><code>Array.filter</code> is really handy. Pass it a test function and it returns you an <code>Array</code> with all the elements that passed the test.</p>
<blockquote class="info">
<h2>Example: Who's Even, Who's Odd?</h2>
Get all even elements in <code>list</code>:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">var</span> <span class="nx">list</span><span class="o">:</span><span class="nx">Array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span>
<span class="nx">list</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">wrap</span><span class="p">(</span><span class="nx">even</span><span class="p">))</span>
<span class="c1">//? 2,4,6,8</span></code></pre></figure>
…and all odd elements:
<figure class="highlight"><pre><code class="language-as" data-lang="as"> <span class="nx">list</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">wrap</span><span class="p">(</span><span class="nx">odd</span><span class="p">))</span>
<span class="c1">//? 1,3,5,7,9</span></code></pre></figure>
</blockquote>
<h2>Friend Number Five: forEach</h2>
<p><code>Array.forEach</code> is pretty much the same as <code>Array.map</code> with a subtle but important difference: <code>forEach</code> executes a function on each element in an <code>Array</code> but unlike <code>map</code> has not the purpose to modify the elements. Therefore <code>forEach</code> returns <code>void</code> and <code>map</code> returns an <code>Array</code>. This may or may not sound confusing. However, the following examples will make the difference clear…</p>
<blockquote class="info">
<h2>Example: Hello</h2>
Let's say <em>hello</em> to all elements in <code>list</code>:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">var</span> <span class="nx">list</span><span class="o">:</span><span class="nx">Array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span>
<span class="kd">function</span> <span class="nx">sayHello</span><span class="p">(</span><span class="nx">element</span><span class="o">:*</span><span class="p">,</span>
<span class="nx">index</span><span class="o">:</span><span class="nx">int</span><span class="p">,</span>
<span class="nx">array</span><span class="o">:</span><span class="nx">Array</span><span class="p">)</span><span class="o">:</span><span class="nb">void</span>
<span class="p">{</span>
<span class="kr">trace</span><span class="p">(</span><span class="s2">"Hello, Number"</span><span class="p">,</span> <span class="nx">element</span><span class="p">)</span>
<span class="p">}</span>
<span class="nx">list</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">sayHello</span><span class="p">)</span>
<span class="c1">//? Hello, Number 1</span>
<span class="c1">//? Hello, Number 2</span>
<span class="c1">//? Hello, Number 3</span>
<span class="c1">//? Hello, Number 4</span>
<span class="c1">//? Hello, Number 5</span>
<span class="c1">//? Hello, Number 6</span>
<span class="c1">//? Hello, Number 7</span>
<span class="c1">//? Hello, Number 8</span>
<span class="c1">//? Hello, Number 9</span></code></pre></figure>
In this example I purposely didn't use my carefully crafted <code>wrap</code> function from above to show you how ugly the callback function can end up (line 3–6).
</blockquote>
<h2>Old Friend: map</h2>
<p>We've already met <code>map</code> in the <a href="/blog/functional-actionscript-part-1/#map">first part</a> on <em>Functional ActionScript</em> but I allow myself to introduce her here once again. <code>Array.map</code> takes a function, applies it to all elements in an <code>Array</code> and returns an <code>Array</code> with all modified elements.</p>
<blockquote class="info">
<h2>Example: We're Square</h2>
Square all elements in <code>list</code>:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">var</span> <span class="nx">list</span><span class="o">:</span><span class="nx">Array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span>
<span class="kd">function</span> <span class="nx">square</span><span class="p">(</span><span class="nx">x</span><span class="o">:</span><span class="nx">Number</span><span class="p">)</span><span class="o">:</span><span class="nx">Number</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nx">x</span> <span class="o">*</span> <span class="nx">x</span>
<span class="p">}</span>
<span class="nx">list</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">wrap</span><span class="p">(</span><span class="nx">square</span><span class="p">))</span>
<span class="c1">//? 1,4,9,16,25,36,49,64,81</span></code></pre></figure>
…or take the square root of all elements:
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">function</span> <span class="nx">squareRoot</span><span class="p">(</span><span class="nx">x</span><span class="o">:</span><span class="nx">int</span><span class="p">)</span><span class="o">:</span><span class="nx">Number</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">sqrt</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span>
<span class="p">}</span>
<span class="nx">list</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">wrap</span><span class="p">(</span><span class="nx">squareRoot</span><span class="p">))</span>
<span class="c1">//? 1,1.4142135623730951,1.7320508075688772,2,</span>
<span class="c1">//? 2.23606797749979,2.449489742783178,</span>
<span class="c1">//? 2.6457513110645907,2.8284271247461903,3</span></code></pre></figure>
</blockquote>
<h2>Friends Forever</h2>
<p>When I'm talking about friends, I actually mean <em>friends</em>. Not only will the functions above be nice to you but they also get along very well with each other. Let's see how…</p>
<blockquote class="info">
<h2>Example: Rendez-Vous</h2>
Let's look at this real-world scenario: If any of the elements in <code>list</code> is odd, you want to pick out the even elements, square them and then say hello to them. No sooner said than done:
<!-- line="1" -->
<figure class="highlight"><pre><code class="language-as" data-lang="as"><span class="kd">var</span> <span class="nx">list</span><span class="o">:</span><span class="nx">Array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span>
<span class="k">if</span><span class="p">(</span><span class="nx">list</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="nx">wrap</span><span class="p">(</span><span class="nx">odd</span><span class="p">)))</span>
<span class="p">{</span>
<span class="nx">list</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">wrap</span><span class="p">(</span><span class="nx">even</span><span class="p">))</span>
<span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">wrap</span><span class="p">(</span><span class="nx">square</span><span class="p">))</span>
<span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">sayHello</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">//? Hello, Number 4</span>
<span class="c1">//? Hello, Number 16</span>
<span class="c1">//? Hello, Number 36</span>
<span class="c1">//? Hello, Number 64</span></code></pre></figure>
Isn't the expressivess of this code just beautiful?
<em>Finding a more useless example is left as an exercise to the reader.</em>
</blockquote>
<h2>Doggy Bag (a.k.a Source Code)</h2>
<p>Like what you saw? Have look at it, download it, and play with it!</p>
<blockquote class="info">
<h2>Source</h2>
<a href="/blog/examples/2008/03/31/functional-actionscript-part-2/source/">View Source</a> | <a href="/blog/examples/2008/03/31/functional-actionscript-part-2/source/FunctionalActionScript2.zip">Download Source (ZIP, 3KB)</a>
</blockquote>
<p>Thank you for your attention and stay tuned for Part III of <em>Functional ActionScript</em>…</p>
http://www.gasi.ch/blog/functional-actionscript-1Functional ActionScript – Part I2008-03-29T00:00:00+00:00<p>One of the things I love about studying Computer Science is all the different languages & concepts I am being exposed to. When I started this blog, one of my goals was to present these concepts to people who maybe haven’t yet come across them. In the following series of posts, I will give you a sneak peek into one of these concepts, called <a href="http://en.wikipedia.org/wiki/Functional_programming">Functional Programming</a> and how it can make your life as ActionScript programmer easier.</p>
<p>The inspiration for this series comes from two sources, namely…</p>
<ol>
<li><a href="http://www.rubenswieringa.com/blog/arraytool">An old blog post</a> by <a href="http://www.rubenswieringa.com/">Ruben Swieringa</a></li>
<li>The course <a href="http://www.infsec.ethz.ch/education/ss08/fmfp">Formal Methods & Functional Programming</a></li>
</ol>
<h2>Retrospective</h2>
<p>Back in time, <a href="http://www.rubenswieringa.com/">Ruben</a> wrote a utility function that…
<q>…lets you increase/decrease/multiply/divide<br /> all values of an Array all at once.</q></p>
<p>This idea behind it shows a great deal of understanding from Ruben. I assume that he recognized he was writing the same code over and over again (a violation of the <acronym title="Don't Repeat Yourself">DRY</acronym> principle.) Namely, code to apply a certain operation to the elements of an <code>Array</code>. So he did the right thing and extracted this code into a separate class and released it under the name <code><a href="http://www.rubenswieringa.com/code/as3/flex/ArrayTool/source/">ArrayTool</a></code>.</p>
<p>Feel free to take look at <a href="http://www.rubenswieringa.com/code/as3/flex/ArrayTool/source/">Ruben’s code</a>. Later, I will show you how to take his good idea, take it even one step further and how all of this relates to Functional Programming.</p>
<h2>Functions Are Your Friends</h2>
<p>This semester, I am taking a course called <a href="http://www.infsec.ethz.ch/education/ss08/fmfp">Formal Methods & Functional Programming</a>. As part of the course we are learning a programming language called <a href="http://haskell.org/">Haskell</a>. Haskell belongs to the family of <a href="http://en.wikipedia.org/wiki/Functional_programming">functional programming languages</a>. Besides the fact that it has no assignments in the traditional sense and everything revolves around lists and functions, there’s even more to find out in this very enlightening <a href="http://haskell.org/haskellwiki/Introduction">introduction to Haskell</a>.</p>
<p>Now I will show you what concepts we can take from Haskell (or any other functional programming language for that matter) and apply them in ActionScript. Strange enough, many people are not aware that ActionScript allows us to program in a functionial way to a certain extent. We can do that because functions are first-class citizens in ActionScript and you will see what this means at the end of this article.</p>
<h2>Step 1</h2>
<p>Alright, let us begin with a small example which is probably familiar to most of us…</p>
<blockquote class="info">
<h2>Example: Operations on an Array</h2>
<h3>Code Example 1</h3>
Something like this…
<pre lang="actionscript" line="1">
var list : Array = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
for( var i : int = 0; i < list.length; i++ )
{
list[ i ] = list[ i ] * 2
}
// list = [ 2,4,6,8,10,12,14,16,18 ]
</pre>
…I'd say, is the classical way to double all elements of an <code>Array</code>…
<pre lang="actionscript" line="9">
for( i = 0; i < list.length; i++ )
{
list[ i ] = list[ i ] * 3
}
// list = [ 3,6,9,12,15,18,21,24,27 ]
</pre>
…and that is the classical way to triple all elements of an <code>Array</code>.
<h3>Code Example 2</h3>
Taking the functional approach, the above would look more like this…
<pre lang="actionscript" line="1">
var list : Array = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
list = list.map(
function( x : int, index : int, array : Array ) : int
{
return x * 2
}
)
// list = [ 2,4,6,8,10,12,14,16,18 ]
</pre>
…or this…
<pre lang="actionscript" line="11">
list = list.map( triple )
function triple( x : int, index : int, array : Array ) : int
{
return x * 3
}
// list = [ 3,6,9,12,15,18,21,24,27 ]
</pre>
</blockquote>
<h2>Step 1: Discussion</h2>
<p>Let us compare these two ways of doing things:
First, what has stayed the same in both examples?
The operation we apply to an element of the <code>Array</code> is defined in almost the same way in both approaches:</p>
<ul>
<li><em>Code Example 1</em>, line 5 vs. <em>Code Example 2</em>, line 6</li>
<li><em>Code Example 1</em>, line 11 vs. <em>Code Example 2</em>, line 15</li>
</ul>
<p>However, what did we gain by the functional approach?
Well, we got rid of the loop that looked exactly the same in the first example (line 3 & 9) — great! But what's more important, I believe, is the greater expressiveness of the functional code. For instance, take the following code snippet:</p>
<pre lang="actionscript">list.map( triple )</pre>
<p>You can read it out loud and actually understand what it's doing. This is a very powerful side-effect (no pun intended) of programming in a functional way. I couldn’t say it better than the following quote which appears at the beginning of <a href="http://mitpress.mit.edu/sicp/full-text/book/book.html">Structure and Interpretation of Computer Programs</a>… <q>Programs should be written for people to read,<br /> and only incidentally for machines to execute.</q></p>
<p> </p>
<blockquote class="info">
<a name="map" />
<h2>Friend Number One: Map</h2>
With this example, I've just introduced you to our first friend from Functional Programming: <code>map</code>. The function <code><a href="http://livedocs.adobe.com/flex/3/langref/Array.html#map()">Array.map</a></code> is the functional abstraction of the concept of a loop over an <code>Array</code> that modifies its elements. Give <code>map</code> a function and it will apply it to all the elements of an <code>Array</code>. In the next part of this series I will introduce you to even more friends by presenting a more systematic overview of ActionScripts functional <acronym title="Application Programming Interface">APIs</acronym>.
</blockquote>
<h2>Step 2</h2>
<p>Alright, now we have abstracted the notion of applying an operation to all the elements of an <code>Array</code> with the <code>map</code> function. At the beginning, I told you that I will show you how we could take the idea behind Ruben's <a href="http://www.rubenswieringa.com/code/as3/flex/ArrayTool/source/">ArrayTool</a> and take it to the next step. Well, this was the first step but the example above is still missing one thing which is a way to define the value we want the operation to work with.</p>
<p>Let's take a look at that next.</p>
<h3>Higher-Order Goodness</h3>
<p>For instance, we would like to make the function <code>triple</code> from <em>Code Example 2</em> more generic by allowing it to take any value as multiplier (and by changing its name to <code>multiplyBy</code> on the way there.)</p>
<p>Our first realization will be that we cannot add another argument to <code>multiplyBy</code>, e.g. like this…</p>
<pre lang="actionscript">
function multiplyBy( x : int, index : int,
array : Array, multiplicator : Number ) : int
{
return x * multiplicator
}
</pre>
<p>…because <code><a href="http://livedocs.adobe.com/flex/3/langref/Array.html#map()">Array.map</a></code> only takes functions as argument that have the following form:</p>
<pre lang="actionscript">
function callback( item : *, index : int, array : Array) : void;
</pre>
<p>The bad news is that, if we try nevertheless, Flash will complain…</p>
<pre class="error">
ArgumentError: Error #1063: Argument count mismatch on Examples/$construct/multiplyBy(). Expected 4, got 3.
at Array$/_map()
at Array/http://adobe.com/AS3/2006/builtin::map()
at …]
</pre>
<p>…the good news is that there is a very elegant way (or in some sense, another friend) to solve our problem called <a href="http://en.wikipedia.org/wiki/Higher-order_function">Higher-Order Functions</a>.
Wikipedia says…</p>
<blockquote class="info">
<a name="higher-order-functions" />
<h2>Higher-Order Functions</h2>
<p>In mathematics and computer science, higher-order functions or functionals are functions which do at least one of the following:</p>
<ul>
<li>take one or more functions as an input</li>
<li>output a function</li>
</ul>
</blockquote>
<p>I suggest we have a look at the code first and discuss it afterwards…</p>
<blockquote class="info">
<h2>Code Example 3</h2>
The following example shows a way to make our code from <em>Step 1</em> even more generic…
<pre lang="actionscript" line="1">
var list : Array = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
list = list.map( multiplyBy( 2 ) )
// list = [ 2,4,6,8,10,12,14,16,18 ]
list = list.map( multiplyBy( 3 ) )
// list = [ 3,6,9,12,15,18,21,24,27 ]
function multiplyBy( value : Number ) : Function
{
return function( x : int, index : int,
array : Array ) : Number
{
return x * value
}
}
</pre>
</blockquote>
<h2>Step 2: Discussion</h2>
<p>What happened here? First, <code>multiplyBy</code> now only takes one argument, the value by which we want to multiply our elements (line 9). This is nice but not all that special. In my opinion, where it really get's interesting is the return type of <code>multiplyBy</code>. It's indeed a function and thus makes <code>multiplyBy</code> a higher-order function according to the second part of Wikipedia's definition. If you look at it closely, you will see that <code>multiplyBy</code> now creates exactly the type of function in regards of arguments and return type that <code>Array.map</code> requires. Besides, on the way there, we used an elegant way to define the multiplier <code>value</code> from the outside by using it within the body of the returned function which we want to use for our operations.</p>
<p>The example above embodies the very idea I wanted to bring across in this first part on <em>Functional ActionScript</em>, namely the idea that we can use functions just like any other type and go beyond that by creating functions that return functions themselves (or take functions as arguments). By doing this we can write code that much better expresses its intent, and therefore is easier to understand and ultimately easier to maintain.</p>
<p>In the next part of this series you will see more mechanisms like <code>map</code> that are available to you within ActionScript, as well as some examples on how Functional Programming can make your life easier.
Stay tuned.</p>
<h2>Epilogue</h2>
<p>If you look at <em>Code Example 3</em>, this is exactly what I meant by… <q>Functions are first-class citizens in ActionScript.</q> It's true. ActionScript allows you to create functions, store them in a variable or pass them around like any other type such as <code>Number</code> or <code>Array</code>. Additionally, you can apply these functions in other places. I don't know if you've realized it but event listeners in ActionScript 3 are functions that are passed around and executed with their own context. Such functions are called <a href="http://en.wikipedia.org/wiki/Closure_(computer_science)">closures</a> just as the function returned by <code>multiplyBy</code> is a closure with the argument <code>value</code> as its sole <a href="http://en.wikipedia.org/wiki/Free_variables_and_bound_variables">free variable</a>. Furthermore we can create functions that take functions as arguments and return functions themselves. I think this is really cool. And in fact, this is basically what Functional Programming is all about and the reason why it's called that way. Functional programming languages use functions as their main tool for building abstractions. If you learn more about it, you will see how powerful and how mind-boggling this concept can be at times. Enjoy.</p>
<h3>Further Reading</h3>
<ul>
<li><a href="http://haskell.org/">Haskell</a></li>
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Functional_programming">Functional Programming</a></li>
<li>Joel on Software: <a href="http://www.joelonsoftware.com/items/2006/08/01.html">Can Your Programming Language Do This?</a></li>
<li>John Hughes: <a href="http://www.math.chalmers.se/~rjmh/Papers/whyfp.pdf">Why Functional Programming Matters (PDF)</a></li>
<ul>
</ul></ul>
http://www.gasi.ch/blog/deep-zoom-microsofts-take-on-bringing-zooming-to-the-webDeep Zoom: Microsoft’s Take on Bringing Zooming to the Web2008-03-06T00:00:00+00:00<p>Yesterday, Microsoft announced <a href="http://silverlight.net/">Silverlight Beta 2</a> at <a href="http://visitmix.com/2008/">MIX08</a> which features a technology called <a href="http://blogs.msdn.com/stevecla01/archive/2008/03/05/prepare-to-be-blown-away-with-deep-zoom.aspx">Deep Zoom</a>. Deep Zoom seems to stem from Microsoft research project called <a href="http://labs.live.com/Seadragon.aspx">SeaDragon</a> which I mentioned in the footnote of <a href="/blog/zoomable-user-interfaces/">my post on Zoomable User Interfaces</a>.</p>
<p>Deep Zoom is simply another way of bringing the zooming metaphor to the web, although, I admit, a quite impressive one. However, when I tried it out with my laptop running on battery it felt a little sluggish. I am sure performance will be better when Silverlight 2.0 is released.</p>
<p>Go and check out Deep Zoom for yourself in this impressive demonstration on the <a href="http://memorabilia.hardrock.com/">Hard Rock Memorabilia</a> site.</p></p>
<p>Besides that, Microsoft released a preview of <a href="http://blogs.msdn.com/expression/archive/2008/03/05/download-the-preview-of-the-deep-zoom-composer.aspx">Deep Zoom Composer</a>, a tool for developers to publish galleries with Deep Zoom support.</p>
<p>After all, I think it's time we bring this kind of zooming to the Flash Platform. I am working on it. Stay tuned.</p>
<h3>Further Reading</h3>
<ul>
<li>Wired Science: <a href="http://www.pbs.org/kcet/wiredscience/video/86-photosynth.html">SeaDragon & Photosynth Demo</a></li>
<li><a href="http://www.ted.com/index.php/talks/view/id/129" title="Blaise Aguera y Arcas: Jaw-dropping Photosynth demo">Microsoft SeaDragon & Photosynth Demo @ TED</a></li>
</ul>
http://www.gasi.ch/blog/adobe-air-camp-switzerland-was-a-blastAdobe AIR Camp Switzerland Was a Blast!2008-03-01T00:00:00+00:00<p>I just got up after 18 hours of sleep.
The reason? The <a href="http://aircamp.ch/">Adobe AIR Camp Switzerland</a>.</p>
<p>About three weeks ago, I got a call from Adobe and was asked if I would like to find out more about Adobe's newest addition to the family, namely <a href="http://adobe.com/products/air/">Adobe AIR</a>. The idea was to get the best Swiss web developers together for a two day workshop & contest here in Zurich. I signed up immediately and since I was given the opportunity to bring someone along, I've asked my brilliant college buddy <a href="http://424f.com/">Boris</a> to join me. Although his background is more into heavy-duty C++ programming, he surprisingly agreed to come along.</p>
<p>On Day 1, Thursday, we got an overview of <a href="http://adobe.com/go/air">Adobe AIR</a> by <a href="http://madowney.com/blog/">Mike Downey</a>. He showed us some of the cool applications that have already been built for AIR such as <a href="http://desktop.ebay.com/">eBay Desktop</a>, a <a href="http://labs.adobe.com/showcase/air/nickelodeon.html">Nickelodeon application</a> and an internal Adobe <a href="http://adobe.com/devnet/air/flex/samples.html">employee directory application</a>. Later we had a good introduction to the API new in Adobe AIR by <a href="http://www.webkitchen.be/">Serge Jespers</a>. After that we were given time until Day 2, Friday, 4pm to build a cool application for Adobe AIR.</p>
<p>I worked the 24+ hours — with about 2 hours of sleep in between — on the same idea that I've had for quite a while now: exploring <a href="http://flickr.com">Flickr</a> in a different way.</p>
<p>The approach I took, given the tight time constraint, was to seriously follow a <acronym title="Rapid Application Development">RAD</acronym> paradigm. Surprisingly, it worked out quite well. The application I've built, called <em>Monad</em>, is by no means anything close to finished or even stable but it succeeds in showing some of my ideas of how Zoomable User Interfaces (<acronym title="Zoomable User Interface">ZUI</acronym>) can improve the way we explore information spaces.</p>
<p>Well, in the end — to my surprise — I've won the 4th place in the contest.</p>
<p>Thanks to Adobe for making that event happen!
It was a blast — the people, the food & the atmosphere were awesome!</p>
<p>Especially, I'd like to thank <a href="http://424f.com/">Boris</a> for joining me — it wouldn't have been half the fun without him! I am very proud of him, as he built an impressive RSS reader called <em>AirReader</em> based on the (still unreleased) <a href="http://www.niallkennedy.com/blog/2005/12/google-reader-api.html">Google Reader API</a> using some cool tricks for authentication while having never before touched ActionScript or Flex Builder.</p>
<p>Well, the following are some screenshots of <em>Monad</em> & <em>AirReader</em>…</p>
<blockquote class="info">
<h2>Screenshots: Monad</h2>
<img src="http://farm3.static.flickr.com/2334/2302211649_8383f35207_o.png" width="460" height="336" alt="Monad Screenshot" />
<img src="http://farm4.static.flickr.com/3221/2303008652_fdc3264e49_o.png" width="460" height="336" alt="Monad Screenshot" />
<img src="http://farm3.static.flickr.com/2363/2302211505_bc3fe12571_o.png" width="460" height="336" alt="Monad Screenshot" />
<img src="http://farm3.static.flickr.com/2005/2302211617_07ba36a057_o.png" width="460" height="336" alt="Monad Screenshot" />
<img src="http://farm4.static.flickr.com/3208/2303008348_9dca789990_o.png" width="460" height="280" alt="Monad Screenshot" />
It's cool to see the logo of your application on the desktop among all the other «real» applications – thanks to Adobe AIR!
<h2>Screenshots: AirReader</h2>
<img src="http://farm4.static.flickr.com/3238/2303052154_3e7f9f73a7_o.png" width="460" height="330" alt="AirReader" />
<img src="http://farm3.static.flickr.com/2306/2341700584_2905b4c967_o.png" width="460" height="330" alt="AirReader" />
An impressive RSS reader application by <a href="http://424f.com/">Boris</a>.
</blockquote>
http://www.gasi.ch/blog/zooming-in-flash-flexZooming in Flash & Flex2008-02-05T00:00:00+00:00<p>Have you ever tried to implement zooming that works like <a href="http://maps.google.com/?ie=UTF8&ll=47.365688,8.502731&spn=0.097201,0.240669&z=13&om=0" title="Google Maps">Google Maps</a> where you can use your mouse wheel to zoom to a particular point on the map?</p>
<p>Well, I did and it took me quite some time to get zooming to work like that. Typically, in Flash or Flex, if you scale an object it uses the upper left corner as reference point and this can lead to akward results like the ones in the following example:</p>
<blockquote class="info">
<h2>Example: Zooming Broken</h2>
To zoom, use your mouse wheel or the arrow keys on your Keyboard, especially if you're on a Mac where Flash Player is (still) missing mouse wheel support.
To rotate the object, press and hold the <code>Alt</code> key while scrolling.
Panning works with drag and drop or, again, with the arrow keys on your keyboard.
<a href="/blog/examples/2008/02/05/zooming-broken/">
<img src="/blog/examples/2008/02/05/zooming-broken/zooming-broken.png" width="460" height="320" alt="Example: Zooming Broken" title="" />
</a>
<a href="/blog/examples/2008/02/05/zooming-broken/">View Example</a>
</blockquote>
<p>Basically, any transformation that you apply to a <code>DisplayObject</code> has its origin at the object's registration point. The registration point of an object is typically the upper left corner and cannot be changed directly in ActionScript.</p>
<p>While working on <a href="http://tandem.gasi.ch" title="tandem">tandem</a>, I tried to implement the behavior that normal mapping applications have where you can zoom to the coordinates your mouse points at by scrolling. Oh my, how long it took me. To work around the issue, I nested two <code>Sprites</code> and moved the inner object in such a way that the registration point of the outer one was at the coordinates of the mouse. This way to dynamically change the registration point was a hack at best.</p>
<p>However, the revelation came soon: zooming (or scaling) is a linear transformation. This means I can scale an object and readjust its position afterwards so that it appears as if it has never moved but rather scaled right from the origin I pointed at.</p>
<p>At this point I successfully wrote a function to scale from an arbitrary point on an object. This code was not too long, actually pretty sweet after all my previous, fruitless endeavours. But yesterday, after looking at the source code of <a href="http://www.cs.umd.edu/hcil/jazz/" title="Piccolo">Piccolo</a>, a powerful framework for building Zoomable User Interfaces (ZUI) in Java or C#, I came across an even more elegant solution:</p>
<p><code><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/geom/AffineTransform.html" title="AffineTransform" alt="AffineTransform">AffineTransform</a></code> is the name of the class where the magic lies in Java.</p>
<h2>What Are Affine Transformations?</h2>
<p>Affine transformations are part of the mathematics of linear algebra. Simply put, they are a way to mathematically describe transformations such as scale, rotation, skew and translation. An interesting property of affine transformations is that they preserve the straightness of lines while transforming, so that parallel lines stay parallel.</p>
<p>I won't go into deeper discussion of this topic at this point but if you have time, I recommend you to read the theory behind affine transformations and linear algebra in general. Before this enlightening experience I am about to share here, linear algebra and I were not very close. I took a linear algebra course in first semester computer science but unfortunately the professors never got around using 2D or 3D as basis for their examples which would have been great. They didn't do it because it seemed that they only got excited about n-dimensional vector spaces where n > 5. Too bad.</p>
<h2>Affine Transformations & Flash</h2>
<p>When I made my discovery in the source code of Piccolo, I was about to port the Java <code>AffineTransform</code> class to ActionScript. Then I discovered that ActionScript had a similar class called <code>Matrix</code>.</p>
<p>Basically, any visible object in Flash has a transformation attached to it which can be accessed through a property called <code>transform</code>. What you'll get is a <code>Transform</code> object that gives you, through its <code>matrix</code> property, access to the transformation matrix of a <code>DisplayObject</code>.</p>
<p>Usually, we change the appearance of a <code>Sprite</code> or <code>MovieClip</code> (both inherit from <code>DisplayObject</code>) through properties such as <code>x</code>, <code>y</code>, <code>width</code>, <code>height</code>, <code>scaleX</code>, <code>scaleY</code> or <code>rotation</code>. These properties can be seen as a high-level abstraction of the underlying transformation matrix of a <code>DisplayObject</code>.</p>
<p>Basically, if you need more low-level access, for example to implement zooming or rotating around a particular point, use the <code>Transform</code> and <code>Matrix</code> class.</p>
<p>Enough theory, let's see how it's done.</p>
<blockquote class="info">
<h2>Example: Zooming Done Right</h2>
<a href="/blog/examples/2008/02/05/zooming/"><img src="/blog/examples/2008/02/05/zooming/zooming.png" width="460" height="320" alt="Example: Zooming" /></a>
<a href="/blog/examples/2008/02/05/zooming/">View Example</a> | <a href="/blog/examples/2008/02/05/zooming/source/">View Source</a> | <a href="/blog/examples/2008/02/05/zooming/source/Zooming.zip">Download Source (ZIP, 5KB)</a>
</blockquote>
<blockquote class="info">
<h2>Code Walk-Trough</h2>
Let us go step by step through the code of the example class called <code>ZoomCanvas</code> that you'll find in the <a href="/blog/examples/2008/02/05/zooming/source/" title="Example: Zooming Source Code">source</a> of the example above.
Let's say you have an object you want to scale at a certain point.
First, you get its transformation <code>Matrix</code>:
<pre lang="actionscript" line="44">
affineTransform = transform.matrix
</pre>
Then you move the object to the origin of the point you want to scale from:
<pre lang="actionscript" line="49">
affineTransform.translate( -originX, -originY )
</pre>
After that, you are safe to scale the object:
<pre lang="actionscript" line="52">
affineTransform.scale( scale, scale )
</pre>
Then, you simply move the object back to its original position:
<pre lang="actionscript" line="55">
affineTransform.translate( originX, originY )
</pre>
In the end, you apply the new transformation to the object
<pre lang="actionscript" line="59">
transform.matrix = affineTransform
</pre>
</blockquote>
<p>Since I love clear and simple code, I was very pleased with the result.
From my observations this method for transforming an object seems at least as fast as the conventional way of doing it.</p>
<p>I hope this helps you as much as it did help me.</p>
<h3>Further Reading</h3>
<ul>
<li>Senocular.com: <a href="http://www.senocular.com/flash/tutorials/transformmatrix/" title="Understanding the Transformation Matrix in Flash">Understanding the Transformation Matrix in Flash</a></li>
<li>ActionScript Documentation: <code><a href="http://livedocs.adobe.com/labs/flex3/langref/flash/geom/Matrix.html">Matrix</a></code> class</li>
<li>Piccolo: <a href="http://www.cs.umd.edu/hcil/jazz/learn/graphics.shtml#transformations">Geometric Transformations</a></li>
<li>Piccolo: <a href="http://www.cs.umd.edu/hcil/piccolo/download/index.shtml">Download Source</a></li>
<li>Lawrence Kesteloot: <a href="http://www.teamten.com/lawrence/graphics/homogeneous/">Homogeneous Coordinates</a></li>
</ul>
http://www.gasi.ch/blog/photospread-a-spreadsheet-for-managing-photosPhotoSpread: A Spreadsheet for Managing Photos2007-11-19T00:00:00+00:00<p>As you may know, during daytime I study computer science at <a href="http://www.ethz.ch/">ETH Zürich</a>.
Every Monday <a href="http://www.inf.ethz.ch/">our department</a> hosts the <a href="http://www.inf.ethz.ch/news/colloquium/">Computer Science Colloquium</a>.</p>
<p>Normally these talks are held in the basement of our department’s building.
Today was different. The talk took place in the seldomly used <em>Audi Max</em>.
The reason? Today’s special guest was <a href="http://infolab.stanford.edu/people/hector.html">Hector Garcia-Molina</a> from <a href="http://stanford.edu/">Stanford
University</a>. Last Saturday, Prof. Garcia received an honorary doctor from
ETH Zürich for his outstanding academic achievements and contributions to our
field.</p>
<p>The inspiring talk he gave today was:</p>
<blockquote class="info">
<h2 id="photospread-a-spreadsheet-for-managing-photos">PhotoSpread: A Spreadsheet for Managing Photos</h2>
<p>PhotoSpread is a spreadsheet system for organizing and analyzing photo
collections. It extends the current spreadsheet paradigm in two ways:</p>
<ul>
<li>PhotoSpread accommodates sets of objects (e.g., photos) annotated with tags
(attribute-value pairs). Formulas can manipulate object sets and refer to tags.</li>
<li>Photos can be reorganized (tags and location changed) by drag-and-drop
operations on the spreadsheet.</li>
</ul>
<p>The PhotoSpread design was driven by the needs of field biologists who have
large collections of annotated photos. In the talk I will describe the
PhotoSpread functionality and the design choices made. I will also describe
some of the other data management tools we have developed with field
biologists.</p>
<p><img src="http://farm3.static.flickr.com/2093/2059596840_b343381859.jpg" title="PhotoSpread screenshot" alt="" height="283" width="500" /></p>
<p><del>Unfortunately I didn’t take any photos during the demo of the application.</del>
<ins>Added screenshot taken from the <a href="http://dbpubs.stanford.edu/pub/showDoc.Fulltext?lang=en&doc=2007-28&format=pdf&compression=&name=2007-28.pdf">original paper</a>.</ins></p>
</blockquote>
<p>Basically, on the surface the tool looks like Microsoft Excel. On the left side,
PhotoSpread sports a grid with cells as you know it from Excel while at the right
hand side you can view the details of the cell that is currently selected within
the grid. The special thing about those cells is that you can put photos inside.
Actually, as Prof. Garcia noted, you could put any kind objects inside, but at this
point the tool focuses on the workflow with photos. Just as with most other modern
photo management tools one can attach metadata to these photos.</p>
<p>You may say, everyone knows Excel and most people are familiar with tools like
Picasa, Aperture or Lightroom. What’s so special about PhotoSpread?
PhotoSpread stands out exactly in the way as it combines the concepts &
mechanisms of those two worlds. As I said, it is difficult to explain without
actually seeing it live but imagine you have some photos of people and import
them into the tool. All these photos can have arbitrary key-value pairs
attached to them. Let’s say we’ve tagged all those people with their age.
Now you can select a cell within PhotoSpread and enter a formula just as you
would in Excel. For example…</p>
<blockquote>
<p><code class="highlighter-rouge">average(B1[age])</code></p>
</blockquote>
<p>…where B1 is the cell with the photos of your people. The tool would now
dynamically display the average age of those people in the cell where you
entered the formula. This is one of the more basic use cases and there are far
more interesting ones as Prof. Garcia showed us. For example if you drag a photo
into a cell with an existing formula then the tool, if possible, matches the
attributes in the newly added object to satisfy the formula or creates a union
with the objects already present in the cell.</p>
<p>In the context of scientists who are working in the field collecting lots and
lots of data such as photos, PhotoSpread really brings big improvements to the
workflow. Before, the field biologists Prof. Garcia and his team were working
with used a conventional photo management tool to handle the photos themselves
and additionally to that Excel to classify and analyze the data associated with
them.</p>
<p>If you want to find out more about the tool and the ideas behind it, I suggest
you take a look at the following paper by Prof. Garcia and his team:</p>
<blockquote class="info">
<h2 id="paper">Paper</h2>
<p><a href="http://dbpubs.stanford.edu/pub/showDoc.Fulltext?lang=en&doc=2007-28&format=pdf&compression=&name=2007-28.pdf">PhotoSpread: A Spreadsheet for Managing Photos</a></p>
</blockquote>
<p>Interesting to note is also the technology this version of PhotoSpread was
built on. From what I saw and heard, the application is built with <a href="http://www.adobe.com/products/flex/">Adobe Flex</a>
running on top of the <a href="http://www.adobe.com/go/air">Adobe AIR</a> client, using its built-in database for
managing the photos. First I was flabbergasted by this fact because I am also
working on improving the workflow with photos with my work on <a href="http://tandem.gasi.ch/2">tandem</a> which
also happens to be built with Flex.
Second, I am glad to have witnessed that the things I care about also have a
place in academia.</p>
<p><em>Thank you, Prof. Garcia for this inspiring session.</em></p>
<blockquote class="info">
<h2 id="update-november-27-2008">Update: November 27, 2008</h2>
<p>Enjoy this video of PhotoSpread in action:</p>
<object width="500" height="400">
<param name="movie" value="http://www.youtube.com/v/rf7rA-roBlM?fs=1&hl=en_US&rel=0" /></param>
<param name="allowFullScreen" value="true" /></param>
<param name="allowscriptaccess" value="always" /></param>
<embed src="http://www.youtube.com/v/rf7rA-roBlM?fs=1&hl=en_US&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="500" height="400" />
</embed>
</object>
</blockquote>
http://www.gasi.ch/blog/zui-the-next-stepZUI: The Next Step2007-11-08T00:00:00+00:00<p>Yesterday I talked a little bit about zooming user interfaces. Today I came
across this jaw-dropping video of Jeff Han’s Perceptive Pixels Multitouch
Display. It perfectly demonstrates what experience is possible if we put
together multitouch technology with zoomable user interfaces.</p>
<p>Enough talked. See for yourself:</p>
<iframe class="youtube-player" type="text/html" width="500" height="306" src="http://www.youtube.com/embed/ysEVYwa-vHM" frameborder="0">
</iframe>
<p>My vision for <a href="http://tandem.gasi.ch/">tandem</a> is much more humble. It is about improving the
experience for exploring Flickr. Part of that is harnessing the power of
zoomable user interfaces. Another part, that I haven’t talked about yet,
are filtering & layouting techniques that enable us to digest information in
a more intuitive way. What I am really trying to do is bring together the
concepts others have explored before me and apply them to Flickr. It’s as
simple as that. In the spirit of Sir Isaac Newton:</p>
<blockquote>
<p>If I have seen further it is by standing on the shoulders of Giants.</p>
</blockquote>
<p>P.S. Today I’ve got my <a href="/blog/zoomable-user-interfaces/#comment-142">first comment</a> (that is not spam) on this blog.
Besides that, it is an interesting one, too. I am glad that I am not the only
one being fascinated by the potential of ZUI. Franklin Servan-Schreiber of
<a href="http://www.zoomorama.com/">Zoomorama</a> is also working on bringing zooming to the web. To get an idea
of what he’s up to, check out <a href="http://zoomorama.typepad.com/zooming/2007/09/index.html">this introductory blog post</a>
and <a href="http://mail.zoomodev.com/ab5c3015b9361c504a573bdc8166f449">this beautiful example</a> of his work. Thanks, Franklin.</p>
<h3 id="more">More</h3>
<ul>
<li>TED | <a href="http://www.ted.com/index.php/talks/view/id/65">Jeff Han: Unveiling the genius of multi-touch interface design</a> –
The original talk I knew before I found the one shown above.</li>
<li><a href="http://billaut.typepad.com/jm/2007/10/connaissez-v-13.html">Zoomomail Demo by Franklin</a> (French)</li>
<li><a href="http://www.zoomorama.com/">Zoomorama Website</a></li>
</ul>
http://www.gasi.ch/blog/zoomable-user-interfacesZoomable User Interfaces2007-11-07T00:00:00+00:00<p>In my <a href="/blog/tandem/">last post</a> I talked about the shortcomings of <a href="http://flickr.com/">Flickr</a> to provide you with enough context on where you've been, where you are and where you are going. A particularly interesting concept to adress this issue and provide users with more context are <a href="http://en.wikipedia.org/wiki/Zooming_User_Interface" title="Zoomable User Interface">Zoomable User Interfaces (ZUI)</a>.</p>
<h2>What are ZUIs?</h2>
<p>If you use Google Maps, Google Earth or own an Apple iPhone you've already been exposed to zoomable user interfaces. Although these applications are fairly new — as far as I've read about the history of ZUI — the concepts behind them are not.</p>
<p>In 1963 <a href="http://de.wikipedia.org/wiki/Ivan_Sutherland">Ivan Sutherland</a> developed Sketchpad, today considered to be the first graphical user interface, which also featured a zoom function. <a href="http://en.wikipedia.org/wiki/Jef_Raskin">Jef Raskin</a>, the creative mind behind the Apple Macintosh user interface and author of <a href="http://en.wikipedia.org/wiki/The_Humane_Interface">The Humane Interface</a>, was a a big proponent of ZUI. Part of his legacy lives on in the project <a href="http://en.wikipedia.org/wiki/Archy">Archy</a> where the concept of zooming plays an important role. <a href="http://en.wikipedia.org/wiki/Aza_Raskin">Aza Raskin</a>, Jef's son and founder of <a href="http://humanized.com/">Humanized</a>, gave a talk at Google called <a href="http://video.google.com/videoplay?docid=-6856727143023456694" title="Away with Applications: The Death of the Desktop">«Away with Applications: The Death of the Desktop»</a>. If you've got some time at hand, I definitely recommend watching the whole session but the reason I am referring to it is the <a href="http://video.google.com/videoplay?docid=-6856727143023456694#1h05m00s">following sequence</a> where he talks about zoomable user interfaces. The main point to take away from Aza's discussion about ZUI is</p>
<p align="center"><em>«Where it is, is what it is.»</em></p>
<h2>What Does This Mean For Us?</h2>
<p>Part of what he is trying to say is that we should take advantage of the excellent spatial awareness we, as human beings, possess. In regards to <a href="http://flickr.com/">Flickr</a> and <a href="http://tandem.gasi.ch/">tandem,</a> it also means we should take advantage of certain mental models of entities we're used to such as photos, sets, collections and their relationships. Photos reside inside sets and sets inside collections. Why not represent this on the screen?</p>
<p>I think we should be able to navigate through Flickr's data model through zooming, not through pages. We should zoom into a collection and discover its sets. Then we zoom into the sets in front of us and discover the individual photos that belong to it. If we get the<em> feeling of being lost</em> we can just zoom out and find ourselves in the place where we came from. How awesome would that be? It's definitely a big step forward from using the back button which sometimes doesn't take us back to the exact place we were before. And because zooming is so continuous — that is without a page refresh — it is even more intuitive.</p>
<p>Imagine you're walking in a city and you take the wrong turn. When you realize it, do you suddenly re-appear at the block you were two minutes ago? No. You turn around and find yourself in more familiar surroundings. It's a continous, smooth process just like zooming. Unlike the sudden context switch which a page refresh represents. Browsing something rich such as Flickr by clicking through pages is like walking down the street blinking permanently.</p>
<p>You may have noticed that <a href="http://tandem.gasi.ch/" title="tandem">this early version</a> of tandem does not quite implement the principles I discussed in this post. Although the zooming is quite continuous, it is indirect and only between two states.</p>
<p>To get an idea of how the zooming might look in the future, check out this <a href="http://tandem.gasi.ch/2" title="tandem">bleeding edge version of tandem</a>. <strike>However, be warned. </strike>Due to a nasty bug in the Flash Player for the Macintosh, the mouse wheel is not recognized without the use of a nasty hack<strike> (which I have not yet implemented.)</strike><em> [Fixed.]</em> Furthermore, zooming from collections into sets and from there to the photos is not realized (yet.) This is certainly the part I will be spending most of my time on in the future.</p>
<p>Another subtle but interesting difference you may notice between the <a href="http://tandem.gasi.ch/1" title="tandem">two</a> <a href="http://tandem.gasi.ch/2" title="tandem">versions</a> of tandem is that I got rid of the paging navigation. I felt proud to have realized its shortcomings myself until I found out that I was obviously <a href="http://www.humanized.com/weblog/2006/04/25/no_more_more_pages/" title="No More Pages — Humanized">not the first one</a> to do so. But let's talk about this another time…</p>
<h3>Inspiration</h3>
<ul>
<li><a href="http://video.google.com/videoplay?docid=-6856727143023456694">Away with Applications: The Death of the Desktop</a>
Aza Raskin's talk at Google.</li>
<li><a href="http://rchi.raskincenter.org/demos/zoomdemo.swf" title="Raskin Center Zoom Demo">Raskin Center Zoom Demo</a>
The demo from Aza's talk. Warning: 8 megabyte Flash file!</li>
<li><a href="http://www.ted.com/index.php/talks/view/id/129" title="Blaise Aguera y Arcas: Jaw-dropping Photosynth demo">Microsoft SeaDragon & Photosynth Demo @ TED</a></li>
<li><a href="http://www.airtightinteractive.com/projects/postcardviewer/example/">Airtight Interactive: Postcard Viewer
</a></li>
</ul>
<h3>Further Reading</h3>
<ul>
<li><a href="http://www.cs.umd.edu/hcil/piccolo/learn/Toolkit_Design_2004.pdf">Toolkit Design for Interactive Structured Graphics</a>
Bederson, B. B., Grosjean, J., & Meyer, J. (2004).
IEEE Transactions on Software Engineering, 30 (8), pp. 535-546.</li>
</ul>
http://www.gasi.ch/blog/tandemtandem2007-10-29T00:00:00+00:00<p>For the past three months I've been thinking a lot about user interfaces & user experience. The thing that started me on that, I think, is <a href="http://flickr.com/">Flickr</a>. Flickr as you all know «is almost certainly the best online photo management and sharing application in the world.» I admit, <a href="http://flickr.com/photos/gasi/">I am a big fan</a>.</p>
<p>However, as good as Flickr may be, there's room for improvement. Considering that Flickr has such a rich data model underneath — photos, tags, people, groups — the ability to interact with it are frankly speaking quite crippled. I think that the page-based interaction metaphor we're used to from the web is not a good match for something like Flickr. The following image provides you with a remarkable insight into Flickr's complex data model & interactions:</p>
<p><a href="http://www.flickr.com/photos/bryce/58299511/" title="photo sharing"><img src="http://farm1.static.flickr.com/24/58299511_2bcff18db2.jpg" class="flickr-photo" /></a></p>
<p>The main problem I see is <strong>context</strong>. Considering myself a heavy user, many times I browse Flickr, in a sense I get «lost.» Not literally but nonetheless I wish the platform would provide me with more feedback on where I am in respect to time, location, people, groups etc. While the underlying model is very rich, its visualization and interactions on the surface are not. At least not as much as they could be. During the recent months I asked myself the following question, which in itself are probably not very relevant but illustrate the problem I want to start a discussion on.</p>
<p>In Flickr…</p>
<ul>
<li>…when I am looking at a photo, why can I only go to the next or the previous? Why can't I easily skip twelve?</li>
<li>…why is it possible to comment on a photo, not even seeing it because I have to scroll down to the comment field?</li>
<li>…why is it not possible to view the photos in a different order? In order of popularity for example.</li>
<li>…why can't I easily rearrange, filter or group them so that they are easier for me to explore? For example, by events, people or location.</li>
<li>…</li>
</ul>
<p>Many open questions. Although each single problem they point out is probably easy to solve or already solved (although not easily accessible), doesn't it question the whole structure itself? Some of these problems arise because Flickr is trying to push its rich data model into the rigid structure of pages. A metaphor that works well with textual information. Flat information. However, it was never meant to provide means to handle a model on which for example Flickr is based on. Flickr is not flat. If you don't believe me, check out the illustration above.</p>
<p>It is apparent that Flickr is not a <em>web page</em> but rather a <em>web application.
</em>Now the question is: Why do we interact with it as if it was a <em>web page</em>?</p>
<p>I honestly don't know. But I know we can do better.</p>
<p>Having followed the uprise of «Rich Internet Applications» very closely myself, I sat down, did a lot of brainstorming and started to come up with something that tries to improve the situation at least a little bit. Ever since I wrote my final paper in high school called «<a href="/publications/maturaarbeit/architecting-rich-internet-applications-paper-daniel-gasienica.pdf">Architecting Rich Internet Applications</a>», times have changed quite a bit. The technology for this form of applications has improved drastically but more importantly, penetration & user acceptance have increased dramatically due to their ease of use and the (still ongoing) shift in perception within big companies towards better user experience.</p>
<p>For today, I suggest I jump straight ahead and give a glimpse on an early version of what I came up with. In future posts I will try to dive deeper into the discussion of which I barely scratched the surface today.</p>
<p>Enter <a href="http://tandem.gasi.ch/">tandem</a>. Tandem is work in progress and a place where I experiment with concepts I will discuss on this blog. Check it out, let me know what you think and try to form an opinion on the areas where it successfully addresses the problems I mentioned above but more importantly also on those where it fails.</p>
<p align="center">
<a href="http://tandem.gasi.ch/">tandem — exploring the Flickr universe</a>
</p>
<p>Enjoy & stay tuned.</p>
<p><em>P.S. Thanks to <a href="http://www.flickr.com/photos/bryce/" title="Bryce Glass">Bryce Glass</a> for his wonderful diagram.</em></p>