<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Tutorial on Cameron Little's Website</title><link>https://camlittle.com/tags/tutorial/</link><description>Recent tutorial on Cameron Little's Website</description><image><title>Tutorial on Cameron Little's Website</title><url>https://camlittle.com/profile.jpg?s=144</url><link>https://camlittle.com/tags/tutorial/</link><width>144</width></image><language>en-us</language><managingEditor>cameron@camlittle.com (Cameron Little)</managingEditor><webMaster>cameron@camlittle.com (Cameron Little)</webMaster><lastBuildDate>Sun, 25 Sep 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://camlittle.com/tags/tutorial/index.xml" rel="self" type="application/rss+xml"/><item><title>Simple `struct` dependency mocking in Go</title><link>https://camlittle.com/posts/2022-09-25-go-struct-mocking/</link><pubDate>Sun, 25 Sep 2022 00:00:00 +0000</pubDate><guid isPermaLink="true">https://camlittle.com/posts/2022-09-25-go-struct-mocking/</guid><enclosure url="https://camlittle.com/posts/2022-09-25-go-struct-mocking/thumbnail.png" length="10079" type="image/png"/><description>&lt;p>I work with many people using Go for the first time, and one of the common struggles I see is testing code that depends on external &lt;code>struct&lt;/code> types.&lt;/p>
&lt;p>Let&amp;rsquo;s start with a simple (though contrived) example. I&amp;rsquo;m writing some code that depends on the &lt;a href="https://github.com/google/go-github">go-github&lt;/a> library to interact with the GitHub API.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">package&lt;/span> &lt;span class="nx">before&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;context&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;github.com/google/go-github/v47/github&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">type&lt;/span> &lt;span class="nx">obj&lt;/span> &lt;span class="kd">struct&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">user&lt;/span> &lt;span class="kt">string&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">gh&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">github&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">OrganizationsService&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">New&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nx">obj&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">obj&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">user&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">gh&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">github&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewClient&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">nil&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">Organizations&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">m&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">CountOrganizations&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span> &lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Context&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">orgs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">m&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">gh&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">List&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">m&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">nil&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Errorf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;couldn&amp;#39;t list organizations: %w&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">orgs&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">m&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">IsMemberOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span> &lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">org&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">bool&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">m&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">gh&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">IsMember&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">org&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">m&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Errorf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;couldn&amp;#39;t call IsMember: %w&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>How do you test this? &lt;code>github.OrganizationsService&lt;/code> is a &lt;code>struct&lt;/code> type, so we&amp;rsquo;re required to have a direct instance of it to pass type checking. In many cases, this will force your tests to make network calls. Usually, you can pass &lt;code>http.Client&lt;/code> and override its behavior, but that gets messy fast.&lt;/p></description><content:encoded><![CDATA[
  
<p>I work with many people using Go for the first time, and one of the common struggles I see is testing code that depends on external <code>struct</code> types.</p>
<p>Let&rsquo;s start with a simple (though contrived) example. I&rsquo;m writing some code that depends on the <a href="https://github.com/google/go-github">go-github</a> library to interact with the GitHub API.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">before</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;context&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="s">&#34;github.com/google/go-github/v47/github&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">obj</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">user</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="nx">gh</span>   <span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">OrganizationsService</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">New</span><span class="p">(</span><span class="nx">user</span> <span class="kt">string</span><span class="p">)</span> <span class="nx">obj</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">obj</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">user</span><span class="p">:</span> <span class="nx">user</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">gh</span><span class="p">:</span>   <span class="nx">github</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="kc">nil</span><span class="p">).</span><span class="nx">Organizations</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">obj</span><span class="p">)</span> <span class="nf">CountOrganizations</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">orgs</span><span class="p">,</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nx">gh</span><span class="p">.</span><span class="nf">List</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">m</span><span class="p">.</span><span class="nx">user</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;couldn&#39;t list organizations: %w&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="nx">orgs</span><span class="p">),</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">obj</span><span class="p">)</span> <span class="nf">IsMemberOf</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">org</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">bool</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">result</span><span class="p">,</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nx">gh</span><span class="p">.</span><span class="nf">IsMember</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">org</span><span class="p">,</span> <span class="nx">m</span><span class="p">.</span><span class="nx">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;couldn&#39;t call IsMember: %w&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">result</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>How do you test this? <code>github.OrganizationsService</code> is a <code>struct</code> type, so we&rsquo;re required to have a direct instance of it to pass type checking. In many cases, this will force your tests to make network calls. Usually, you can pass <code>http.Client</code> and override its behavior, but that gets messy fast.</p>
<h2 id="setup"><a class="heading-link" href="#setup"></a>Setup</h2>
<p>The key to testing this behavior is to depend on an <code>interface</code> type instead of the <code>struct</code>. Go makes this easy, which you may not know if you&rsquo;re used to other languages. In Go, an interface describes behavior that both your code <em>or dependency code</em> can implement. This feature means we can define an interface in our code that <code>github.OrganizationService</code> implements and the compiler will recognize it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">githubOrganizations</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">obj</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">user</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="nx">gh</span>   <span class="nx">githubOrganizations</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>I usually start with an empty interface and add methods as needed. Doing this helps keep my interface as clean as possible, which makes understanding code when refactoring or changing dependencies later easier.</p>
<p>Swapping <code>*github.OrganizationsService</code> for <code>githubOrganizations</code> works because all structs conform to the empty interface. However, I now see errors wherever I use <code>.gh</code>: <code>m.gh.List undefined (type githubOrganizations has no field or method List)</code>.</p>
<p>We need to add each of the undefined methods to the interface. If I&rsquo;m too lazy to look at the docs, I&rsquo;ll temporarily swap <code>githubOrganizations</code> back to <code>*github.OrganizationsService</code>, jump to the definition of the previously undefined method and port it over.</p>
<p>
<img src="https://content.camlittle.com/2022-09-24-go-mocking-interface-filling.mp4" alt="Filling example" />

</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">githubOrganizations</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nf">List</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">user</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">opts</span> <span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">ListOptions</span><span class="p">)</span> <span class="p">([]</span><span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">Organization</span><span class="p">,</span> <span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nf">IsMember</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">org</span><span class="p">,</span> <span class="nx">user</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">bool</span><span class="p">,</span> <span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="writing-a-test"><a class="heading-link" href="#writing-a-test"></a>Writing a test</h2>
<p>Now let&rsquo;s use this interface to write a test verifying <code>CountOrganizations</code> works as expected.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestCountOrganizations</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">obj</span> <span class="o">:=</span> <span class="nx">obj</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">gh</span><span class="p">:</span> <span class="nx">mockGHList</span><span class="p">{},</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">c</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">obj</span><span class="p">.</span><span class="nf">CountOrganizations</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">t</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">c</span> <span class="o">!=</span> <span class="mi">3</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">t</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;unexpected number of organizations: %d&#34;</span><span class="p">,</span> <span class="nx">c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Here&rsquo;s the real magic: <code>mockGHList</code> is a <code>struct</code> that embeds a pointer to <code>github.OrganizationsService</code>, which we originally depended on.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">mockGHList</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">OrganizationsService</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Go pointer semantics and struct embedding rules mean all methods of <code>*github.OrganizationsService</code> can be called on <code>mockGHList</code>, even when the embedded value is <code>nil</code>. This allows us to run the test without compilation issues, but because <code>github.OrganizationService</code> is <code>nil</code>, the test panics.</p>
<pre tabindex="0"><code>--- FAIL: TestCountOrganizations (0.00s)
...
github.com/google/go-github/v47/github.(*OrganizationsService).List
</code></pre><p>A <code>nil</code> pointer was found when trying to call <code>.List</code> on the <code>nil</code> <code>*github.OrganizationsService</code>. Go struct embedding rules mean that methods defined on the <code>mockGHList</code> object override those on the nested <code>*github.OrganizationsService</code> object. This rule lets us write our mock method.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">mockGHList</span><span class="p">)</span> <span class="nf">List</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="kt">string</span><span class="p">,</span> <span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">ListOptions</span><span class="p">)</span> <span class="p">([]</span><span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">Organization</span><span class="p">,</span> <span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="p">[]</span><span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">Organization</span><span class="p">{{},</span> <span class="p">{},</span> <span class="p">{}},</span> <span class="kc">nil</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Rerun the test, and it passes!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="o">---</span> <span class="nx">PASS</span><span class="p">:</span> <span class="nf">TestCountOrganizations</span> <span class="p">(</span><span class="mf">0.00</span><span class="nx">s</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="benefits"><a class="heading-link" href="#benefits"></a>Benefits</h2>
<h3 id="single-use-minimal-mocks"><a class="heading-link" href="#single-use-minimal-mocks"></a>Single-use, minimal mocks</h3>
<p>By wrapping the original struct, we only need to implement the methods our tests directly depend on. This encourages single-use, minimal mock objects instead of manually maintaining large shared mocks.</p>
<p>Any hardcoded mock codifies its expectations of how specific scenarios behave, usually the first tests it&rsquo;s used in. Those expectations make it harder to use for new tests. As the codebase grows, it either doesn&rsquo;t thoroughly test new behavior or combinatorially increases in complexity (by introducing [boolean flags](<a href="https://martinfowler.com/bliki/FlagArgument.html">https://martinfowler.com/bliki/FlagArgument.html</a>, for example). At some point, the mock deserves its own tests to track what it&rsquo;s doing.</p>
<h3 id="fail-by-default"><a class="heading-link" href="#fail-by-default"></a>Fail-by-default</h3>
<p>This pattern fails when executed code makes unexpected method calls, which can help catch accidental changes.</p>
<h3 id="mocking-libraries"><a class="heading-link" href="#mocking-libraries"></a>Mocking libraries</h3>
<p>I&rsquo;ve also successfully used this pattern with third-party mocking libraries.</p>
<p>Here&rsquo;s an example of use with <a href="https://github.com/stretchr/testify#mock-package"><code>&quot;github.com/stretchr/testify/mock&quot;</code></a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">mockGHIsMember</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">mock</span><span class="p">.</span><span class="nx">Mock</span>
</span></span><span class="line"><span class="cl">	<span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">OrganizationsService</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">mockGHIsMember</span><span class="p">)</span> <span class="nf">IsMember</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">org</span><span class="p">,</span> <span class="nx">user</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">bool</span><span class="p">,</span> <span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">args</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Called</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">org</span><span class="p">,</span> <span class="nx">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">args</span><span class="p">.</span><span class="nf">Bool</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="nx">args</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="mi">1</span><span class="p">).(</span><span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">Response</span><span class="p">),</span> <span class="nx">args</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestIsMember</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">gh</span> <span class="o">:=</span> <span class="nx">mockGHIsMember</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">obj</span> <span class="o">:=</span> <span class="nx">obj</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">user</span><span class="p">:</span> <span class="s">&#34;apexskier&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">gh</span><span class="p">:</span>   <span class="o">&amp;</span><span class="nx">gh</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">gh</span><span class="p">.</span><span class="nf">On</span><span class="p">(</span><span class="s">&#34;IsMember&#34;</span><span class="p">,</span> <span class="nx">mock</span><span class="p">.</span><span class="nf">MatchedBy</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">true</span> <span class="p">}),</span> <span class="s">&#34;org&#34;</span><span class="p">,</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">user</span><span class="p">).</span><span class="nf">Return</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span> <span class="p">(</span><span class="o">*</span><span class="nx">github</span><span class="p">.</span><span class="nx">Response</span><span class="p">)(</span><span class="kc">nil</span><span class="p">),</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">result</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">obj</span><span class="p">.</span><span class="nf">IsMemberOf</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="s">&#34;org&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">require</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">result</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>I still only need to mock the method being called.</p>
<h2 id="caveats"><a class="heading-link" href="#caveats"></a>Caveats</h2>
<ul>
<li>Defining an interface does not work when using struct fields since interfaces can only declare methods.</li>
<li>Auto-complete won&rsquo;t show other methods available on the original <code>struct</code> type. <a href="https://pkg.go.dev">https://pkg.go.dev</a> is your friend, or you can manually declare a variable of the original type and use auto-complete on that.</li>
</ul>
<hr>
<p>You can view the code from this post at <a href="https://github.com/apexskier/go-struct-mocking-demo/">https://github.com/apexskier/go-struct-mocking-demo/</a>.</p>


  
  ]]></content:encoded></item><item><title>Type Unions in Swift</title><link>https://camlittle.com/posts/type-unions-in-swift/</link><pubDate>Sun, 14 Mar 2021 00:00:00 +0000</pubDate><guid isPermaLink="true">https://camlittle.com/posts/type-unions-in-swift/</guid><enclosure url="https://camlittle.com/posts/type-unions-in-swift/thumbnail.png" length="32207" type="image/png"/><description>&lt;p>Type unions are one of my favorite patterns to enforce stricter typing in a codebase. The basic idea is to allow a single type to represent a value that is one of a set of different, distinct types.&lt;/p>
&lt;p>One of my frequent use cases is iteration over a collection of varied objects. A type union allows me to avoid complicated merging and ordering logic and use simple conditional logic within the iteration body. It’s also used commonly in APIs I interact with, where a single resource can produce different but similar things, or &lt;a href="https://camlittle.com/posts/2020-04-03-typing-tricks-to-reduce-ambiguity/#type-unions">I want to reduce ambiguity between conditional fields&lt;/a>.&lt;/p></description><content:encoded><![CDATA[
  
<p>Type unions are one of my favorite patterns to enforce stricter typing in a codebase. The basic idea is to allow a single type to represent a value that is one of a set of different, distinct types.</p>
<p>One of my frequent use cases is iteration over a collection of varied objects. A type union allows me to avoid complicated merging and ordering logic and use simple conditional logic within the iteration body. It’s also used commonly in APIs I interact with, where a single resource can produce different but similar things, or <a href="https://camlittle.com/posts/2020-04-03-typing-tricks-to-reduce-ambiguity/#type-unions">I want to reduce ambiguity between conditional fields</a>.</p>
<p>Type unions can be a stronger pattern than inheritance. In class-based inheritance, the possible set of types is unbounded, while type unions have an explicit set known at compile time. In languages like TypeScript (and Swift, as we’ll see), sub-types of a type union can be unrelated and have distinct inheritance chains.</p>
<h2 id="in-other-languages"><a class="heading-link" href="#in-other-languages"></a>In other languages</h2>
<p>Type unions are simple in weakly typed languages like JavaScript or Python<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> since a variable can contain any value. Strongly typed languages take different approaches to support them.</p>
<h3 id="typescript"><a class="heading-link" href="#typescript"></a>TypeScript</h3>
<p>TypeScript has first-class support with <a href="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types">union types</a>. Type narrowing is done with <a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates">type predicates and custom functions</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">interface</span> <span class="nx">Rabbit</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">name</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="nx">eat</span><span class="p">(</span><span class="nx">food</span>: <span class="kt">Carrot</span><span class="p">)</span><span class="o">:</span> <span class="k">void</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Dog</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">name</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="nx">pet() {</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kr">interface</span> <span class="nx">Turtle</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;turtle&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">type</span> <span class="nx">Pet</span> <span class="o">=</span> <span class="nx">Rabbit</span> <span class="o">|</span> <span class="nx">Dog</span> <span class="o">|</span> <span class="nx">Turtle</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">whatIsIt</span><span class="p">(</span><span class="nx">pet</span>: <span class="kt">Pet</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">pet</span> <span class="o">==</span> <span class="s2">&#34;string&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="s2">&#34;turtle&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">pet</span> <span class="k">instanceof</span> <span class="nx">Dog</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">pet</span><span class="p">.</span><span class="nx">pet</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="s2">&#34;dog&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="s2">&#34;rabbit&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>TypeScript is nice because it automatically casts the inspected variable&rsquo;s type: within the conditional blocks, <code>pet</code> assumes the narrowed type. TypeScript automatically infers common attributes of the sub-types, allowing access to the common <code>name</code> property (<code>pet.name</code>).</p>
<h3 id="kotlin"><a class="heading-link" href="#kotlin"></a>Kotlin</h3>
<p>Kotlin supports type unions with <a href="https://kotlinlang.org/docs/sealed-classes.html">sealed classes</a>. Type narrowing is done with <a href="https://kotlinlang.org/docs/sealed-classes.html#sealed-classes-and-when-expression">when expressions</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="line"><span class="cl"><span class="k">sealed</span> <span class="k">class</span> <span class="nc">Pet</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">data</span> <span class="k">class</span> <span class="nc">Rabbit</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="k">val</span> <span class="py">name</span><span class="p">:</span> <span class="n">String</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="k">val</span> <span class="py">carrot</span><span class="p">:</span> <span class="n">Double</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="p">:</span> <span class="n">Pet</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">fun</span> <span class="nf">eat</span><span class="p">(</span><span class="n">food</span><span class="p">:</span> <span class="n">Carrot</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Dog</span><span class="p">(</span><span class="k">val</span> <span class="py">name</span><span class="p">:</span> <span class="n">String</span><span class="p">)</span> <span class="p">:</span> <span class="n">Pet</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">fun</span> <span class="nf">pet</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">object</span> <span class="nc">Turtle</span> <span class="p">:</span> <span class="n">Pet</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">val</span> <span class="py">name</span><span class="p">:</span> <span class="n">String</span> <span class="p">=</span> <span class="s2">&#34;turtle&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">fun</span> <span class="nf">whatIsIt</span><span class="p">(</span><span class="n">pet</span><span class="p">:</span> <span class="n">Pet</span><span class="p">):</span> <span class="n">String</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">	<span class="k">when</span> <span class="p">(</span><span class="n">pet</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">is</span> <span class="n">Rabbit</span> <span class="o">-&gt;</span> <span class="s2">&#34;rabbit&#34;</span>
</span></span><span class="line"><span class="cl">		<span class="k">is</span> <span class="n">Dog</span> <span class="o">-&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="n">pet</span><span class="p">.</span><span class="n">pet</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">			<span class="s2">&#34;dog&#34;</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">		<span class="k">is</span> <span class="n">Turtle</span> <span class="o">-&gt;</span> <span class="s2">&#34;turtle&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span></code></pre></div><p>Like TypeScript, variable types are automatically narrowed. Kotlin does not automatically infer common attributes, but they can be defined on the base sealed class, following standard inheritance patterns.</p>
<h2 id="in-swift"><a class="heading-link" href="#in-swift"></a>In Swift</h2>
<p>The core construct for a type union in Swift is an <a href="https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html#ID148">enumeration with associated values</a>. Type narrowing is done with <code>case</code> statements.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">protocol</span> <span class="nc">Rabbit</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nv">name</span><span class="p">:</span> <span class="nb">String</span> <span class="p">{</span> <span class="kr">get</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="kd">func</span> <span class="nf">eat</span><span class="p">(</span><span class="n">food</span><span class="p">:</span> <span class="n">Carrot</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">Dog</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nv">name</span><span class="p">:</span> <span class="nb">String</span>
</span></span><span class="line"><span class="cl">	<span class="kd">func</span> <span class="nf">pet</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">typealias</span> <span class="n">Turtle</span> <span class="p">=</span> <span class="nb">String</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">enum</span> <span class="nc">Pet</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">rabbit</span><span class="p">(</span><span class="n">Rabbit</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">dog</span><span class="p">(</span><span class="n">Dog</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">turtle</span><span class="p">(</span><span class="n">Turtle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">whatIsIt</span><span class="p">(</span><span class="n">pet</span><span class="p">:</span> <span class="n">Pet</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">String</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">switch</span> <span class="n">pet</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="p">.</span><span class="n">rabbit</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="s">&#34;rabbit&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="p">.</span><span class="n">dog</span><span class="p">(</span><span class="kd">let</span> <span class="nv">dog</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">		<span class="n">dog</span><span class="p">.</span><span class="n">pet</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="s">&#34;dog&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="p">.</span><span class="n">turtle</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="s">&#34;turtle&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Notice that to get a variable of the narrowed type, a new variable is directly defined within the case expression (<code>let dog</code>).</p>
<p>A more concise form of type narrowing is also possible within an <code>if</code> statement<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="k">if</span> <span class="k">case</span> <span class="p">.</span><span class="n">rabbit</span><span class="p">(</span><span class="kd">let</span> <span class="nv">rabbit</span><span class="p">)</span> <span class="p">=</span> <span class="n">pet</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// ...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The main downside to Swift’s form of type unions is that the value is nested within another. It’s not possible to directly use attributes on a <code>Pet</code> variable. To work around that, you need to define accessors on the enum itself<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">enum</span> <span class="nc">Pet</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">rabbit</span><span class="p">(</span><span class="n">Rabbit</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">dog</span><span class="p">(</span><span class="n">Dog</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">turtle</span><span class="p">(</span><span class="n">Turtle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nv">name</span><span class="p">:</span> <span class="nb">String</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">switch</span> <span class="kc">self</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="p">.</span><span class="n">rabbit</span><span class="p">(</span><span class="kd">let</span> <span class="nv">rabbit</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="n">rabbit</span><span class="p">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="p">.</span><span class="n">dog</span><span class="p">(</span><span class="kd">let</span> <span class="nv">dog</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="n">dog</span><span class="p">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="p">.</span><span class="n">turtle</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="n">turtle</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">example</span><span class="p">(</span><span class="n">pet</span><span class="p">:</span> <span class="n">Pet</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="n">pet</span><span class="p">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If the different sub-types share a <a href="https://docs.swift.org/swift-book/LanguageGuide/Protocols.html">protocol</a>, you can define a single accessor to access the protocol.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">protocol</span> <span class="nc">Mammal</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nv">furColor</span><span class="p">:</span> <span class="nb">String</span> <span class="p">{</span> <span class="kr">get</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">enum</span> <span class="nc">Pet</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">rabbit</span><span class="p">(</span><span class="n">Rabbit</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">dog</span><span class="p">(</span><span class="n">Dog</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">turtle</span><span class="p">(</span><span class="n">Turtle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nv">mammal</span><span class="p">:</span> <span class="n">Mammal</span><span class="p">?</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">switch</span> <span class="kc">self</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="p">.</span><span class="n">rabbit</span><span class="p">(</span><span class="kd">let</span> <span class="nv">mammal</span> <span class="k">as</span> <span class="n">Mammal</span><span class="p">),</span> <span class="p">.</span><span class="n">dog</span><span class="p">(</span><span class="kd">let</span> <span class="nv">mammal</span> <span class="k">as</span> <span class="n">Mammal</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="n">mammal</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="p">.</span><span class="n">turtle</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">example</span><span class="p">(</span><span class="n">pet</span><span class="p">:</span> <span class="n">Pet</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="n">pet</span><span class="p">.</span><span class="n">mammal</span><span class="p">?.</span><span class="n">furColor</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Taking this further, if <em>all</em> sub-types share a protocol, you can extend the enum itself from that protocol and delegate to that accessor.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">protocol</span> <span class="nc">Animal</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">func</span> <span class="nf">sleep</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">enum</span> <span class="nc">Pet</span><span class="p">:</span> <span class="n">Animal</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">rabbit</span><span class="p">(</span><span class="n">Rabbit</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">dog</span><span class="p">(</span><span class="n">Dog</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="n">turtle</span><span class="p">(</span><span class="n">Turtle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	
</span></span><span class="line"><span class="cl">	<span class="kd">private</span> <span class="kd">var</span> <span class="nv">animal</span><span class="p">:</span> <span class="n">Animal</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">switch</span> <span class="kc">self</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="p">.</span><span class="n">rabbit</span><span class="p">(</span><span class="kd">let</span> <span class="nv">animal</span> <span class="k">as</span> <span class="n">Animal</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">			 <span class="p">.</span><span class="n">dog</span><span class="p">(</span><span class="kd">let</span> <span class="nv">animal</span> <span class="k">as</span> <span class="n">Animal</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">			 <span class="p">.</span><span class="n">turtle</span><span class="p">(</span><span class="kd">let</span> <span class="nv">animal</span> <span class="k">as</span> <span class="n">Animal</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="n">animal</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	
</span></span><span class="line"><span class="cl">	<span class="kd">func</span> <span class="nf">sleep</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="n">animal</span><span class="p">.</span><span class="n">sleep</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">example</span><span class="p">(</span><span class="n">pet</span><span class="p">:</span> <span class="n">Pet</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="n">pet</span><span class="p">.</span><span class="n">sleep</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>I find type unions significantly simplify my code and prevent mistakes by strengthening my type semantics. I hope this helps you out as well.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Unless you&rsquo;re using <a href="https://docs.python.org/3/library/typing.html#typing.Union">Python 3.5 type hinting</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://sarunw.com/posts/lesser-known-ways-of-using-swift-enums/">Here&rsquo;s another good article about working with Swift enums</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>There&rsquo;s some <a href="https://forums.swift.org/t/extract-payload-for-enum-cases-having-associated-value/27606">discussion on the Swift forums</a> about proposed syntax changes to make it easier to access the associated value within an enum, so keep your eyes out for changes in this space.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>


  
  ]]></content:encoded></item><item><title>Css overscroll styling</title><link>https://camlittle.com/posts/2020-05-15-css-overscroll-styling/</link><pubDate>Fri, 15 May 2020 20:32:52 +0200</pubDate><guid isPermaLink="true">https://camlittle.com/posts/2020-05-15-css-overscroll-styling/</guid><enclosure url="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/thumbnail.png" length="30513" type="image/png"/><description>&lt;p>You may have heard about the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior">&lt;code>overscroll-behavior&lt;/code>&lt;/a> css property. It allows you to disable the default behavior when someone scrolls beyond the bounds of your website.&lt;/p>
&lt;p>
&lt;video autoplay controls height="300" style="height: 300px;" alt="basic-overscroll.mp4">
&lt;source src="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/basic-overscroll.0bcad16dfb74b2cb17e84ea507a6af3e.mp4" />
&lt;/video>
&lt;/p>
&lt;p>But maybe you don&amp;rsquo;t want to disable that nice physics interaction and want to have some fun with it instead. The goal here is to use css to add some sort of &amp;ldquo;hidden&amp;rdquo; content in the background of the site that&amp;rsquo;s only visible when over-scrolling.&lt;/p></description><content:encoded><![CDATA[
  
<p>You may have heard about the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior"><code>overscroll-behavior</code></a> css property. It allows you to disable the default behavior when someone scrolls beyond the bounds of your website.</p>
<p>





  
  <video autoplay controls height="300" style="height: 300px;" alt="basic-overscroll.mp4">
      
      <source src="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/basic-overscroll.0bcad16dfb74b2cb17e84ea507a6af3e.mp4" />
      
  </video>



</p>
<p>But maybe you don&rsquo;t want to disable that nice physics interaction and want to have some fun with it instead. The goal here is to use css to add some sort of &ldquo;hidden&rdquo; content in the background of the site that&rsquo;s only visible when over-scrolling.</p>
<p>By default, there are already two elements to play with: <code>&lt;html&gt;</code> and <code>&lt;body&gt;</code>. The first thing to understand is the sizing of these elements. Both element&rsquo;s intrinsic content size is that of the containing content, but the <code>&lt;html&gt;</code> element is special, since its <em>background</em> takes up the full window size.</p>
<p><a href="https://codepen.io/apexskier/pen/WNQgNQp">





  
  
  
  
  
  
  
  
  

  
  
  
  
  
  

  <picture title="sizing.png">
      
      
        
        
          







<source
    media="(-webkit-min-device-pixel-ratio: 2) and (min-width: 1200px), (min-resolution: 192dpi) and (min-width: 1200px)"
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_1200x0at2x.webp"
    type="image/webp"
/>
<source
    media="(-webkit-min-device-pixel-ratio: 2) and (min-width: 1200px), (min-resolution: 192dpi) and (min-width: 1200px)"
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_1200x0at2x.jpg"
    type="image/jpeg"
/>



<source
    
    media="min-width: 1200px"
    
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_1200x0at1x.webp"
    type="image/webp"
/>
<source
    
    media="min-width: 1200px"
    
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_1200x0at1x.jpg"
    type="image/jpeg"
/>


        
          







<source
    media="(-webkit-min-device-pixel-ratio: 2) and (min-width: 992px), (min-resolution: 192dpi) and (min-width: 992px)"
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_992x0at2x.webp"
    type="image/webp"
/>
<source
    media="(-webkit-min-device-pixel-ratio: 2) and (min-width: 992px), (min-resolution: 192dpi) and (min-width: 992px)"
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_992x0at2x.jpg"
    type="image/jpeg"
/>



<source
    
    media="min-width: 992px"
    
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_992x0at1x.webp"
    type="image/webp"
/>
<source
    
    media="min-width: 992px"
    
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_992x0at1x.jpg"
    type="image/jpeg"
/>


        
          







<source
    media="(-webkit-min-device-pixel-ratio: 2) and (min-width: 768px), (min-resolution: 192dpi) and (min-width: 768px)"
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_768x0at2x.webp"
    type="image/webp"
/>
<source
    media="(-webkit-min-device-pixel-ratio: 2) and (min-width: 768px), (min-resolution: 192dpi) and (min-width: 768px)"
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_768x0at2x.jpg"
    type="image/jpeg"
/>



<source
    
    media="min-width: 768px"
    
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_768x0at1x.webp"
    type="image/webp"
/>
<source
    
    media="min-width: 768px"
    
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_768x0at1x.jpg"
    type="image/jpeg"
/>


        
          







<source
    media="(-webkit-min-device-pixel-ratio: 2) and (min-width: 480px), (min-resolution: 192dpi) and (min-width: 480px)"
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_480x0at2x.webp"
    type="image/webp"
/>
<source
    media="(-webkit-min-device-pixel-ratio: 2) and (min-width: 480px), (min-resolution: 192dpi) and (min-width: 480px)"
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_480x0at2x.jpg"
    type="image/jpeg"
/>



<source
    
    media="min-width: 480px"
    
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_480x0at1x.webp"
    type="image/webp"
/>
<source
    
    media="min-width: 480px"
    
    srcset="https://content.camlittle.com/site-media/posts/2020-05-15-css-overscroll-styling/sizing_6bdf2a703e4472d8504751a2edd04319_480x0at1x.jpg"
    type="image/jpeg"
/>


        
      
      <img
          height="300"
          
          style="max-height: 300px;"
          src="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/sizing.6bdf2a703e4472d8504751a2edd04319.png"
          alt="sizing.png"
      />
  </picture>



</a></p>
<p>First, I want to make sure the <code>&lt;body&gt;</code> always covers the full content, so I don&rsquo;t have anything in the background of the <code>&lt;html&gt;</code> peeking out at the bottom. I also need to make sure my layout doesn&rsquo;t have any margins collapsing outside of the body, which in this demo I&rsquo;ll do by making my body <code>flex</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">min-height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vh</span><span class="p">;</span> <span class="c">/* full height */</span>
</span></span><span class="line"><span class="cl">	<span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span> <span class="c">/* optional - prevents margin collapse */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>With a background color on the <code>&lt;html&gt;</code> element it&rsquo;s visible when I overscroll. However, this is &ldquo;fake&rdquo; content on desktop browsers&mdash;the html is still scrolling so its <em>contents</em> aren&rsquo;t shown (for example, a background image).</p>
<p>There are a couple of approaches I want to try to get content in there.</p>
<ol>
<li>Don&rsquo;t scroll the html - scroll the body instead</li>
</ol>
<p>Unfortunately (at least in the desktop browsers I tested) this doesn&rsquo;t work as I want. The only element that allows scrolling outside of its content is the <code>&lt;html&gt;</code> tag, so the behavior is the same as if I set <code>overscroll-behavior: none</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">html</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">overflow</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">overflow</span><span class="p">:</span> <span class="kc">scroll</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vh</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="n">overscroll-behavior</span><span class="p">:</span> <span class="kc">contain</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>





  
  <video autoplay controls height="300" style="height: 300px;" alt="no-overscroll.mp4">
      
      <source src="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/no-overscroll.2424cf6343debf8c859078a7c27a4f07.mp4" />
      
  </video>



</p>
<ol start="2">
<li>Add fake fixed elements that don&rsquo;t scroll</li>
</ol>
<p>I can use some trusty pseudo elements to add content, right?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">html</span><span class="p">::</span><span class="nd">before</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">z-index</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">content</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">right</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">background-color</span><span class="p">:</span> <span class="kc">red</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">position</span><span class="p">:</span> <span class="kc">fixed</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">body</span><span class="p">::</span><span class="nd">before</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">z-index</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">content</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">background-color</span><span class="p">:</span> <span class="kc">blue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">position</span><span class="p">:</span> <span class="kc">fixed</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">z-index</span><span class="p">:</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">background-color</span><span class="p">:</span> <span class="kc">white</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Well, not really. On desktop, even <code>position: fixed</code> and <code>position: sticky</code> won&rsquo;t scroll into the overflow area, just like a <code>background-image</code> of <code>&lt;html&gt;</code>. Another issue is that html pseudo elements display <em>above</em> the background of the <code>&lt;body&gt;</code>. Generally, a pseudo element follows the standard <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context">stacking context</a>: <code>body { background-color: white }</code> <em>should</em> cover pseudo elements of <code>&lt;html&gt;</code> the same as it covers <code>&lt;html&gt;</code>.</p>
<p>





  
  <video autoplay controls height="300" style="height: 300px;" alt="desktop-pseudo-elements.mp4">
      
      <source src="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/desktop-pseudo-elements.14b777ef056c0fc266b315ba38af44ba.mp4" />
      
  </video>



</p>
<p>However, this confirms that iOS <em>doesn&rsquo;t</em> scroll fixed content when overscrolling. The body background issue persists though.</p>
<p>





  
  <video autoplay controls height="300" style="height: 300px;" alt="ios-pseudo-elements.mp4">
      
      <source src="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/ios-pseudo-elements.82a12917dcd31f1518b0ebd8363aeed0.mp4" />
      
  </video>



</p>
<ol start="3">
<li>Add a wrapper element</li>
</ol>
<p>Now the the DOM gets a little messy. We can introduce a wrapper element (instead of using <code>&lt;body&gt;</code>) to cover the full document with a non-opaque background color.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">#</span><span class="nn">wrapper</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">min-height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vh</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">min-width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vw</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">background-color</span><span class="p">:</span> <span class="kc">white</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">		<span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;wrapper&#34;</span><span class="p">&gt;</span><span class="c">&lt;!-- content --&gt;</span><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>And we finally have a hidden background element!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">html</span><span class="p">::</span><span class="nd">before</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">z-index</span><span class="p">:</span> <span class="mi">-1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">position</span><span class="p">:</span> <span class="kc">fixed</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vh</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vw</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">content</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>However, the browser support is pretty limited. As discovered above, desktop browsers scroll <code>fixed</code> elements during overscroll, meaning they&rsquo;re not &ldquo;revealed&rdquo; like I want. In iOS Safari, however, it&rsquo;s a pretty nice effect.</p>
<p>





  
  <video autoplay controls height="300" style="height: 300px;" alt="final-effect.mp4">
      
      <source src="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/final-effect.daa5f1678c01be4c89f4475f88ed3f8c.mp4" />
      
  </video>



</p>
<p>Unfortunately, there&rsquo;s some glitchy behavior on desktop Safari where the background texture shows through un-rendered content.</p>
<p>





  
  <video autoplay controls height="300" style="height: 300px;" alt="glitchy.mp4">
      
      <source src="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/glitchy.356351185cac0e31ea44b350ebdd0375.mp4" />
      
  </video>



</p>
<h3 id="bonus"><a class="heading-link" href="#bonus"></a>Bonus!</h3>
<p>We can use a hidden element and <code>position: sticky</code> to emulate snapchat&rsquo;s pull-to-refresh ghost animation with pure CSS.</p>
<p>





  
  <video autoplay controls height="300" style="height: 300px;" alt="snapchat-demo.mp4">
      
      <source src="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/snapchat-demo.39905578b1d684e5f046357104ad77d4.mp4" />
      
  </video>



</p>
<p>





  
  <video autoplay controls height="300" style="height: 300px;" alt="css-snapchat-demo.mp4">
      
      <source src="https://camlittle.com/posts/2020-05-15-css-overscroll-styling/css-snapchat-demo.bceeefd866d630044b219b01b2c4c81c.mp4" />
      
  </video>



</p>
<p><a href="snap-ghost.html">Try it here</a></p>


  
  ]]></content:encoded></item><item><title>GLGE Tutorial 2</title><link>https://camlittle.com/posts/glge-tut2/</link><pubDate>Wed, 14 Dec 2011 00:00:00 -0700</pubDate><guid isPermaLink="true">https://camlittle.com/posts/glge-tut2/</guid><enclosure url="https://camlittle.com/posts/glge-tut2/thumbnail.jpg" length="2315" type="image/jpeg"/><description>&lt;p>content:
&lt;a href="#elements-needed">elements needed&lt;/a> | &lt;a href="#meshes">meshes&lt;/a> | &lt;a href="#animation">animation&lt;/a> | &lt;a href="#the-rest-including-groups">the rest&lt;/a> | &lt;a href="#javascript">javascript&lt;/a> | &lt;a href="#here-it-is---update-glge-isnt-supported-any-more">demo/download&lt;/a>&lt;/p>
&lt;h2 id="3d-objects-with-movement">&lt;a class="heading-link" href="#3d-objects-with-movement">&lt;/a>3D objects with movement&lt;/h2>
&lt;p>If you haven&amp;rsquo;t already, go and read the &lt;a href="https://camlittle.com/posts/glge-tut1/">first tutorial&lt;/a> in
this series. This tutorial will expand on the XML file from the first tutorial.
The javascript and html are exactly the same.&lt;/p>
&lt;p>The XML file is the only one that will change. This example will include a
rotating cube and a sphere circling around it.&lt;/p>
&lt;h3 id="elements-needed">&lt;a class="heading-link" href="#elements-needed">&lt;/a>Elements needed&lt;/h3>
&lt;p>If you&amp;rsquo;ll remember, there are several things needed in our XML file. For this
one we will need&amp;hellip;&lt;/p></description><content:encoded><![CDATA[
  
<p>content:
<a href="#elements-needed">elements needed</a> | <a href="#meshes">meshes</a> | <a href="#animation">animation</a> | <a href="#the-rest-including-groups">the rest</a> | <a href="#javascript">javascript</a> | <a href="#here-it-is---update-glge-isnt-supported-any-more">demo/download</a></p>
<h2 id="3d-objects-with-movement"><a class="heading-link" href="#3d-objects-with-movement"></a>3D objects with movement</h2>
<p>If you haven&rsquo;t already, go and read the <a href="https://camlittle.com/posts/glge-tut1/">first tutorial</a> in
this series. This tutorial will expand on the XML file from the first tutorial.
The javascript and html are exactly the same.</p>
<p>The XML file is the only one that will change. This example will include a
rotating cube and a sphere circling around it.</p>
<h3 id="elements-needed"><a class="heading-link" href="#elements-needed"></a>Elements needed</h3>
<p>If you&rsquo;ll remember, there are several things needed in our XML file. For this
one we will need&hellip;</p>
<ul>
<li>Two <code>&lt;mesh&gt;</code>s composed of&hellip;
<ul>
<li><code>&lt;positions&gt;</code>,</li>
<li><code>&lt;normals&gt;</code>,</li>
<li><code>&lt;uv1&gt;</code> and</li>
<li><code>&lt;faces&gt;</code>,</li>
</ul>
</li>
<li>Two <code>&lt;material&gt;</code>s,</li>
<li>A <code>&lt;camera&gt;</code> and</li>
<li>A <code>&lt;scene&gt;</code> composed of&hellip;
<ul>
<li>Two <code>&lt;object&gt;</code>s</li>
<li>Two <code>&lt;light&gt;</code>s.</li>
</ul>
</li>
</ul>
<h3 id="meshes"><a class="heading-link" href="#meshes"></a>Meshes</h3>
<p>We need to define two meshes for two separate objects in the scene: a cube and
a sphere. I&rsquo;ll go into a little more detail on making them in this tutorial
compared to the last one.</p>
<h4 id="cube"><a class="heading-link" href="#cube"></a>Cube</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;mesh</span> <span class="na">id=</span><span class="s">&#34;cube&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/mesh&gt;</span>
</span></span></code></pre></div><p>The cube is composed of six sides. Each side is made of two flat triangles
(like the one from the last tutorial). This will make a total of 36 corners or
36 coordinates in the <code>&lt;positions&gt;</code> element of the <code>&lt;mesh&gt;</code>, 36 coordinates in
the <code>&lt;normals&gt;</code> element of the <code>&lt;mesh&gt;</code> and 36 numbers in the <code>&lt;faces&gt;</code> element
of the <code>&lt;mesh&gt;</code>.</p>
<p>I&rsquo;ll start off with the <code>&lt;positions&gt;</code> element. Each of the 36 corners will need
an x, y, z coordinate. Each face of the cube will have be made of two triangles
composed of 6 coordinates.</p>
<p>For the top face, a flat square, we will need&hellip;</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">  1, 1, 1,   // right 1 unit, forward 1 unit, up 1 unit
</span></span><span class="line"><span class="cl"> -1, 1, 1,   // left  1 unit, forward 1 unit, up 1 unit
</span></span><span class="line"><span class="cl">  1,-1, 1,   // right 1 unit, back    1 unit, up 1 unit
</span></span><span class="line"><span class="cl"> -1, 1, 1,   // left  1 unit, forward 1 unit, up 1 unit
</span></span><span class="line"><span class="cl"> -1,-1, 1,   // left  1 unit, back    1 unit, up 1 unit
</span></span><span class="line"><span class="cl">  1,-1, 1... // right 1 unit, back    1 unit, up 1 unit
</span></span></code></pre></div><p>Now we have the <code>&lt;normals&gt;</code> for the top face. Each line is an x, y, z
coordinate and defines which direction the light is reflected towards. A good
rule of thumb (at least for cubes and flat faces) is that the normal for each
corner of one face is +1 at the axis that all corners on the face are equal at.
For the top face, each corner of the face is +1 at the z axis, so each normal
is 0, 0, 1. The light is reflected up (to the z axis) for each corner and the
space in between corners.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">0, 0, 1,
</span></span><span class="line"><span class="cl">0, 0, 1,
</span></span><span class="line"><span class="cl">0, 0, 1,
</span></span><span class="line"><span class="cl">0, 0, 1,
</span></span><span class="line"><span class="cl">0, 0, 1,
</span></span><span class="line"><span class="cl">0, 0, 1...
</span></span></code></pre></div><p>Since our example will not have images associated with objects and because all
of the corners are defined separately, we don&rsquo;t need a <code>&lt;uv1&gt;</code> section or a
<code>&lt;faces&gt;</code> section. See another tutorial to learn about defining multiple
corners with the same position element.</p>
<p>Lets build the rest of the <code>&lt;positions&gt;</code> and <code>&lt;normals&gt;</code> elements.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;positions&gt;</span>
</span></span><span class="line"><span class="cl">    /* back */
</span></span><span class="line"><span class="cl">     1,  1, 1,
</span></span><span class="line"><span class="cl">     1,  1, -1,
</span></span><span class="line"><span class="cl">    -1,  1, -1,
</span></span><span class="line"><span class="cl">     1,  1, 1,
</span></span><span class="line"><span class="cl">    -1,  1, -1,
</span></span><span class="line"><span class="cl">    -1,  1, 1,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    /* left */
</span></span><span class="line"><span class="cl">    -1, -1, -1,
</span></span><span class="line"><span class="cl">    -1, -1, 1,
</span></span><span class="line"><span class="cl">    -1,  1, 1,
</span></span><span class="line"><span class="cl">    -1, -1, -1,
</span></span><span class="line"><span class="cl">    -1,  1, 1,
</span></span><span class="line"><span class="cl">    -1,  1, -1,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    /* front */
</span></span><span class="line"><span class="cl">     1, -1, -1,
</span></span><span class="line"><span class="cl">     1, -1, 1,
</span></span><span class="line"><span class="cl">    -1, -1, -1,
</span></span><span class="line"><span class="cl">     1, -1, 1,
</span></span><span class="line"><span class="cl">    -1, -1, 1,
</span></span><span class="line"><span class="cl">    -1, -1, -1,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    /* right */
</span></span><span class="line"><span class="cl">     1,  1, -1,
</span></span><span class="line"><span class="cl">     1,  1, 1,
</span></span><span class="line"><span class="cl">     1, -1, -1,
</span></span><span class="line"><span class="cl">     1,  1, 1,
</span></span><span class="line"><span class="cl">     1, -1, 1,
</span></span><span class="line"><span class="cl">     1, -1, -1,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    /* top */
</span></span><span class="line"><span class="cl">     1,  1, 1,
</span></span><span class="line"><span class="cl">    -1,  1, 1,
</span></span><span class="line"><span class="cl">     1, -1, 1,
</span></span><span class="line"><span class="cl">    -1,  1, 1,
</span></span><span class="line"><span class="cl">    -1, -1, 1,
</span></span><span class="line"><span class="cl">     1, -1, 1,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    /* bottom */
</span></span><span class="line"><span class="cl">     1,  1, -1,
</span></span><span class="line"><span class="cl">     1, -1, -1,
</span></span><span class="line"><span class="cl">    -1, -1, -1,
</span></span><span class="line"><span class="cl">     1,  1, -1,
</span></span><span class="line"><span class="cl">    -1, -1, -1,
</span></span><span class="line"><span class="cl">    -1,  1, -1
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/positions&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;normals&gt;</span>
</span></span><span class="line"><span class="cl">    /* back */
</span></span><span class="line"><span class="cl">    0, 1, 0,
</span></span><span class="line"><span class="cl">    0, 1, 0,
</span></span><span class="line"><span class="cl">    0, 1, 0,
</span></span><span class="line"><span class="cl">    0, 1, 0,
</span></span><span class="line"><span class="cl">    0, 1, 0,
</span></span><span class="line"><span class="cl">    0, 1, 0,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    /* left */
</span></span><span class="line"><span class="cl">    -1, 0, 0,
</span></span><span class="line"><span class="cl">    -1, 0, 0,
</span></span><span class="line"><span class="cl">    -1, 0, 0,
</span></span><span class="line"><span class="cl">    -1, 0, 0,
</span></span><span class="line"><span class="cl">    -1, 0, 0,
</span></span><span class="line"><span class="cl">    -1, 0, 0,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    /* front */
</span></span><span class="line"><span class="cl">    0, -1, 0,
</span></span><span class="line"><span class="cl">    0, -1, 0,
</span></span><span class="line"><span class="cl">    0, -1, 0,
</span></span><span class="line"><span class="cl">    0, -1, 0,
</span></span><span class="line"><span class="cl">    0, -1, 0,
</span></span><span class="line"><span class="cl">    0, -1, 0,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    /* right */
</span></span><span class="line"><span class="cl">    1, 0, 0,
</span></span><span class="line"><span class="cl">    1, 0, 0,
</span></span><span class="line"><span class="cl">    1, 0, 0,
</span></span><span class="line"><span class="cl">    1, 0, 0,
</span></span><span class="line"><span class="cl">    1, 0, 0,
</span></span><span class="line"><span class="cl">    1, 0, 0,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    /* top */
</span></span><span class="line"><span class="cl">    0, 0, 1,
</span></span><span class="line"><span class="cl">    0, 0, 1,
</span></span><span class="line"><span class="cl">    0, 0, 1,
</span></span><span class="line"><span class="cl">    0, 0, 1,
</span></span><span class="line"><span class="cl">    0, 0, 1,
</span></span><span class="line"><span class="cl">    0, 0, 1,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    /* bottom */
</span></span><span class="line"><span class="cl">    0, 0,-1,
</span></span><span class="line"><span class="cl">    0, 0,-1,
</span></span><span class="line"><span class="cl">    0, 0,-1,
</span></span><span class="line"><span class="cl">    0, 0,-1,
</span></span><span class="line"><span class="cl">    0, 0,-1,
</span></span><span class="line"><span class="cl">    0, 0,-1
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/normals&gt;</span>
</span></span></code></pre></div><p>*don&rsquo;t include comments in your XML file, they&rsquo;re just here to help clear things up.</p>
<p>There&rsquo;s the mesh for the cube!</p>
<p>Now we can define the material for the cube. This is pretty easy and no
different than the last tutorial, an <code>id</code>, the <code>specular</code> value, and a <code>color</code>
(this time, blue).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;material</span> <span class="na">id=</span><span class="s">&#34;cubeMat&#34;</span> <span class="na">specular=</span><span class="s">&#34;1&#34;</span> <span class="na">color=</span><span class="s">&#34;#0088bb&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><h4 id="sphere"><a class="heading-link" href="#sphere"></a>Sphere</h4>
<p>Now we will make the sphere. GLGE doesn&rsquo;t have a very good way of dealing with
spheres, so it has to be defined in the same way as the cube. The sphere in
this example uses 1260 corners. I won&rsquo;t walk you through that, because I
haven&rsquo;t made one manually myself, I&rsquo;ve just copied the code from other
examples. You can <a href="https://camlittle.com/posts/glge-tut2/demo_xml.xml
"
target="_blank">click here</a> to see the code I&rsquo;m using if you really want to.</p>
<p>For the sphere material, I&rsquo;m going to make it red, and a bit less shiny.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;material</span> <span class="na">id=</span><span class="s">&#34;sphereMat&#34;</span> <span class="na">specular=</span><span class="s">&#34;0.5&#34;</span> <span class="na">color=</span><span class="s">&#34;#cc0000&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><h3 id="animation"><a class="heading-link" href="#animation"></a>Animation</h3>
<p>There are two ways to animate objects in GLGE. Animation can be defined in the javascript or the XML file. Usually animations are defined in the XML file and manipulated in the javascript file. For example, there could be an animation for a character walking and an animation for a character jumping (both in the XML file) and they could be switched via javascript. In this example, I&rsquo;m only using the XML file.</p>
<h4 id="cube-spin"><a class="heading-link" href="#cube-spin"></a>Cube spin</h4>
<p>I want the cube to rotate on the y and z axises.</p>
<p>First, I define this animation in an <code>&lt;animation_vector&gt;</code>. An <code>&lt;animation_vector&gt;</code> contains and id and the number of frames. These frames contribute to the speed of the animation (more on this later) and determine how we set keyframes.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;animation_vector</span> <span class="na">id=</span><span class="s">&#34;spin&#34;</span> <span class="na">frames=</span><span class="s">&#34;240&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/animation_vector&gt;</span>
</span></span></code></pre></div><p>Inside the <code>&lt;animation_vector&gt;</code> we can set the attribute changes that will occur. To do this we add an <code>&lt;animation_curve&gt;</code> for each changing attribute.</p>
<p>So, we first add an <code>&lt;animation_curve&gt;</code> that changes the rotation around the y axis.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;animation_curve</span> <span class="na">channel=</span><span class="s">&#34;RotY&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/animation_curve&gt;</span>
</span></span></code></pre></div><p>Inside this, we add the keyframes by adding <code>&lt;linear_point&gt;</code>s. Each has a <code>x</code> value that sets what frame it happens on, and a <code>y</code> value that sets the attribute value on that frame. So, in this example, at 0 frames in the animation (remember, the animation lasts 240 frames) the rotation on the y value is 0 radians, then it increases to 6.282 radians (about 360 degrees) on the 240th frame.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;0&#34;</span> <span class="na">y=</span><span class="s">&#34;0&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;240&#34;</span> <span class="na">y=</span><span class="s">&#34;6.282&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><p>I also want it to rotate around the z axis, so we&rsquo;ll add another <code>&lt;animation_curve&gt;</code> inside the <code>&lt;animation_vector&gt;</code>. It&rsquo;s almost exaclty the same, only the <code>&lt;animation_curve&gt;</code>&rsquo;s channel is set to the z axis.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;animation_curve</span> <span class="na">channel=</span><span class="s">&#34;RotZ&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;0&#34;</span> <span class="na">y=</span><span class="s">&#34;0&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;240&#34;</span> <span class="na">y=</span><span class="s">&#34;6.282&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/animation_curve&gt;</span>
</span></span></code></pre></div><p>So here&rsquo;s the final code for the sphere&rsquo;s animation.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;animation_vector</span> <span class="na">id=</span><span class="s">&#34;spin&#34;</span> <span class="na">frames=</span><span class="s">&#34;240&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;animation_curve</span> <span class="na">channel=</span><span class="s">&#34;RotY&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;0&#34;</span> <span class="na">y=</span><span class="s">&#34;0&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;240&#34;</span> <span class="na">y=</span><span class="s">&#34;6.282&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/animation_curve&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;animation_curve</span> <span class="na">channel=</span><span class="s">&#34;RotZ&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;0&#34;</span> <span class="na">y=</span><span class="s">&#34;0&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;240&#34;</span> <span class="na">y=</span><span class="s">&#34;6.282&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/animation_curve&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/animation_vector&gt;</span>
</span></span></code></pre></div><h4 id="sphere-circling"><a class="heading-link" href="#sphere-circling"></a>Sphere circling</h4>
<p>I want the sphere to circle around the cube (or circling around the z axis. I could set an x and y location attribute for each frame, but there&rsquo;s a much more efficient way to do this.</p>
<p>What we really want to do is move the sphere out 3 units, then rotate the whole system it&rsquo;s on. Imagine holding a broom by the handle and swinging it around in a circle. The sphere is the broom&rsquo;s end, and it is rotating around you, the z axis.</p>
<p>To do this, we need to introduce a new element into the XML file, a <code>&lt;group&gt;</code>. Then we can apply the animation to the group. I&rsquo;ll go over the animation quickly, since it is very simple, then move onto the <code>&lt;object&gt;</code>s and <code>&lt;group&gt;</code>s. The only difference in this animation is that it has a total of 480 frames, rather than 240. This will make it take two times longer than the cube&rsquo;s animation.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;animation_vector</span> <span class="na">id=</span><span class="s">&#34;circle&#34;</span> <span class="na">frames=</span><span class="s">&#34;480&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;animation_curve</span> <span class="na">channel=</span><span class="s">&#34;RotZ&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;0&#34;</span> <span class="na">y=</span><span class="s">&#34;0&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;480&#34;</span> <span class="na">y=</span><span class="s">&#34;6.282&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/animation_curve&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/animation_vector&gt;</span>
</span></span></code></pre></div><h3 id="the-rest-including-groups"><a class="heading-link" href="#the-rest-including-groups"></a>The rest (including groups)</h3>
<p>Next we need to add a camera (see the <a href="https://camlittle.com/posts/glge-tut1/">previous tutorial</a> for more info).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;camera</span> <span class="na">id=</span><span class="s">&#34;mainCamera&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><p>and make a scene, with a light in it (again, see the <a href="https://camlittle.com/posts/glge-tut1/">previous tutorial</a> for more info).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;scene</span> <span class="na">id=</span><span class="s">&#34;mainScene&#34;</span> <span class="na">camera=</span><span class="s">&#34;#mainCamera&#34;</span> <span class="na">ambient_color=</span><span class="s">&#34;#fff&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;light</span> <span class="na">id=</span><span class="s">&#34;mainlight&#34;</span> <span class="na">loc_x=</span><span class="s">&#34;10&#34;</span> <span class="na">loc_y=</span><span class="s">&#34;15&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;10&#34;</span> <span class="na">rot_x=</span><span class="s">&#34;-1.3&#34;</span> <span class="na">color=</span><span class="s">&#34;#fff&#34;</span> <span class="na">type=</span><span class="s">&#34;L_POINT&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/scene&gt;</span>
</span></span></code></pre></div><p>Now we&rsquo;ll add the cube. It has an <code>id</code>, references the mesh set up earlier, the material set up earlier, has some location and rotation attributes, and some new attributes. The first new attribute is the frame rate. This sets how many frames per second is applied to the animation. So, a frame rate of 60 means that an animation with a length of 240 will take (240 / 60 = 4) 4 seconds to complete and an animation with a length 480 will take 8 seconds to complete.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;object</span> <span class="na">id=</span><span class="s">&#34;cube&#34;</span> <span class="na">mesh=</span><span class="s">&#34;#cube&#34;</span> <span class="na">material=</span><span class="s">&#34;#cubeMat&#34;</span> <span class="na">frame_rate=</span><span class="s">&#34;60&#34;</span> <span class="na">animation=</span><span class="s">&#34;#spin&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;-10&#34;</span> <span class="na">rot_y=</span><span class="s">&#34;1&#34;</span> <span class="na">rot_x=</span><span class="s">&#34;1&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><h4 id="groups"><a class="heading-link" href="#groups"></a>Groups</h4>
<p>Well, one group.</p>
<p>I want to do two things separately to the sphere, so I need to surround it with a group (sort of like adding a container element for extra css).</p>
<p>The group is moved down 10 units (same level as the cube) with a location attribute, and has the <code>#circle</code> animation applied to it. Inside the group is the sphere object moved out 3. It also has, the mesh defined above, material defined above, and scale attributes that make it 80% of the original size.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;group</span> <span class="na">id=</span><span class="s">&#34;sphereGroup&#34;</span> <span class="na">animation=</span><span class="s">&#34;#circle&#34;</span> <span class="na">frame_rate=</span><span class="s">&#34;60&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;-10&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;object</span> <span class="na">id=</span><span class="s">&#34;sphere&#34;</span> <span class="na">mesh=</span><span class="s">&#34;#sphere&#34;</span> <span class="na">material=</span><span class="s">&#34;#sphereMat&#34;</span> <span class="na">loc_x=</span><span class="s">&#34;3&#34;</span> <span class="na">scale_x=</span><span class="s">&#34;0.8&#34;</span> <span class="na">scale_y=</span><span class="s">&#34;0.8&#34;</span> <span class="na">scale_z=</span><span class="s">&#34;0.8&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/group&gt;</span>
</span></span></code></pre></div><p>Woohoo! We&rsquo;re done with the XML file for this tutorial. Here&rsquo;s the whole thing.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;glge&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;mesh</span> <span class="na">id=</span><span class="s">&#34;cube&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        /* cube mesh code */
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/mesh&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;mesh</span> <span class="na">id=</span><span class="s">&#34;sphere&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        /* sphere mesh code */
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/mesh&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;material</span> <span class="na">id=</span><span class="s">&#34;cubeMat&#34;</span> <span class="na">specular=</span><span class="s">&#34;1&#34;</span> <span class="na">color=</span><span class="s">&#34;#0088bb&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;material</span> <span class="na">id=</span><span class="s">&#34;sphereMat&#34;</span> <span class="na">specular=</span><span class="s">&#34;0.5&#34;</span> <span class="na">color=</span><span class="s">&#34;#cc0000&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;animation_vector</span> <span class="na">id=</span><span class="s">&#34;spin&#34;</span> <span class="na">frames=</span><span class="s">&#34;240&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;animation_curve</span> <span class="na">channel=</span><span class="s">&#34;RotY&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;0&#34;</span> <span class="na">y=</span><span class="s">&#34;0&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;240&#34;</span> <span class="na">y=</span><span class="s">&#34;6.282&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/animation_curve&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;animation_curve</span> <span class="na">channel=</span><span class="s">&#34;RotZ&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;0&#34;</span> <span class="na">y=</span><span class="s">&#34;0&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;240&#34;</span> <span class="na">y=</span><span class="s">&#34;6.282&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/animation_curve&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/animation_vector&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;animation_vector</span> <span class="na">id=</span><span class="s">&#34;circle&#34;</span> <span class="na">frames=</span><span class="s">&#34;480&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;animation_curve</span> <span class="na">channel=</span><span class="s">&#34;RotZ&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;0&#34;</span> <span class="na">y=</span><span class="s">&#34;0&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;linear_point</span> <span class="na">x=</span><span class="s">&#34;480&#34;</span> <span class="na">y=</span><span class="s">&#34;6.282&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/animation_curve&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/animation_vector&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;camera</span> <span class="na">id=</span><span class="s">&#34;mainCamera&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;scene</span> <span class="na">id=</span><span class="s">&#34;mainScene&#34;</span> <span class="na">camera=</span><span class="s">&#34;#mainCamera&#34;</span> <span class="na">ambient_color=</span><span class="s">&#34;#fff&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;group</span> <span class="na">id=</span><span class="s">&#34;sphereGroup&#34;</span> <span class="na">animation=</span><span class="s">&#34;#circle&#34;</span> <span class="na">frame_rate=</span><span class="s">&#34;60&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;-10&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;object</span> <span class="na">id=</span><span class="s">&#34;sphere&#34;</span> <span class="na">mesh=</span><span class="s">&#34;#sphere&#34;</span> <span class="na">material=</span><span class="s">&#34;#sphereMat&#34;</span> <span class="na">loc_x=</span><span class="s">&#34;3&#34;</span> <span class="na">scale_x=</span><span class="s">&#34;0.8&#34;</span> <span class="na">scale_y=</span><span class="s">&#34;0.8&#34;</span> <span class="na">scale_z=</span><span class="s">&#34;0.8&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/group&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;object</span> <span class="na">id=</span><span class="s">&#34;cube&#34;</span> <span class="na">mesh=</span><span class="s">&#34;#cube&#34;</span> <span class="na">material=</span><span class="s">&#34;#cubeMat&#34;</span> <span class="na">frame_rate=</span><span class="s">&#34;60&#34;</span> <span class="na">animation=</span><span class="s">&#34;#spin&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;-10&#34;</span> <span class="na">rot_y=</span><span class="s">&#34;1&#34;</span> <span class="na">rot_x=</span><span class="s">&#34;1&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;light</span> <span class="na">id=</span><span class="s">&#34;mainlight&#34;</span> <span class="na">loc_x=</span><span class="s">&#34;10&#34;</span> <span class="na">loc_y=</span><span class="s">&#34;15&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;10&#34;</span> <span class="na">rot_x=</span><span class="s">&#34;-1.3&#34;</span> <span class="na">color=</span><span class="s">&#34;#fff&#34;</span> <span class="na">type=</span><span class="s">&#34;L_POINT&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/scene&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/glge&gt;</span>
</span></span></code></pre></div><h3 id="javascript"><a class="heading-link" href="#javascript"></a>Javascript</h3>
<p>Last thing to do to make the animation work is to modify our javascript file a bit. If you use the javascript from the previous tutorial, it will render one frame of the animation and no more than that. Let&rsquo;s look at the javascript for a static scene first.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">canvasElement</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;canvas&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">doc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Document</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">doc</span><span class="p">.</span><span class="nx">load</span><span class="p">(</span><span class="s2">&#34;tutorial2.xml&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">doc</span><span class="p">.</span><span class="nx">onLoad</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">renderer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Renderer</span><span class="p">(</span><span class="nx">canvasElement</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">scene</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Scene</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">scene</span> <span class="o">=</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">getElement</span><span class="p">(</span><span class="s2">&#34;mainScene&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">renderer</span><span class="p">.</span><span class="nx">setScene</span><span class="p">(</span><span class="nx">scene</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">renderer</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The line we need to look at is here.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">renderer</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
</span></span></code></pre></div><p>This renders the scene once.</p>
<p>We need to set it to render over and over again with a <code>setInterval()</code> function. GLGE has logic that slows the animation down, so you can set it to render every 1 millisecond.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">    <span class="nx">renderer</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="mi">1</span><span class="p">);</span>
</span></span></code></pre></div><p>Here is the complete javascript code.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">canvasElement</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;canvas&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">doc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Document</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">doc</span><span class="p">.</span><span class="nx">load</span><span class="p">(</span><span class="s2">&#34;tutorial2.xml&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">doc</span><span class="p">.</span><span class="nx">onLoad</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">renderer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Renderer</span><span class="p">(</span><span class="nx">canvasElement</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">scene</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Scene</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">scene</span> <span class="o">=</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">getElement</span><span class="p">(</span><span class="s2">&#34;mainScene&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">renderer</span><span class="p">.</span><span class="nx">setScene</span><span class="p">(</span><span class="nx">scene</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">        <span class="nx">renderer</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="here-it-is---update-glge-isnt-supported-any-more"><a class="heading-link" href="#here-it-is---update-glge-isnt-supported-any-more"></a><del>Here it is</del>&mdash;update, glge isn&rsquo;t supported any more</h3>
<p><canvas id="flatTriangle" width="400" height="300"></canvas></p>
<!--
<script src="http://example.com/glge-compiled-min.js"></script>

<script type="text/javascript">
var canvasElement = document.getElementById('flatTriangle');
var doc = new GLGE.Document();

doc.load("/posts/glge-tut2/demo_xml.xml
");

doc.onLoad = function() {
	var renderer = new GLGE.Renderer(canvasElement);
	var scene = new GLGE.Scene();
	scene = doc.getElement("mainScene");
	renderer.setScene(scene);

	setInterval(function(){
		renderer.render();
	}, 1);
}
</script>
-->


  
  ]]></content:encoded></item><item><title>GLGE Tutorial 1</title><link>https://camlittle.com/posts/glge-tut1/</link><pubDate>Thu, 01 Dec 2011 00:00:00 -0700</pubDate><guid isPermaLink="true">https://camlittle.com/posts/glge-tut1/</guid><enclosure url="https://camlittle.com/posts/glge-tut1/thumbnail.jpg" length="652" type="image/jpeg"/><description>&lt;p>content:
&lt;a href="#the-html-file">the HTML file&lt;/a> | &lt;a href="#the-xml-file">the XML file&lt;/a> | &lt;a href="#the-javascript">the javascript&lt;/a> | &lt;a href="#here-it-is----update-glge-isnt-supported-any-more">demo/download&lt;/a>&lt;/p>
&lt;h2 id="a-basic-scene">&lt;a class="heading-link" href="#a-basic-scene">&lt;/a>A basic scene&lt;/h2>
&lt;p>Most GLGE scenes need three things: an HTML file, some javascript, and an XML file. You&amp;rsquo;ll also need to &lt;a href="http://www.glge.org/downloads">download GLGE&lt;/a>.&lt;/p>
&lt;p>I&amp;rsquo;m going to walk through setting up a simple scene with a flat triangle and a lighting source.&lt;/p>
&lt;p>Lets start with the HTML file.&lt;/p>
&lt;h3 id="the-html-file">&lt;a class="heading-link" href="#the-html-file">&lt;/a>The HTML file&lt;/h3>
&lt;p>The HTML file does what you&amp;rsquo;d expect by setting up the basic structure of the web page. If you&amp;rsquo;re reading this tutorial you should have a solid grasp of HTML. Inside the &lt;code>body&lt;/code> there is a &lt;code>canvas&lt;/code> element with a width, height, and id, a &lt;code>script&lt;/code> referencing GLGE and a place for our javascript.&lt;/p></description><content:encoded><![CDATA[
  
<p>content:
<a href="#the-html-file">the HTML file</a> | <a href="#the-xml-file">the XML file</a> | <a href="#the-javascript">the javascript</a> | <a href="#here-it-is----update-glge-isnt-supported-any-more">demo/download</a></p>
<h2 id="a-basic-scene"><a class="heading-link" href="#a-basic-scene"></a>A basic scene</h2>
<p>Most GLGE scenes need three things: an HTML file, some javascript, and an XML file. You&rsquo;ll also need to <a href="http://www.glge.org/downloads">download GLGE</a>.</p>
<p>I&rsquo;m going to walk through setting up a simple scene with a flat triangle and a lighting source.</p>
<p>Lets start with the HTML file.</p>
<h3 id="the-html-file"><a class="heading-link" href="#the-html-file"></a>The HTML file</h3>
<p>The HTML file does what you&rsquo;d expect by setting up the basic structure of the web page. If you&rsquo;re reading this tutorial you should have a solid grasp of HTML. Inside the <code>body</code> there is a <code>canvas</code> element with a width, height, and id, a <code>script</code> referencing GLGE and a place for our javascript.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>GLGE Demo<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">canvas</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;canvas&#34;</span> <span class="na">width</span><span class="o">=</span><span class="s">&#34;400&#34;</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;300&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">canvas</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/assets/js/lib/glge-compiled-min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="c">&lt;!--</span> <span class="nx">javascript</span> <span class="nx">goes</span> <span class="nx">here</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>And that&rsquo;s it.</p>
<h3 id="the-xml-file"><a class="heading-link" href="#the-xml-file"></a>The XML file</h3>
<p>The XML file defines the scene, lighting, cameras, and any objects. The very outside structure is this&hellip;</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34;?&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;glge&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/glge&gt;</span>
</span></span></code></pre></div><p>Above is a bare GLGE document, which needs, at minimum, the following&hellip;</p>
<ul>
<li>At least one <code>&lt;mesh&gt;</code> composed of&hellip;
<ul>
<li><code>&lt;positions&gt;</code>,</li>
<li><code>&lt;normals&gt;</code>,</li>
<li><code>&lt;uv1&gt;</code> and</li>
<li><code>&lt;faces&gt;</code>,</li>
</ul>
</li>
<li>At least one <code>&lt;material&gt;</code>,</li>
<li>A <code>&lt;camera&gt;</code> and</li>
<li>A <code>&lt;scene&gt;</code> composed of&hellip;
<ul>
<li>At least one <code>&lt;object&gt;</code></li>
<li>Optional <code>&lt;light&gt;</code>s.</li>
</ul>
</li>
</ul>
<h4 id="mesh"><a class="heading-link" href="#mesh"></a>Mesh</h4>
<p>A <code>&lt;mesh&gt;</code> is what defines an object&rsquo;s shape. Meshes can be generated by 3rd party software, or can be created by hand, as shown in this example. The simplest object defined by a mesh is a flat triangle, the building block for any object. The <code>&lt;mesh&gt;</code> behind a flat triangle is very simple.</p>
<p>It has four components, <code>&lt;positions&gt;</code>, <code>&lt;normals&gt;</code>, <code>&lt;uv1&gt;</code>, and <code>&lt;faces&gt;</code>. <code>&lt;positions&gt;</code> define corners in 3D space. In the syntax I prefer to use, each line defines x, y and z coordinates of one corner. The <code>&lt;normals&gt;</code> define the perpendicular direction from the surface, which plays into how lighting is computed. In the syntax I use, each line defines x, y and z coordinates of one normal of a corner. The <code>&lt;uv1&gt;</code> defines how an (image) texture is applied to the object. The triangle we&rsquo;re creating is a solid color, so a <code>&lt;uv1&gt;</code> is not strictly necessary. In my syntax, each line is an x and y coordinate on the flat triangle. The last part, the <code>&lt;faces&gt;</code>, define how each point is used. The renderer draws the 0th point first (position -1, 1, 0; normal 0, 0, 1; and uv1 0, 0) then moves to the 1st and so on.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;mesh</span> <span class="na">id=</span><span class="s">&#34;triangle&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;positions&gt;</span>
</span></span><span class="line"><span class="cl">        -1,  1, 0,
</span></span><span class="line"><span class="cl">        -1, -1, 0,
</span></span><span class="line"><span class="cl">         1, -1, 0
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/positions&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;normals&gt;</span>
</span></span><span class="line"><span class="cl">        0, 0, 1,
</span></span><span class="line"><span class="cl">        0, 0, 1,
</span></span><span class="line"><span class="cl">        0, 0, 1
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/normals&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;uv1&gt;</span>
</span></span><span class="line"><span class="cl">        0, 0,
</span></span><span class="line"><span class="cl">        1, 0,
</span></span><span class="line"><span class="cl">        1, 1
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/uv1&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;faces&gt;</span>
</span></span><span class="line"><span class="cl">        0, 1, 2
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/faces&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/mesh&gt;</span>
</span></span></code></pre></div><h4 id="material"><a class="heading-link" href="#material"></a>Material</h4>
<p>If the XML document only contained a mesh, we still wouldn&rsquo;t see anything. It&rsquo;s just the definition of an object, not the object itself. The next thing needed is a <code>&lt;material&gt;</code>. A <code>&lt;material&gt;</code> defines an image or color to apply to the object and how lighting affects it.</p>
<p>The simple <code>&lt;material&gt;</code> defined in my example has three parts. An <code>id</code> to identify the element, a specular shine value (think reflection), and a color. The specular value is a value from 0 to 1. The color can be a hexadecimal or rgb value.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;material</span> <span class="na">id=</span><span class="s">&#34;triangleMat&#34;</span> <span class="na">specular=</span><span class="s">&#34;1&#34;</span> <span class="na">color=</span><span class="s">&#34;#aaa&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><h4 id="camera"><a class="heading-link" href="#camera"></a>Camera</h4>
<p>Now we have the shape of an object and what it will look like defined. Next we need to create a <code>&lt;camera&gt;</code> to see it from.</p>
<p>The <code>&lt;camera&gt;</code> is composed of an <code>id</code> and the values for it&rsquo;s location and angle. The location is defined by the attributes <code>loc_x</code>, <code>loc_y</code>, and <code>loc_z</code>. The rotation is defined by the attributes <code>rot_x</code>, <code>rot_y</code>, and <code>rot_z</code> in units of radians. In my example, I don&rsquo;t need location or rotation attributes because they are at the origin, 0, the default. A default camera is at point 0, 0, 0 and is looking straight down.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;camera</span> <span class="na">id=</span><span class="s">&#34;mainCamera&#34;</span> <span class="na">loc_x=</span><span class="s">&#34;0&#34;</span> <span class="na">loc_y=</span><span class="s">&#34;0&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;0&#34;</span> <span class="na">rot_x=</span><span class="s">&#34;0&#34;</span> <span class="na">rot_y=</span><span class="s">&#34;0&#34;</span> <span class="na">rot_z=</span><span class="s">&#34;0&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/* same as */
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;camera</span> <span class="na">id=</span><span class="s">&#34;mainCamera&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><h4 id="scene"><a class="heading-link" href="#scene"></a>Scene</h4>
<p>Now we have an object and our frame of viewing reference. Next we can start setting up a scene, comprised of an <code>id</code>, a reference to the <code>&lt;camera&gt;</code> we set up and ambient lighting, defined by a color.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;scene</span> <span class="na">id=</span><span class="s">&#34;mainScene&#34;</span> <span class="na">camera=</span><span class="s">&#34;#mainCamera&#34;</span> <span class="na">ambient_color=</span><span class="s">&#34;#fff&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/scene&gt;</span>
</span></span></code></pre></div><p>Inside our scene we can add <code>&lt;object&gt;</code>s and <code>&lt;light&gt;</code>s. My simple example has one <code>&lt;object&gt;</code> and one <code>&lt;light&gt;</code>, but the <code>&lt;light&gt;</code>&rsquo;s is not necessary to see the object.</p>
<h4 id="object"><a class="heading-link" href="#object"></a>Object</h4>
<p>The <code>&lt;object&gt;</code> has an <code>id</code>, a reference to the <code>&lt;mesh&gt;</code> set up earlier, location attributes (the same syntax as for the camera), rotation attributes, and scale attributes (<code>scale_x</code>, <code>scale_y</code> and <code>scale_z</code>). Again, the location and rotation attributes default to 0. The scale attributes default to 1. In my example, I&rsquo;ve moved my triangle down (z axis) by 4 units so it&rsquo;s not at the same location as the <code>&lt;camera&gt;</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;object</span> <span class="na">id=</span><span class="s">&#34;triangle&#34;</span> <span class="na">mesh=</span><span class="s">&#34;#triangle&#34;</span> <span class="na">material=</span><span class="s">&#34;#triangleMat&#34;</span> <span class="na">loc_x=</span><span class="s">&#34;0&#34;</span> <span class="na">loc_y=</span><span class="s">&#34;0&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;-4&#34;</span> <span class="na">rot_x=</span><span class="s">&#34;0&#34;</span> <span class="na">rot_y=</span><span class="s">&#34;0&#34;</span> <span class="na">rot_z=</span><span class="s">&#34;0&#34;</span> <span class="na">scale_x=</span><span class="s">&#34;1&#34;</span> <span class="na">scale_y=</span><span class="s">&#34;1&#34;</span> <span class="na">scale_z=</span><span class="s">&#34;1&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/* same as */
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;object</span> <span class="na">id=</span><span class="s">&#34;triangle&#34;</span> <span class="na">mesh=</span><span class="s">&#34;#triangle&#34;</span> <span class="na">material=</span><span class="s">&#34;#triangleMat&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;-4&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><h4 id="light"><a class="heading-link" href="#light"></a>Light</h4>
<p>Now we have a functioning scene that will work if we were to render it! But&hellip; I want to make it look a bit more three dimentional (we are using WebGL). So I&rsquo;ll add a <code>&lt;light&gt;</code>. The light has an <code>id</code>, location, rotation, a <code>color</code>, and a <code>type</code>. Location and rotation are the same as <code>&lt;object&gt;</code>s and <code>&lt;camera&gt;</code>s. The <code>type</code> that I&rsquo;m using is <code>L_POINT</code> for point lighting. A point light has a location and faces a certain direction.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;light</span> <span class="na">id=</span><span class="s">&#34;mainlight&#34;</span> <span class="na">loc_x=</span><span class="s">&#34;10&#34;</span> <span class="na">loc_y=</span><span class="s">&#34;15&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;10&#34;</span> <span class="na">rot_x=</span><span class="s">&#34;-1.3&#34;</span> <span class="na">color=</span><span class="s">&#34;#fff&#34;</span> <span class="na">type=</span><span class="s">&#34;L_POINT&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><h4 id="done"><a class="heading-link" href="#done"></a>Done!</h4>
<p>So, that&rsquo;s it for this basic GLGE XML document. Here&rsquo;s it is in full.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;glge&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;mesh</span> <span class="na">id=</span><span class="s">&#34;triangle&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;positions&gt;</span>
</span></span><span class="line"><span class="cl">            -1,  1, 0,
</span></span><span class="line"><span class="cl">            -1, -1, 0,
</span></span><span class="line"><span class="cl">             1, -1, 0
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/positions&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;normals&gt;</span>
</span></span><span class="line"><span class="cl">            0, 0, 1,
</span></span><span class="line"><span class="cl">            0, 0, 1,
</span></span><span class="line"><span class="cl">            0, 0, 1
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/normals&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;uv1&gt;</span>
</span></span><span class="line"><span class="cl">            0, 0,
</span></span><span class="line"><span class="cl">            1, 0,
</span></span><span class="line"><span class="cl">            1, 1
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/uv1&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;faces&gt;</span>
</span></span><span class="line"><span class="cl">            0, 1, 2
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/faces&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/mesh&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;material</span> <span class="na">id=</span><span class="s">&#34;triangleMat&#34;</span> <span class="na">specular=</span><span class="s">&#34;1&#34;</span> <span class="na">color=</span><span class="s">&#34;#aaa&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;camera</span> <span class="na">id=</span><span class="s">&#34;mainCamera&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;scene</span> <span class="na">id=</span><span class="s">&#34;mainScene&#34;</span> <span class="na">camera=</span><span class="s">&#34;#mainCamera&#34;</span> <span class="na">ambient_color=</span><span class="s">&#34;#fff&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;object</span> <span class="na">id=</span><span class="s">&#34;triangle&#34;</span> <span class="na">mesh=</span><span class="s">&#34;#triangle&#34;</span> <span class="na">material=</span><span class="s">&#34;#triangleMat&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;-4&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;light</span> <span class="na">id=</span><span class="s">&#34;mainlight&#34;</span> <span class="na">loc_x=</span><span class="s">&#34;10&#34;</span> <span class="na">loc_y=</span><span class="s">&#34;15&#34;</span> <span class="na">loc_z=</span><span class="s">&#34;10&#34;</span> <span class="na">rot_x=</span><span class="s">&#34;-1.3&#34;</span> <span class="na">color=</span><span class="s">&#34;#fff&#34;</span> <span class="na">type=</span><span class="s">&#34;L_POINT&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/scene&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/glge&gt;</span>
</span></span></code></pre></div><h3 id="the-javascript"><a class="heading-link" href="#the-javascript"></a>The javascript</h3>
<p>The javascript is pretty important. It sets up a GLGE environment, fetches all the data in the XML file, and renders it as a 3D scene. For this example, it&rsquo;s pretty simple because there&rsquo;s no action, movement or controls.</p>
<p>First, we need to get our canvas element.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">canvasElement</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;canvas&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>Then we create the GLGE environment.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">doc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Document</span><span class="p">();</span>
</span></span></code></pre></div><p>Next we will load the XML file into the GLGE document.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">doc</span><span class="p">.</span><span class="nx">load</span><span class="p">(</span><span class="s2">&#34;path/to/demo_xml.xml&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>Now we set up a function to run once the XML file is loaded.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">doc</span><span class="p">.</span><span class="nx">onLoad</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Inside this function we will create a renderer and scene.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">renderer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Renderer</span><span class="p">(</span><span class="nx">canvasElement</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">scene</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Scene</span><span class="p">();</span>
</span></span></code></pre></div><p>Then we bind the scene in the javascript with the scene from the XML file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">scene</span> <span class="o">=</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">getElement</span><span class="p">(</span><span class="s2">&#34;mainScene&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>Then we bind the renderer to the scene.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">renderer</span><span class="p">.</span><span class="nx">setScene</span><span class="p">(</span><span class="nx">scene</span><span class="p">);</span>
</span></span></code></pre></div><p>And finally, we render everything.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">renderer</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
</span></span></code></pre></div><h4 id="done-1"><a class="heading-link" href="#done-1"></a>Done!</h4>
<p>Here&rsquo;s all the javascript.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">canvasElement</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;canvas&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">doc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Document</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">doc</span><span class="p">.</span><span class="nx">load</span><span class="p">(</span><span class="s2">&#34;triangle.xml&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">doc</span><span class="p">.</span><span class="nx">onLoad</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">renderer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Renderer</span><span class="p">(</span><span class="nx">canvasElement</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">scene</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GLGE</span><span class="p">.</span><span class="nx">Scene</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">scene</span> <span class="o">=</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">getElement</span><span class="p">(</span><span class="s2">&#34;mainScene&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">renderer</span><span class="p">.</span><span class="nx">setScene</span><span class="p">(</span><span class="nx">scene</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">renderer</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="here-it-is----update-glge-isnt-supported-any-more"><a class="heading-link" href="#here-it-is----update-glge-isnt-supported-any-more"></a><del>Here it is</del> &ndash; update, glge isn&rsquo;t supported any more</h3>
<p>This is the scene described above.</p>
<p><canvas id="flatTriangle" width="400" height="300"></canvas></p>
<!--
<script src="http://example.com/glge-compiled-min.js"></script>

<script type="text/javascript">
var canvasElement = document.getElementById('flatTriangle');
var doc = new GLGE.Document();

doc.load("/posts/glge-tut1/demo_xml.xml
");

doc.onLoad = function() {
    var renderer = new GLGE.Renderer(canvasElement);
    var scene = new GLGE.Scene();
    scene = doc.getElement("mainScene");
    renderer.setScene(scene);
    renderer.render();
}
</script>

-->


  
  ]]></content:encoded></item></channel></rss>