<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Johan Larsson</title>
<link>https://jolars.co/blog/</link>
<atom:link href="https://jolars.co/blog/index.xml" rel="self" type="application/rss+xml"/>
<description>My personal website, where I write about my work as a researcher in statistics as well as software that I am developing.
</description>
<generator>quarto-1.8.27</generator>
<lastBuildDate>Mon, 12 Jan 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>Introducing Tomat: A Pomodoro Timer for Status Bars</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2026-01-12-tomat/</link>
  <description><![CDATA[ 





<section id="introduction" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="introduction">Introduction</h2>
<p>I have on-and-off been a user of the Pomodoro technique, which is based on splitting your work day into increments of, typically, 25-minute work sessions followed by 5-minute breaks, repeated say, four times, and then followed by a longer, 15-minute break. In Figure&nbsp;1, you can see a simple illustration of how the Pomodoro technique works.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<div class="cell-output-display page-columns page-full">
<div id="fig-basic-pomodoro" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-basic-pomodoro-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div>
<pre class="mermaid mermaid-js" data-label="fig-basic-pomodoro">graph TD
    A[25 min work] --&gt;|Sessions 1-4| B[5 min break]
    B --&gt; A
    A --&gt;|Session 5| C[15 min long break]
    C --&gt; A
</pre>
</div>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-basic-pomodoro-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: An illustration of the Pomodoro technique.
</figcaption>
</figure>
</div>
</div>
</div>
<p>Every time I have used the technique, I have always found it to be helpful for maintaining focus. But perhaps more importantly (at least for me), the short breaks have helped me to avoid burnout and the physical discomfort from sitting in front of a computer for too long.</p>
<p>Before I switched to tiling window managers like i3 and sway<sup>1</sup>, I used the <a href="https://gnomepomodoro.org/">GNOME Pomodoro Timer</a>, which I was happy with. But ever since I switched from GNOME, I’ve been struggling to find a good replacement. Unfortunately, there is a jungle of Pomodoro timers out there, and grasping the scope and limitations of each has been difficult. What I have been looking for is a Pomodoro timer that integrates with my status bar (currently waybar) and that I can control using either keyboard shortcuts or mouse clicks. I want it to be lightweight and easy to configure.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Which is what I am currently using.</p></div></div><p>The alternatives I have considered include</p>
<ul>
<li><a href="https://github.com/Andeskjerf/waybar-module-pomodoro">waybar-pomodoro-timer</a>,</li>
<li><a href="https://github.com/gabrielzschmitz/Tomato.C">Tomato.C</a>,</li>
<li><a href="https://github.com/nimaaskarian/potato-c">potato-c</a>,</li>
<li><a href="https://github.com/jbirnick/waybar-timer">waybar-timer</a>, and</li>
<li><a href="https://github.com/metent/uair">uair</a>.</li>
</ul>
<p>Each of these are good in their own way, and yet none of them ever quite fit my use case. So, eventually, I decided to write my own Pomodoro timer, which I named <strong>Tomat</strong> (Swedish 🇸🇪 for tomato).</p>
<p>The main goal was to create a Pomodoro timer that’s easy to use and configure and that easily integrates with status bars like waybar. It roughly follows the GNU Philosophy of “Do One Thing Well”, and does not try to be a full-fledged time management application. It also tries to be as lightweight as possible, without unnecessary dependencies, and is written in Rust (which helps).</p>
<p>Tomat is designed as a client–server application. The server runs in the background and keeps track of the timer state, while the client is used to interact with the server, e.g., to start or stop the timer or to query the current status. This design makes it easy to integrate Tomat with status bars, because the timer state isn’t tied to the lifetime of the status bar process, which may be restarted every now and then.</p>
<p>This comes at the cost of having to start and manage a background daemon process, but in return you get a more robust and flexible application. The architecture is illustrated in Figure&nbsp;2.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<div class="cell-output-display page-columns page-full">
<div id="fig-tomat-architecture" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-tomat-architecture-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div>
<pre class="mermaid mermaid-js" data-label="fig-tomat-architecture">graph TD
    A[Client] --&gt;|Commands| B(Server)
    B --&gt;|Status| A
    B --&gt;|Notifications| C[Notification System] 
    B --&gt;|Sound| D[Sound System]    
</pre>
</div>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-tomat-architecture-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: A high-level overview of the Tomat architecture.
</figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="quick-start" class="level2">
<h2 class="anchored" data-anchor-id="quick-start">Quick Start</h2>
<p>Tomat is available on <a href="https://crates.io/crates/tomat">crates.io</a>, so if you have Rust and Cargo installed, installing it is as simple as running:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">cargo</span> install tomat</span></code></pre></div></div>
<p>To then get going, just start the daemon:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">tomat</span> daemon start</span>
<span id="cb2-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Started daemon in background (PID: 2171200)</span></span>
<span id="cb2-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Daemon started successfully</span></span></code></pre></div></div>
<p>By default, Tomat starts in a paused state, so to start your first Pomodoro session, just run:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">tomat</span> start</span>
<span id="cb3-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Pomodoro started: 25min work, 5min break, 15min long break every 4 sessions</span></span></code></pre></div></div>
<p>You can then check the status of the timer by running:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">tomat</span> status</span>
<span id="cb4-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#{"text":"🍅 24:27 ▶","tooltip":"Work (1/4) - 25.0min","class":"work","percentage":2.1999999999999997}</span></span></code></pre></div></div>
<p>The default output is the JSON format that Waybar expects, but Tomat supports multiple formats, including a plain text format:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb5-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">tomat</span> status <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-o</span> plain</span>
<span id="cb5-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">🍅</span> 22:29 ▶</span></code></pre></div></div>
</section>
<section id="systemd-integration" class="level2">
<h2 class="anchored" data-anchor-id="systemd-integration">Systemd Integration</h2>
<p>Most users will want to start the Tomat daemon automatically when they log in, so that it’s always running in the background and ready to be interacted with.</p>
<p>For this reason, Tomat comes bundled with a systemd service file that can be installed through the following command:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">tomat</span> daemon install</span></code></pre></div></div>
<p>Then, you just need to enable and start the service:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">systemctl</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--user</span> enable <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--now</span> tomat</span></code></pre></div></div>
</section>
<section id="configuration" class="level2">
<h2 class="anchored" data-anchor-id="configuration">Configuration</h2>
<p>Tomat is configured via a TOML configuration file, which is located at <code>~/.config/tomat/config.toml</code> by default.</p>
<p>Below is an example configuration file with the defaults:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode toml code-with-copy"><code class="sourceCode toml"><span id="cb8-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[timer]</span></span>
<span id="cb8-2"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">work</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">25.0</span>          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Work duration in minutes</span></span>
<span id="cb8-3"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">break</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">5.0</span>          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Break duration in minutes</span></span>
<span id="cb8-4"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">long_break</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">15.0</span>    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Long break duration in minutes</span></span>
<span id="cb8-5"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">sessions</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>         <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Sessions until long break</span></span>
<span id="cb8-6"></span>
<span id="cb8-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[sound]</span></span>
<span id="cb8-8"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">enabled</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">true</span>       <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Enable sound notifications</span></span>
<span id="cb8-9"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">volume</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>         <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Volume level (0.0-1.0)</span></span>
<span id="cb8-10"></span>
<span id="cb8-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[notification]</span></span>
<span id="cb8-12"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">enabled</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">true</span>        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Enable desktop notifications</span></span>
<span id="cb8-13"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">icon</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"auto"</span>         <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Icon mode: "auto", "theme", or custom path</span></span>
<span id="cb8-14"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">timeout</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4000</span>        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Notification timeout in milliseconds</span></span>
<span id="cb8-15"></span>
<span id="cb8-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[display]</span></span>
<span id="cb8-17"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">text_format</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"{icon} {time} {state}"</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Text display format</span></span></code></pre></div></div>
</section>
<section id="command-line-interface" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="command-line-interface">Command-Line Interface</h2>
<p>Tomat is controlled through a command-line interface (CLI), which communicates with the background daemon. Below is a summary of the available commands.<sup>2</sup></p>
<div class="no-row-height column-margin column-container"><div id="fn2"><p><sup>2</sup>&nbsp;Tomat comes with man pages as well, which are installed directly if using nix.</p></div></div><section id="control-timer" class="level3">
<h3 class="anchored" data-anchor-id="control-timer">Control Timer</h3>
<dl>
<dt><code>tomat status</code></dt>
<dd>
Get current status (JSON for Waybar)
</dd>
<dt><code>tomat watch</code></dt>
<dd>
Continuously output status updates
</dd>
<dt><code>tomat toggle</code></dt>
<dd>
Pause/resume timer
</dd>
<dt><code>tomat skip</code></dt>
<dd>
Skip to next phase
</dd>
<dt><code>tomat stop</code></dt>
<dd>
Stop timer and return to idle
</dd>
</dl>
</section>
<section id="daemon-management" class="level3">
<h3 class="anchored" data-anchor-id="daemon-management">Daemon Management</h3>
<p>For managing the background daemon, the following commands are available:</p>
<p><code>tomat daemon &lt;subcommand&gt;</code></p>
<p>Where <code>&lt;subcommand&gt;</code> is one of:</p>
<dl>
<dt><code>start</code></dt>
<dd>
Start background daemon
</dd>
<dt><code>stop</code></dt>
<dd>
Stop daemon
</dd>
<dt><code>status</code></dt>
<dd>
Check daemon status
</dd>
<dt><code>install</code></dt>
<dd>
Install systemd user service
</dd>
<dt><code>uninstall</code></dt>
<dd>
Remove systemd user service
</dd>
</dl>
</section>
</section>
<section id="packaging" class="level2">
<h2 class="anchored" data-anchor-id="packaging">Packaging</h2>
<p>At the moment, Tomat is only packaged as a crate, but I plan to bundle it as deb and rpm packages in the near future, which will make installation and setup even easier.</p>
<p>If you happen to be using <a href="https://nixos.org/">NixOS</a>, however, Tomat is already packaged and available in the nixpkgs repository. In addition, it is also available as a <a href="https://github.com/nix-community/home-manager">home manager</a>, which means that it is extremely easy to set up and configure if you are using home manager to manage your user environment. For instance:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode nix code-with-copy"><code class="sourceCode nix"><span id="cb9-1"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb9-2">  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">services</span>.<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">tomat</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb9-3">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">enable</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb9-4"></span>
<span id="cb9-5">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">settings</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb9-6">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">timer</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb9-7">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">work</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">25</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb9-8">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">break</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb9-9">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb9-10">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb9-11">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb9-12"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>And that’s it!</p>
</section>
<section id="notifications-and-sound-alerts" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="notifications-and-sound-alerts">Notifications and Sound Alerts</h2>
<p>Tomat comes with built-in support for desktop notifications (using <code>notify-rust</code>), which means that it will integrate nicely with your desktop environment’s notification system as long as you have a notification daemon running, such as <code>dunst</code> or <code>mako</code>.</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2026-01-12-tomat/notification.jpg" class="img-fluid figure-img" width="300"></p>
<figcaption class="margin-caption">The standard notification that Tomat shows when a (short) break ends.</figcaption>
</figure>
</div>
<p>Sound alerts are also supported and compiled directly into the binary, which means that you don’t need to install any additional dependencies to get sound alerts working. By default, Tomat uses simple beep sounds:</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><audio src="break-to-work.wav" class="img-fluid" controls=""><a href="break-to-work.wav">Audio</a></audio></p>
<figcaption class="margin-caption">Audio alert for the start of a work session</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><audio src="work-to-break.wav" class="img-fluid" controls=""><a href="work-to-break.wav">Audio</a></audio></p>
<figcaption class="margin-caption">Audio alert for the start of a break</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><audio src="work-to-long-break.wav" class="img-fluid" controls=""><a href="work-to-long-break.wav">Audio</a></audio></p>
<figcaption class="margin-caption">Audio alert for the end of a long break</figcaption>
</figure>
</div>
</section>
<section id="integrations" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="integrations">Integrations</h2>
<p>All of the above just gets you set up with the daemon and CLI client, but the goal of Tomat, as I mentioned, is to use it with status bars. Configuring that is the final piece of the puzzle. A full integration guide is available <a href="https://github.com/jolars/tomat/blob/main/docs/integration.md">in the repo</a>, but here I summarize the steps needed for Waybar.</p>
<section id="waybar" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="waybar">Waybar</h3>
<p><a href="https://github.com/Alexays/Waybar">Waybar</a> is a highly configurable status bar for Wayland window managers, and it is my current status bar of choice. Tomat was built with waybar integration in mind, so setting it up is quite straightforward.</p>
<p>In your waybar configuration file (typically <code>~/.config/waybar/config</code>), you can add a custom module for Tomat like this:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode json code-with-copy"><code class="sourceCode json"><span id="cb10-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb10-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"modules-right"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"custom/tomat"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-3">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"custom/tomat"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb10-4">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"exec"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tomat status"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-5">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"interval"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-6">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"return-type"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"json"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-7">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"format"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"{text}"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-8">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"tooltip"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-9">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"on-click"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tomat toggle"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-10">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"on-click-right"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tomat skip"</span></span>
<span id="cb10-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb10-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>This configuration will add a Tomat module to the right side of your Waybar, which will display the current status of the Pomodoro timer. Clicking on the module will toggle the timer (start/pause), and right-clicking will skip to the next session (work/break).</p>
<p>In my own setup, it looks like Figure&nbsp;3.</p>
<div id="fig-waybar-tomat" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-waybar-tomat-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://jolars.co/blog/2026-01-12-tomat/statusbar.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-waybar-tomat-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: A screenshot of Waybar with the Tomat module showing an active Pomodoro session.
</figcaption>
</figure>
</div>
<section id="css-styling" class="level4">
<h4 class="anchored" data-anchor-id="css-styling">CSS Styling</h4>
<p>You can also style the module based on the timer state using CSS classes. For example, you can add the following to your Waybar CSS file (typically <code>~/.config/waybar/style.css</code>):</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode css code-with-copy"><code class="sourceCode css"><span id="cb11-1"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">#custom-tomat</span> {</span>
<span id="cb11-2">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">padding</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb11-3">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">margin</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb11-4">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">border-radius</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb11-5">}</span>
<span id="cb11-6"></span>
<span id="cb11-7"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">#custom-tomat</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">.work</span> {</span>
<span id="cb11-8">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">background-color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#ff6b6b</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb11-9">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#ffffff</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb11-10">}</span>
<span id="cb11-11"></span>
<span id="cb11-12"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">#custom-tomat</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">.work-paused</span> {</span>
<span id="cb11-13">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">background-color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#ff9999</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb11-14">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#ffffff</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb11-15">}</span></code></pre></div></div>
</section>
</section>
</section>
<section id="hooks" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="hooks">Hooks</h2>
<p>A recent addition to Tomat<sup>3</sup> is the support for event hooks, which allow users to run commands on state changes (e.g., when a Pomodoro session starts or ends). This means that you can integrate Tomat into more complex workflows, such as</p>
<div class="no-row-height column-margin column-container"><div id="fn3"><p><sup>3</sup>&nbsp;As of version 2.6.0.</p></div></div><ul>
<li>locking your screen when a break starts,</li>
<li>pausing or even changing your music when a work session starts,</li>
<li>dimming your screen during breaks, or</li>
<li>opening up a task management application (e.g.&nbsp;<a href="https://taskwarrior.org/">taskwarrior</a>) when a work session starts.</li>
</ul>
<p>Hooks can only be configured through a configuration file (at <code>~/.config/tomat/config.toml</code> by default), and an example configuration with hooks looks like this:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode toml code-with-copy"><code class="sourceCode toml"><span id="cb12-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Example: Dim screen when timer is paused</span></span>
<span id="cb12-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[hooks.on_pause]</span></span>
<span id="cb12-3"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/home/user/scripts/dim-screen.sh"</span></span>
<span id="cb12-4"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cwd</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/home/user"</span></span>
<span id="cb12-5"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">timeout</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span></span>
<span id="cb12-6"></span>
<span id="cb12-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Example: Pause music during work sessions</span></span>
<span id="cb12-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[hooks.on_work_start]</span></span>
<span id="cb12-9"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"playerctl"</span></span>
<span id="cb12-10"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">args</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pause"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb12-11"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">timeout</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span></code></pre></div></div>
</section>
<section id="conclusion" class="level2">
<h2 class="anchored" data-anchor-id="conclusion">Conclusion</h2>
<p>Tomat is a lightweight and easy-to-use Pomodoro timer designed for integration with status bars like waybar. It is highly configurable and comes with features like desktop notifications, sound alerts, and event hooks. If you’re looking for a simple Pomodoro timer that fits well into your tiling window manager setup, give Tomat a try!</p>
<p>And if you have any feedback or want to contribute, feel free to check out the <a href="https://github.com/jolars/tomat">GitHub repository</a>, which provides a <a href="https://github.com/jolars/tomat/blob/main/CONTRIBUTING.md">contributing guide</a>, a <a href="https://github.com/jolars/tomat/discussions">discussion forum</a>, and a <a href="https://github.com/jolars/tomat/issues">bug tracker</a>.</p>


</section>


 ]]></description>
  <category>Tomat</category>
  <category>Productivity</category>
  <category>Pomodoro</category>
  <category>Linux</category>
  <guid>https://jolars.co/blog/2026-01-12-tomat/</guid>
  <pubDate>Mon, 12 Jan 2026 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2026-01-12-tomat/tomato.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>What’s New in Moloch 2.0.0</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2025-12-05-moloch-v2/</link>
  <description><![CDATA[ 





<section id="moloch-2.0.0-is-released" class="level2">
<h2 class="anchored" data-anchor-id="moloch-2.0.0-is-released">Moloch 2.0.0 is Released!</h2>
<p>I’m excited to announce a major update to Moloch, a minimalist Beamer presentation theme. Version 2.0.0 brings a complete overhaul of the color system, new themes, improved documentation, and several other refinements.</p>
<p>Moloch uses semantic versioning, so this major version bump indicates that there are breaking changes. This only applies to aesthetics, however, and all your existing presentations should still compile without issues. But do expect some changes in appearance due to the redesigned color system, updates to the itemize environment, and tweaks to existing themes.</p>
</section>
<section id="redesigned-color-system" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="redesigned-color-system">Redesigned Color System</h2>
<p>The biggest change in this release is a complete redesign of how colors work in Moloch. I have become increasingly frustrated with the previous system for a couple of reasons:</p>
<ul>
<li>The colors of the text, its background, and the respective colors in the frame titles were all linked together, which for instance meant that the frame title background color had to be the same as the text color, which limited customization options.</li>
<li>Switching between light and dark modes was toggled by <code>background=dark</code>, which both felt unintuitive (since it actually inverted all the colors) and also meant that the colors in the primary palette had to make sense if inverted, which again limited flexibility.</li>
<li>Because the light and dark switching was so basic, it meant that alert and example colors needed to be the same for both light and dark variants, but typically one wants to have brighter colors when the background is dark, and vice versa.</li>
<li>Customizing the colors was quite difficult. Users would have to modify colors manually using Beamer’s <code>\setbeamercolor{}</code>, with <code>fg</code> and <code>bg</code> options set correctly. I didn’t really have any problem with that, but it could not allow for granular control of light and dark themes. And more problematically, colors were linked via <code>use</code> options, but these do not automatically refresh if the color linked is updated, which meant that users would have to add multiple lines just to modify the alert color everywhere it was used.</li>
<li>Color themes lived in their own files and were added via <code>\usecolortheme{moloch-tomorrow}</code> etc, which was starting to feel a bit hard to manage given that we might want to eventually support a large variety of color themes.</li>
</ul>
<p>The new color system now promises to fix all of these issues and is activated via a new macro <code>\molochcolors{}</code>. In the next sections I’ll try to outline how it works.</p>
<section id="light-and-dark-variants" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="light-and-dark-variants">Light and Dark Variants</h3>
<p>Every color theme now supports both light and dark variants that you can switch between seamlessly:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\molochcolors</span>{variant=light}</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\molochcolors</span>{variant=dark}</span></code></pre></div></div>
<p>This can<sup>1</sup> even be used mid-presentation, which would technically allow someone to switch back and forth between dark and light modes.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Even though I am not sure how much of practical value there is to this.</p></div></div></section>
<section id="switching-themes" class="level3">
<h3 class="anchored" data-anchor-id="switching-themes">Switching Themes</h3>
<p>Switching the entire color theme can also be done easily via this new macro:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\molochcolors</span>{theme=tomorrow}</span></code></pre></div></div>
<p>Or, if you want the dark theme:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\molochcolors</span>{theme=tomorrow, variant=dark}</span></code></pre></div></div>
<p>Both this option and the variant option are aliased in the package options, so you can also set them when loading the theme:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\usetheme</span>[</span>
<span id="cb4-2">  colortheme=tomorrow,</span>
<span id="cb4-3">  colortheme variant=dark</span>
<span id="cb4-4">]{moloch}</span></code></pre></div></div>
</section>
<section id="granular-color-control" class="level3">
<h3 class="anchored" data-anchor-id="granular-color-control">Granular Color Control</h3>
<p>The new <code>\molochcolors{}</code> command gives you granular control over individual colors in the theme. In particular, you can set colors that apply selectively to either the light or dark variant, or to the currently active variant. For instance, to set colors that apply to the current variant, you can do:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\molochcolors</span>{</span>
<span id="cb5-2">  alerted text=red,</span>
<span id="cb5-3">  progressbar fg=blue</span>
<span id="cb5-4">}</span></code></pre></div></div>
<p>Or, if you want to set colors specifically for the light variant, you can do:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\molochcolors</span>{</span>
<span id="cb6-2">  light/normal text fg=black,</span>
<span id="cb6-3">  light/frametitle bg=white</span>
<span id="cb6-4">}</span></code></pre></div></div>
<p>These settings will be stored separately for light and dark variants, so if you switch between them later, your customizations will persist!</p>
<p>As of now, the available customization options include:</p>
<ul>
<li><code>normal text fg/bg</code> - Body text colors</li>
<li><code>frametitle fg/bg</code> - Frame title colors<br>
</li>
<li><code>alerted text</code> - Alert/emphasis colors</li>
<li><code>example text</code> - Example text colors</li>
<li><code>progressbar fg/bg</code> - Progress bar colors</li>
<li><code>standout fg/bg</code> - Standout frame colors</li>
</ul>
</section>
<section id="user-accessible-semantic-colors" class="level3">
<h3 class="anchored" data-anchor-id="user-accessible-semantic-colors">User-Accessible Semantic Colors</h3>
<p>All color themes now define semantic color names (like <code>mNormaltextFg</code>, <code>mAlertFg</code>, <code>mProgressbarFg</code>) that you can use anywhere in your presentation:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb7-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">% Use theme colors in TikZ graphics</span></span>
<span id="cb7-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\begin</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">tikzpicture</span>}</span>
<span id="cb7-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\node</span>[fill=mAlertFg, text=mNormaltextBg] {Alert!};</span>
<span id="cb7-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\end</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">tikzpicture</span>}</span>
<span id="cb7-5"></span>
<span id="cb7-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">% Or in text</span></span>
<span id="cb7-7">This is <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\textcolor</span>{mExampleFg}{colored text} using theme colors.</span></code></pre></div></div>
<p>These are updated dynamically when you switch themes or variants, so you can rely on them to always reflect the current color scheme. Hopefully, this will make it easy to keep your entire presentation consistent with the chosen theme!</p>
</section>
</section>
<section id="new-color-themes" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="new-color-themes">New Color Themes</h2>
<p>As a result of the redesigned color system, it is now much easier to add new color themes. And in this release, I’ve added two new themes:</p>
<ul>
<li>Catppuccin, which is based on the popular <a href="https://catppuccin.com">Catppuccin color palette</a></li>
<li>Paper, a high-contrast theme that uses black, white, greys, and a set of colors based on Stephen Few’s book <em>Show Me the Numbers</em> <span class="citation" data-cites="few2004">(Few 2004)</span>.</li>
</ul>
<section id="catppuccin" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="catppuccin">Catppuccin</h3>
<p>The Catppuccin theme brings the beautiful pastel colors of the Catppuccin palette to Moloch presentations. It supports both light and dark variants. It uses quite low contrasts, so it is best suited for presentations in well-lit environments or web-based viewing.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\usetheme</span>[colortheme=catppuccin]{moloch}</span></code></pre></div></div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><a href="images/example-colortheme-catppuccin-light.png" class="lightbox" data-gallery="catppuccin" title="Catppuccin Light"><img src="https://jolars.co/blog/2025-12-05-moloch-v2/images/example-colortheme-catppuccin-light.png" class="img-fluid figure-img" alt="Catppuccin Light"></a></p>
<figcaption class="margin-caption">Catppuccin Light</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><a href="images/example-colortheme-catppuccin-dark.png" class="lightbox" data-gallery="catppuccin" title="Catppuccin Dark"><img src="https://jolars.co/blog/2025-12-05-moloch-v2/images/example-colortheme-catppuccin-dark.png" class="img-fluid figure-img" alt="Catppuccin Dark"></a></p>
<figcaption class="margin-caption">Catppuccin Dark</figcaption>
</figure>
</div>
</section>
<section id="paper" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="paper">Paper</h3>
<p>The Paper theme is one that I have long wanted to add, but which was difficult to implement due to the links between colors in the old system. It is a high-contrast theme that uses black, white, and greys for the main elements, and a set of colors inspired by Stephen Few’s <em>Show Me the Numbers</em> <span class="citation" data-cites="few2004">(Few 2004)</span> for alerts and examples. It uses the same background for the main text as for the frame titles, which gives it a different feel compared to other themes.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb9-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\usetheme</span>[colortheme=paper, colortheme variant=light]{moloch}</span></code></pre></div></div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><a href="images/example-colortheme-paper-light.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Paper Light"><img src="https://jolars.co/blog/2025-12-05-moloch-v2/images/example-colortheme-paper-light.png" class="img-fluid figure-img" alt="Paper Light"></a></p>
<figcaption class="margin-caption">Paper Light</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><a href="images/example-colortheme-paper-dark.png" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Paper Dark"><img src="https://jolars.co/blog/2025-12-05-moloch-v2/images/example-colortheme-paper-dark.png" class="img-fluid figure-img" alt="Paper Dark"></a></p>
<figcaption class="margin-caption">Paper Dark</figcaption>
</figure>
</div>
</section>
</section>
<section id="improved-existing-themes" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="improved-existing-themes">Improved Existing Themes</h2>
<p>I’ve also made a few changes to the dark variants of the existing themes to take advantage of the new color system. In general, I do not find the use of a white (or very light) frame title background on dark themes to be appealing, so I have adjusted these to use the same background color as the main text area or a slightly darker shade.</p>
<section id="default-theme" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="default-theme">Default Theme</h3>
<p>I generally try to be more conservative with changes to the default theme, but I’ve changed the alert and example colors to be slightly brighter in the dark variant, and also made the frame title background slightly darker to avoid the stark contrast.</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><a href="images/example-colortheme-default-dark.png" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="Default Dark"><img src="https://jolars.co/blog/2025-12-05-moloch-v2/images/example-colortheme-default-dark.png" class="img-fluid figure-img" alt="Default Dark"></a></p>
<figcaption class="margin-caption">Default Dark</figcaption>
</figure>
</div>
</section>
<section id="tomorrow-theme" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="tomorrow-theme">Tomorrow Theme</h3>
<p>In the Tomorrow theme, I’ve made the frame title of the dark variant almost black, which I find looks better than the previous very light grey. The colors for alerts and examples have also been replaced with other colors from the <a href="https://github.com/chriskempson/tomorrow-theme">Tomorrow color palette</a>.</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><a href="images/example-colortheme-tomorrow-light.png" class="lightbox" data-gallery="quarto-lightbox-gallery-6" title="Tomorrow Light"><img src="https://jolars.co/blog/2025-12-05-moloch-v2/images/example-colortheme-tomorrow-light.png" class="img-fluid figure-img" alt="Tomorrow Light"></a></p>
<figcaption class="margin-caption">Tomorrow Light</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><a href="images/example-colortheme-tomorrow-dark.png" class="lightbox" data-gallery="quarto-lightbox-gallery-7" title="Tomorrow Dark"><img src="https://jolars.co/blog/2025-12-05-moloch-v2/images/example-colortheme-tomorrow-dark.png" class="img-fluid figure-img" alt="Tomorrow Dark"></a></p>
<figcaption class="margin-caption">Tomorrow Dark</figcaption>
</figure>
</div>
</section>
<section id="high-contrast-theme" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="high-contrast-theme">High Contrast Theme</h3>
<p>Because the High Contrast theme is supposed to be high contrast, I’ve kept the white frame title background in the dark variant, but I’ve adjusted the alert and example colors to be brighter versions of the original colors.</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><a href="images/example-colortheme-highcontrast-dark.png" class="lightbox" data-gallery="quarto-lightbox-gallery-8" title="High Contrast Dark"><img src="https://jolars.co/blog/2025-12-05-moloch-v2/images/example-colortheme-highcontrast-dark.png" class="img-fluid figure-img" alt="High Contrast Dark"></a></p>
<figcaption class="margin-caption">High Contrast Dark</figcaption>
</figure>
</div>
</section>
</section>
<section id="new-documentation-website" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="new-documentation-website">New Documentation Website</h2>
<p>The Moloch documentation is now available as both a PDF manual and an <strong>interactive website at <a href="https://moloch.ink">moloch.ink</a></strong>. Even if PDF documentation seems natural for LaTeX packages, I’ve long felt that it is cumbersome to navigate, especially since users often have to scroll through many pages to find what they need and search functionality is limited compared to a website. Not to mention that PDFs are not exactly mobile-friendly.</p>
<p>I’ve been meaning to set this up for a while, but haven’t really figured out a good way to do it. Much of the documentation for the implementation details of Moloch is written inside the <code>.dtx</code> files, which is great for keeping the documentation close to the code, but not so great for building a website.</p>
<p>I realized, however, that I could just keep the documentation in LaTeX syntax there, and just have <a href="https://pandoc.org">Pandoc</a> convert it to Markdown, which can then be used as source files for a Quarto website, which I can thereafter render into both HTML and PDF<sup>2</sup> formats.</p>
<div class="no-row-height column-margin column-container"><div id="fn2"><p><sup>2</sup>&nbsp;Yes, I know this turns it into LaTeX once again, which might feel backwards, but starting to write markdown inside the <code>.dtx</code> documents seemed like a too big change, at least for now.</p></div></div><p>The documentation is built with <a href="https://quarto.org">Quarto</a>, which means:</p>
<ul>
<li><strong>Always up-to-date</strong>: Documentation is generated directly from the theme’s source code</li>
<li><strong>Interactive examples</strong>: Live code samples you can copy and modify</li>
<li><strong>Searchable</strong>: Find what you need quickly</li>
<li><strong>Beautiful presentation</strong>: Clean, modern interface with syntax highlighting</li>
<li><strong>Accessible anywhere</strong>: Read on any device without downloading PDFs</li>
</ul>
<p>The PDF manual is still available for offline reading, but the website offers a much better browsing experience with its table of contents, search functionality, and responsive design.</p>
</section>
<section id="refined-itemized-list-styling" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="refined-itemized-list-styling">Refined Itemized List Styling</h2>
<p>In version 1.1.0, Moloch introduced a refreshed list styling that used new symbols for itemize environments. Instead of using filled circle, circle, smaller circle, I switched to filled circle, filled square, and filled triangle, which I found to be more visually distinct and appealing.</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><a href="images/itemize-redesign-v1-1-0.png" class="lightbox" data-gallery="quarto-lightbox-gallery-9" title="The new and old itemize environments."><img src="https://jolars.co/blog/2025-12-05-moloch-v2/images/itemize-redesign-v1-1-0.png" class="img-fluid figure-img" alt="The new and old itemize environments."></a></p>
<figcaption class="margin-caption">The new and old itemize environments.</figcaption>
</figure>
</div>
<p>However, there were still some issues with the design<sup>3</sup>. In particular, the triangle and circles I used were based on math fonts, which sometimes led to inconsistent sizing and alignment issues that could depend on the compiler and font setup.</p>
<div class="no-row-height column-margin column-container"><div id="fn3"><p><sup>3</sup>&nbsp;See <a href="https://github.com/jolars/moloch/pull/50#issuecomment-3403850799">this comment</a> for instance.</p></div></div><p>Therefore in this release, the symbols are now all drawn using TikZ (or PGF primitives, to be precise), which ensures consistent sizing and alignment across different setups.</p>
</section>
<section id="increased-line-widths-for-the-progress-bar" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="increased-line-widths-for-the-progress-bar">Increased Line Widths for the Progress Bar</h2>
<p>The progress bars for Moloch (at section pages by default), have had quite a thin line width<sup>4</sup> that was often hard to see. So in this release, I’ve increased it from <code>0.4pt</code> to <code>1.0pt</code>, which should make it more visible without being too obtrusive. I hope that most users will find this change beneficial, and know that you can otherwise easily customize it to your liking:</p>
<div class="no-row-height column-margin column-container"><div id="fn4"><p><sup>4</sup>&nbsp;This is configurable via the <code>progressbar linewidth</code> option, but the default value was quite low.</p></div></div><div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb10-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\molochset</span>{progressbar linewidth=0.4pt}</span></code></pre></div></div>
<p>The above line would restore the previous thin line width if desired.</p>
<p>The title separator line on the title page was also increased, but only marginally from <code>0.4pt</code> to <code>0.5pt</code>, given that it has only decorative purpose, but I am considering increasing it further in a future release.</p>
</section>
<section id="migration-guide" class="level2">
<h2 class="anchored" data-anchor-id="migration-guide">Migration Guide</h2>
<p>If you’re upgrading from an earlier version of Moloch, here are the key changes to be aware of:</p>
<section id="color-options" class="level3">
<h3 class="anchored" data-anchor-id="color-options">Color Options</h3>
<p>The old <code>background=dark</code> option is deprecated. Use the new variant system instead:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb11-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">% Old (deprecated)</span></span>
<span id="cb11-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\usetheme</span>[background=dark]{moloch}</span>
<span id="cb11-3"></span>
<span id="cb11-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">% New</span></span>
<span id="cb11-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\usetheme</span>[colortheme variant=dark]{moloch}</span></code></pre></div></div>
</section>
<section id="color-theme" class="level3">
<h3 class="anchored" data-anchor-id="color-theme">Color Theme</h3>
<p>Enabling the tomorrow theme and high contrast theme is now done via the <code>colortheme</code> option:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb12-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">% Old</span></span>
<span id="cb12-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\usecolortheme</span>{moloch-tomorrow}</span>
<span id="cb12-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">% New</span></span>
<span id="cb12-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\usetheme</span>[colortheme=tomorrow]{moloch}</span></code></pre></div></div>
<p>The use of <code>\usecolortheme{moloch-highcontrast}</code> is still supported for backward compatibility, but it is recommended to switch to the new method.</p>
</section>
</section>
<section id="getting-started" class="level2">
<h2 class="anchored" data-anchor-id="getting-started">Getting Started</h2>
<p>Moloch 2.0.0 is available on <a href="https://ctan.org/pkg/moloch">CTAN</a> and will be included in the next TeX Live and MikTeX updates. You can also install it manually from the <a href="https://github.com/jolars/moloch">GitHub repository</a>.</p>
<p>To try out the new features:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb13-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">\documentclass</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">beamer</span>}</span>
<span id="cb13-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\usetheme</span>[colortheme=catppuccin, colortheme variant=light]{moloch}</span>
<span id="cb13-3"></span>
<span id="cb13-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\title</span>{My Presentation}</span>
<span id="cb13-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\author</span>{Your Name}</span>
<span id="cb13-6"></span>
<span id="cb13-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\begin</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">document</span>}</span>
<span id="cb13-8"></span>
<span id="cb13-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\maketitle</span></span>
<span id="cb13-10"></span>
<span id="cb13-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\begin</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">frame</span>}{New Color System}</span>
<span id="cb13-12">  The new color system makes it easy to create beautiful presentations.</span>
<span id="cb13-13">  </span>
<span id="cb13-14">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\begin</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">alertblock</span>}{Try It Out}</span>
<span id="cb13-15">    Customize colors with <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\texttt</span>{<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\textbackslash</span> molochcolors<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\{\}</span>}.</span>
<span id="cb13-16">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\end</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">alertblock</span>}</span>
<span id="cb13-17"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\end</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">frame</span>}</span>
<span id="cb13-18"></span>
<span id="cb13-19"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\end</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">document</span>}</span></code></pre></div></div>
<p>Check out the <a href="https://moloch.ink">full documentation</a> for comprehensive guides, examples, and customization options.</p>



</section>


<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-few2004" class="csl-entry">
Few, Stephen. 2004. <em>Show Me the Numbers: Designing Tables and Graphs to Enlighten</em>. Oakland, Calif: Analytics Press.
</div>
</div></section></div> ]]></description>
  <category>LaTeX</category>
  <category>Presentations</category>
  <category>Moloch</category>
  <guid>https://jolars.co/blog/2025-12-05-moloch-v2/</guid>
  <pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2025-12-05-moloch-v2/images/image.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Qualpal</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2025-10-22-qualpal/</link>
  <description><![CDATA[ 





<section id="qualpalr" class="level2">
<h2 class="anchored" data-anchor-id="qualpalr">Qualpal(r)</h2>
<p>It is now <em>nine</em> years <a href="../../blog/2016-10-15-introducing-qualpalr/index.html">since I first blogged</a> about my pet project Qualpal (at that point called <em>qualpalr</em>), which is an R package for generating qualitative color palettes for data visualization in R. It was motivated by the fact that almost all color palettes were designed for a fixed number of categories and therefore must be suboptimal for other numbers of categories.</p>
<p>At the time, there were only a couple of other alternatives available and out of these I only knew about <a href="http://tools.medialab.sciences-po.fr/iwanthue/">iWantHue</a>, which is a web app that generates these kinds of color palettes. And while iWantHue is great and I think has raised the issue of generating these palettes to more prominence, I felt that there were opportunities to improve upon it, which is why Qualpal was born.</p>
<p>The major innovations of Qualpal were</p>
<ol type="1">
<li>a new algorithm for selecting colors,</li>
<li>an improved metric for computing the distances between colors,</li>
<li>adaptation to color vision deficiencies, and</li>
<li>a C++ backend that made it fast enough to be used interactively and generate palettes from a large input set of colors.</li>
</ol>
<p>Another, but perhaps obvious, addition was that Qualpal was accessible directly from R, which is where I (at least at the time) did most of my work. At the time, it was the first package in R to provide this type of automatic qualitative palette generation.</p>
<p>I have made little progress on the package until the start of this year when I decided that it was time to make the investment to turn Qualpal into a more widely accessible tool. I had the following ideas in mind.</p>
<ol type="1">
<li>Transform the entire codebase into C++, not just the core algorithm, in order to make it easier to create bindings in other languages, such as Python and Julia.</li>
<li>Improve the feature set of the package.</li>
<li>Create a CLI application.</li>
<li>Create a web app, without having to rely on Shiny.</li>
<li>Support supplying a known palette name as input to Qualpal, so that users could pick a subset of colors from a given palette.</li>
<li>Allow adaptation to any kind of color vision deficiency, not just a single dichromacy type.</li>
<li>Allow users to <em>extend</em> a fixed color palette with additional colors, in order to, for instance, extend Tableau’s 10-color palette to a size of 12 colors.</li>
</ol>
</section>
<section id="the-new-c-library" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="the-new-c-library">The New C++ Library</h2>
<p>All of the above has now been implemented. I’ve decoupled the C++ parts of qualpalr into a separate library called Qualpal, which lives <a href="https://github.com/jolars/qualpal">in its own repository on GitHub</a>.</p>
<p>The new C++ library is designed to be lean and carries no other dependencies than the C++ standard library, although there is support for multi-threading via OpenMP (if available).</p>
<p>The functionality of the package is mostly concentrated in the <code>Qualpal</code> class, which uses a builder-type interface to customize palette generation. Here’s an example, where we construct a five-color palette from part of the hue-saturation-lightness (HSL) color space as input.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb1-1"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">#include </span><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">&lt;qualpal.h&gt;</span></span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">using</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">namespace</span> qualpal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-4"></span>
<span id="cb1-5">Qualpal qp<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-6">qp<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>setInputColorspace<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">({</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">170</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">});</span></span>
<span id="cb1-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">auto</span> colorspace_pal <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> qp<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>generate<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">);</span></span></code></pre></div></div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2025-10-22-qualpal/images/cpp-hsl.png" class="img-fluid figure-img"></p>
<figcaption class="margin-caption">Five colors from the HSL color space</figcaption>
</figure>
</div>
<p>In the next example, we change to using one of the built-in color palettes, in this case <em>Set2</em> from <em>ColorBrewer</em> <span class="citation" data-cites="harrower2003">(Harrower and Brewer 2003)</span> as input, and adapting the color palette to the color vision deficiency type deuteranomaly of severity 0.7 (70%).</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb2-1">qp<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>setInputPalette<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ColorBrewer:Set2"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">).</span>setCvd<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">({</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"deutan"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">});</span></span>
<span id="cb2-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">auto</span> cvd_pal <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> qp<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>generate<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">);</span></span></code></pre></div></div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2025-10-22-qualpal/images/cpp-deutan.png" class="img-fluid figure-img"></p>
<figcaption class="margin-caption">Four colors from the ColorBrewer Set2 palette with deuteranomaly adaptation</figcaption>
</figure>
</div>
<p>Extensive documentation, including a complete API reference, is available at <a href="https://jolars.github.io/qualpal/">jolars.github.io/qualpal</a>.</p>
</section>
<section id="the-r-package-qualpalr" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="the-r-package-qualpalr">The R Package (qualpalr)</h2>
<p>The R package, qualpalr, now uses the C++ library as a backend, and new releases of the C++ library are semi-automatically integrated into the R package. Only the R-specific parts remain in the qualpalr package, which means that development is now focused on the C++ library directly.</p>
<p>The main major change of the package is a switch in the character method of the main function of the package, <code>qualpal()</code>, which now assumes that the second argument is the name of a built-in palette. But backwards compatibility with the old behavior<sup>1</sup> is maintained by selectively handling the previous set of allowed input.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Where the argument defined a subspace of the HSL color space.</p></div></div><p>In the following example, we generate a 4-color palette by picking colors from Vermeer’s <em>Girl with a Pearl Earring</em>. This example also showcases a new feature that allows users to optimize the palette against a given background color (in this case white).</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(qualpalr)</span>
<span id="cb3-2">pal <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">qualpal</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Vermeer:PearlEarring"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bg =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>)</span>
<span id="cb3-3">pal<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hex</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] "#80a0c7" "#100f14" "#a65141" "#b1934a"</code></pre>
</div>
</div>
<p>The default plot method shows a multi-dimensional scaling (MDS) plot of the colors in the color difference metric space, which gives an idea of how well-separated the colors are.</p>
<div class="cell page-columns page-full">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(pal)</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2025-10-22-qualpal/index_files/figure-html/unnamed-chunk-2-1.png" class="img-fluid figure-img" width="672"></p>
<figcaption class="margin-caption">A multi-dimensional scaling (MDS) plot of colors in the color difference metric space.</figcaption>
</figure>
</div>
</div>
</div>
<section id="new-vignette" class="level3">
<h3 class="anchored" data-anchor-id="new-vignette">New Vignette</h3>
<p>Another addition to the qualpalr package, which is relevant irrespective of how you choose to use Qualpal, is a new <a href="https://jolars.github.io/qualpalr/articles/comparisons">vignette on comparisons to alternatives</a>. So, in case you are interested in knowing how Qualpal compares to other alternatives, such as <a href="https://www.yunhaiwang.net/infoVis2020/palettailor/color-palette-generation.html">palettailor</a> <span class="citation" data-cites="lu2021">(Lu et al. 2021)</span>, <a href="https://gramaz.io/colorgorical/">Colorgorical</a> <span class="citation" data-cites="gramazio2016">(Gramazio, Laidlaw, and Schloss 2016)</span>, the aforementioned <a href="https://medialab.github.io/iwanthue/">iWantHue</a>, or glasbey <span class="citation" data-cites="mcinnes2025">(McInnes 2025)</span>, please check it out. As you might guess, Qualpal performs very well in these comparisons.</p>
</section>
</section>
<section id="the-cli-tool" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="the-cli-tool">The CLI Tool</h2>
<p>As a result of refactoring the package into a pure C++ library, it was also straightforward to create a command-line interface (CLI) tool for Qualpal, relying on the excellent <a href="https://github.com/CLIUtils/CLI11">CLI11 library</a>. This CLI tool is only available from source as of now, but I hope it will be packaged for various Linux distributions in the future.</p>
<p>Here’s an example of generating a 5-color palette from the HSL colorspace:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">qualpal</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-n</span> 5 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-i</span> colorspace <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0:360"</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.4:0.8"</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.3:0.7"</span></span></code></pre></div></div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2025-10-22-qualpal/images/cli-example.png" class="img-fluid figure-img"></p>
<figcaption class="margin-caption">A palette of five colors generated from input colors as a subspace of the HSL colorspace.</figcaption>
</figure>
</div>
<p>Please see the instructions in the <a href="https://github.com/jolars/qualpal/blob/main/README.md">README</a> for how to build and use the CLI tool.</p>
</section>
<section id="the-web-app" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="the-web-app">The Web App</h2>
<p>One of the major motivations for refactoring Qualpal into a C++ library was to make it possible to serve it as a static web app by compiling the C++ code to WebAssembly (WASM). Although I love how easy it is to turn R code into web apps using Shiny, I have always been bothered by the hoops you have to jump through to deploy and manage Shiny apps, whereas the new Qualpal web app is hosted on GitHub pages as a static site and is served directly upon successful builds from GitHub Actions.</p>
<p>Running a server instance is also not free, so hosting this as a static web app means significant cost savings and automatically scales to any number of users.</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2025-10-22-qualpal/images/web-app.png" class="img-fluid figure-img"></p>
<figcaption class="margin-caption">A screenshot of the Qualpal web app</figcaption>
</figure>
</div>
</section>
<section id="joss-publication" class="level2">
<h2 class="anchored" data-anchor-id="joss-publication">JOSS Publication</h2>
<p>Finally, I am happy to announce that Qualpal was just published in the Journal of Open Source Software (JOSS)! <span class="citation" data-cites="larsson2025c">(Larsson 2025)</span> I have for a long time been interested in submitting something to JOSS, which to me seems to have a great model for papers about software.</p>
<p>First, I think the reviewer system, which is entirely based around GitHub issues, is natural for a journal about software, since much software anyway is located in a public repository, and issues can be cross-referenced seamlessly. It is also completely transparent. Finally, I like the fact that papers are short, motivated by the fact that the software’s documentation should serve as the source of information about the software, rather than an academic paper (which is fixed).</p>
</section>
<section id="new-color-difference-metric" class="level2">
<h2 class="anchored" data-anchor-id="new-color-difference-metric">New Color Difference Metric</h2>
<p>Another change that was made as part of the recent work on Qualpal was the introduction of other color difference metric. In particular, Qualpal now supports the <a href="https://en.wikipedia.org/wiki/Color_difference#CIEDE2000">CIEDE2000 metric</a> <span class="citation" data-cites="sharma2005">(Sharma, Wu, and Dalal 2005)</span>, which is the current standard set by the CIE organization for computing differences between colors. Qualpal previously used the DIN99d metric <span class="citation" data-cites="cui2002">(Cui et al. 2002)</span>, which is computationally appealing since, once the colors are converted into the DIN99d space, the color metric formula is simply Euclidean distance. But the tradeoff is that DIN99d is not as perceptually accurate, and I therefore decided o switch the default to CIEDE2000.</p>
<p>The result is that the palettes are now more distinct than before, at the cost of an increase in computation time. For most practical purposes, however, the difference in speed is negligible and Qualpal is still fast enough for this not to be even noticeable.</p>
</section>
<section id="roadmap" class="level2">
<h2 class="anchored" data-anchor-id="roadmap">Roadmap</h2>
<p>Coming back to the start of this post and the motivation for refactoring Qualpal as a stand-alone C++ library, this change means that it is now easier to create new bindings for Qualpal. The most obvious next step is therefore to create a Python package that wraps around Qualpal. Doing so should be quite straightforward and I will eventually do this myself, unless someone gets there first!</p>
<p>I am also happy for any kind of feedback regarding Qualpal, so please check out the <a href="https://github.com/jolars/qualpal">GitHub repository</a> for the project, where you can <a href="https://github.com/jolars/qualpal/issues">file issues</a> or <a href="https://github.com/jolars/qualpal/discussions">open up discussions</a> should you have any ideas on how the project could be improved or if you would like to contribute directly, which would be very much appreciated.</p>



</section>


<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-cui2002" class="csl-entry">
Cui, G., M. R. Luo, B. Rigg, G. Roesler, and K. Witt. 2002. <span>“Uniform Colour Spaces Based on the <span>DIN99</span> Colour-Difference Formula.”</span> <em>Color Research &amp; Application</em> 27 (4): 282–90. <a href="https://doi.org/10/cz7764">https://doi.org/10/cz7764</a>.
</div>
<div id="ref-gramazio2016" class="csl-entry">
Gramazio, Connor C., David H. Laidlaw, and Karen B. Schloss. 2016. <span>“Colorgorical: Creating Discriminable and Preferable Color Palettes for Information Visualization.”</span> <em>IEEE Transactions on Visualization and Computer Graphics</em> 23 (1): 521–30. <a href="https://doi.org/10.1109/TVCG.2016.2598918">https://doi.org/10.1109/TVCG.2016.2598918</a>.
</div>
<div id="ref-harrower2003" class="csl-entry">
Harrower, Mark, and Cynthia A. Brewer. 2003. <span>“<span>ColorBrewer</span>.org: An Online Tool for Selecting Colour Schemes for Maps.”</span> <em>The Cartographic Journal</em> 40 (1): 27–37. <a href="https://doi.org/10.1179/000870403235002042">https://doi.org/10.1179/000870403235002042</a>.
</div>
<div id="ref-larsson2025c" class="csl-entry">
Larsson, Johan. 2025. <span>“Qualpal: Qualitative Color Palettes for Everyone.”</span> <em>Journal of Open Source Software</em> 10 (114): 8936. <a href="https://doi.org/10.21105/joss.08936">https://doi.org/10.21105/joss.08936</a>.
</div>
<div id="ref-lu2021" class="csl-entry">
Lu, Kecheng, Mi Feng, Xin Chen, Michael Sedlmair, Oliver Deussen, Dani Lischinski, Zhanglin Cheng, and Yunhai Wang. 2021. <span>“Palettailor: Discriminable Colorization for Categorical Data.”</span> <em>IEEE Transactions on Visualization and Computer Graphics</em> 27 (02): 475–84. <a href="https://doi.org/10.1109/TVCG.2020.3030406">https://doi.org/10.1109/TVCG.2020.3030406</a>.
</div>
<div id="ref-mcinnes2025" class="csl-entry">
McInnes, Leland. 2025. <span>“Glasbey.”</span> <a href="https://github.com/lmcinnes/glasbey">https://github.com/lmcinnes/glasbey</a>.
</div>
<div id="ref-sharma2005" class="csl-entry">
Sharma, Gaurav, Wencheng Wu, and Edul N. Dalal. 2005. <span>“The <span>CIEDE2000</span> Color-Difference Formula: <span>Implementation</span> Notes, Supplementary Test Data, and Mathematical Observations.”</span> <em>Color Research &amp; Application</em> 30 (1): 21–30. <a href="https://doi.org/10.1002/col.20070">https://doi.org/10.1002/col.20070</a>.
</div>
</div></section></div> ]]></description>
  <category>R</category>
  <category>C++</category>
  <category>Color</category>
  <category>Research</category>
  <category>Data visualization</category>
  <guid>https://jolars.co/blog/2025-10-22-qualpal/</guid>
  <pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2025-10-22-qualpal/images/image.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>LUSEM’s Best Thesis Award 2024</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2025-05-06-lusem-award/</link>
  <description><![CDATA[ 





<p>It seems that I am on a lucky streak! I was also just<sup>1</sup> awarded Lund School of Economics’s award for best thesis in 2024.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Used to be correct when I started drafting this post, but now it’s been a little more than a month actually.</p></div></div><p>You can read the news post <a href="https://www.lusem.lu.se/article/lusems-best-thesis-award-2024-goes-johan-larsson">here</a>, in which I answer some questions regarding the thesis.</p>
<p>Here is the nomination:</p>
<blockquote class="blockquote">
<p>In his dissertation, Johan Larsson develops new algorithms and optimization methods for sparse regression—a methodology used when the number of predictors is large relative to the number of observations. Special emphasis is placed on regularization methods, which introduce a penalty on model complexity to reduce the risk of overfitting. This is crucial in statistics and machine learning, where models with many variables often risk capturing random noise rather than meaningful signals. Regularization helps identify the most important variables, much like finding a needle in a haystack, making theoretical models more applicable to real-world data. The dissertation introduces new algorithms for sparse regression, where many predictors can be excluded without losing essential relationships, with a particular focus on regularization methods such as LASSO and SLOPE.</p>
</blockquote>
<p>The thesis itself can be found <a href="https://lup.lub.lu.se/search/publication/0b9c97e8-5f65-43eb-9f7a-c4f237568370">here</a>.</p>
<p>Again, I want to thank LUSEM and all of the people who helped me on the way.</p>




 ]]></description>
  <category>Thesis</category>
  <category>Awards</category>
  <category>Research</category>
  <category>Statistics</category>
  <category>LUSEM</category>
  <guid>https://jolars.co/blog/2025-05-06-lusem-award/</guid>
  <pubDate>Tue, 06 May 2025 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2025-05-06-lusem-award/lusem.png" medium="image" type="image/png" height="56" width="144"/>
</item>
<item>
  <title>Cramér Prize for Best PhD Thesis</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2025-02-05-cramer-prize/</link>
  <description><![CDATA[ 





<p>I am very happy to have just heard that my thesis was selected for the 2025 Cramér Prize for best thesis in statistics defended in 2024! The thesis is about optimization and algorithms for sparse regression and I have previously written about it in a <a href="../../blog/2024-05-22-phd-thesis/">blog post here</a> so I will not delve into details. But I am tremendously happy to have received this prize and want to thank my supervisors and committee for the prize.</p>
<p>The <a href="https://statistikframjandet.se/cramersallskapet/start/">Cramér Society</a> is a branch of the Swedish statistical society<sup>1</sup>. Each year they award a prize for the best PhD thesis in statistics (or mathematical statistics) defended in Sweden. The prize is named after Harald Cramér, a Swedish mathematician and statistician who made fundamental contributions to probability theory and statistics. He was a professor at Stockholm University from 1929 to 1958 and is particularly known for his work on mathematical statistics and probability theory.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Statistikfrämjandet</p></div></div><p>The announcement can be found <a href="https://statistikframjandet.se/cramersallskapet/cramerpriset-2025-gar-till-johan-larsson/">here</a> but it is in Swedish, but I have translated it here:</p>
<blockquote class="blockquote">
<p>The Cramér Prize is awarded annually for the best doctoral thesis to a person who defended their PhD in statistics/mathematical statistics during the past calendar year. This year, the prize goes to Johan Larsson, who defended his thesis at the Department of Statistics at Lund University. The motivation for the decision reads as follows:</p>
<p>In his thesis “Optimization and algorithms in sparse regression”, Johan Larsson tackles challenges that typically arise in the analysis of datasets with a large number of predictors and relatively few observations. The thesis thereby addresses problems that are central to contemporary statistical learning. Through the development of prescreening methods and optimization algorithms, Johan improves regression modeling using established regularization methods, such as LASSO and SLOPE, in high-dimensional contexts. The thesis, which contains both theoretical and applied parts, is very well written and, in addition to the developed statistical methods, also contributes software intended for objective comparison of different optimization methods.</p>
<p>Information about previous years’ prize winners can be found at: <a href="https://statistikframjandet.se/cramersallskapet/cramerpriset/" class="uri">https://statistikframjandet.se/cramersallskapet/cramerpriset/</a></p>
</blockquote>
<p>The thesis itself can be found <a href="https://lup.lub.lu.se/search/publication/0b9c97e8-5f65-43eb-9f7a-c4f237568370">here</a>.</p>




 ]]></description>
  <category>Thesis</category>
  <category>Lasso</category>
  <category>Ridge regression</category>
  <category>Research</category>
  <category>Optimization</category>
  <category>Statistics</category>
  <guid>https://jolars.co/blog/2025-02-05-cramer-prize/</guid>
  <pubDate>Wed, 05 Feb 2025 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2025-02-05-cramer-prize/cramer-logo.png" medium="image" type="image/png" height="138" width="144"/>
</item>
<item>
  <title>New Paper on Feature Normalization and Regularized Regression</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2025-01-21-normreg/</link>
  <description><![CDATA[ 





<section id="the-lasso-elastic-net-and-ridge-regression" class="level2">
<h2 class="anchored" data-anchor-id="the-lasso-elastic-net-and-ridge-regression">The Lasso, Elastic Net, and Ridge Regression</h2>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>The paper has now been published in <a href="https://openreview.net/forum?id=6xKyDBIwQ5">Transactions on Machine Learning Research (TMLR)</a>! <span class="citation" data-cites="larsson2025">(Larsson and Wallin 2025)</span> You can find the final version <a href="https://openreview.net/forum?id=6xKyDBIwQ5">on the TMLR website</a>. The content of this blog post is mostly unchanged from the arXiv version, but please refer to the published version for the final and authoritative account of our work.</p>
<p>I’ve also summarized the paper in a <a href="https://youtu.be/GYqYAy3-B1k">short video</a>, in case you prefer that format.</p>
</div>
</div>
<p>The lasso is probably the most well-known regularization method and the first one I encountered when I first learned about regularized regression methods. The model can be written as a special case of the elastic net, which is an optimization problem that takes the following form: <img src="https://latex.codecogs.com/png.latex?%0A%20%20%5Coperatorname*%7Bminimize%7D_%7B%5Cbeta_0%20%5Cin%20%5Cmathbb%7BR%7D,%5Cbm%7B%5Cbeta%7D%20%5Cin%20%5Cmathbb%7BR%7D%5Ep%7D%0A%20%20%5Cfrac%7B1%7D%7B2%7D%20%5ClVert%20%5Cbm%20y%20-%20%5Cbeta_0%20-%20%5Cbm%7BX%7D%5Cbm%7B%5Cbeta%7D%20%5CrVert%5E2_2%20%20+%0A%20%20%5Clambda_1%20%5ClVert%20%5Cbm%5Cbeta%20%5CrVert_1%20+%20%5Cfrac%7B%5Clambda_2%7D%7B2%7D%5ClVert%20%5Cbm%20%5Cbeta%20%5CrVert_2%5E2,%0A"> where <img src="https://latex.codecogs.com/png.latex?%5Cbm%7BX%7D"> is the matrix of features, <img src="https://latex.codecogs.com/png.latex?%5Cbm%7By%7D"> is the response, and <img src="https://latex.codecogs.com/png.latex?%5Cbeta_0"> and <img src="https://latex.codecogs.com/png.latex?%5Cbm%7B%5Cbeta%7D"> are the intercept and coefficients, respectively. Setting <img src="https://latex.codecogs.com/png.latex?%5Clambda_2%20=%200"> gives us the lasso, whereas <img src="https://latex.codecogs.com/png.latex?%5Clambda_1%20=%200"> gives us ridge regression: another special case of the elastic net.</p>
<p>In the equation above, <img src="https://latex.codecogs.com/png.latex?%0A%5Clambda_1%20%5ClVert%20%5Cbm%5Cbeta%20%5CrVert_1%20+%20%5Cfrac%7B%5Clambda_2%7D%7B2%7D%5ClVert%20%5Cbm%20%5Cbeta%20%5CrVert_2%5E2%0A"> is a <em>regularization term</em> that penalizes coefficients by their magnitudes, and <img src="https://latex.codecogs.com/png.latex?%5Clambda_1"> and <img src="https://latex.codecogs.com/png.latex?%5Clambda_2"> control the strength of this penalization. The higher these are set, the more the coefficients are shrunk towards zero. A high enough value of <img src="https://latex.codecogs.com/png.latex?%5Clambda_1"> will make some or all the coefficients zero, which gives us a <em>sparse</em> model. This is the primary reason why the lasso has been so popular, since this sparsity leads to a model that is easier to interpret.</p>
</section>
<section id="normalization" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="normalization">Normalization</h2>
<p>Like most people who have been introduced to the lasso, I quickly learned that you need to normalize the features (<img src="https://latex.codecogs.com/png.latex?%5Cbm%7BX%7D">) before fitting the model since their scales affect the resulting coefficients. The larger the scale (as measured in variance or, equivalently, standard deviation), the smaller the effect of regularization becomes. The reason for this is that the coefficient can be smaller for an equivalent effect on the predicted response.</p>
<p>Many sources, including one of the first papers on the lasso by <span class="citation" data-cites="tibshiraniRegressionShrinkageSelection1996">Tibshirani (1996)</span>, recommend that you <em>standardize</em> your data, which means centering and scaling each feature by its mean and standard deviation. If your features are normally distributed, this practice speaks to intuition: each feature’s distribution can be described fully by its mean and standard deviation, so standardizing it will create a standard normal distribution, ensuring that the effects of regularization will be fair across the full set of features. After having standardized, most people will eventually want to return the coefficients to the original scales of the features, but doing so is simple.</p>
<p>The problem with the procedure, however, is that not all features are normally distributed. What, for instance, should we do about binary features?<sup>1</sup> At this point you may wonder if this matters in practice, and the answer is that it does. Consider Figure&nbsp;1, which shows the lasso paths for four different data sets and under two types of normalization schemes: standardization and max–abs normalization. The latter of these scales the data to lie in <img src="https://latex.codecogs.com/png.latex?%5B-1,%201%5D,"> which preserves sparsity for binary data but makes it sensitive to outliers.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Features that values in <img src="https://latex.codecogs.com/png.latex?%5C%7B0,1%5C%7D"> only as, for instance, whether or not you have a specific gene present in your DNA.</p></div></div><div id="fig-realdata-paths" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-realdata-paths-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://jolars.co/blog/2025-01-21-normreg/images/realdata-paths.svg" class="img-fluid figure-img" width="500">
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-realdata-paths-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: Lasso paths for some real data sets. Notice that the paths differ heavily depending on which normalization type (standardization or max–abs) is used. The first five features to be selected in either normalization scheme are colored in the plots.
</figcaption>
</figure>
</div>
<p>The point of this figure is to illustrate that the choice of normalization matters in a critical way, leading to different models and affecting the conclusions you draw from it.</p>
</section>
<section id="the-non-existent-literature" class="level2">
<h2 class="anchored" data-anchor-id="the-non-existent-literature">The (Non-Existent) Literature</h2>
<p>Remarkably, this (strong) relationship between normalization and regularization has not been studied at all. When searching for motivation for whatever normalization approach a given paper used, I typically found that papers either just stated their method of normalization as a matter of fact or motivated it by being “standard”.</p>
<p>To me, this seems fine if you’re just researching simulated data that you’ve sampled from some normal distribution. But that’s rarely the case when dealing with real data, and never so when the data is binary. There are some more informal discussions on normalization, however, so I’d like to take some time to briefly review these here. For instance, the <a href="https://scikit-learn.org/stable/modules/preprocessing.html#scaling-sparse-data">current documentation for scikit-learn</a> recommends users to use <code>MaxAbsScaler</code> (max–abs normalization) to deal with sparse data (in order to preserve sparsity), but there is no discussion on the fact that this will affect the model (as we saw in Figure&nbsp;1). My feeling about this is that I would be very careful before I based my choice of normalization (and, implicitly, the choice of model) on the way the data is stored on my hard drive (sparse or dense), particularly since it is often simple to normalize the data during optimization without having to ever store the full matrix in a dense format. This is for instance what <a href="https://glmnet.stanford.edu">glmnet</a> does, which, on the other hand, only supports a single type of normalization (standardization).</p>
<p><a href="https://github.com/pbreheny/ncvreg/issues/18#issuecomment-689496385">In a comment to an issue for the <strong>ncvreg</strong> package</a>, Patrick Breheny, the author of said package, writes that you should definitely standardize dummy features. Finally, <a href="https://stats.stackexchange.com/questions/69568/whether-to-rescale-indicator-binary-dummy-predictors-for-lasso">there is also a question on stack exchange about this</a>, but no real conclusions to be drawn from it.</p>
<p>In spite of all this, lasso, ridge, and elastic net regression have been used frequently on binary data in spite of there being very little understanding of how these methods actually work in this situation.</p>
</section>
<section id="the-paper" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="the-paper">The Paper</h2>
<p>The paper that we have written about this is titled <em>The Choice of Normalization Influences Shrinkage in Regularized Regression</em> <span class="citation" data-cites="larsson2025">(Larsson and Wallin 2025)</span> and is now <a href="https://arxiv.org/abs/2501.03821">out on arXiv</a><sup>2</sup>. In it, we begin to address this apparent knowledge gap by studying this interplay between normalization and regularization in lasso, ridge, and elastic net regression. Our focus is on the case of binary features and mixes of binary and normally distributed features.</p>
<div class="no-row-height column-margin column-container"><div id="fn2"><p><sup>2</sup>&nbsp;You can see an abstract and citation info <a href="../../publications/normreg/">here</a> as well</p></div><div id="fn3"><p><sup>3</sup>&nbsp;Or equivalently, its mean.</p></div></div><p>Our first result is that the class balance (proportion of ones versus zeros<sup>3</sup>) of the binary feature directly influences shrinkage. For both ridge and the lasso, the effect is basically that the more imbalanced the feature is, the more the coefficient will be shrunk by the estimator. Note that this effect is a by-product of regularization and does not, for instance, occur with standard linear regression.</p>
<p>Interestingly, this effect from a given normalization scheme depends directly on which regularization method is used, which means that to mitigate this effect you will need different normalization strategies depending on which type of penalty we use in our model.</p>
<p>To study this more formally, we introduce a parameterization for scaling a feature, given by <img src="https://latex.codecogs.com/png.latex?%0A%20%20s_j%20=%20(q_j%20-%20q_j%5E2)%5E%5Cdelta,%0A"> where <img src="https://latex.codecogs.com/png.latex?q_j"> is the class balance of the <img src="https://latex.codecogs.com/png.latex?j">th feature. When <img src="https://latex.codecogs.com/png.latex?%5Cdelta%20=%200">, there would for instance be no scaling for the binary feature (as in max–abs normalization). If <img src="https://latex.codecogs.com/png.latex?%5Cdelta%20=%201/2">, we would scale by the <em>standard deviation</em> of the feature—as in standardization. If <img src="https://latex.codecogs.com/png.latex?%5Cdelta%20=%201">, then we would instead scale by its variance.</p>
<p>Throughout the paper we assume that our data comes from a linear model <img src="https://latex.codecogs.com/png.latex?%0A%5Cbm%7By%7D%20=%20%5Cbm%7BX%5Cbeta%7D%20+%20%5Cbm%7B%5Cvarepsilon%7D,%0A"> where <img src="https://latex.codecogs.com/png.latex?%5Cbm%7By%7D"> is the response, <img src="https://latex.codecogs.com/png.latex?%5Cbm%7BX%7D"> is the matrix of features, and <img src="https://latex.codecogs.com/png.latex?%5Cbm%7B%5Cvarepsilon%7D"> is the vector of errors, which we assume to be identically and independently distributed and, for many of our results, normally distributed.</p>
<p>These are classical and mostly non-controversial assumptions in this field. But we also make a strong<sup>4</sup> assumption, assuming that the features are orthogonal to one another, so that <img src="https://latex.codecogs.com/png.latex?%5Cbm%7BX%7D%5E%5Cintercal%20%5Cbm%7BX%7D"> is some diagonal matrix. In this case, the elastic net estimator admits a closed-form expression, which means that we can study the effect of varying the scaling parameter <img src="https://latex.codecogs.com/png.latex?%5Cdelta">.</p>
<div class="no-row-height column-margin column-container"><div id="fn4"><p><sup>4</sup>&nbsp;Although the assumption is strong, our empirical experiments suggest that our results likely extend to more general cases.</p></div></div></section>
<section id="biasvariance-trade-offs" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="biasvariance-trade-offs">Bias–Variance Trade-Offs</h2>
<p>What we show in our paper is that there is a bias–variance trade-off with respect to this normalization parameter, <img src="https://latex.codecogs.com/png.latex?%5Cdelta">. See Figure&nbsp;2 for a simple example of this, where we consider a two-dimensional problem with one binary feature and one normally distributed feature. We have kept the true coefficients fixed at one for each of the features, and only vary <img src="https://latex.codecogs.com/png.latex?q">: the class balance (proportion of ones and zeros in the binary feature). We consider the cases <img src="https://latex.codecogs.com/png.latex?%5Cdelta=0"> (no scaling), <img src="https://latex.codecogs.com/png.latex?%5Cdelta=1"> (standard deviation scaling), and <img src="https://latex.codecogs.com/png.latex?%5Cdelta=1"> (variance scaling) and we see that there are only two settings that seem to mitigate this class-balance bias: in ridge and lasso regression, respectively, these are <img src="https://latex.codecogs.com/png.latex?%5Cdelta%20=%201/2"> and <img src="https://latex.codecogs.com/png.latex?1">.</p>
<div id="fig-mixed-data" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-mixed-data-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://jolars.co/blog/2025-01-21-normreg/images/mixed-data.svg" class="img-fluid figure-img" width="500">
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-mixed-data-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: Lasso and ridge estimates for a two-dimensional problem where one feature is a binary feature with class balance <img src="https://latex.codecogs.com/png.latex?q_j"> (<img src="https://latex.codecogs.com/png.latex?%5Coperatorname%7BBernoulli%7D(q_j)">) and the other is quasi-normal with standard deviation 1/2, (<img src="https://latex.codecogs.com/png.latex?%5Coperatorname%7BNormal%7D(0,%200.5)">).
</figcaption>
</figure>
</div>
<p>This figure also shows that this type of variance scaling, while <em>reducing</em> bias, <em>increases</em> variance in the estimates. This is not particularly surprising, however, since an unbalanced feature necessarily provides less information than a balanced one.</p>
<p>Figure&nbsp;2 is purely an empirical result from a simulation, but under our particular assumptions we can work out this bias–variance trade-off exactly. In Figure&nbsp;3, we have done exactly that for the lasso. (The paper includes results for ridge regression and the elastic net as well.) In this figure, <img src="https://latex.codecogs.com/png.latex?%5Csigma_%5Cvarepsilon"> represents the standard deviation of the error term in our data—the measurement noise. And what we can deduce from this figure is that this bias–variance trade-off very much depends on the noise level. If the signal is strong, then we can reduce the mean-squared error (MSE) by employing this variance scaling, but if the problem is noisier, we do better (in a prediction error sense) by using standardization instead.</p>
<div id="fig-biasvar-lasso" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-biasvar-lasso-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://jolars.co/blog/2025-01-21-normreg/images/biasvar-lasso.svg" class="img-fluid figure-img" width="500">
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-biasvar-lasso-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: Bias and variance
</figcaption>
</figure>
</div>
<p>To study if and how this choice of the normalization parameter <img src="https://latex.codecogs.com/png.latex?%5Cdelta"> might impact predictive performance in a real-data setting, we’ve also conducted experiments where we have varied both <img src="https://latex.codecogs.com/png.latex?%5Cdelta"> as well as the regularization strength (<img src="https://latex.codecogs.com/png.latex?%5Clambda">) for lasso and ridge models, recording hold-out error for each fit. The results are shown in Figure&nbsp;4, from which it is evident that the optimal value for <img src="https://latex.codecogs.com/png.latex?%5Cdelta"> is data-dependent, although standardization might make for a good default since it mitigates some of the class-balance bias without imposing too much variance.</p>
<div id="fig-hyperopt" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-hyperopt-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://jolars.co/blog/2025-01-21-normreg/images/hyperopt.svg" class="img-fluid figure-img" width="500">
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-hyperopt-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;4: Normalized mean-squared error (NMSE) for the hold-out set for lasso and ridge regression on three different real datasets (a1a, w1a, and rhee2006). The dotted line marks the best value for <img src="https://latex.codecogs.com/png.latex?%5Cdelta"> as a function of <img src="https://latex.codecogs.com/png.latex?%5Clambda/%5Clambda_%5Ctext%7Bmax%7D"> (where <img src="https://latex.codecogs.com/png.latex?%5Clambda_%5Ctext%7Bmax%7D"> is the value at which all the coefficients are zero). The circle marks the optimal combination of <img src="https://latex.codecogs.com/png.latex?%5Cdelta"> and <img src="https://latex.codecogs.com/png.latex?%5Clambda">.
</figcaption>
</figure>
</div>
</section>
<section id="mixed-data" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="mixed-data">Mixed Data</h2>
<p>We spend a substantial amount of time in the paper to also discuss the problem of mixed data: data sets that include both binary and continuous features, although in the paper we restrict ourselves to study the case of normal features.</p>
<p>When dealing with regularized methods, this scenario imposes a tricky question, namely: how do we put binary and continuous features on the same scale? This question is critical because, as we have already seen, shrinkage from the lasso and company will depend directly on how we choose to normalize the features to deal with this.</p>
<p>To put this problem into perspective, let’s say that we standardize <em>all</em> our features and also assume that our binary features are <em>perfectly balanced</em> (so that <img src="https://latex.codecogs.com/png.latex?q_j%20=%201/2"> for all <img src="https://latex.codecogs.com/png.latex?j"> corresponding to a binary features). If all of this holds, then it actually turns out that the coefficients of the binary and normal features will be regularized by the same amount if flipping the binary feature from 0 to 1 corresponds to a change of one standard deviation in the normal feature. In other words, our choice of normalization imposes this particular choice of what a one-unit change in terms of a binary feature should be equivalent to in terms of a normal feature (or vice versa).</p>
<p>You may or may not think this is a reasonable approach to scale binary and normal features relative to one another, but I would be surprised if you thought that this was reasonable for <em>all</em> binary features, given that they often represent entirely different things. Some features are indeed truly binary in nature, but many are just too distant points on the same continuum. It is true that some binary features come from dichotomization of continuous variables, in which case you should be able to base your choice of scaling on the original continuous feature’s scale, but even in this case it is not the case that there is one single way to dichotomize a variable that ensures that they are put on the same scale.<sup>5</sup></p>
<div class="no-row-height column-margin column-container"><div id="fn5"><p><sup>5</sup>&nbsp;In any case, you should always be careful about dichotomizing your features since you are in effect throwing away information about your data.</p></div></div><p>The particular relative scaling imposed by standardization was actually the subject of a paper by <span class="citation" data-cites="gelman2008">Gelman (2008)</span>, although tackled from the perspective of presenting standardized coefficients in standard regression model settings. In this paper, he argues that the default of equating a one-unit change in a binary feature to a one-standard deviation change in the normal feature is typically <em>not</em> a good default and advocated for instead using <em>two</em> standard deviations. In our paper, we adopt this setting, but here I want to stress that there is nothing critical in our results that depend on this. But we want to stress that this choice should if possible be done actively.</p>
<p>Another interesting part about this is that if we switch normalization method to, say, maximum–absolute value normalization, then this relationship changes.<sup>6</sup> With another normalization method comes another relative scaling of binary and normal features. So, the choice of normalization does not just lead to a different behavior with respect to class balance, it also leads to an implicit weighting of binary and normal features relative to one another.</p>
<div class="no-row-height column-margin column-container"><div id="fn6"><p><sup>6</sup>&nbsp;Even if the binary features are perfectly balanced.</p></div></div></section>
<section id="the-weighted-elastic-net" class="level2">
<h2 class="anchored" data-anchor-id="the-weighted-elastic-net">The Weighted Elastic Net</h2>
<p>We have so far discussed the normalization in the context of the lasso and ridge regression. Careful readers might, however, have noticed that we have not yet said anything about the elastic net (save for a few words in the introduction). The reason is that it turns out to me impossible to fully mitigate the class-balance bias in the case of the elastic net through modifications to the normalization procedure, which you can almost guess from Figure&nbsp;2, since the modifications for ridge and lasso are different.</p>
<p>Thankfully there turns out to be a solution to this problem, which is to use the <em>weighted elastic net</em>, in which we modify the weights of each penalty factor instead of normalizing. The problem then becomes <img src="https://latex.codecogs.com/png.latex?%0A%20%20%5Cfrac%7B1%7D%7B2%7D%20%5ClVert%20%5Cbm%7By%7D%20-%20%5Cbeta_0%20-%20%5Cbm%7BX%7D%5Cbm%7B%5Cbeta%7D%5CrVert_2%5E2%20+%20%5Clambda_1%20%5Csum_%7Bj=1%7D%5Ep%20u_j%20%7C%5Cbeta_j%7C%20+%20%5Cfrac%7B%5Clambda_2%7D%7B2%7D%20%5Csum_%7Bj=1%7D%5Ep%20v_j%20%5Cbeta_j%5E2.%0A"> I won’t cover any details here, but please see the paper for more information.</p>
</section>
<section id="summary" class="level2">
<h2 class="anchored" data-anchor-id="summary">Summary</h2>
<p>The main takeaway from this paper is that the choice of normalization matters greatly in the case of regularized regression, at least in the case of lasso, ridge, and elastic net regression. In spite of this fact, there is scant research on this topic, and the little information that is available through non-formal channels is typically anecdotal and not motivated by any theoretical or even empirical considerations.</p>
<p>We have focused on binary features in this paper and have shown that standard approaches such as standardization and max–abs normalization lead to biased estimates in terms of class balance (the proportion of ones) of these binary features, depending on whether a lasso or ridge penalty is used. I think this may come as a surprise to some and have real implications for data sets in which a given feature is rare but has a strong effect on the response. Consider, for instance, gene expression data where the presence of a rare gene might be crucial for predicting a disease. If you would apply the lasso this data set after having standardized it, then chances are that your model might not pick up on this effect at all.</p>
<p>I have covered a few highlights in this blog post, but there is much more in the paper itself, including a section on normalization in the case of interactions between the features as well as many more experiments (and of course much more theory). I think you’ll find the paper interesting and hope that it might be able to spur some future research on this topic, which is surprisingly understudied.</p>



</section>


<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-gelman2008" class="csl-entry">
Gelman, Andrew. 2008. <span>“Scaling Regression Inputs by Dividing by Two Standard Deviations.”</span> <em>Statistics in Medicine</em> 27 (15): 2865–73. <a href="https://doi.org/10.1002/sim.3107">https://doi.org/10.1002/sim.3107</a>.
</div>
<div id="ref-larsson2025" class="csl-entry">
Larsson, Johan, and Jonas Wallin. 2025. <span>“The Choice of Normalization Influences Shrinkage in Regularized Regression.”</span> <em>Transactions on Machine Learning Research</em>, November. <a href="https://openreview.net/forum?id=6xKyDBIwQ5">https://openreview.net/forum?id=6xKyDBIwQ5</a>.
</div>
<div id="ref-tibshiraniRegressionShrinkageSelection1996" class="csl-entry">
Tibshirani, Robert. 1996. <span>“Regression Shrinkage and Selection via the Lasso.”</span> <em>The Journal of the Royal Statistical Society, Series B (Statistical Methodology)</em> 58 (1): 267–88. <a href="https://doi.org/10.1111/j.2517-6161.1996.tb02080.x">https://doi.org/10.1111/j.2517-6161.1996.tb02080.x</a>.
</div>
</div></section></div> ]]></description>
  <category>Research</category>
  <category>Lasso</category>
  <category>Ridge regression</category>
  <category>Research</category>
  <guid>https://jolars.co/blog/2025-01-21-normreg/</guid>
  <pubDate>Tue, 21 Jan 2025 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2025-01-21-normreg/image.png" medium="image" type="image/png" height="129" width="144"/>
</item>
<item>
  <title>Moloch: A Revival of the Metropolis LaTeX Beamer Theme</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2024-05-30-moloch/</link>
  <description><![CDATA[ 





<p>My beamer configuration for presentations has been in a state of flux for as long as I can remember. I have tried many different themes and configurations, and have typically tried to keep the theme minimalisic but at the same time functional and visually appealing. Nevertheless, I have frequently found myself scrapping my custom modifications and returning to the <a href="https://github.com/matze/mtheme">Metropolis theme</a>, which I think is the most well-designed theme that I have so far encountered for beamer.</p>
<p>The only problem is that Metropolis is no longer actively maintained. The latest update (at the time of writing) was six years ago and since then a number of issues have cropped up. Most of them are not major and can be circumvented through various hacks and workarounds, but I have grown increasingly frustrated with the separate file of Metropolis patches that I’ve had to keep around to fix these issues. Beamer itself in fact even includes several patches now in order to stop the theme from breaking (see <a href="https://github.com/josephwright/beamer/blob/c0d91f15165421646b5383546e6195187b7f97c9/base/beamerbasecompatibility.sty#L253">here</a>, <a href="https://github.com/josephwright/beamer/blob/c0d91f15165421646b5383546e6195187b7f97c9/base/beamerbasesection.sty#L209">here</a>, and <a href="https://github.com/josephwright/beamer/blob/c0d91f15165421646b5383546e6195187b7f97c9/base/beamerbaselocalstructure.sty#L29">here</a>.)</p>
<section id="moloch" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="moloch">Moloch</h2>
<p>This has now (since some months back actually) led me to fork Metropolis to try to fix these outstanding issues. I call the new theme <em>Moloch</em> (which is likely familiar to you if you know your Metropolis). The original design is still pretty much intact save for a few tweaks that I will summarize in Section&nbsp;3, which overall bring the theme closer to normal beamer behavior. The code for the theme has also been simplified and made more robust. Metropolis, for instance, made much use of <code>\patchcmd</code> from the etoolbox package to patch beamer theme internals in order to support modifications to, for instance, frame titles. This was what lead the theme to break in the first place as beamer introduced changes in these commands and I have thus opted to remove all this kind of patching in favor of relying on standard functionality from the beamer theme instead.</p>
<p>In fact, it is now possible to change the title format directly through beamer, for instance by calling <code>\setbeamertemplate{frametitle}{\MakeUppercase{\insertframetitle}}</code> to make titles upper-case.<sup>1</sup></p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Thanks <a href="https://github.com/samcarter">samcarter</a> for informing me of this!</p></div><div id="fn2"><p><sup>2</sup>&nbsp;And see <a href="https://github.com/matze/mtheme/issues/371">this issue</a> for instance.</p></div></div><p>This comes at the price of sacrificing some features, such as toggling title formatting between uppercase, small caps, and regular text. But, as the Metropolis documentation itself noted,<sup>2</sup> these modifications were problematic in the first place and I therefore think that their removal is on the whole a good thing.</p>
<p>I’ve also removed the pgfplots theme that was included in Metropolis. I don’t mind the theme per se, but I don’t think it belongs in a general-purpose beamer theme.</p>
</section>
<section id="getting-started" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="getting-started">Getting Started</h2>
<p>The design of the theme does not stray far from the original Metropolis design (and will not do so in the future either). Below is a simple example of a few slides of the theme.</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><a href="https://raw.githubusercontent.com/jolars/moloch/main/assets/screenshot.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="The first four slides of a presentation using Moloch."><img src="https://raw.githubusercontent.com/jolars/moloch/main/assets/screenshot.svg" class="img-fluid figure-img" alt="The first four slides of a presentation using Moloch."></a></p>
<figcaption class="margin-caption">The first four slides of a presentation using Moloch.</figcaption>
</figure>
</div>
<p>Moloch is now <a href="https://ctan.org/pkg/moloch">on CTAN</a>, so you can install it with the TeXLive package manager by calling the following line of code:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">tlmgr</span> install moloch</span></code></pre></div></div>
<p>If you have TeXLive 2024 (or later), then Moloch is already included in the distribution and you don’t have to do anything to install it.</p>
<p>Using the theme is as simple as using any other beamer theme. Here is a simple example:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb2-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">\documentclass</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">beamer</span>}</span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\usetheme</span>{moloch}</span>
<span id="cb2-4"></span>
<span id="cb2-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\title</span>{Your Title}</span>
<span id="cb2-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\author</span>{Your Name}</span>
<span id="cb2-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\institute</span>{Your Institute}</span>
<span id="cb2-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\date</span>{<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\today</span>}</span>
<span id="cb2-9"></span>
<span id="cb2-10"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\begin</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">document</span>}</span>
<span id="cb2-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\maketitle</span></span>
<span id="cb2-12">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\section</span>{First Section}</span>
<span id="cb2-13">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\begin</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">frame</span>}</span>
<span id="cb2-14">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\frametitle</span>{First Frame}</span>
<span id="cb2-15">    Hello, world!</span>
<span id="cb2-16">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\end</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">frame</span>}</span>
<span id="cb2-17"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\end</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">document</span>}</span></code></pre></div></div>
<p>See <a href="http://mirrors.ctan.org/macros/latex/contrib/beamer-contrib/themes/moloch/moloch.pdf">the package documentation</a> to learn more about the theme and its various options. If you’re used to Metropolis, then you mostly need to know that <code>\metroset</code> has been replaced by <code>\molochset</code> and that some things are no longer supported, which is precisely what we’ll dig into in the next section!</p>
</section>
<section id="sec-changes" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="sec-changes">Changes</h2>
<p>I’ve tried to outline the main changes that I can think of in the following sections.</p>
<section id="new-secondary-color" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="new-secondary-color">New Secondary Color</h3>
<p>I always thought the green color in Metropolis was lurid and not exactly color-blind friendly. I therefore changed it to a teal color that I think is a little more subdued and easier on the eyes. You can see the difference in the figure below. I hope you agree that the new color is an improvement!</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2024-05-30-moloch/new-secondary-color.svg" class="border img-fluid figure-img" style="width:100.0%"></p>
<figcaption class="margin-caption">The old versus the new secondary color.</figcaption>
</figure>
</div>
</section>
<section id="subtitles" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="subtitles">Subtitles</h3>
<p>Subtitles are now supported in Moloch. They were were not in Metropolis because the author <a href="https://github.com/matze/mtheme/issues/135">thought subtitles were a bad idea in general</a>. On the whole I agree that subtitles are usually best avoided, but I didn’t see any reason to impose this opinion on others. Subtitles are therefore supported in Moloch.</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2024-05-30-moloch/subtitles.png" class="img-fluid figure-img"></p>
<figcaption class="margin-caption">Subtitles are supported in Moloch.</figcaption>
</figure>
</div>
</section>
<section id="frame-numbering" class="level3">
<h3 class="anchored" data-anchor-id="frame-numbering">Frame Numbering</h3>
<p>Metropolis sported its own frame numbering system. There was nothing wrong with this system except it necessitated a separate package (appendixframenumber) to get frame/page numbers to restart (and not count towards the total number) for appendix slides. Beamer has, however, improved its own system in recent years and there is no longer any need for a custom solution (or separate package) to support this functionality. As a result, Moloch just relies on beamer templates for the frame numbering. The design is <em>slightly</em> different, with smaller font size and somewhat different margins, but I only think this is for the better anyway.</p>
<p>Now, you can just change it via the standard beamer commands for frame numbering, like so:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\setbeamertemplate</span>{page number in head/foot}[appendixframenumber]</span></code></pre></div></div>
</section>
<section id="title-page-redesign" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="title-page-redesign">Title Page Redesign</h3>
<p>The title page has been redesigned. The primary changes are the following.</p>
<ol type="1">
<li>The institute is now positioned below the author (rather than the date), which I think makes more sense since the author and institute are closer related (in my mind at least). This was suggested in <a href="https://github.com/matze/mtheme/issues/180">an issue on the Metropolis repo</a>, but never adopted.</li>
<li>The titlegraphic now has margins added above and below. It was previously put in a zero-height <code>vbox</code>, which meant that it basically didn’t affect the page layout. Now it does and will push the titles and other content down. The new layout gives equal margins between top and bottom of the frame and the content, and adapts to the size of the title graphic. This may or may not be what you want, but in this case you can just wrap the graphic in a <code>vbox</code> of zero height yourself, so I see this as a less invasive default.</li>
<li>The margins around the elements on the title page were changed everywhere. Please see the screenshots below to see what I mean, but the main change is that there is less spacing between the title and the subtitle and even spacing above and below the orange line.</li>
</ol>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2024-05-30-moloch/metropolis-titlepage.svg" class="border img-fluid figure-img" style="width:100.0%"></p>
<figcaption class="margin-caption">The old title page from Metropolis</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2024-05-30-moloch/moloch-titlepage.svg" class="border img-fluid figure-img" style="width:100.0%"></p>
<figcaption class="margin-caption">The new title page in Moloch</figcaption>
</figure>
</div>
<p>For reference, the code for generating the slides is given below.</p>
<details>
<summary>
Code
</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb4-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">\documentclass</span>[10pt]{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">beamer</span>}</span>
<span id="cb4-2"></span>
<span id="cb4-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\usetheme</span>{moloch}</span>
<span id="cb4-4"></span>
<span id="cb4-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\title</span>{Title}</span>
<span id="cb4-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\subtitle</span>{Subtitle}</span>
<span id="cb4-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\author</span>{Author}</span>
<span id="cb4-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\institute</span>{Institute}</span>
<span id="cb4-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\date</span>{<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\today</span>}</span>
<span id="cb4-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\titlegraphic</span>{<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\hfill\includegraphics</span>[height=2cm]{logo.pdf}}</span>
<span id="cb4-11"></span>
<span id="cb4-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\begin</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">document</span>}</span>
<span id="cb4-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\maketitle</span></span>
<span id="cb4-14"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">\end</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">document</span>}</span></code></pre></div></div>
</details>
<p>I am open for suggestions and discussions on how to further improve the title page layout, or make customizing it easier.</p>
</section>
<section id="font-settings" class="level3">
<h3 class="anchored" data-anchor-id="font-settings">Font Settings</h3>
<p>Metropolis includes special handling of font settings. If you use LuaTeX or XeTeX, then Metropolis automatically checks if the Fira Sans font is available and sets it up for you. I like the Fira fonts myself and think that they are a nice choice for presentations, but I do not think that they should be set as part of the theme, especially since this means that you get different output by default if you run your document through pdfTeX instead, which I think is undesireable.</p>
<p>I’ve therefore disabled these font settings, but if you want to replicate the look of Metropolis when it comes to the fonts as well, then all you need is you use XeTeX or LuaTeX and set your font options according to the following example (or something similar).</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode latex code-with-copy"><code class="sourceCode latex"><span id="cb5-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">\usepackage</span>{<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">fontspec</span>}</span>
<span id="cb5-2"></span>
<span id="cb5-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\setsansfont</span>[</span>
<span id="cb5-4">  ItalicFont={Fira Sans Light Italic},</span>
<span id="cb5-5">  BoldFont={Fira Sans},</span>
<span id="cb5-6">  BoldItalicFont={Fira Sans Italic}</span>
<span id="cb5-7">]{Fira Sans Light}</span>
<span id="cb5-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\setmonofont</span>[BoldFont={Fira Mono Medium}]{Fira Mono}</span>
<span id="cb5-9"></span>
<span id="cb5-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\AtBeginEnvironment</span>{tabular}{<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span></span>
<span id="cb5-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">\addfontfeature</span>{Numbers={Monospaced}}</span>
<span id="cb5-12">}</span></code></pre></div></div>
<p>If you want to have <code>\operatorname</code>, <code>\mathrm</code>, and company in the Fira font as well, then you’ll need to set <code>\setmainfont</code> as well.</p>
<p>Note that there is only a beta version of the Fira Math Light font available and that it is not at all complete, so unfortunately there is no good way to get a matching math font for Fira Sans Light at the moment. (Otherwise we could use <strong>unicode-math</strong> and <code>Fira Math Light</code>). This I think is another good argument for why Fira should not be set as the default font for the theme.</p>
</section>
<section id="no-more-automatic-paragraph-spacing" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="no-more-automatic-paragraph-spacing">No More Automatic Paragraph Spacing</h3>
<p>Unlike standard Beamer, in which <code>\parskip</code> (paragraph spacing, roughly speaking) is set to zero, Metropolis instead sets it to 0.5 em units. This means that in Metropolis, you don’t need to sprinkle <code>\medskip</code> (or whatever you use for paragraph spacing) in your slides to have them neatly separated.</p>
<p>As I noted in <a href="https://github.com/jolars/moloch/issues/8">this issue</a> and also <a href="https://github.com/jolars/moloch/issues/9">this one</a>, however, this has some undesireable side-effects,<sup>3</sup> such as introducing additional spacing between table captions and the table, for instance. As a consequence, I’ve therefore removed this setting from Moloch.</p>
<div class="no-row-height column-margin column-container"><div id="fn3"><p><sup>3</sup>&nbsp;Also see <a href="https://github.com/josephwright/beamer/issues/512">this issue</a> on the Beamer repo for more background.</p></div></div><p>As with many other changes, this puts Moloch more in line with the standard Beamer behavior, which I think is generally speaking a good thing and simplifies switching between themes.</p>
</section>
<section id="block-environments" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="block-environments">Block Environments</h3>
<p>Metropolis introduced a bit of custom logic to handle block environments. In particular, filled blocks environments were modified such that the main body text (for the frame) aligns with the boundaries of the box and not the text inside the box (which is the default behavior in beamer). See below for a comparison.</p>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2024-05-30-moloch/blocks-beamer.png" class="img-fluid figure-img"></p>
<figcaption class="margin-caption">Block environment alignments in Beamer. Image credit: <a href="https://github.com/samcarter">samcarter</a>.</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2024-05-30-moloch/blocks-metro.png" class="img-fluid figure-img"></p>
<figcaption class="margin-caption">Block environment alignments in Metropolis. Image credit: <a href="https://github.com/samcarter">samcarter</a>.</figcaption>
</figure>
</div>
<p>I think the proper choice is the default beamer behavior, especially since this otherwise means that the content inside the blocks look different if you switch to filled blocks. In addition, the spacing for the normal block environments in Metropolis <a href="https://github.com/matze/mtheme/issues/307">does not work properly</a>, so switching to the default behavior also solves this issue.</p>
</section>
<section id="build-system" class="level3">
<h3 class="anchored" data-anchor-id="build-system">Build System</h3>
<p>Moloch is part of CTAN and included in TeXLive 2024, so you typically do not need to concern yourself with installing the theme from source. But if you want to do so nonetheless, for instance to enable some new feature or fix from the development version, then Moloch now uses <a href="https://ctan.org/pkg/l3build">l3build</a> instead of a custom Makefile to handle the build process, which should make life easier for most people, and you simply just need to call these lines:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> clone https://github.com/jolars/moloch.git</span>
<span id="cb6-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">cd</span> moloch</span>
<span id="cb6-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">l3build</span> install</span></code></pre></div></div>
<p>In addition, it also means that the package now includes unit tests to make sure that nothing in the theme breaks unexpectedly.</p>
</section>
<section id="other-miscellaneous-changes" class="level3">
<h3 class="anchored" data-anchor-id="other-miscellaneous-changes">Other Miscellaneous Changes</h3>
<p>There are several other small changes. I’ve tried to list some of them here below.</p>
<ul>
<li><a href="https://github.com/matze/mtheme/pull/384">Progress bars work better for wide presentation</a></li>
<li><a href="https://github.com/matze/mtheme/pull/342">Removed custom appendix section modifications</a></li>
<li><a href="https://github.com/jolars/moloch/issues/5">No longer hardcode bibliography styling</a></li>
</ul>
</section>
</section>
<section id="roadmap" class="level2">
<h2 class="anchored" data-anchor-id="roadmap">Roadmap</h2>
<p>I currently don’t foresee any major changes to the theme and will likely upgrade it to a stable state in the near future. So you can count on the theme not to introduce any breaking changes. I think the original Metropolis design is great and I don’t want to stray too far from it.</p>
<p>That being said, one thing that I want to do is to make the colors in the theme easier to customize and perhaps introduce alternative color schemes. That also means bringing back the hi-contrast theme that was in Metropolis but that I removed from Moloch (for reasons that I can’t quite recall now..). In any case, I don’t intend to modify the default choices.</p>
<section id="contributing" class="level3">
<h3 class="anchored" data-anchor-id="contributing">Contributing</h3>
<p>If you feel that you can contribute, then please do! The project <a href="https://github.com/jolars/moloch">is on github</a> and you are welcome to <a href="https://github.com/jolars/moloch/issues">raise an issue</a> or <a href="https://github.com/jolars/moloch/discussions">start a new discussion</a> if there’s anything you think could be improved.</p>
</section>
<section id="acknowledgements" class="level3">
<h3 class="anchored" data-anchor-id="acknowledgements">Acknowledgements</h3>
<p><a href="https://github.com/samcarter">samcarter</a> helped out a lot with discussions and testing of the theme and also helped make transitioning from Metropolis to Moloch smoother. She will actually give a talk on the <a href="https://www.tug.org/tug2024/">TUG 2024 meeting</a> in Prague July 19-21 about the theme, so please check it out if you have the chance!</p>
<p>Finally: credit where credit is due. I want to stress that the vast majority of the code in Moloch was written by <a href="https://github.com/matze">Matthias Vogelsang</a>, who created the Metropolis package, and that my job has mostly been to patch up its rough spots.</p>


</section>
</section>


 ]]></description>
  <category>LaTeX</category>
  <category>Software</category>
  <category>Presentations</category>
  <category>Beamer</category>
  <guid>https://jolars.co/blog/2024-05-30-moloch/</guid>
  <pubDate>Thu, 30 May 2024 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2024-05-30-moloch/moloch-logo.png" medium="image" type="image/png" height="78" width="144"/>
</item>
<item>
  <title>PhD Thesis</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2024-05-22-phd-thesis/</link>
  <description><![CDATA[ 





<p><strong>Spoiler alert:</strong> I passed my defense! 🎉</p>
<p>My PhD thesis is now published and available for download <span class="citation" data-cites="larsson2024">(Larsson 2024)</span>! It is the culmination of five years of research on optimization other related numerical algoritms for sparse regression, in particular the lasso and sorted l-one penalized estimation (SLOPE).</p>
<div id="fig-cover" class="lightbox quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-cover-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="front-cover.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Figure&nbsp;1: The cover of my thesis. The image features a subset of the elastic net path for the famous diabetes data set."><img src="https://jolars.co/blog/2024-05-22-phd-thesis/front-cover.png" class="border img-fluid figure-img" width="300"></a>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-cover-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: The cover of my thesis. The image features a subset of the elastic net path for the famous diabetes data set.
</figcaption>
</figure>
</div>
<p>In the following sections I will give an overview over the papers that are included in the thesis. This a somewhat abridged version of the paper summary section in the actual thesis and comes without a lengthy introduction to the field and these topics. If you are interested in that, I suggest you read the thesis itself!</p>
<section id="paper-1" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="paper-1">Paper 1</h2>
<p>The first of the papers introduces <em>the strong screening rule for SLOPE</em> <span class="citation" data-cites="larsson2020b">(Larsson, Bogdan, and Wallin 2020)</span>, which is the first screening rule for SLOPE. If you haven’t heard about screening rules before, they are algorithms that discard features (predictors/variables) prior to fitting the model. They are remarkably effective for sparse methods in the high-dimensional setting and typically offer speed ups of several orders of magnitude in the high-dimensional setting. They were first discovered for the lasso with <span class="citation" data-cites="elghaoui2010a">El Ghaoui, Viallon, and Rabbani (2010)</span> and have since proven to be a key ingredient in making the lasso computationally efficient.</p>
<p>They are based on the following reasoning:</p>
<ol type="1">
<li>The solution to a sparse regression problem is, of course, <em>sparse</em>, particularly in the case when the number of features (<img src="https://latex.codecogs.com/png.latex?p">) outnumber the number of observations (<img src="https://latex.codecogs.com/png.latex?n">). For the lasso, for instance, the size of the support <em>must</em> in fact be no larger than <img src="https://latex.codecogs.com/png.latex?n">.</li>
<li>We can often guess quite accurately which features have little chance of being in the support, for instance by looking at the correlation between the features and the response or the solution to a problem with a larger (or smaller) penalty.<sup>1</sup></li>
<li>Even if we are wrong about which features are in the support, it is typically cheap to check if we made a mistake and refit with these features added back in.</li>
</ol>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;This is typically the case when we are fitting a regularization path. We start with a penalty that’s large enough to make every coefficient zero and then progressively increase it.</p></div></div><p>This reasoning turns out to be pretty-much on spot and as a result screening rules have turned out to be critical for good performance for the lasso and related methods.</p>
<p>Screening rules are typically separated into <em>safe</em> and <em>heuristic</em> rules. Safe rules guarantee that discarded features are in fact not in the optimal solution, whereas heuristic rules do not. This division is something is something of a misnomer, however, since it is easy to check optimality conditions after fitting the model on the reduced set of features, catch any mistakes, and refit is necessary. And because safe rules sacrifice effectiveness for safety together with the fact that the optimality checks are not very expensive, it is my experience that heuristic rules typically offer better performance. They can even be used together.</p>
<p>The first heuristic screening rule for the lasso was introduced by <span class="citation" data-cites="tibshirani2012">Tibshirani et al. (2012)</span>: <em>the strong screening rule</em>. And in the first paper of my thesis, we extend this screening rule strategy to the problem of solving sorted (_1) penalized regression (SLOPE) <span class="citation" data-cites="bogdan2015">(Bogdan et al. 2015)</span>.</p>
<p>I have provided some results from the first paper in Table&nbsp;1. As you can see, screening improves performance considerably and offers no computational overhead even when it has little effect (as in the case of the physician data set).</p>
<div id="tbl-paper1-results" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-tbl figure page-columns page-full">
<div aria-describedby="tbl-paper1-results-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="table">
<colgroup>
<col style="width: 15%">
<col style="width: 18%">
<col style="width: 5%">
<col style="width: 8%">
<col style="width: 27%">
<col style="width: 23%">
</colgroup>
<thead>
<tr class="header">
<th>Dataset</th>
<th>Model</th>
<th style="text-align: right;"><img src="https://latex.codecogs.com/png.latex?n"></th>
<th style="text-align: right;"><img src="https://latex.codecogs.com/png.latex?p"></th>
<th style="text-align: right;">Time (No screening)</th>
<th style="text-align: right;">Time (Screening)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>dorothea</td>
<td>Logistic</td>
<td style="text-align: right;">800</td>
<td style="text-align: right;">88119</td>
<td style="text-align: right;">914</td>
<td style="text-align: right;">14</td>
</tr>
<tr class="even">
<td>e2006-tfidf</td>
<td>Least squares</td>
<td style="text-align: right;">3308</td>
<td style="text-align: right;">150358</td>
<td style="text-align: right;">43353</td>
<td style="text-align: right;">4944</td>
</tr>
<tr class="odd">
<td>news20</td>
<td>Multinomial</td>
<td style="text-align: right;">1000</td>
<td style="text-align: right;">62061</td>
<td style="text-align: right;">5485</td>
<td style="text-align: right;">517</td>
</tr>
<tr class="even">
<td>physician</td>
<td>Poisson</td>
<td style="text-align: right;">4406</td>
<td style="text-align: right;">25</td>
<td style="text-align: right;">34</td>
<td style="text-align: right;">34</td>
</tr>
</tbody>
</table>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-tbl margin-caption" id="tbl-paper1-results-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Table&nbsp;1: Results from one of the experiments in the first paper <span class="citation" data-cites="larsson2020b">(Larsson, Bogdan, and Wallin 2020)</span>, showing time taken to fit a full SLOPE path to a few different data sets.
</figcaption>
</figure>
</div>
</section>
<section id="paper-2" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="paper-2">Paper 2</h2>
<p>Screening rules are particularly effective when they are sequential, that is, operate along the regularization path.<sup>2</sup> But another possibility that had previously not been explored is the idea of screening not only for the next step on the path, but for <em>all</em> of the remaining steps as well. This is the idea behind <em>look-ahead screening rules</em>, which I introduce in the second paper of the thesis, which is a short paper <span class="citation" data-cites="larsson2021">(Larsson 2021)</span>. We use the Gap-Safe screening rule <span class="citation" data-cites="ndiaye2017">(Ndiaye et al. 2017)</span> here. As the name suggests, it is a safe screening rule. This means that if a feature is screened out, it is guaranteed to be zero in the solution.</p>
<div class="no-row-height column-margin column-container"><div id="fn2"><p><sup>2</sup>&nbsp;The regularization path starts at the point where all of the model’s coefficients are zero and proceed until they are almost not penalized at all.</p></div></div><p>As I show in the paper, the results are quite promising (Figure&nbsp;2), especially since you get this kind of screening essentially for free (if you’re screening anyway).</p>
<div id="fig-paper2" class="lightbox quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-paper2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="paper2-highlight.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Figure&nbsp;2: Look-ahead screening for 20 randomly selected features from the leukemia data set. Blue squares indicate that the respective feature can be discarded from the problem for that step of the regularization path."><img src="https://jolars.co/blog/2024-05-22-phd-thesis/paper2-highlight.png" class="img-fluid figure-img"></a>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-paper2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: Look-ahead screening for 20 randomly selected features from the <em>leukemia</em> data set. Blue squares indicate that the respective feature can be discarded from the problem for that step of the regularization path.
</figcaption>
</figure>
</div>
</section>
<section id="paper-3" class="level2">
<h2 class="anchored" data-anchor-id="paper-3">Paper 3</h2>
<p>Even though the strong rule for the lasso is highly effective in general, there is one area in which it struggles, namely, when features are highly correlated. <span class="citation" data-cites="tibshirani2012">Tibshirani et al. (2012)</span> in fact noted this themselves and motivated using a modified technique: the working-set strategy (where the model is initially fit using the ever-active set, rather than the strong set) because of this.</p>
<p>The reason for this is that the strong rule (and every other screening rules we know of), ignores information about how close the features are to becoming active. This is the motivation for the <em>Hessian screening rule</em> that we introduce in the third paper of the thesis <span class="citation" data-cites="larsson2022b">(Larsson and Wallin 2022)</span>. The name stems from the fact that we use second-order information about the optimization problem, which involves the Hessian matrix <img src="https://latex.codecogs.com/png.latex?X%5E%5Cintercal%20X">. The rule offers a better estimate of the correlation vector, which in practice leads to better screening performance.</p>
</section>
<section id="paper-4" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="paper-4">Paper 4</h2>
<p>An ongoing problem in literature on optimization (including screening rules) is that there are now so many methods to examine and so many different models and datasets on which to compare them on, that it has become difficult to keep track of which methods it is that actually do best on a given problem. You can easily find a paper A that studies optimization methods X and Y on datasets I and II and conclude that X is better than Y but then find another paper B, which studies methods X, Y, and Z on datasets I and III and conclude that, actually, Y is better than X and, by the way, Z happens to be best of them all. Then, later, you find paper C, which claims that Z actually is considerably worse than X, which in fact also performs better for data set IV. This confused state of affairs is typically the result of authors having benchmarked their methods using different hardware, programming languages for their implementations, hyperparameters for their methods, and convergence criteria, to name a few of the many possible sources of variation.</p>
<p>In short, there is a dire need for a framework through which this process can be made simple, reproducible, and transparent. This is the motivation behind the <strong>benchopt</strong> package, which we present in the fourth of this thesis’ papers <span class="citation" data-cites="moreau2022a">(Moreau et al. 2022)</span>.</p>
<p>The goal of benchopt is to make life easier for both researchers in optimization and users of optimization software. For a researcher who has developed a new optimization method for SLOPE, for instance, all you need to do is to write the code for your solver (optimization method) and plug it into the existing benchopt benchmark for SLOPE and run it. The package will then automatically compare your method with all the other methods in the benchmark and output table and plots of the results Figure&nbsp;3. If you instead are a user who is interested in using SLOPE for your applied work and want to know which algorithm to use, you can either browse the extensive database of results that other users have already uploaded or just download the benchmark and run it yourself on the data that you are interested in using it for.</p>
<div id="fig-paper4" class="lightbox fig-cap-location-bottom quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-paper4-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="paper4-highlight.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Figure&nbsp;3: A schematic over how a benchmark is set up and run using benchopt. The benchmark consists of a set of files that define objectives, datasets, and solvers. When the user runs benchopt, the package combines all of the possible combinations of objectives, datasets, and solvers and outputs a neatly formatted database of the results."><img src="https://jolars.co/blog/2024-05-22-phd-thesis/paper4-highlight.svg" class="fig-cap-location-bottom img-fluid figure-img"></a>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-paper4-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: A schematic over how a benchmark is set up and run using benchopt. The benchmark consists of a set of files that define objectives, datasets, and solvers. When the user runs benchopt, the package combines all of the possible combinations of objectives, datasets, and solvers and outputs a neatly formatted database of the results.
</figcaption>
</figure>
</div>
</section>
<section id="paper-5" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="paper-5">Paper 5</h2>
<p>Proximal coordinate descent is a very efficient optimization algorithm for fitting the lasso, but it cannot handle the case when the penalty term is non-separable, which is the case in SLOPE. In practice, this has reduced the applicability of SLOPE to large data, which is unfortunate given the many appealing properties of the model.</p>
<p>In paper 5 <span class="citation" data-cites="larsson2023">(Larsson et al. 2023)</span>, however, we present a way to circumvent this issue by using a hybrid of proximal coordinate and proximal gradient descent. Our main discovery is that if we fix the clusters and optimize over each cluster in turn, rather than each feature, the problem becomes separable, which means that coordinate descent can be used. And if we combine this with proximal gradient descent steps, which allow us to discover the clusters, then we can guarantee convergence and at the same time benefit from the efficiency of coordinate descent.</p>
<p>The solver is illustrated for a two-dimensional SLOPE problem in Figure&nbsp;4. The orange cross marks the optimum. Dashed lines indicate PGD steps and solid lines CD steps. Each dot marks a complete epoch, which may correspond to only a single coefficient update for the CD and hybrid solvers if the coefficients flip order. The CD algorithm converges quickly but is stuck after the third epoch. The hybrid and PGD algorithms, meanwhile, reach convergence after 67 and 156 epochs respectively.</p>
<div id="fig-paper5" class="lightbox quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-paper5-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="paper5-highlight.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Figure&nbsp;4: An illustration of the hybrid coordinate descent solver we developed for SLOPE, showing progress until convergence for the coordinate descent solver (CD) that we use as part of the hybrid method, our hybrid method, and proximal gradient descent (PGD)."><img src="https://jolars.co/blog/2024-05-22-phd-thesis/paper5-highlight.svg" class="img-fluid figure-img" width="700"></a>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-paper5-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;4: An illustration of the hybrid coordinate descent solver we developed for SLOPE, showing progress until convergence for the coordinate descent solver (CD) that we use as part of the hybrid method, our hybrid method, and proximal gradient descent (PGD).
</figcaption>
</figure>
</div>
</section>
<section id="paper-6" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="paper-6">Paper 6</h2>
<p>The final paper of the thesis is a working paper in which we tackle the issue of normalization of binary features. Normalization is necessary in order to put the features on the same scale when dealing with regularized methods. What “same scale” means, however, however, is not clear, yet has been met mostly with neglect in the literature. We think that this is both surprising and problematic given the almost universal use of normalization in regularized methods and the apparent and large effects it has on the solution paths.</p>
<p>In our paper, we begin to bridge this knowledge gap by studying normalization for the lasso and ridge regression when they are used on binary features (features that only contain values 0 or 1) or mix of binary and normally distributed features. What we find is that there is a large effect of normalization with respect to the class balance of the features: the proportion of ones to zeros (or vice versa). Both the lasso and the ridge estimators turn out to be sensitive to this class balance and, depending on the type of normalization used, have trouble recovering effects that are associated with binary features as long as their class balance is severe enough Figure&nbsp;4.</p>
<div id="fig-paper6" class="lightbox quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-paper6-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="paper6-highlight.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="Figure&nbsp;5: Estimated coefficients from lasso and ridge regression for a two-feature problem where one of the features has a quasi-normal distribution (values deterministically set via the quantile function), with standard deviation 1/2, and the other is a binary (quasi-Bernoulli) feature with class-balance q. The normal feature is standardized in every case, whereas the binary feature is scaled with (q - q^2)^\delta—its variance to the power of \delta. In other words, we have no scaling for \delta=0, standard deviation scaling when \delta=1/2, and variance-scaling when \delta = 1."><img src="https://jolars.co/blog/2024-05-22-phd-thesis/paper6-highlight.svg" class="img-fluid figure-img" width="700"></a>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-paper6-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;5: Estimated coefficients from lasso and ridge regression for a two-feature problem where one of the features has a quasi-normal distribution (values deterministically set via the quantile function), with standard deviation 1/2, and the other is a binary (quasi-Bernoulli) feature with class-balance <img src="https://latex.codecogs.com/png.latex?q">. The normal feature is standardized in every case, whereas the binary feature is scaled with <img src="https://latex.codecogs.com/png.latex?(q%20-%20q%5E2)%5E%5Cdelta">—its variance to the power of <img src="https://latex.codecogs.com/png.latex?%5Cdelta">. In other words, we have no scaling for <img src="https://latex.codecogs.com/png.latex?%5Cdelta=0">, standard deviation scaling when <img src="https://latex.codecogs.com/png.latex?%5Cdelta=1/2">, and variance-scaling when <img src="https://latex.codecogs.com/png.latex?%5Cdelta%20=%201">.
</figcaption>
</figure>
</div>
<p>I will offer more details on this paper once work on it has been completed, but I think the results are interesting and that this field is ripe for further exploration.</p>



</section>


<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-bogdan2015" class="csl-entry">
Bogdan, Małgorzata, Ewout van den Berg, Chiara Sabatti, Weijie Su, and Emmanuel J. Candès. 2015. <span>“<span>SLOPE</span> – Adaptive Variable Selection via Convex Optimization.”</span> <em>The Annals of Applied Statistics</em> 9 (3): 1103–40. <a href="https://doi.org/10.1214/15-AOAS842">https://doi.org/10.1214/15-AOAS842</a>.
</div>
<div id="ref-elghaoui2010a" class="csl-entry">
El Ghaoui, Laurent, Vivian Viallon, and Tarek Rabbani. 2010. <span>“Safe Feature Elimination for the <span>LASSO</span> and Sparse Supervised Learning Problems.”</span> <em>arXiv:1009.4219 [Cs, Math]</em>, September. <a href="https://arxiv.org/abs/1009.4219">https://arxiv.org/abs/1009.4219</a>.
</div>
<div id="ref-larsson2021" class="csl-entry">
Larsson, Johan. 2021. <span>“Look-Ahead Screening Rules for the Lasso.”</span> In <em>22nd <span>European</span> Young Statisticians Meeting - Proceedings</em>, edited by Andreas Makridis, Fotios S. Milienos, Panagiotis Papastamoulis, Christina Parpoula, and Athanasios Rakitzis, 61–65. Athens, Greece: <span>Panteion university of social and political sciences</span>. <a href="https://www.eysm2021.panteion.gr/files/Proceedings\%5FEYSM\%5F2021.pdf">https://www.eysm2021.panteion.gr/files/Proceedings\%5FEYSM\%5F2021.pdf</a>.
</div>
<div id="ref-larsson2024" class="csl-entry">
———. 2024. <span>“Optimization and Algorithms in Sparse Regression: Screening Rules, Coordinate Descent, and Normalization.”</span> PhD thesis, Lund, Sweden: Department of Statistics, Lund University. <a href="https://lup.lub.lu.se/record/0b9c97e8-5f65-43eb-9f7a-c4f237568370">https://lup.lub.lu.se/record/0b9c97e8-5f65-43eb-9f7a-c4f237568370</a>.
</div>
<div id="ref-larsson2020b" class="csl-entry">
Larsson, Johan, Małgorzata Bogdan, and Jonas Wallin. 2020. <span>“The Strong Screening Rule for <span>SLOPE</span>.”</span> In <em>Advances in Neural Information Processing Systems 33</em>, edited by Hugo Larochelle, Marc’Aurelio Ranzato, Raia Hadsell, Maria-Florina Balcan, and Hsuan-Tien Lin, 33:14592–603. Virtual: Curran Associates, Inc.<a href="
    https://papers.nips.cc/paper\_files/paper/2020/hash/a7d8ae4569120b5bec12e7b6e9648b86-Abstract.html
  ">https://papers.nips.cc/paper\%5Ffiles/paper/2020/hash/a7d8ae4569120b5bec12e7b6e9648b86-Abstract.html </a>.
</div>
<div id="ref-larsson2023" class="csl-entry">
Larsson, Johan, Quentin Klopfenstein, Mathurin Massias, and Jonas Wallin. 2023. <span>“Coordinate Descent for <span>SLOPE</span>.”</span> In <em>Proceedings of the 26th International Conference on Artificial Intelligence and Statistics</em>, edited by Francisco Ruiz, Jennifer Dy, and Jan-Willem van de Meent, 206:4802–21. Proceedings of Machine Learning Research. Valencia, Spain: PMLR. <a href="https://proceedings.mlr.press/v206/larsson23a.html">https://proceedings.mlr.press/v206/larsson23a.html</a>.
</div>
<div id="ref-larsson2022b" class="csl-entry">
Larsson, Johan, and Jonas Wallin. 2022. <span>“The <span>Hessian</span> Screening Rule.”</span> In <em>Advances in Neural Information Processing Systems 35</em>, edited by Sanmi Koyejo, Sidahmed Mohamed, Alekh Agarwal, Danielle Belgrave, Kyunghyun Cho, and Alice Oh, 35:15823–35. New Orleans, USA: Curran Associates, Inc.<a href="
    https://papers.nips.cc/paper\_files/paper/2022/hash/65a925049647eab0aa06a9faf1cd470b-Abstract-Conference.html
  ">https://papers.nips.cc/paper\%5Ffiles/paper/2022/hash/65a925049647eab0aa06a9faf1cd470b-Abstract-Conference.html </a>.
</div>
<div id="ref-moreau2022a" class="csl-entry">
Moreau, Thomas, Mathurin Massias, Alexandre Gramfort, Pierre Ablin, Pierre-Antoine Bannier, Benjamin Charlier, Mathieu Dagréou, et al. 2022. <span>“Benchopt: Reproducible, Efficient and Collaborative Optimization Benchmarks.”</span> In <em>Advances in Neural Information Processing Systems 35</em>, edited by S. Koyejo, S. Mohamed, A. Agarwal, D. Belgrave, K. Cho, and A. Oh, 35:25404–21. New Orleans, USA: Curran Associates, Inc.<a href="
    https://proceedings.neurips.cc/paper\_files/paper/2022/hash/a30769d9b62c9b94b72e21e0ca73f338-Abstract-Conference.html
  ">https://proceedings.neurips.cc/paper\%5Ffiles/paper/2022/hash/a30769d9b62c9b94b72e21e0ca73f338-Abstract-Conference.html </a>.
</div>
<div id="ref-ndiaye2017" class="csl-entry">
Ndiaye, Eugene, Olivier Fercoq, Alexandre Gramfort, and Joseph Salmon. 2017. <span>“Gap <span>Safe</span> Screening Rules for Sparsity Enforcing Penalties.”</span> <em>Journal of Machine Learning Research</em> 18 (128): 1–33. <a href="https://jmlr.org/papers/v18/16-577.html">https://jmlr.org/papers/v18/16-577.html</a>.
</div>
<div id="ref-tibshirani2012" class="csl-entry">
Tibshirani, Robert, Jacob Bien, Jerome Friedman, Trevor Hastie, Noah Simon, Jonathan Taylor, and Ryan J. Tibshirani. 2012. <span>“Strong Rules for Discarding Predictors in Lasso-Type Problems.”</span> <em>Journal of the Royal Statistical Society. Series B: Statistical Methodology</em> 74 (2): 245–66. <a href="https://doi.org/10/c4bb85">https://doi.org/10/c4bb85</a>.
</div>
</div></section></div> ]]></description>
  <category>Rhesis</category>
  <category>Statistics</category>
  <category>Optimization</category>
  <guid>https://jolars.co/blog/2024-05-22-phd-thesis/</guid>
  <pubDate>Wed, 22 May 2024 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2024-05-22-phd-thesis/cover-image.png" medium="image" type="image/png" height="122" width="144"/>
</item>
<item>
  <title>New Julia Package: ProjectRoot</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2024-05-13-projectroot-jl-new/</link>
  <description><![CDATA[ 





<p>I have created my first <a href="https://julialang.org">Julia</a> package! It is called <a href="https://github.com/jolars/ProjectRoot.jl">ProjectRoot</a> and is a small utility package that helps with file referencing withing a project-oriented workflow.</p>
<p>I’ve stolen the idea of the package from the R and Python packages <a href="https://here.r-lib.org/">here</a> and <a href="https://github.com/chendaniely/pyprojroot">pyprojroot</a>, which are package that I have used frequently in the past to simplify file referencing for research projects, particularly when referencing data files or creating plot files in a project.</p>
<section id="installation" class="level2">
<h2 class="anchored" data-anchor-id="installation">Installation</h2>
<p>The package is on the general Julia registry, so it can be installed in Julia by calling</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode julia code-with-copy"><code class="sourceCode julia"><span id="cb1-1">]add ProjectRoot</span></code></pre></div></div>
</section>
<section id="usage" class="level2">
<h2 class="anchored" data-anchor-id="usage">Usage</h2>
<p>The package is designed to be light on dependencies and carries only a single exported macro, <code>@projectroot</code>. And its usage is simple. Consider the following simple directory structure.</p>
<pre><code>MyProject
├── scripts
│   └── A.jl
├── src
│   └── B.jl
└── Project.toml</code></pre>
<p>If you want to refer to a file, say <code>src/B.jl</code>, you simply need to use</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode julia code-with-copy"><code class="sourceCode julia"><span id="cb3-1"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">@projectroot</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"src"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B.jl"</span>)</span></code></pre></div></div>
<p>anywhere in your project, for instance in <code>scripts/A.jl</code>.</p>
</section>
<section id="how-it-works" class="level2">
<h2 class="anchored" data-anchor-id="how-it-works">How it Works</h2>
<p>The <code>@projectroot</code> macro fetches the file from where it is called and then recursively searches upwards in the file hierarchy until it finds one of the following:</p>
<ol type="1">
<li>A <code>.projectroot</code> file</li>
<li>A <code>Project.toml</code> file</li>
<li>A <code>JuliaProject.toml</code> file</li>
<li>A <code>Manifest.toml</code> file</li>
<li>A <code>.git</code> folder</li>
<li>An <code>.svn</code> folder</li>
</ol>
<p>The search terminates when it finds one of these files or when you reach the root of the file system. And this is what <code>@projectroot</code> returns.</p>
<p>String interpolation is also supported, so you can use</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode julia code-with-copy"><code class="sourceCode julia"><span id="cb4-1">file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B.jl"</span></span>
<span id="cb4-2"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">@projectroot</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"src"</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>(file)<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span></code></pre></div></div>
<p>and so on.</p>
</section>
<section id="repl" class="level2">
<h2 class="anchored" data-anchor-id="repl">REPL</h2>
<p>Calling <code>@projectroot</code> from the REPL uses the same logic as above, but the search starts from the current working directory instead.</p>
</section>
<section id="alternatives" class="level2">
<h2 class="anchored" data-anchor-id="alternatives">Alternatives</h2>
<p>There is already similar functionality in the excellent <a href="https://github.com/JuliaDynamics/DrWatson.jl">DrWatson</a> package. But I generally prescribe to the Unix philosophy (Doug McIlroy):</p>
<blockquote class="blockquote">
<p>Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new “features”.</p>
</blockquote>
<p>So if you just need a lightweight package in the same spirit as <code>here</code> and <code>pyprojroot</code>, then <code>ProjectRoot</code> might just be the right tool for you.</p>
</section>
<section id="contributing" class="level2">
<h2 class="anchored" data-anchor-id="contributing">Contributing</h2>
<p>As always, I am happy for any kind of contribution. This is my first Julia package and I still haven’t wrapped my head around all the intricacies of Julia and its package ecosystem. So if you have any suggestions, please let me know. The source code is stored in the GitHub repository at <a href="https://github.com/jolars/ProjectRoot.jl">jolars/ProjectRoot.jl</a> and you can find the documentation for the latest stable version <a href="https://jolars.github.io/ProjectRoot.jl/stable/">here</a>.</p>


</section>

 ]]></description>
  <category>Julia</category>
  <category>Software</category>
  <guid>https://jolars.co/blog/2024-05-13-projectroot-jl-new/</guid>
  <pubDate>Mon, 13 May 2024 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2024-05-13-projectroot-jl-new/projectroot-logo.svg" medium="image" type="image/svg+xml"/>
</item>
<item>
  <title>SLOPE 0.2.0</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2020-04-14-slope-0-2-0/</link>
  <description><![CDATA[ 





<section id="introduction-to-slope" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="introduction-to-slope">Introduction to SLOPE</h2>
<p>SLOPE <span class="citation" data-cites="bogdan2015">(Bogdan et al. 2015)</span> stands for sorted L1 penalized estimation and is a generalization of OSCAR <span class="citation" data-cites="bondell2008">(Bondell and Reich 2008)</span>. As the name suggests, SLOPE is a type of <img src="https://latex.codecogs.com/png.latex?%5Cell_1">-regularization. More specifically, SLOPE fits generalized linear models regularized with the sorted <img src="https://latex.codecogs.com/png.latex?%5Cell_1"> norm. The objective in SLOPE is</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Coperatorname%7Bminimize%7D%5Cleft%5C%7B%20f(%5Cbeta)%20+%20J(%5Cbeta%20%5Cmid%20%5Clambda)%5Cright%5C%7D,%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?f(%5Cbeta)"> is typically the log-likelihood of some model in the family of generalized linear models and</p>
<p><img src="https://latex.codecogs.com/png.latex?J(%5Cbeta%5Cmid%20%5Clambda)%20=%20%5Csum_%7Bi=1%7D%5Ep%20%5Clambda_i%7C%5Cbeta%7C_%7B(i)%7D"></p>
<p>is the sorted <img src="https://latex.codecogs.com/png.latex?%5Cell_1"> norm.</p>
<p>Some people will note that this penalty is a generalization of the standard <img src="https://latex.codecogs.com/png.latex?%5Cell_1"> norm penalty<sup>1</sup>. As such, SLOPE is a type of sparse regression—just like the lasso. Unlike the lasso, however, SLOPE gracefully handles correlated features. Whereas the lasso often discards all but a few among a set of correlated features <span class="citation" data-cites="jia2010">(Jia and Yu 2010)</span>, SLOPE instead <em>clusters</em> such features together by setting such clusters to have the same coefficient in absolut value.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Simply set <img src="https://latex.codecogs.com/png.latex?%5Clambda_i%20=%20%5Clambda_j"> for all <img src="https://latex.codecogs.com/png.latex?i,j%20%5Cin%20%5C%7B1,%5Cdots,p%5C%7D"> and you get the lasso penalty.</p></div></div></section>
<section id="slope-0.2.0" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="slope-0.2.0">SLOPE 0.2.0</h2>
<p>SLOPE 0.2.0 is a new verison of the R package <a href="https://CRAN.R-project.org/package=SLOPE">SLOPE</a> featuring a range of improvements over the previous package. If you are completely new to the package, please start with the <a href="https://jolars.github.io/SLOPE/articles/introduction.html">introductory vignette</a>.</p>
<section id="more-model-families" class="level3">
<h3 class="anchored" data-anchor-id="more-model-families">More model families</h3>
<p>Previously, SLOPE only features ordinary least-squares regression. Now the package features logistic, Poisson, and multinomial regression on top of that. Just as in other similar packages, this is enabled simply by setting <code>family = "binomial"</code> for logistic regression, for instance.</p>
<div class="cell" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(SLOPE)</span>
<span id="cb1-2">fit <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SLOPE</span>(wine<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x, wine<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">family =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"multinomial"</span>)</span></code></pre></div></div>
</div>
</section>
<section id="regularization-path-fitting" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="regularization-path-fitting">Regularization path fitting</h3>
<p>By default, SLOPE now fits a full regularization path instead of only a single penalty sequence at once. This behavior is now analogous with the default behavior in glmnet.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(fit)</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2020-04-14-slope-0-2-0/index_files/figure-html/unnamed-chunk-3-1.png" class="img-fluid figure-img" width="768"></p>
<figcaption class="margin-caption">Coefficients from the regularization path for a multinomial model.</figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="predictor-screening-rules" class="level3">
<h3 class="anchored" data-anchor-id="predictor-screening-rules">Predictor screening rules</h3>
<p>The package now uses predictor screening rules to vastly improve performance in the <img src="https://latex.codecogs.com/png.latex?p%20%5Cgg%20n"> domain. Screening rules are part of what makes other related packages such as glmnet so efficient. In SLOPE, we use a variant of the strong screening rules for the lasso <span class="citation" data-cites="tibshirani2012">(Tibshirani et al. 2012)</span>.</p>
<div class="cell" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">xy <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> SLOPE<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">randomProblem</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span>)</span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">system.time</span>({<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SLOPE</span>(xy<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x, xy<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">screen =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)})</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>   user  system elapsed 
  1.198   0.004   0.159 </code></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">system.time</span>({<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SLOPE</span>(xy<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x, xy<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">screen =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)})</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>   user  system elapsed 
  2.781   0.006   0.364 </code></pre>
</div>
</div>
</section>
<section id="cross-validation-and-caret" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="cross-validation-and-caret">Cross-validation and caret</h3>
<p>There is now a function <code>trainSLOPE()</code>, which can be used to run cross-validation for optimal selection of <code>sigma</code> and <code>q</code>. Here, we run 8-fold cross-validation repeated 5 times.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 8-fold cross-validation repeated 5 times</span></span>
<span id="cb7-2">tune <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">trainSLOPE</span>(</span>
<span id="cb7-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">subset</span>(mtcars, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">select =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mpg"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"drat"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"wt"</span>)),</span>
<span id="cb7-4">  mtcars<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hp,</span>
<span id="cb7-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">q =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span>),</span>
<span id="cb7-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">number =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>,</span>
<span id="cb7-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">repeats =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span></span>
<span id="cb7-8">)</span>
<span id="cb7-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(tune)</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2020-04-14-slope-0-2-0/index_files/figure-html/unnamed-chunk-5-1.png" class="img-fluid figure-img" width="672"></p>
<figcaption class="margin-caption">Cross-validation with SLOPE.</figcaption>
</figure>
</div>
</div>
</div>
<p>In addition, the package now also features a function <code>caretSLOPE()</code> that can be used via the excellent caret package, which enables a swath of resampling methods and comparisons.</p>
</section>
<section id="c-and-admm" class="level3">
<h3 class="anchored" data-anchor-id="c-and-admm">C++ and ADMM</h3>
<p>All of the performance-critical code for SLOPE has been rewritten in C++. In addition, the package now features an ADMM solver for <code>family = "gaussian"</code>, enabled by setting <code>solver = "admm"</code> in the call to <code>SLOPE()</code>. Preliminary testing shows that this solver is faster for many designs, particularly when there is high correlation among predictors.</p>
</section>
<section id="sparse-design-matrices" class="level3">
<h3 class="anchored" data-anchor-id="sparse-design-matrices">Sparse design matrices</h3>
<p>SLOPE now also allows sparse design matrcies of classes from the Matrix package.</p>
</section>
<section id="and-much-more" class="level3">
<h3 class="anchored" data-anchor-id="and-much-more">And much more…</h3>
<p>For a full list of changes, please see <a href="https://jolars.github.io/SLOPE/news/index.html#slope-0-2-0-unreleased">the changelog</a>.</p>
</section>
</section>
<section id="references" class="level2">




</section>


<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-bogdan2015" class="csl-entry">
Bogdan, Małgorzata, Ewout van den Berg, Chiara Sabatti, Weijie Su, and Emmanuel J. Candès. 2015. <span>“<span>SLOPE</span> – Adaptive Variable Selection via Convex Optimization.”</span> <em>The Annals of Applied Statistics</em> 9 (3): 1103–40. <a href="https://doi.org/10.1214/15-AOAS842">https://doi.org/10.1214/15-AOAS842</a>.
</div>
<div id="ref-bondell2008" class="csl-entry">
Bondell, Howard D., and Brian J. Reich. 2008. <span>“Simultaneous Regression Shrinkage, Variable Selection, and Supervised Clustering of Predictors with <span>OSCAR</span>.”</span> <em>Biometrics</em> 64 (1): 115–23. <a href="https://doi.org/10.1111/j.1541-0420.2007.00843.x">https://doi.org/10.1111/j.1541-0420.2007.00843.x</a>.
</div>
<div id="ref-jia2010" class="csl-entry">
Jia, J., and B. Yu. 2010. <span>“On Model Selection Consistency of the Elastic Net When p <span><img src="https://latex.codecogs.com/png.latex?%3E%3E"></span> n.”</span> <em>Statistica Sinica</em> 20 (2): 595–611.
</div>
<div id="ref-tibshirani2012" class="csl-entry">
Tibshirani, Robert, Jacob Bien, Jerome Friedman, Trevor Hastie, Noah Simon, Jonathan Taylor, and Ryan J. Tibshirani. 2012. <span>“Strong Rules for Discarding Predictors in Lasso-Type Problems.”</span> <em>Journal of the Royal Statistical Society. Series B: Statistical Methodology</em> 74 (2): 245–66. <a href="https://doi.org/10/c4bb85">https://doi.org/10/c4bb85</a>.
</div>
</div></section></div> ]]></description>
  <category>R</category>
  <category>SLOPE</category>
  <category>Statistics</category>
  <category>Software</category>
  <guid>https://jolars.co/blog/2020-04-14-slope-0-2-0/</guid>
  <pubDate>Tue, 14 Apr 2020 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2020-04-14-slope-0-2-0/slope.svg" medium="image" type="image/svg+xml"/>
</item>
<item>
  <title>Polygon Labeling with polylabelr</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2018-10-29-polygon-labeling-with-polylabelr/</link>
  <description><![CDATA[ 





<section id="labeling-euler-diagram-overlaps" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="labeling-euler-diagram-overlaps">Labeling Euler diagram overlaps</h2>
<p>The purpose of my R package <a href="https://github.com/jolars/eulerr">eulerr</a> is to fit and <em>visualize</em> Euler diagrams. Besides the various intricacies involved in fitting the diagrams, there are many interesting problems involved in their visualization. One of these is the labeling of the overlaps.</p>
<p>Naturally, simply positioning the labels at the shapes’ centers fails more often than not. Nevertheless, this stategy is employed by <strong>venneuler</strong>, for instance, and the plots usually demand manual tuning.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># an example set combination</span></span>
<span id="cb1-2">s <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb1-3">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SE"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">13</span>,</span>
<span id="cb1-4">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Treat"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">28</span>,</span>
<span id="cb1-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Anti-CCP"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">101</span>,</span>
<span id="cb1-6">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"DAS28"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">91</span>,</span>
<span id="cb1-7">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SE&amp;Treat"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb1-8">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SE&amp;DAS28"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span>,</span>
<span id="cb1-9">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Treat&amp;Anti-CCP"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>,</span>
<span id="cb1-10">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SE&amp;Anti-CCP&amp;DAS28"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb1-11">)</span>
<span id="cb1-12"></span>
<span id="cb1-13"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(venneuler, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">quietly =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb1-14">fit_venneuler <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">venneuler</span>(s)</span>
<span id="cb1-15"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(fit_venneuler)</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2018-10-29-polygon-labeling-with-polylabelr/index_files/figure-html/unnamed-chunk-2-1.png" class="img-fluid figure-img" width="336"></p>
<figcaption class="margin-caption">A plot from venneuler with suboptimal label placements.</figcaption>
</figure>
</div>
</div>
</div>
<p>Up til now, I solved this in <strong>eulerr</strong> by, for each overlap, filling one of the involved shapes (circles or ellipses) with points and then numerically optimizing the location of the point using a Nelder–Mead optimizer. However, given that the solution to finding the distance between a point and an ellipse—at least one that is rotated—itself requires a numerical solution <span class="citation" data-cites="eberly2013">(Eberly 2013)</span>, this procedure turned out to be quite inefficient.</p>
</section>
<section id="the-promise-of-polygons" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="the-promise-of-polygons">The promise of polygons</h2>
<p>R has powerful functionality for plotting in general, but lacks capabilities for drawing ellipses using curves. High-resolution polygons are thankfully a readily available remedy for this and have since several version back been used also in <strong>eulerr</strong>.</p>
<p>The upside of using polygons, however, are that they are usually much easier, even if sometimes inefficient, to work with. For instance, they make constructing separate shapes for each overlap a breeze using the polyclip package <span class="citation" data-cites="johnson2018">(Johnson and Baddeley 2018)</span>.</p>
<p>And because basically all shapes in digital maps are polygons, there happens to exist a multitude of other useful tools to deal with a wide variety of tasks related to polygons. One of these turned out to be precisely what I needed: polylabel <span class="citation" data-cites="mapbox2018">(Mapbox [2016] 2018)</span> from the Mapbox suite. Because the details of the library <a href="https://blog.mapbox.com/a-new-algorithm-for-finding-a-visual-center-of-a-polygon-7c77e6492fbc">have already been explained elsewhere</a> I will spare you the details, but briefly put it uses quadtree binning to divide the polygon into square bins, pruning away dead-ends. It is inefficient and will, according to the authors, find a point that is “guaranteed to be a global optimum within the given precision”.</p>
<p>Because it appeared to be such a valuable tool for R users, I decided to create a wrapper for the c++ header for polylabel and bundle it as a package for R users.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># install.packages("polylabelr")</span></span>
<span id="cb2-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(polylabelr)</span>
<span id="cb2-3"></span>
<span id="cb2-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># a concave polygon with a hole</span></span>
<span id="cb2-5">x <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb2-6">y <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb2-7"></span>
<span id="cb2-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># locate the pole of inaccessibility</span></span>
<span id="cb2-9">p <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">poi</span>(x, y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">precision =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>)</span>
<span id="cb2-10"></span>
<span id="cb2-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot.new</span>()</span>
<span id="cb2-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot.window</span>(</span>
<span id="cb2-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">range</span>(x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb2-14">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">range</span>(y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb2-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">asp =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb2-16">)</span>
<span id="cb2-17"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">polypath</span>(x, y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"grey90"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rule =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"evenodd"</span>)</span>
<span id="cb2-18"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">points</span>(p, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cex =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pch =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>)</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2018-10-29-polygon-labeling-with-polylabelr/index_files/figure-html/unnamed-chunk-3-1.png" class="img-fluid figure-img" width="336"></p>
<figcaption class="margin-caption">Locating poles of inaccessibility with polylabel.</figcaption>
</figure>
</div>
</div>
</div>
<p>The package <a href="https://CRAN.R-project.org/package=polylabelr">is availabe on cran</a>, the source code is located at <a href="https://github.com/jolars/polylabelr" class="uri">https://github.com/jolars/polylabelr</a> and is documented at <a href="https://jolars.github.io/polylabelr/" class="uri">https://jolars.github.io/polylabelr/</a>.</p>
</section>
<section id="euler-diagrams" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="euler-diagrams">Euler diagrams</h2>
<p>To come back around to where we started at, <strong>polylabelr</strong> has now been employed in the development branch of <strong>eulerr</strong> where it is used to quickly and appropriately figure out locations for the labels of the diagram.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(eulerr)</span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">euler</span>(s))</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2018-10-29-polygon-labeling-with-polylabelr/index_files/figure-html/unnamed-chunk-4-1.png" class="img-fluid figure-img" width="336"></p>
<figcaption class="margin-caption">An Euler diagram with appropriate label placement.</figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="references" class="level2">




</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-eberly2013" class="csl-entry">
Eberly, David. 2013. <span>“Distance from a Point to an Ellipse, an Ellipsoid, or a Hyperellipsoid.”</span> <span>Geometric Tools</span>. June 28, 2013. <a href="https://www.geometrictools.com/Documentation/DistancePointEllipseEllipsoid.pdf">https://www.geometrictools.com/Documentation/DistancePointEllipseEllipsoid.pdf</a>.
</div>
<div id="ref-johnson2018" class="csl-entry">
Johnson, Angus, and Adrian Baddeley. 2018. <span>“Polyclip: Polygon Clipping.”</span> <a href="https://CRAN.R-project.org/package=polyclip">https://CRAN.R-project.org/package=polyclip</a>.
</div>
<div id="ref-mapbox2018" class="csl-entry">
Mapbox. (2016) 2018. <span>“A Fast Algorithm for Finding the Pole of Inaccessibility of a Polygon (in <span>JavaScript</span> and <span>C</span>++): Mapbox/Polylabel.”</span> <span>Mapbox</span>. <a href="https://github.com/mapbox/polylabel">https://github.com/mapbox/polylabel</a>.
</div>
</div></section></div> ]]></description>
  <category>R</category>
  <category>Geometry</category>
  <category>Polylabelr</category>
  <guid>https://jolars.co/blog/2018-10-29-polygon-labeling-with-polylabelr/</guid>
  <pubDate>Mon, 29 Oct 2018 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2018-10-29-polygon-labeling-with-polylabelr/polylabelr.png" medium="image" type="image/png" height="60" width="144"/>
</item>
<item>
  <title>Finding the Farthest Points in a Point Cloud</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2016-10-30-farthest-points/</link>
  <description><![CDATA[ 





<p>My R package <a href="https://github.com/jolars/qualpalr">qualpalr</a> selects qualitative colors by projecting a bunch of colors (as points) to the three-dimensional DIN99d color space wherein the distance between any pair colors approximate their differences in appearance. The package then tries to choose the <code>n</code> colors so that the minimal pairwise distance among them is maximized, that is, we want the most similar pair of colors to be as dissimilar as possible.</p>
<p>This turns out to be much less trivial that one would suspect, which posts on <a href="http://scicomp.stackexchange.com/questions/20030/selecting-most-scattered-points-from-a-set-of-points">Computational Science</a>, <a href="https://se.mathworks.com/matlabcentral/answers/42622-how-can-i-choose-a-subset-of-k-points-the-farthest-apart">MATLAB Central</a>, <a href="http://stackoverflow.com/questions/27971223/finding-largest-minimum-distance-among-k-objects-in-n-possible-distinct-position">Stack Overflow</a>, and and <a href="http://cs.stackexchange.com/questions/22767/choosing-a-subset-to-maximize-the-minimum-distance-between-points">Computer Science</a> can attest to.</p>
<p>Up til now, qualpalr solved this problem with a greedy approach. If we, for instance, want to find <code>n</code> points we did the following.</p>
<pre><code>M &lt;- Compute a distance matrix of all points in the sample
X &lt;- Select the two most distant points from M
for i = 3:n
    X(i) &lt;- Select point in M that maximize the
            mindistance to all points in X</code></pre>
<p>In R, this code looked like this (in two dimensions):</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set.seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb2-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># find n points</span></span>
<span id="cb2-3">n <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span></span>
<span id="cb2-4">mat  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">matrix</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runif</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ncol =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb2-5"></span>
<span id="cb2-6">dmat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>(stats<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dist</span>(mat))</span>
<span id="cb2-7">ind <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">integer</span>(n)</span>
<span id="cb2-8">ind[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.vector</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrayInd</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which.max</span>(dmat), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.dim =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(dmat)))</span>
<span id="cb2-9"></span>
<span id="cb2-10"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>n) {</span>
<span id="cb2-11">  mm <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> dmat[ind, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>ind, drop <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>]</span>
<span id="cb2-12">  k <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which.max</span>(mm[(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ncol</span>(mm) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(mm) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max.col</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>mm))])</span>
<span id="cb2-13">  ind[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dimnames</span>(mm)[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]][k])</span>
<span id="cb2-14">}</span>
<span id="cb2-15"></span>
<span id="cb2-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(mat, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">asp =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>)</span>
<span id="cb2-17"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(mat, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">asp =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>)</span>
<span id="cb2-18"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">points</span>(mat[ind, ], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pch =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">19</span>)</span>
<span id="cb2-19"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">text</span>(mat[ind, ], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">adj =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span>))</span></code></pre></div></div>
<div class="cell quarto-layout-panel page-columns page-full" data-layout-ncol="2" data-fig-lab="greedy-approach">
<div class="quarto-layout-row page-full">
<div class="quarto-layout-cell page-columns page-full" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2016-10-30-farthest-points/index_files/figure-html/unnamed-chunk-1-1.png" class="img-fluid figure-img" width="384"></p>
<figcaption class="margin-caption">Point cloud</figcaption>
</figure>
</div>
</div>
<div class="quarto-layout-cell page-columns page-full" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2016-10-30-farthest-points/index_files/figure-html/unnamed-chunk-1-2.png" class="img-fluid figure-img" width="384"></p>
<figcaption class="margin-caption">Points selected by the greedy algorithm</figcaption>
</figure>
</div>
</div>
</div>
</div>
<p>While this greedy procedure is fast and works well for large values of <code>n</code> it is quite inefficient in the example above. It is plain to see that there are other subsets of 3 points that would have a larger minimum distance but because we base our selection on the previous 2 points that were selected to be maximally distant, the algorithm has to pick a suboptimal third point. The minimum distance in our example is 0.7641338.</p>
<p>The solution I came up with is based on a solution from Schlomer et al. <span class="citation" data-cites="schlomer2011">(Schlömer, Heck, and Deussen 2011)</span> who devised of an algorithm to partition a sets of points into subsets whilst maximizing the minimal distance. They used <a href="https://en.wikipedia.org/wiki/Delaunay_triangulation">delaunay triangulations</a> but I decided to simply use the distance matrix instead. The algorithm works as follows.</p>
<pre><code>M &lt;- Compute a distance matrix of all points in the sample
S &lt;- Sample n points randomly from M
repeat
    for i = 1:n
        M    &lt;- Add S(i) back into M
        S(i) &lt;- Find point in M\S with max mindistance to any point in S
                until M did not change</code></pre>
<p>Iteratively, we put one point from our candidate subset (S) back into the original se (M) and check all distances between the points in S to those in M to find the point with the highest minimum distance. Rinse and repeat until we are only putting back the same points we started the loop with, which always happens. Let’s see how this works on the same data set we used above.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1">r <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample.int</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(dmat), n)</span>
<span id="cb4-2"></span>
<span id="cb4-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">repeat</span> {</span>
<span id="cb4-4">  r_old <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> r</span>
<span id="cb4-5">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>n) {</span>
<span id="cb4-6">    mm <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> dmat[r[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>i], <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>r[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>i], drop <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>]</span>
<span id="cb4-7">    k <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which.max</span>(mm[(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ncol</span>(mm) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(mm) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max.col</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>mm))])</span>
<span id="cb4-8">    r[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dimnames</span>(mm)[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]][k])</span>
<span id="cb4-9">  }</span>
<span id="cb4-10">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">identical</span>(r_old, r)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb4-11">}</span>
<span id="cb4-12"></span>
<span id="cb4-13"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(mat, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">asp =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>)</span>
<span id="cb4-14"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(mat, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">asp =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>)</span>
<span id="cb4-15"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">points</span>(mat[r, ], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pch =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">19</span>)</span>
<span id="cb4-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">text</span>(mat[r, ], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">adj =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span>))</span></code></pre></div></div>
<div class="cell quarto-layout-panel page-columns page-full" data-layout-ncol="2" data-fig-lab="new-approach">
<div class="quarto-layout-row page-full">
<div class="quarto-layout-cell page-columns page-full" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2016-10-30-farthest-points/index_files/figure-html/unnamed-chunk-2-1.png" class="img-fluid figure-img" width="384"></p>
<figcaption class="margin-caption">Point cloud</figcaption>
</figure>
</div>
</div>
<div class="quarto-layout-cell page-columns page-full" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2016-10-30-farthest-points/index_files/figure-html/unnamed-chunk-2-2.png" class="img-fluid figure-img" width="384"></p>
<figcaption class="margin-caption">Points selected by the new algorithm</figcaption>
</figure>
</div>
</div>
</div>
</div>
<p>Here, we end up with a minimum distance of 0.8619587. In qualpalr, this means that we now achieve slightly more distinct colors.</p>
<section id="performance" class="level2">
<h2 class="anchored" data-anchor-id="performance">Performance</h2>
<p>The new algorithm is slightly slower than the old, greedy approach and slightly more verbose</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1">f_greedy <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(data, n) {</span>
<span id="cb5-2">  dmat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>(stats<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dist</span>(data))</span>
<span id="cb5-3">  ind <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">integer</span>(n)</span>
<span id="cb5-4">  ind[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.vector</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrayInd</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which.max</span>(dmat), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.dim =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(dmat)))</span>
<span id="cb5-5">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>n) {</span>
<span id="cb5-6">    mm <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> dmat[ind, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>ind, drop <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>]</span>
<span id="cb5-7">    k <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which.max</span>(mm[(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ncol</span>(mm) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(mm) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max.col</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>mm))])</span>
<span id="cb5-8">    ind[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dimnames</span>(mm)[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]][k])</span>
<span id="cb5-9">  }</span>
<span id="cb5-10">  ind</span>
<span id="cb5-11">}</span>
<span id="cb5-12"></span>
<span id="cb5-13">f_new <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(dat, n) {</span>
<span id="cb5-14">  dmat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>(stats<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dist</span>(data))</span>
<span id="cb5-15">  r <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample.int</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(dmat), n)</span>
<span id="cb5-16">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">repeat</span> {</span>
<span id="cb5-17">    r_old <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> r</span>
<span id="cb5-18">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>n) {</span>
<span id="cb5-19">      mm <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> dmat[r[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>i], <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>r[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>i], drop <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>]</span>
<span id="cb5-20">      k <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which.max</span>(mm[(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ncol</span>(mm) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(mm) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max.col</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>mm))])</span>
<span id="cb5-21">      r[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dimnames</span>(mm)[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]][k])</span>
<span id="cb5-22">    }</span>
<span id="cb5-23">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">identical</span>(r_old, r)) <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(r)</span>
<span id="cb5-24">  }</span>
<span id="cb5-25">}</span></code></pre></div></div>
</div>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1">n <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span></span>
<span id="cb6-2">data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">matrix</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runif</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">900</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ncol =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb6-3">microbenchmark<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">microbenchmark</span>(</span>
<span id="cb6-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">f_greedy</span>(data, n),</span>
<span id="cb6-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">f_new</span>(data, n),</span>
<span id="cb6-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">times =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span>L</span>
<span id="cb6-7">)</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>Unit: microseconds
              expr      min       lq     mean    median       uq      max neval
 f_greedy(data, n)  799.503  887.881 1158.959  990.9285 1143.117 39747.68  1000
    f_new(data, n) 1066.954 1508.136 1893.722 1710.8860 2002.331 13596.49  1000
 cld
  a 
   b</code></pre>
</div>
</div>
<p>The newest development version of qualpalr now uses this updated algorithm which has also been generalized and included as a new function in my R package <a href="https://github.com/jolars/euclidr">euclidr</a> called <code>farthest_points</code>.</p>
</section>
<section id="references" class="level2">




</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-schlomer2011" class="csl-entry">
Schlömer, Thomas, Daniel Heck, and Oliver Deussen. 2011. <span>“Farthest-Point Optimized Point Sets with Maximized Minimum Distance.”</span> In <em>Proceedings of the <span>ACM SIGGRAPH Symposium</span> on <span>High Performance Graphics</span></em>, 135–42. <span>HPG</span> ’11. New York, NY, USA: Association for Computing Machinery. <a href="https://doi.org/10.1145/2018323.2018345">https://doi.org/10.1145/2018323.2018345</a>.
</div>
</div></section></div> ]]></description>
  <category>R</category>
  <category>Qualpalr</category>
  <category>Data visualization</category>
  <category>Geometry</category>
  <guid>https://jolars.co/blog/2016-10-30-farthest-points/</guid>
  <pubDate>Sun, 30 Oct 2016 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2016-10-30-farthest-points/farthest-points.png" medium="image" type="image/png" height="141" width="144"/>
</item>
<item>
  <title>Introducing eulerr</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2016-10-19-introducing-eulerr/</link>
  <description><![CDATA[ 





<p><strong>eulerr</strong> is an <a href="https://www.r-project.org">R</a> package that generates area-proportional euler diagrams to display set relationships (intersections, unions, and disjoints) with circles. <a href="https://en.wikipedia.org/wiki/Euler_diagram">Euler diagrams</a> are Venn diagrams without the requirement that all set interactions be present (whether they are empty or not). That is, depending on input, eulerr will sometimes produce Venn diagrams but sometimes not.</p>
<section id="background" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="background">Background</h2>
<p>R features a number of packages that produce Euler and/or Venn diagrams; some of the more prominent ones (on CRAN) are</p>
<ul>
<li><a href="https://cran.r-project.org/package=eVenn">eVenn</a>,</li>
<li><a href="https://cran.r-project.org/package=VennDiagram">VennDiagram</a>,</li>
<li><a href="https://cran.r-project.org/package=venn">venn</a>,</li>
<li><a href="https://cran.r-project.org/package=colorfulVennPlot">colorfulVennPlot</a>, and</li>
<li><a href="https://cran.r-project.org/package=venneuler">venneuler</a>.</li>
</ul>
<p>The last of these (venneuler) serves as the primary inspiration for this package, along with the refinements that Ben Fredrickson has presented on his <a href="http://www.benfrederickson.com/">blog</a> and made available in his javascript <a href="https://github.com/benfred/venn.js">venn.js</a>.</p>
<p>venneuler, however, is written in java, preventing R users from browsing the source code (unless they are also literate in java) or contributing. Furthermore, venneuler is known to produce imperfect output for set configurations that have perfect solutions. Consider, for instance, the following example in which the intersection between <code>A</code> and <code>B</code> is unwanted.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(venneuler, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">quietly =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb1-2">venn_fit <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">venneuler</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">A =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">75</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">B =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A&amp;B"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>))</span>
<span id="cb1-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(venn_fit)</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2016-10-19-introducing-eulerr/index_files/figure-html/unnamed-chunk-2-1.png" class="img-fluid figure-img" width="384"></p>
<figcaption class="margin-caption">venneuler plot with unwanted overlap.</figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="enter-eulerr" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="enter-eulerr">Enter eulerr</h2>
<p>eulerr is based on the improvements to <strong>venneuler</strong> that Ben Fredrickson introcued with <strong>venn.js</strong> but has been coded from scratch, uses different optimizers, and returns the residuals and stress statistic that venneuler features.</p>
<section id="input" class="level3">
<h3 class="anchored" data-anchor-id="input">Input</h3>
<p>Currently, it is possible to provide input to <code>eulerr</code> as either</p>
<ul>
<li>a named numeric vector or</li>
<li>a matrix of logicals with columns representing sets and rows the set relationships for each observation.</li>
</ul>
<div class="cell" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(eulerr)</span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Input in the form of a named numeric vector</span></span>
<span id="cb2-4">fit1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">euler</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">25</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"C"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>,</span>
<span id="cb2-5">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A&amp;B"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A&amp;C"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B&amp;C"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>,</span>
<span id="cb2-6">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A&amp;B&amp;C"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>))</span>
<span id="cb2-7"></span>
<span id="cb2-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Input as a matrix of logicals</span></span>
<span id="cb2-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set.seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb2-10">mat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span></span>
<span id="cb2-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cbind</span>(</span>
<span id="cb2-12">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">A =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">replace =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb2-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">B =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">replace =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb2-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">C =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">replace =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb2-15">  )</span>
<span id="cb2-16">fit2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">euler</span>(mat)</span></code></pre></div></div>
</div>
</section>
<section id="fit" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="fit">Fit</h3>
<p>We inspect our results by printing the eulerr object</p>
<div class="cell" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">fit2</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>      original fitted residuals regionError
A           13     13         0       0.008
B            4      4         0       0.002
C            0      0         0       0.000
A&amp;B         17     17         0       0.010
A&amp;C          5      5         0       0.003
B&amp;C          1      0         1       0.024
A&amp;B&amp;C        2      2         0       0.001

diagError: 0.024 
stress:    0.002 </code></pre>
</div>
</div>
<p>or directly access and plot the residuals.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Cleveland dot plot of the residuals</span></span>
<span id="cb5-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dotchart</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">resid</span>(fit2))</span>
<span id="cb5-3"></span>
<span id="cb5-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abline</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">v =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2016-10-19-introducing-eulerr/index_files/figure-html/residual_plot-1.png" class="img-fluid figure-img" width="384"></p>
<figcaption class="margin-caption">Residuals for the eulerr fit.</figcaption>
</figure>
</div>
</div>
</div>
<p>This shows us that the <code>A&amp;B&amp;C</code> intersection is somewhat overrepresented in <code>fit2</code>. Given that these residuals are on the scale of the original values, however, the residuals are arguably of little concern.</p>
<p>For an overall measure of the fit of the solution, we use the same stress statistic that Leland Wilkinson presented in his academic paper on venneuler <span class="citation" data-cites="wilkinson2012">(Wilkinson 2012)</span>, which is given by the sums of squared residuals divided by the total sums of squares:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B%5Csum_%7Bi=1%7D%5En%20(f_i%20-%20y_i)%5E2%7D%7B%5Csum_%7Bi=1%7D%5En%20(y_i%20-%20%5Cbar%7By%7D)%5E2%7D."></p>
<p>We fetch it from the <code>stress</code> attribute of the <code>eulerr</code> object.</p>
<div class="cell" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1">fit2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>stress</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] 0.00198</code></pre>
</div>
</div>
<p>We can now be confident that eulerr provides a reasonable representation of our input. Were it otherwise, we would do best to stop here and look for another way to visualize our data. (I suggest the excellent <a href="https://cran.r-project.org/package=UpSetR">UpSetR</a> package.)</p>
</section>
<section id="plotting" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="plotting">Plotting</h3>
<p>No we get to the fun part: plotting our diagram. This is easy, as well as highly customizable, with eulerr.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(fit2)</span>
<span id="cb8-2"></span>
<span id="cb8-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Change fill colors, border type (remove) and fontface.</span></span>
<span id="cb8-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(</span>
<span id="cb8-5">  fit2,</span>
<span id="cb8-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fills =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dodgerblue4"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"plum2"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"seashell2"</span>),</span>
<span id="cb8-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">edges =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>),</span>
<span id="cb8-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">font =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb8-9">)</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2016-10-19-introducing-eulerr/index_files/figure-html/eulerr_plot-1.png" class="img-fluid figure-img" width="240"></p>
<figcaption class="margin-caption">eulerr plots can be modified in many ways.</figcaption>
</figure>
</div>
</div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p><img src="https://jolars.co/blog/2016-10-19-introducing-eulerr/index_files/figure-html/eulerr_plot-2.png" class="img-fluid figure-img" width="240"></p>
<figcaption class="margin-caption">eulerr plots can be modified in many ways.</figcaption>
</figure>
</div>
</div>
</div>
<p>eulerr’s default color palette is taken from <a href="https://cran.r-project.org/package=qualpalr">qualpalr</a> – another package that I have developed – which uses color difference algorithms to generate distinct qualitative color palettes.</p>
</section>
</section>
<section id="details" class="level2">
<h2 class="anchored" data-anchor-id="details">Details</h2>
<p>Details of the implementation will be left for a future vignette but almost completely resemble the approach documented <a href="http://www.benfrederickson.com/better-venn-diagrams/">here</a>.</p>
</section>
<section id="thanks" class="level2">
<h2 class="anchored" data-anchor-id="thanks">Thanks</h2>
<p>eulerr would not be possible without Ben Fredrickson’s work on <a href="http://www.benfrederickson.com">venn.js</a> or Leland Wilkinson’s <a href="https://cran.r-project.org/package=venneuler">venneuler</a>.</p>
</section>
<section id="references" class="level2">




</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-wilkinson2012" class="csl-entry">
Wilkinson, L. 2012. <span>“Exact and <span>Approximate Area</span>-<span>Proportional Circular Venn</span> and <span>Euler Diagrams</span>.”</span> <em>IEEE Transactions on Visualization and Computer Graphics</em> 18 (2): 321–31. <a href="https://doi.org/10.1109/TVCG.2011.56">https://doi.org/10.1109/TVCG.2011.56</a>.
</div>
</div></section></div> ]]></description>
  <category>R</category>
  <category>Eulerr</category>
  <category>Euler diagrams</category>
  <category>Data visualization</category>
  <guid>https://jolars.co/blog/2016-10-19-introducing-eulerr/</guid>
  <pubDate>Wed, 19 Oct 2016 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2016-10-19-introducing-eulerr/eulerr.svg" medium="image" type="image/svg+xml"/>
</item>
<item>
  <title>Introducing qualpalr</title>
  <dc:creator>Johan Larsson</dc:creator>
  <link>https://jolars.co/blog/2016-10-15-introducing-qualpalr/</link>
  <description><![CDATA[ 





<section id="introduction" class="level2">
<h2 class="anchored" data-anchor-id="introduction">Introduction</h2>
<p>With the advent of <a href="http://colorbrewer2.org/">colorbrewer</a> there now exists good options to generate color palettes for sequential, diverging, and qualitative data. In R, these palettes can be accessed via the popular <a href="https://cran.r-project.org/package=RColorBrewer">RColorBrewer</a> package. Those palettes, however, are limited to a fixed number of colors. This isn’t much of a problem for sequential of diverging data since we can interpolate colors to any range we desire:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1">pal <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> RColorBrewer<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">brewer.pal</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"PuBuGn"</span>)</span>
<span id="cb1-2">color_ramp <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">colorRampPalette</span>(pal, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">space =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Lab"</span>) </span></code></pre></div></div>
</div>
<p>There is not, however, an analogue for qualitative color palettes that will get you beyond the limits of 8–12 colors of colorbrewer’s qualitative color palettes. There is also no customization in colorbrewer. Other R packages, such as <a href="https://cran.r-project.org/package=colorspace">colorspace</a> offer this, but they are primarily adapted to sequential and diverging data – not qualitative data.</p>
<p>This is where qualpalr comes in. qualpalr provides the user with a convenient way of generating distinct qualitative color palettes, primarily for use in R graphics. Given <code>n</code> (the number of colors to generate), along with a subset in the <a href="https://en.wikipedia.org/wiki/HSL_and_HSV">hsl color space</a> (a cylindrical representation of the RGB color space) <code>qualpalr</code> attempts to find the <code>n</code> colors in the provided color subspace that <em>maximize the smallest pairwise color difference</em>. This is done by projecting the color subset from the HSL color space to the DIN99d space. DIN99d is (approximately) perceptually uniform, that is, the euclidean distance between two colors in the space is proportional to their perceived difference.</p>
</section>
<section id="examples" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="examples">Examples</h2>
<p><code>qualpalr</code> relies on one basic function, <code>qualpal()</code>, which takes as its input <code>n</code> (the number of colors to generate) and <code>colorspace</code>, which can be either</p>
<ul>
<li>a list of numeric vectors <code>h</code> (hue from -360 to 360), <code>s</code> (saturation from 0 to 1), and <code>l</code> (lightness from 0 to 1), all of length 2, specifying a min and max, or</li>
<li>a character vector specifying one of the predefined color subspaces, which at the time of writing are <em>pretty</em>, <em>pretty_dark</em>, <em>rainbow</em>, and <em>pastels</em>.</li>
</ul>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(qualpalr)</span>
<span id="cb2-2">pal <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">qualpal</span>(</span>
<span id="cb2-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>,</span>
<span id="cb2-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb2-5">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">h =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">360</span>),</span>
<span id="cb2-6">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">s =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.4</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span>),</span>
<span id="cb2-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">l =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.85</span>)</span>
<span id="cb2-8">  )</span>
<span id="cb2-9">)</span>
<span id="cb2-10"></span>
<span id="cb2-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Adapt the color space to deuteranopia</span></span>
<span id="cb2-12">pal <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">qualpal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colorspace =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pretty"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cvd =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"deutan"</span>)</span></code></pre></div></div>
</div>
<p>The resulting object, <code>pal</code>, is a list with several color tables and a distance matrix based on the din99d color difference formula.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">pal</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>---------------------------------------- 
Colors in the HSL color space 

        Hue Saturation Lightness
#73CA6F 117       0.46      0.61
#D37DAD 327       0.50      0.66
#C6DBE8 203       0.42      0.84
#6C7DCC 229       0.48      0.61
#D0A373  31       0.50      0.63

 ---------------------------------------- 
DIN99d color difference distance matrix 

        #73CA6F #D37DAD #C6DBE8 #6C7DCC
#D37DAD      28                        
#C6DBE8      19      21                
#6C7DCC      27      19      19        
#D0A373      19      18      20      25</code></pre>
</div>
</div>
<p>Methods for <code>pairs</code> and <code>plot</code> have been written for <code>qualpal</code> objects to help visualize the results.</p>
<div class="cell page-columns page-full">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(pal)</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div id="fig-multidim" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-multidim-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://jolars.co/blog/2016-10-15-introducing-qualpalr/index_files/figure-html/fig-multidim-1.png" class="img-fluid figure-img" width="480">
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-multidim-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: Multidimensional scaling plot
</figcaption>
</figure>
</div>
</div>
</div>
<div class="cell page-columns page-full">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pairs</span>(pal, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colorspace =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"DIN99d"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">asp =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div id="fig-pairs" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-pairs-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://jolars.co/blog/2016-10-15-introducing-qualpalr/index_files/figure-html/fig-pairs-1.png" class="img-fluid figure-img" width="480">
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-pairs-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: Pairs plot in the din99d color space
</figcaption>
</figure>
</div>
</div>
</div>
<p>The colors are normally used in R by fetching the <code>hex</code> attribute of the palette. And so it is straightforward to use the output to, say, color the provinces of France (Figure&nbsp;3).</p>
<div class="cell page-columns page-full">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(maps)</span>
<span id="cb7-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">map</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"france"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> pal<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hex, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mar =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>))</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div id="fig-france" class="quarto-float quarto-figure quarto-figure-center anchored page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-france-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://jolars.co/blog/2016-10-15-introducing-qualpalr/index_files/figure-html/fig-france-1.png" class="img-fluid figure-img" width="480">
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-france-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: A map of France colored via qualpalr
</figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="details" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="details">Details</h2>
<p><code>qualpal</code> begins by generating a point cloud out of the HSL color subspace provided by the user, using a quasi-random torus sequence from <a href="https://cran.r-project.org/package=randtoolbox">randtoolbox</a>. Here is the color subset in HSL with settings <code>h = c(-200, 120), s = c(0.3, 0.8), l = c(0.4, 0.9)</code>.</p>
<div class="cell page-columns page-full">
<div id="fig-hsl-space" class="cell-output-display quarto-float quarto-figure quarto-figure-center anchored no-overflow-x page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-hsl-space-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div id="rgl90957" style="width:100%;height:650px;" class="rglWebGL html-widget" aria-labelledby="rgl90957-aria"></div>
<script type="application/json" data-for="rgl90957">{"x":{"material":{"color":"#000000","alpha":1,"lit":true,"ambient":"#000000","specular":"#FFFFFF","emission":"#000000","shininess":50,"smooth":true,"front":"filled","back":"filled","size":3,"lwd":1,"fog":true,"point_antialias":false,"line_antialias":false,"texture":null,"textype":"rgb","texmode":"modulate","texmipmap":false,"texminfilter":"linear","texmagfilter":"linear","texenvmap":false,"depth_mask":true,"depth_test":"less","isTransparent":false,"polygon_offset":[0,0],"margin":"","floating":false,"tag":"","blend":["src_alpha","one_minus_src_alpha"]},"rootSubscene":1,"objects":{"7":{"id":7,"type":"points","material":{"lit":false},"vertices":"0","colors":"1","centers":"2","ignoreExtent":false,"flags":34816},"9":{"id":9,"type":"text","material":{"lit":false,"margin":0,"edge":[0,1,1]},"vertices":"3","colors":"4","texts":[["HSL"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"5","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"10":{"id":10,"type":"text","material":{"lit":false,"margin":0,"floating":true,"edge":[0,1,1]},"vertices":"6","colors":"7","texts":[["x"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"8","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"11":{"id":11,"type":"text","material":{"lit":false,"margin":1,"floating":true,"edge":[1,1,1]},"vertices":"9","colors":"10","texts":[["y"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"11","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"12":{"id":12,"type":"text","material":{"lit":false,"margin":2,"floating":true,"edge":[1,1,1]},"vertices":"12","colors":"13","texts":[["L"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"14","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"5":{"id":5,"type":"light","vertices":[[0,0,1]],"colors":[[1,1,1,1],[1,1,1,1],[1,1,1,1]],"viewpoint":true,"finite":false},"4":{"id":4,"type":"background","material":{},"colors":"15","centers":"16","sphere":false,"fogtype":"none","fogscale":1,"flags":32768},"6":{"id":6,"type":"background","material":{"lit":false,"back":"lines"},"colors":"17","centers":"18","sphere":false,"fogtype":"none","fogscale":1,"flags":32768},"8":{"id":8,"type":"bboxdeco","material":{"front":"lines","back":"lines"},"vertices":"19","colors":"20","axes":{"mode":["pretty","pretty","pretty"],"step":[0.5,0.5,0.1000000014901161],"nticks":[5,5,5],"marklen":[15,15,15],"expand":[1.029999971389771,1.029999971389771,1.029999971389771]},"draw_front":true,"flags":32769},"1":{"id":1,"type":"subscene","par3d":{"antialias":8,"FOV":30,"ignoreExtent":false,"listeners":1,"mouseMode":{"none":"none","left":"trackball","right":"zoom","middle":"fov","wheel":"pull"},"observer":[0,0,5.620292663574219],"modelMatrix":[[0.8383641839027405,0,0,0.002952601062133908],[0,0.2854519784450531,2.49704909324646,-1.623473525047302],[0,-0.7842727899551392,0.9088515639305115,-6.209969997406006],[0,0,0,1]],"projMatrix":[[3.732050895690918,0,0,0],[0,3.732050895690918,0,0],[0,0,-3.863703489303589,-20.26050758361816],[0,0,-1,0]],"skipRedraw":false,"userMatrix":[[1,0,0,0],[0,0.3420201433256682,0.9396926207859085,0],[0,-0.9396926207859085,0.3420201433256682,0],[0,0,0,1]],"userProjection":[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],"scale":[0.8383641839027405,0.8346056938171387,2.657304048538208],"viewport":{"x":0,"y":0,"width":1,"height":1},"zoom":1,"bbox":[-0.7943815588951111,0.7873378396034241,-0.7930490374565125,0.7957932949066162,0.4004882872104645,0.8995116949081421],"windowRect":[0,0,256,256],"family":"sans","font":1,"cex":1,"useFreeType":true,"fontname":"NULL","maxClipPlanes":2147483647,"glVersion":"NA","activeSubscene":0},"embeddings":{"viewport":"replace","projection":"replace","model":"replace","mouse":"replace"},"objects":[6,8,7,9,10,11,12,5],"subscenes":[],"flags":36113}},"crosstalk":{"key":[],"group":[],"id":[],"options":[]},"width":480,"height":480,"buffer":{"accessors":[{"bufferView":0,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":1,"componentType":5121,"count":1000,"type":"VEC4","normalized":true},{"bufferView":2,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":3,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":4,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":5,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":7,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":8,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":10,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":11,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":12,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":13,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":14,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":15,"componentType":5126,"count":1,"type":"VEC4"},{"bufferView":16,"componentType":5121,"count":1,"type":"VEC3"},{"bufferView":17,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":18,"componentType":5121,"count":1,"type":"VEC3"},{"bufferView":19,"componentType":5126,"count":10,"type":"VEC3"},{"bufferView":20,"componentType":5121,"count":1,"type":"VEC4"}],"bufferViews":[{"buffer":0,"byteLength":12000,"byteOffset":0},{"buffer":0,"byteLength":4000,"byteOffset":12000},{"buffer":0,"byteLength":12000,"byteOffset":16000},{"buffer":0,"byteLength":12,"byteOffset":28000},{"buffer":0,"byteLength":4,"byteOffset":28012},{"buffer":0,"byteLength":12,"byteOffset":28016},{"buffer":0,"byteLength":12,"byteOffset":28028},{"buffer":0,"byteLength":4,"byteOffset":28040},{"buffer":0,"byteLength":12,"byteOffset":28044},{"buffer":0,"byteLength":12,"byteOffset":28056},{"buffer":0,"byteLength":4,"byteOffset":28068},{"buffer":0,"byteLength":12,"byteOffset":28072},{"buffer":0,"byteLength":12,"byteOffset":28084},{"buffer":0,"byteLength":4,"byteOffset":28096},{"buffer":0,"byteLength":12,"byteOffset":28100},{"buffer":0,"byteLength":16,"byteOffset":28112},{"buffer":0,"byteLength":3,"byteOffset":28128},{"buffer":0,"byteLength":4,"byteOffset":28131},{"buffer":0,"byteLength":3,"byteOffset":28135},{"buffer":0,"byteLength":120,"byteOffset":28140},{"buffer":0,"byteLength":4,"byteOffset":28260}],"buffers":[{"byteLength":28264,"bytes":"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\nUD7NTO4+Rc32vqi7FT5mph0/rf8QP9iJ2b5mpl0/9Z7oPo7f2T5mpj0/GzG0vuzHMb/NTPs+\nGHoEPmgKDr9mpk0/eZC2PW5FPT9mpg0/8yPNPk8yszzNTNs+jzYdv75Ghr5mpi0/YlvxvjMO\n4r5mpgU/rxI7P5/Hnz5mpkU/u6v6vTFiBj9mpmU/vVfLPp3QG79mpiU/j/dhvbRE6r5mpjU/\n6VWfPio0HT/NTOs+N1ISP0yLMb5mphU/kMNCv98hKr1mplU/Jo8RvzDCB75mpkE/bns/P2uy\nuL1mpgE/IiEBPmLX1D5mpiE/B3IXPR5fLb9mpmE/pLm2PvoXw77NTOM+YkeQvpnsKD9mpjE/\niwIIPyh/sT5mplE/EK/xvrkXIb9mphE/4dKVviyGQ75mplk/scsjP2XEGD5mphk/vcf3vKrT\nDT/NTPM+9PWTPi08Lb9mpjk/A141vs94Fb9mpgk/1KrsPrq/HT9mpkk/xdbcPm7VX75mpik/\nsn4xv5A0qz3NTNM+Hc8gv391qDxmpl8/Hew5P8pKlb5mph8/77mKPg0X3z7NTP8+KzMWvtGS\nMr9mpj8//0BKPvkxvb5mpg8/7M/CvbWRKj9mpk8/MFoMP5vENz5mpi8/CdoWv0HH7L7NTN8+\nOOiTvp7a7b5mpjc/CkcTPxIr5z7NTO8+rDydvroVEz9mphc/DDcYP3R0CL9mplc/BsSuPXAE\nGb/NTM8+A3AhPh3vPz9mpic/VtXxPnVWfbxmpkc/g7Uov3rlXL5mpgc/5jDjvqm9gb3NTPc+\n4zIrPzf/D75mpjs/NFhhPkNRDD9mpls/xRXLvNrcQb9mphs/HVzKPnPsAL9mpis/0dx7vv5b\nQD/NTNc+aKLvPlYWgD5mpgs/Sc/2vmKoCb9mpks/Y/H9vj68h75mpiM/Fpw7P1RG1j1mpmM/\n2YdLPL9Jwj5mpkM/uwRTPjAqIb9mpgM/gm88voGr6r5mplM/DFfwPp8MBj9mphM/tf8FP9Wm\npr7NTOc+zgNCvxEwIz5mpjM/Hi0OvxDBET7NTOk+ps8dP6ro175mpjQ/UPuSPk05lj5mplQ/\no6mNvvxTHL9mphQ/gzwMPna2+r5mpkQ/Y/tdPWpXNT9mpgQ/DlwfP/kgez1mpiQ/kiw0v+PX\nrL5mpmQ/GiinvgPYqr5mpgw/lXsfP2v+mD5mpkw/kKIlvl8PFD9mpiw/tNvjPub8Hr/NTNk+\n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\nIL9mhjQ/p40xPm3V775mhiQ/8lV8u2q4ND9mhmQ/Wx0cP40/4z1mhkQ/4qkrv4a5yL5mhgQ/\nvuuUvql0s75mhiw/4Gu5/+XQpv8tLd//xYni/5TQG//IZGT/ud/y/0x4z//zwan/otCL/7gk\nuP/YzO//4+NY/80uY/+K8fH/tOXv/+g7WP+brzX/snrq/8x4vv/U9s3/5sKd/yND4f9/qtT/\ntTgf/4rSQf/loPP/TDW//+3chv/oy9r/V93H/4re1//JHmb/u7Rt/8W38P+2SMD/ve2a//Tb\n0/9dmOr/tbni/9qZPv83qy3/623L/6Zd2//q98D/35ie/yOo1f9Hvsj/8J+z/9fgb/9aFbv/\n78Dr/3DnSf+wb0D/hp/p/yp8tv/ufHL/1Oi4/7dB3/+qptr/1aor/9Zpqf/I9+v/ZNrB//jF\n4f/e0Z//PCbY/9m84f+V2UX/rUAx/3O06v84Urb/67KD/8fwvf/pRNX/onPb/6G1Gf+9UWj/\noOPt/5rI2//QJi3/tNde/+K/9v+xJYj/be5n/+fSrv82PeD/0t7z/+iGXv92t0//6Jrr/3xb\nyv/y8LP/44On/xnNxf/M7en/4ViX/8m2Mf+fivD/vE7M/8ryqP/JnJD/JWe0/6Cp6v/ljSX/\ngdR2//fS8P95Qqj/0ueC/+28wv9Fw+X/NsbM//CRrf/t79P/kWDi/9uH1/9Pwx//2H9K/6zB\n9f9wo8L/8sG//8Phkf+eH9P/qqfq/+WvLf+eNHb/bObC/6vp2P/jMp3/moA7/3hw5P+zZc//\n4PW//+aUjv8aj9v/lqXP/75jJ/92z1b/87Hv/3I2z//r8ZP/8NTc/2Hb4/82jaf/6HWB/9/t\nr/+ZNub/45nS/zTdIP/Go3b/yM30/zh91f/zrpn/oNd2/6Icsf/Mw+n/39BO/74vbv9/7uL/\nYcC+//Cwx//e3IL/URzF//PM9P+L61b/wmg//5Oz7f8vMaL/6Ltp/7fatv/WO6b/wpba/5jM\nJf/XWlz/uuP2/3nR4P/BGDj/tcZT/8uo8P+tS5//oOmQ//DbyP9Tbub/vdXr/+JeR/94vyn/\n2Xnw/35u2v/58c//36nF/zDZuf+k4tT/3CuO/97Daf/Szfn/mDC3/7rsev/mxMD/Spzc/8XP\n8f/okk7/W7dA/+uM4f+GYrT/5u2p/9p9kv8cqrv/XLLS//W5vf/E1Jr/iCjH/+Or1P893DP/\nqHUn/2Nr7P9Ec7r/66uT/9vyzP/fVuj/mHzj/8i9F//MU4P/rPLu/yzHsf/wgrf/7efF/2tQ\n4//Fg9D/ZrIh/89eRP+gx/L/c4DQ//bizP+m55v/4yC+/9K36//E40D/nUZT/37R5f/a7/D/\n4mmP/8nMP/+4mvH/2FPV/8n2tP/bqo//IlbJ/5XJ5f/eIR3/q8xv/+bF9f80Man/6cRx/+6t\n1/8z6K//jOfL/9sYlP/RsGL/v771/343nP+65W7/6qup/zCi5P/S2/H/5I9f/2LRM//yke//\ngFLR//H0r//SkaT/Jbq+/4O6yP/21Nn/2eal/4ss4P/vvOX/WudE/7B5O/+CkOn/M3nI/++f\njP/c7c7/zlvg/5OC2f+9qB7/1kWJ/6b16v9Bxr//75i5//Xz0f+EW+z/3Ibh/1/MG//FfmL/\ntczx/11k2v/34r//nd6Z/9Mkov/QtuD/rdk+/6kuNf9tx+r/wejt/+RLbf+Zo03/s4jn/8JO\ntP+x7qD/3aJy/xg7uP+kwtv/1j0p/6XXaP/tx/f/OSm3/+7Tcf/ottL/QOC9/7vm3v/cRZL/\ntJwu/4d17P/IZN3/4fnI/+Kpn/8lg9z/d4na/7dnG/9quFr/7aTh/3c8tf/b6of/8MDJ/0nP\n6P8rhK//7G1u/9XltP+mPdz/06bE/y3OK//Trmb/w8P2/12DzP/zybb/puSF/84Z0P/f1fT/\n6edg/7lQdP+d7Ov/RdHE//OixP/Sy4P/RCG1/+vG7v+Q5FH/ylEr/4Sy8f9DTKL/5rp//73s\nuf/kQb3/yJzo/7XkIf/Sc3z/zu33/4fQ1v+8IUn/ytRJ/8ep9P/GOLv/qO6P/+rc0v9fgN7/\nos3r/+Y1KP9xnTD/xWfm/29oxf/y5Lv/4Yy+/x3Qov9szrP/9cXi/+bPlP8mHN//2bDp/6Hi\nOP+ZRkH/drrk/zZj1P/zupf/3/PX/+Zk4P+tk9f/vcYl/9RWef+18vX/ndXl/+AiOP+4ynj/\n48z1/6w2kv+G6Xn/7tS0/ztO6P/L1+b/3HNW/3K8Nv/hhez/YUHR//Ppn//Tf6X/ILOi/8vz\n8v/qVIv/wLw+/62R7f+7YL7/xu+u/92dgf8cWcP/kJLe/86PIP9Y21P/97fk/3gppv/C6mT/\n4ayu/zSo2v9Eo7L/6Yyf/+rwxf+WT+f/4XXS/zjAFv/Jh07/pbTx/2+m1//41M7/xNqs/7Ux\n1f/Dven/38FH/7oseP947tf/MreX/+x8vP/m3cL/Wkzc/9Sm4v+R2y3/3XZr/87n+f9WcML/\n78ep/5Leev+/Gq//2MXz/9zrTf/AOVn/i+Tt/6vW5f/eMz//iKsm/7Vj7f/VXLX/v/W6/9e+\nmf8mMsr/f6fg/8dHGv+Gxlr/7K/x/2pSr//q45b/8c7d/1rm3P+Dyb7/qiJl/8u1Qv+qm/D/\nqS2//7Xvff/qycH/TJXg/7G66//kkDj/TKA8/+Z20v+fatL/7fbG/+iUof8av+L/TdXX//Wu\nxf/Z2or/WSHE/+7V7v+M4WP/y2s6/5Sy8P83ep3/5XBv/9Tqqv+qMOT/lZTg/9SaIf+7eqT/\nwfLi/1nPqv/0tN3/zL2c/ysowP/p1/H/t+Nl/888Ov+X0vH/Plyb/+SbdP/B6q7/4jbg/6+S\n5v/b3hv/z2mK/8P09f96xtf/tB00/7nVPP/JnPT/wDGm/5Lugv/q2Mb/Umff/7PP7f/mWjr/\nb6g4/9d46P+Je8X/9e7L/+Scv/8i3sL/sdvY/9Y3dv+gmC3/h2Xo/85W1v/Q9rb/2qWS/yNm\nyf9/hd7/wn8b/2PAXf/vrd7/izzB/9Ttj//zycz/UsLr/yiuvP/vdZL/5uq6/4dD4f/eptn/\nVNgs/9qbav/M1vj/T5LG//CspP+x33X/lhe9/8nF7//lwk//rEiB/4zo0/8/u6P/64+///Lq\nyP9nUen/yX/b/3K+HP+4bmL/rM/u/2yC3//65ND/suSm/90txP/Uwuj/yd1N/7swS/993e3/\nz+jz/+lZXf+YvUb/zZbs/81VqP+x86//5L9+/xcczP+csdb/y1oo/4nUXv/0vPX/UCes/+zp\nZv/lrcH/Nd3Z/5jm3v/hHXT/zcNx/9PI9f+OSZb/qeR7/+rAtP89huH/wsfs/+KgTf88wyz/\n8H7W/49Bzf/e8Z3/zYGJ/yGOrf9txMn/9MLQ/9/kkv9nHdn/7anp/2LnL/+lXzL/bpDo/z2T\nyv/wm5b/5e7Y/79m4P+Rjdn/xZgi/9ZQov+x9eL/jtPC/74kfv/SsFD/s630/6gw0f/J8o//\n8dPQ/12t5f+quuf/4Hkx/1aQQv/icNn/kGbL/+/zvf/kjaP/G8jW/0Wx1P/0paz/wteD/3kf\nuv/rzuT/Zt9b/8SINf+LlO7/NWu0/+uZfv/Q8Ln/2kDp/7Oh6P/i0if/z3qe/9L39P8tqaj/\n6muW/+HhtP9wPNn/3pfe/17TI//ahVv/vtH3/25vt//w3LX/id2I/8cekP/o0fT/u+pc/75H\nSP+Y0e3/tt/n/94/W/+dtCr/rnDt/9VoxP/P98b/2L6l/ypH0/9up+D/uDAV/4fHRv/hnu//\nUkGt/+jXhf/vvtf/R+bL/8Lx5//pSqH/up46/5SI6/+nVr3/ze6l/9yEeP8acbr/pLDf/9l6\nKv+B22n/+Mvz/2wzsf/d63n/4sHI/0rI2v9Vm7f/7J+j/+z01/+2Y+j/44jJ/yPSGv/LomD/\nuLvz/2OQ2P/30sT/ttyf/8wn1P/Cs+j/4Ng7/7UnXf9t7uj/c9TI//fP4v/q4J3/SiLl/+e6\n7f+O5UL/qFVA/4Cw5/8vQ8j/8L+H/87tyv/iVsX/rozM/5azJP/NS1z/peLy/47e4f/SHlP/\nwMNs/9C98v+dMpn/juZp/+q+pf8qYuX/0OPu/+JiXf+UzTP/04/w/0xH2P/136n/24S7/x7C\nkf/U8un/52C2/9SjM/+Wk/P/rVLV/931sv/XkY//I4rE/5Gr5v/cYBv/iM9o//XC8/9fPpn/\n4eRz/+mtwP813uL/NKG7/+yCkf/h58j/m1Pc/9N8wv8zsR//0Ys+/5yo8v90ncv/9dLJ/73l\nmv/FH+D/vLHu/+jPN/+rNGz/dunZ/1jb1P/3u9P/39uU/1Ih0v/fsOL/eNs4/6lNKv9om+v/\nQknB/+3Llf/R9M7/6ljC/7uF3v+Yxh3/vmVq/7Pf8P+w1tr/1jVZ/8zXc//j0vj/vi6u/5bu\nfP/q08H/TG3f/8nf8f/nZFT/hLVG/9aR6v9fUsn/8eGp/+F5s/8XxJ3/eN3K/7wZbf/ArFX/\nsabv/6Q5vP+87Ij/8srC/0ud6v/Ax+T/25BK/0m0Mf/redr/oWnc//L5zP/hpK//KsDb/1ii\nxf/wrK3/wN99/4gZxP/wzOX/WuVX/6qMU/+Ulen/JlOo/+yNYf+95Kn/2jDd/6uX1v/HxCb/\n01qE/7f19P88ppv/53qp/+zntP9kPOX/4Zfp/3PlHP/UhW3/yt33/0RSzf/xzp//i8yE/68i\njv/bxOz/v+FP/8QtPP+B1vD/refr/+U0Y/+bojf/oXLm/8F3vP/R88X/4rWX/yBR2f+NvNz/\nySwg/6LZUP/ksvb/RjzN//HZmP/w2Of/Z+LB/zDOof/xjcv/7+bO/2Fb4/+9j8//gbkl/89V\nT/+r1/P/X3/P//TQuf+j5Yj/1hjN/7+l6P/a4iz/lzhT/2re5P/L5Or/31dn/6TDM//Ah+7/\n1UG1/6v0ov/Xr3//HjG4/6HA5P/eTyj/oMOC//HQ9f9TO6v/6Nt+/+640P9A5tL/m9/c/9Yk\nZv/b1l//0sL4/6curP+d6m//4sS4/0B52v/T1fb/7bhd/0fIQv/vmtb/omPH/+Hyt//iiIv/\nG5rP/2nK2f/4ytP/1d2l/3sr1//puOP/YuBB/7psKf9zjO//S3yn/+eWiv/a7sP/xk3l/4J0\n3v+7mRj/w0+N/6Pv3/9UuJ//7J/M/9nAc/8nGrL/47zw/6PoRP+yRTr/gr7q/yxMsP/spHD/\nwuW3/9xAzv+4muH/ytoi/91ffv/D9Pj/gcrj/8wYJv+uy1j/17Hy/7lLnf+f65j/8+XQ/1xn\n6P+wxeT/3GI4/1+rKf/haOz/fmHS//Xxvv/ToLX/Kcu+/7vs5P/jRJP/opNH/5OC5v/DdtH/\n4/fQ/+isn/8kg+T/hpLS/6kqAD/TFte+ZmYmP9a31z5NArU+ZmZGP7imu77BgiK/ZmYGPzKU\n1z160xi/ZmY2P6uCCD4ZjEE/zczsPhMc9D4AAAAAZmYWPzRCJ780gnO+ZmZWP7JK475wuL6+\nZmYOP4GWND8OdYM+ZmZOP7Qkl71PS9Y+ZmYuP83MrD4xphW/zczcPpivt72CNwK/ZmZeP5fu\ntj57bB4/ZmYePwC6Fz9U5Vy+zcz8Pki8SL+NbN2kZmY+P/7vIb96buS9ZmZSPxG0Rz812gy+\nZmYSP4f4Oj6tbAA/zczkPrJqTCQ5UTm/ZmYyP8EdlT62tbG+ZmYiP9QAcL7S2SQ/ZmZiP8iA\nAz++2Jc+ZmZCP2Ti+b555hS/ZmYCPzio3L77yn6+ZmYqP/G/MT+VvPo9zczUPj+1LiRiYh4/\nZmYKP+Dihz4TrDq/ZmZKP97vRb709Ae/zcz0Pqwt9D4QgBE/ZmY6P502rD7W2ka+ZmZaP9iC\nJ783S+w9ZmYaP/Y5Dr9XF0c9ZmY0P2UcKz+mlJ++zczoPl3pVD7dCJg+ZmYUPyCqLr7Z9iK/\nZmZUP5b4Uj7kNuK+ZmYEP3D2eb1hkTI/ZmZEPwl1Fz/QVCI+ZmZkP77cIb+jrOK+ZmYkP6Qk\ngb59b7i+ZmZMP1uiDj9Av8c+ZmYMP5/Gfb5XDgg/zczYPryrCD+8qwi/ZmYsPwVAYz2vVyK/\nZmYcP6fhUD6Z40I/ZmZcPyD6BT8nizu9ZmY8P27bMb9woD6+zcz4Plw9Cr+vgkG9ZmYIP88i\nND+zEUG+ZmZIPx82jD6YVxY/ZmYoP5IYjr1DBUu/zczQPk0gsD7EiPu+ZmZYP69aSr6QzDw/\nZmYYP8jw1z60Y0k+zczwPnbr+b526/m+ZmY4PxX0EL+RL4e+zczgPjzwRj8lPYs9ZmYwP9oY\nNT0vXwE/ZmZQPx4hPD7vhi+/ZmYQPxStML4Kcb2+ZmZAPyp68j4qevI+ZmYAP7sU8D5FG6i+\nZmYgP3SNOL+GzUU+ZmZgP0LcGb+rcAg+ZmYfPwgZJz/z59S+ZmZfP5Bnqj7E9rk+ZmY/Py6r\nib5ILia/zcz+Pv3k5z1s3re+ZmZPPxl27DzWPik/ZmYPP86ADj9QFpY9zczePiX5J7/14a6+\nZmYvPxo0t7567se+zczuPpjaIz8HmKo+ZmY3P6XnRL4oIBw/ZmZXP0lG9j7CeSC/ZmYXP+LA\n0rxi2Ba/ZmYnP9tZlD5TEzM/zczOPhA63z7p88W9ZmYHP8pILb+rgba9ZmZHPwwS5r73FBG+\nZmY7P4wwMT9/j/e8zcz2PhakBT4wtBY/ZmYbP4qpzD0OUkK/ZmZbPxWp9T46G+G+zczWPmmr\nvL4nNzU/ZmYrPxvP3T41M6o+ZmZLPyEHyb53xh2/ZmYLP2X36b5ah7O+ZmZjP7XANj+mfGY+\nZmYjP17nXr3qo9M+ZmYDP2f2nj6crhi/ZmZDPyXu4r1w5/++ZmYTP8Dtwz7zxRk/ZmZTP8d4\nFD8C/3W+ZmYzPwUHSL/zuws9zczmPnPI9L6654A9ZmZdP91wHj9M9aS+ZmYdP7ztvT4Uhfc+\nzcz6PjcJbb4P8ju/ZmY9P9gLWT6//wK/ZmYNP5ldA71HDDw/ZmZNP9UMrD4Ekpg9ZmYtP5yz\nDb85jLS+zczaPqGAxb7ysQC/ZmZFP7O7KT+SQ9g+ZmYFP9gmTL7VbvY+ZmYlP5P89z6hUAe/\nZmZlPyGTmjx+Rd2+zczqPqClUD6YbyU/ZmY1P1/3Ez9P15u9ZmZVP5spPL/H2ya+ZmYVP6GM\nGL9jItW8ZmYBP6zlOT/dc2q+ZmZBPysxWT5tnNA+ZmZhP5jct70vki6/ZmYhPx4AlD5uUOi+\nZmYxPwDzIL6ufzU/zcziPjKXGD8G0nw+ZmYRP8bhFb9rVwm/ZmZRPyWnvL4CSRy+ZmYZP76d\nKj9dYO48ZmZZP2wOmD1xXxA/ZmY5P5ztJD5Y/Dm/zczyPg39kr5eLg2/ZmZJPyrZEj/KjwY/\nZmYJP5QfzD4doZy+zczSPpy5LL8J11k+ZmYpP6eKEL8ohSg+ZuZKP6IXHD875+S+ZuYKP3Lr\noz5C65w+zczVPhJRmr4udhy/ZuYqP6ZjAD6/KwO/ZuYaPyygoD0zVTc/ZuZaP8j7Ij+K6yo9\nZuY6PzrpOL/CuaK+zcz1PpfMiL7s9IK+ZuYyPza+Gj9XLYg+zczlPmUyCL7NJQs/ZuYSP7MZ\n0j7+Nx2/ZuZSP/3oiL3BQxy/ZuYCPzMGrz7+dDE/ZuZCP1bm8D6Hbgy+ZuZiP2otM78M5zu9\nZuYiPxl0/b4nFPi9zczdPhLxND/ch569ZuYuP5VzMz7k6hk/ZuZOPxw/UT3Ch0e/ZuYOP/BN\nyz7lX9S+ZuY+Pwsgmr5XIy8/zcz9PiSurz6FxWo+ZuYeP/Mqy77+igq/ZuZeP8w5Cr8RuLi+\nZuYGP0AzRj9E/0E+ZuZGP23AEb3Z+wo/ZuYmP5dzlj6r9iq/zczNPmIvBr4gL+a+ZuZWP95e\n0T7RxQ4/ZuYWP6YSCj8ULoi+zcztPrGQQr+Dd6o9ZuY2P3eE277CRhk8ZuYQP3BcIT/l/3q+\nZuZQPw1jmj75a/4+ZuYwPyUvFr63wTy/zczhPgSwmz734w2/ZuZgPzub9L12p0Y/ZuYgPzCB\n+z6nvyo+ZuYAP5DmDr84YeW+ZuZAP3Oakr5EmPG+zczRPjaSEj+FRes+ZuYoP8ImI75fsZQ+\nZuZIP1kc/D5eGN2+ZuYIP6FklT3Wp/K+ZuY4PyglCz7q4S4/zczxPsiNGz+FOlm8ZuYYPxlb\nOr+CCX2+ZuZYP37tHL9VOsG9ZuYsP7UXQz+vORu+zczZPsGaOT7/o+4+ZuYMP6VsfLzJwTS/\nZuZMP1JffD6HOZ2+zcz5PjgkW757ZCE/ZuY8P3CE/j4Moos+ZuZcP0nX+r7AAw+/ZuYcP06S\n8L5n+4O+ZuZUP5dzNz8a4+E9ZuYUPwEWZjzDwiQ/zczpPl2Hgj4/Q0C/ZuY0P6bBXL4n6w2/\nZuYkPwVXAD/4VxI/ZuZkPxm1xz7xXnK+ZuZEP6FgLL/sJgk+ZuYEP94XAr88q/49ZuZDP+/Z\nGD+TQ8y+ZuYDP+jd4D4i5uo+ZuYjPyZQor45cTi/ZuZjPwmLJz4ftA+/zcznPs74ST3ul0A/\nZuYzP9ED3j5GhEI9ZuZTPzLfG78ovJm+ZuYTP8xN2L459OG+ZuZbP1n7MD85jq4+ZuYbP27w\nCr7gVu4+zcz3PlOd0z6LTRC/ZuY7Pxy8vbyz7LS+ZuYLP8Kdhz4NGxo/ZuZLP440Cj9ERge+\nZuYrP7pkO7/xLqS9zczXPvw+DL8QgyO+ZuYXP71fPj/ypEe9ZuZXP4N8xD1Ovsg+ZuY3P38I\nlT3+GSq/zczvPq2+uj7vxLK+ZuZHP295oL50tCI/ZuYHP52rAD+msLw+zczPPnCa3b6N0yW/\nZuYnP5zqvr5Y/Iu+zcz/Ph5aKT9LckU+ZuY/P3Rahb2ANBg/ZuZfP/KorD5TDy+/ZuYfPwRf\nHb7dxyC/ZuYvP3Eq4j6SPSk/zczfPgQV/T6zt16+ZuYPP7jMOb/n2EI9ZuZPPwEpIr+zq8c9\nzczzPjiQMT9F1MK+ZuY5P9Vpqj6pVNQ+ZuZZP7vkbb7xMy+/ZuYZP472Jj4mrNa+ZuYpP2Z5\ndLxhEC8/zczTPnKCFD+3Uuw9ZuYJP+TwJb8TZMm+ZuZJPyGvnr5ft8W+ZuYhP1gKGj+x8ro+\nZuZhP0kMZb69PxM/ZuZBP6nOAj84KBW/ZuYBP4tWSTzFLBC/ZuZRP1fZcz7HljM/ZuYRP9Sm\nwT5ecm69zczjPmh9Jr+zdwS+ZuYxPzWE176Uexa8ZuZlPzdQIz/nv12+ZuYlPyVIjj46rAE/\nZuYFPy6K6b2iqj2/ZuZFPygipz6ztgm/ZuYVP8pxHL75n0Q/ZuZVP4hj9T5c2j4+ZuY1P/1O\nCb8t1fC+zczrPjxAEL9sYmC+ZuY9PwE4RD8fAok8zcz7Pvqrkz0Z3O8+ZuYdP8+mCj4dQy6/\nZuZdP6z2hr6OAfa+zczbPsrdDD+uEvc+ZuYtP7k0Aj+tANG+ZuZNP7+xQb8wgIM+ZuYNP/DW\nHr9a+U8+ZiY6P8ECIT9eufy+zUz0PqFO0T7Cors+ZiYaP7zHsL7DXSW/ZiZaPzJYyD3VMO6+\nzUzUPk8SyT2wDDA/ZiYqP3TlGj/UQ6I8ZiZKPyRCNr+eWZK+ZiYKP+p6xL4JI7C+ZiZiP+ae\nKT/HM4g+ZiYiPyu/BL7J0h0/ZiYCPxPe0z71Wiq/ZiZCP1l8p72vpBK/ZiYSP5INtT75XCk/\nZiZSP0TYzT5/wga+ZiYyP2+KLL+wv7S8zUzkPl4xwb4Qf6K9ZiYmP8g3KD9yIMC9ZiZmP2C+\nMz5bRwk/ZiZGP863xjynsT2/ZiYGP/Eu0z4Nk+u+ZiZWP1ruk74uOjg/ZiYWP/jU1z6BNoY+\nzUzsPlSs3r6D3Q2/ZiY2P2fEAL8rJaC+zUz8PhgVPj9Q4R8+ZiY+P64HcrwDCuc+ZiZeP5ua\ngj4VpiK/ZiYeP8u6Kb5toQG/ZiYuP8+j5D6sqhE/zUzcPqvbED9E25q+ZiYOP3CfSL8qI+U9\nZiZOP5NNCr+8n/E8ZiYEPyxfKz+KXpK+ZiZEP6oatT4zxgo/ZiZkP5j+OL57WUa/ZiYkP4ye\nij7Bwwi/ZiY0P1gMu71560E/zUzoPiBH4z5R4wk+ZiYUPwM9Db+V5dO+ZiZUP3uXrr6wyAW/\nZiYcP9CkHz+Xgu8+ZiZcP09Gar7iI+c+ZiY8P9CEBD8cPPi+zUz4PjANRz0zXc6+ZiZMP/SU\nGz5E0CY/ZiYMP1MYEj+vPP+8zUzYPqGzNr8TsF2+ZiYsP3yADb/dfIi9ZiZYP5C6Nz+wWyu+\nZiYYP0ApED5Syag+zUzwPkEBE73DSSi/ZiY4P1hglT6qIce+ZiYIP9/zT77JYSs/ZiZIP6mv\nCz+YlI0+ZiYoP0X8Br9bHxC/zUzQPpMIyL4Cv0q+ZiZAP3a1LD/AlqY9ZiYAP/q/Aj0FrhU/\nZiYgP3FEYD7d0zi/ZiZgP/i2f76ashW/zUzgPjXWCT+zKhM/ZiYwPz7M4D4Fr5K+ZiZQPwkt\nM7+jHCc+ZiYQP5hg5r4636w9ZiZRPxlhFz+yOrO+ZiYRP9WQxj5Wd+c+zUziPoQ5ib5yTDa/\nZiYxPyruWj5ooxu/ZiYhPzLVDTwIJ0s/ZiZhP4CgBz8GI7M9ZiZBPxXgIb/xPLa+ZiYBP6dS\nvb4bsdy+ZiYpP7DiJT+wwLo+zUzSPlaiBr54bL8+ZiYJPy532D7L4QO/ZiZJPxcws7sqVAC/\nzUzyPr72fj4NWyk/ZiY5P7k+HD/7fOq9ZiZZPxdjRL/OsQG+ZiYZP3/4Er/ovE6+zUzaPurr\nRD+Newm8ZiYtPySVtT1I/PE+ZiZNPxKS6D0ZFTC/ZiYNPxl0gz6hiWG+ZiY9P06opL5VQhI/\nzUz6PiLV2j5bl7M+ZiYdPzKgv7562SG/ZiZdP4/Azb5A26i+ZiYVP3P/LD9sWXM+ZiZVP4Um\n071s3R8/ZiY1P3FPxT5zQy+/zUzqPkHQ272HdxK/ZiZlP144xD7juiU/ZiYlP+Blzj6KXRu+\nZiYFP9RJLb9B9/E7ZiZFP/1pGb9nwnE9ZiYLP0SZMD/Saam+ZiZLP1IWjD7QfcM+ZiYrP0ro\nPr6dpiq/zUzWPj5aZj4NM/6+ZiZbPx4nZL3tYTo/ZiYbPwY2IT+ZRCU+zUz2PkCEKL82jua+\nZiY7Pxlsd76Bo6y+zUzmPm/uDT/bLsI+ZiYzPwIMc76fGgY/ZiZTP9HpBT8C3gi/ZiYTPwLq\nQz3MCyC/ZiZDPzGRVz6tsUA/ZiYDPx8iAj96Ek29ZiYjP854ML+x6TS+ZiZjP7bY+r4siBm9\nZiYvP3UTLT/UnkG+zUzePnwkiD6v6g0/ZiYPPyZBm71pCkW/ZiZPP98Ioj4H3uy+zUz+Po3q\nO76nTTc/ZiY/P1sWrj6BwR0+ZiZfP9rt8b4xtey+ZiYfP0zxFL9S+Ia+ZiZHP/mOST/YunY9\nZiYHPwE2VD3TqQY/zUzOPllMNz55zDK/ZiYnPykHRb5wYs2+ZiYXP6Cf+T50PPQ+ZiZXP1Ec\n9T7PpK++ZiY3PwSJOr9hrVA+zUzuPkXN9r6ouxU+ZqYdP63/ED/Yidm+ZqZdP/We6D6O39k+\nZqY9PxsxtL7sxzG/zUz7Phh6BD5oCg6/ZqZNP3mQtj1uRT0/ZqYNP/MjzT5PMrM8zUzbPo82\nHb++Roa+ZqYtP2Jb8b4zDuK+ZqYFP68SOz+fx58+ZqZFP7ur+r0xYgY/ZqZlP71Xyz6d0Bu/\nZqYlP4/3Yb20ROq+ZqY1P+lVnz4qNB0/zUzrPjdSEj9MizG+ZqYVP5DDQr/fISq9ZqZVPyaP\nEb8wwge+ZqZBP257Pz9rsri9ZqYBPyIhAT5i19Q+ZqYhPwdyFz0eXy2/ZqZhP6S5tj76F8O+\nzUzjPmJHkL6Z7Cg/ZqYxP4sCCD8of7E+ZqZRPxCv8b65FyG/ZqYRP+HSlb4shkO+ZqZZP7HL\nIz9lxBg+ZqYZP73H97yq0w0/zUzzPvT1kz4tPC2/ZqY5PwNeNb7PeBW/ZqYJP9Sq7D66vx0/\nZqZJP8XW3D5u1V++ZqYpP7J+Mb+QNKs9zUzTPh3PIL9/dag8ZqZfPx3sOT/KSpW+ZqYfP++5\nij4NF98+zUz/PiszFr7RkjK/ZqY/P/9ASj75Mb2+ZqYPP+zPwr21kSo/ZqZPPzBaDD+bxDc+\nZqYvPwnaFr9Bx+y+zUzfPjjok76e2u2+ZqY3PwpHEz8SK+c+zUzvPqw8nb66FRM/ZqYXPww3\n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\nwH8zMzM/AADAfwAAwH/NzEw/AAAAAQ=="}]},"context":{"shiny":false,"rmarkdown":null},"vertexShader":"#line 2 1\n// File 1 is the vertex shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\n\nattribute vec3 aPos;\nattribute vec4 aCol;\nuniform mat4 mvMatrix;\nuniform mat4 prMatrix;\nvarying vec4 vCol;\nvarying vec4 vPosition;\n\n#ifdef NEEDS_VNORMAL\nattribute vec3 aNorm;\nuniform mat4 normMatrix;\nvarying vec4 vNormal;\n#endif\n\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nattribute vec2 aTexcoord;\nvarying vec2 vTexcoord;\n#endif\n\n#ifdef FIXED_SIZE\nuniform vec3 textScale;\n#endif\n\n#ifdef FIXED_QUADS\nattribute vec3 aOfs;\n#endif\n\n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\nvarying float normz;\nuniform mat4 invPrMatrix;\n#else\nattribute vec3 aPos1;\nattribute vec3 aPos2;\nvarying float normz;\n#endif\n#endif // IS_TWOSIDED\n\n#ifdef FAT_LINES\nattribute vec3 aNext;\nattribute vec2 aPoint;\nvarying vec2 vPoint;\nvarying float vLength;\nuniform float uAspect;\nuniform float uLwd;\n#endif\n\n#ifdef USE_ENVMAP\nvarying vec3 vReflection;\n#endif\n\nvoid main(void) {\n  \n#ifndef IS_BRUSH\n#if defined(NCLIPPLANES) || !defined(FIXED_QUADS) || defined(HAS_FOG) || defined(USE_ENVMAP)\n  vPosition = mvMatrix * vec4(aPos, 1.);\n#endif\n  \n#ifndef FIXED_QUADS\n  gl_Position = prMatrix * vPosition;\n#endif\n#endif // !IS_BRUSH\n  \n#ifdef IS_POINTS\n  gl_PointSize = POINTSIZE;\n#endif\n  \n  vCol = aCol;\n  \n// USE_ENVMAP implies NEEDS_VNORMAL\n\n#ifdef NEEDS_VNORMAL\n  vNormal = normMatrix * vec4(-aNorm, dot(aNorm, aPos));\n#endif\n\n#ifdef USE_ENVMAP\n  vReflection = normalize(reflect(vPosition.xyz/vPosition.w, \n                        normalize(vNormal.xyz/vNormal.w)));\n#endif\n  \n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\n  /* normz should be calculated *after* projection */\n  normz = (invPrMatrix*vNormal).z;\n#else\n  vec4 pos1 = prMatrix*(mvMatrix*vec4(aPos1, 1.));\n  pos1 = pos1/pos1.w - gl_Position/gl_Position.w;\n  vec4 pos2 = prMatrix*(mvMatrix*vec4(aPos2, 1.));\n  pos2 = pos2/pos2.w - gl_Position/gl_Position.w;\n  normz = pos1.x*pos2.y - pos1.y*pos2.x;\n#endif\n#endif // IS_TWOSIDED\n  \n#ifdef NEEDS_VNORMAL\n  vNormal = vec4(normalize(vNormal.xyz), 1);\n#endif\n  \n#if defined(HAS_TEXTURE) || defined(IS_TEXT)\n  vTexcoord = aTexcoord;\n#endif\n  \n#if defined(FIXED_SIZE) && !defined(ROTATING)\n  vec4 pos = prMatrix * mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w;\n  gl_Position = pos + vec4(aOfs*textScale, 0.);\n#endif\n  \n#if defined(IS_SPRITES) && !defined(FIXED_SIZE)\n  vec4 pos = mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w + vec4(aOfs,  0.);\n  gl_Position = prMatrix*pos;\n#endif\n  \n#ifdef FAT_LINES\n  /* This code was inspired by Matt Deslauriers' code in \n   https://mattdesl.svbtle.com/drawing-lines-is-hard */\n  vec2 aspectVec = vec2(uAspect, 1.0);\n  mat4 projViewModel = prMatrix * mvMatrix;\n  vec4 currentProjected = projViewModel * vec4(aPos, 1.0);\n  currentProjected = currentProjected/currentProjected.w;\n  vec4 nextProjected = projViewModel * vec4(aNext, 1.0);\n  vec2 currentScreen = currentProjected.xy * aspectVec;\n  vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\n  float len = uLwd;\n  vec2 dir = vec2(1.0, 0.0);\n  vPoint = aPoint;\n  vLength = length(nextScreen - currentScreen)/2.0;\n  vLength = vLength/(vLength + len);\n  if (vLength > 0.0) {\n    dir = normalize(nextScreen - currentScreen);\n  }\n  vec2 normal = vec2(-dir.y, dir.x);\n  dir.x /= uAspect;\n  normal.x /= uAspect;\n  vec4 offset = vec4(len*(normal*aPoint.x*aPoint.y - dir), 0.0, 0.0);\n  gl_Position = currentProjected + offset;\n#endif\n  \n#ifdef IS_BRUSH\n  gl_Position = vec4(aPos, 1.);\n#endif\n}","fragmentShader":"#line 2 2\n// File 2 is the fragment shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\nvarying vec4 vCol; // carries alpha\nvarying vec4 vPosition;\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nvarying vec2 vTexcoord;\nuniform sampler2D uSampler;\n#endif\n\n#ifdef HAS_FOG\nuniform int uFogMode;\nuniform vec3 uFogColor;\nuniform vec4 uFogParms;\n#endif\n\n#if defined(IS_LIT) && !defined(FIXED_QUADS)\nvarying vec4 vNormal;\n#endif\n\n#if NCLIPPLANES > 0\nuniform vec4 vClipplane[NCLIPPLANES];\n#endif\n\n#if NLIGHTS > 0\nuniform mat4 mvMatrix;\n#endif\n\n#ifdef IS_LIT\nuniform vec3 emission;\nuniform float shininess;\n#if NLIGHTS > 0\nuniform vec3 ambient[NLIGHTS];\nuniform vec3 specular[NLIGHTS]; // light*material\nuniform vec3 diffuse[NLIGHTS];\nuniform vec3 lightDir[NLIGHTS];\nuniform bool viewpoint[NLIGHTS];\nuniform bool finite[NLIGHTS];\n#endif\n#endif // IS_LIT\n\n#ifdef IS_TWOSIDED\nuniform bool front;\nvarying float normz;\n#endif\n\n#ifdef FAT_LINES\nvarying vec2 vPoint;\nvarying float vLength;\n#endif\n\n#ifdef USE_ENVMAP\nvarying vec3 vReflection;\n#endif\n\nvoid main(void) {\n  vec4 fragColor;\n#ifdef FAT_LINES\n  vec2 point = vPoint;\n  bool neg = point.y < 0.0;\n  point.y = neg ? (point.y + vLength)/(1.0 - vLength) :\n                 -(point.y - vLength)/(1.0 - vLength);\n#if defined(IS_TRANSPARENT) && defined(IS_LINESTRIP)\n  if (neg && length(point) <= 1.0) discard;\n#endif\n  point.y = min(point.y, 0.0);\n  if (length(point) > 1.0) discard;\n#endif // FAT_LINES\n  \n#ifdef ROUND_POINTS\n  vec2 coord = gl_PointCoord - vec2(0.5);\n  if (length(coord) > 0.5) discard;\n#endif\n  \n#if NCLIPPLANES > 0\n  for (int i = 0; i < NCLIPPLANES; i++)\n    if (dot(vPosition, vClipplane[i]) < 0.0) discard;\n#endif\n    \n#ifdef FIXED_QUADS\n    vec3 n = vec3(0., 0., 1.);\n#elif defined(IS_LIT)\n    vec3 n = normalize(vNormal.xyz);\n#endif\n    \n#ifdef IS_TWOSIDED\n    if ((normz <= 0.) != front) discard;\n#endif\n\n#ifdef IS_LIT\n    vec3 eye = normalize(-vPosition.xyz/vPosition.w);\n    vec3 lightdir;\n    vec4 colDiff;\n    vec3 halfVec;\n    vec4 lighteffect = vec4(emission, 0.);\n    vec3 col;\n    float nDotL;\n#ifdef FIXED_QUADS\n    n = -faceforward(n, n, eye);\n#endif\n    \n#if NLIGHTS > 0\n    // Simulate two-sided lighting\n    if (n.z < 0.0)\n      n = -n;\n    for (int i=0;i<NLIGHTS;i++) {\n      colDiff = vec4(vCol.rgb * diffuse[i], vCol.a);\n      lightdir = lightDir[i];\n      if (!viewpoint[i]) {\n        if (finite[i]) {\n          lightdir = (mvMatrix * vec4(lightdir, 1.)).xyz;\n        } else {\n          lightdir = (mvMatrix * vec4(lightdir, 0.)).xyz;\n        }\n      }\n      if (!finite[i]) {\n        halfVec = normalize(lightdir + eye);\n      } else {\n        lightdir = normalize(lightdir - vPosition.xyz/vPosition.w);\n        halfVec = normalize(lightdir + eye);\n      }\n      col = ambient[i];\n      nDotL = dot(n, lightdir);\n      col = col + max(nDotL, 0.) * colDiff.rgb;\n      col = col + pow(max(dot(halfVec, n), 0.), shininess) * specular[i];\n      lighteffect = lighteffect + vec4(col, colDiff.a);\n    }\n#endif\n    \n#else // not IS_LIT\n    vec4 colDiff = vCol;\n    vec4 lighteffect = colDiff;\n#endif\n    \n#ifdef IS_TEXT\n    vec4 textureColor = lighteffect*texture2D(uSampler, vTexcoord);\n#endif\n    \n#ifdef HAS_TEXTURE\n\n// These calculations use the definitions from \n// https://docs.gl/gl3/glTexEnv\n\n#ifdef USE_ENVMAP\n    float m = 2.0 * sqrt(dot(vReflection, vReflection) + 2.0*vReflection.z + 1.0);\n    vec4 textureColor = texture2D(uSampler, vReflection.xy / m + vec2(0.5, 0.5));\n#else\n    vec4 textureColor = texture2D(uSampler, vTexcoord);\n#endif\n\n#ifdef TEXTURE_rgb\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(textureColor.rgb, lighteffect.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = lighteffect*vec4(textureColor.rgb, 1.);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - textureColor.rgb) * lighteffect.rgb, lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(lighteffect.rgb + textureColor.rgb, lighteffect.a);\n#endif\n\n#endif //TEXTURE_rgb\n        \n#ifdef TEXTURE_rgba\n\n#ifdef TEXMODE_replace\n// already done\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = lighteffect*textureColor;\n#endif\n\n#ifdef TEXMODE_decal\n    textureColor = vec4((1. - textureColor.a)*lighteffect.rgb) +\n                     textureColor.a*textureColor.rgb, \n                     lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - textureColor.rgb) * lighteffect.rgb,\n                    lighteffect.a*textureColor.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(lighteffect.rgb + textureColor.rgb,\n                    lighteffect.a*textureColor.a);\n#endif\n    \n#endif //TEXTURE_rgba\n    \n#ifdef TEXTURE_alpha\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(lighteffect.rgb, luminance);\n#endif \n\n#if defined(TEXMODE_modulate) || defined(TEXMODE_blend) || defined(TEXMODE_add)\n    textureColor = vec4(lighteffect.rgb, lighteffect.a*luminance);\n#endif\n \n#endif // TEXTURE_alpha\n    \n// The TEXTURE_luminance values are not from that reference    \n#ifdef TEXTURE_luminance\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(luminance, luminance, luminance, lighteffect.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = vec4(luminance*lighteffect.rgb, lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - luminance)*lighteffect.rgb,\n                        lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(luminance + lighteffect.rgb, lighteffect.a);\n#endif\n\n#endif // TEXTURE_luminance\n \n    \n#ifdef TEXTURE_luminance_alpha\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(luminance, luminance, luminance, textureColor.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = vec4(luminance*lighteffect.rgb, \n                        textureColor.a*lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - luminance)*lighteffect.rgb,\n                        textureColor.a*lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(luminance + lighteffect.rgb, \n                        textureColor.a*lighteffect.a);\n\n#endif\n\n#endif // TEXTURE_luminance_alpha\n    \n    fragColor = textureColor;\n\n#elif defined(IS_TEXT)\n    if (textureColor.a < 0.1)\n      discard;\n    else\n      fragColor = textureColor;\n#else\n    fragColor = lighteffect;\n#endif // HAS_TEXTURE\n    \n#ifdef HAS_FOG\n    // uFogParms elements: x = near, y = far, z = fogscale, w = (1-sin(FOV/2))/(1+sin(FOV/2))\n    // In Exp and Exp2: use density = density/far\n    // fogF will be the proportion of fog\n    // Initialize it to the linear value\n    float fogF;\n    if (uFogMode > 0) {\n      fogF = (uFogParms.y - vPosition.z/vPosition.w)/(uFogParms.y - uFogParms.x);\n      if (uFogMode > 1)\n        fogF = mix(uFogParms.w, 1.0, fogF);\n      fogF = fogF*uFogParms.z;\n      if (uFogMode == 2)\n        fogF = 1.0 - exp(-fogF);\n      // Docs are wrong: use (density*c)^2, not density*c^2\n      // https://gitlab.freedesktop.org/mesa/mesa/-/blob/master/src/mesa/swrast/s_fog.c#L58\n      else if (uFogMode == 3)\n        fogF = 1.0 - exp(-fogF*fogF);\n      fogF = clamp(fogF, 0.0, 1.0);\n      gl_FragColor = vec4(mix(fragColor.rgb, uFogColor, fogF), fragColor.a);\n    } else gl_FragColor = fragColor;\n#else\n    gl_FragColor = fragColor;\n#endif // HAS_FOG\n    \n}","players":[],"webGLoptions":{"preserveDrawingBuffer":true}},"evals":[],"jsHooks":[]}</script>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-hsl-space-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;4: Points in the HSL space
</figcaption>
</figure>
</div>
</div>
<p>The function then proceeds by projecting these colors into the sRGB space (Figure&nbsp;5).</p>
<div class="cell page-columns page-full">
<div id="fig-rgb-space" class="cell-output-display quarto-float quarto-figure quarto-figure-center anchored no-overflow-x page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-rgb-space-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div id="rgl66578" style="width:100%;height:650px;" class="rglWebGL html-widget" aria-labelledby="rgl66578-aria"></div>
<script type="application/json" data-for="rgl66578">{"x":{"material":{"color":"#000000","alpha":1,"lit":true,"ambient":"#000000","specular":"#FFFFFF","emission":"#000000","shininess":50,"smooth":true,"front":"filled","back":"filled","size":3,"lwd":1,"fog":true,"point_antialias":false,"line_antialias":false,"texture":null,"textype":"rgb","texmode":"modulate","texmipmap":false,"texminfilter":"linear","texmagfilter":"linear","texenvmap":false,"depth_mask":true,"depth_test":"less","isTransparent":false,"polygon_offset":[0,0],"margin":"","floating":false,"tag":"","blend":["src_alpha","one_minus_src_alpha"]},"rootSubscene":1,"objects":{"19":{"id":19,"type":"points","material":{"lit":false},"vertices":"0","colors":"1","centers":"2","ignoreExtent":false,"flags":34816},"21":{"id":21,"type":"text","material":{"lit":false,"margin":0,"edge":[0,1,1]},"vertices":"3","colors":"4","texts":[["RGB"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"5","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"22":{"id":22,"type":"text","material":{"lit":false,"margin":0,"floating":true,"edge":[0,1,1]},"vertices":"6","colors":"7","texts":[["R"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"8","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"23":{"id":23,"type":"text","material":{"lit":false,"margin":1,"floating":true,"edge":[1,1,1]},"vertices":"9","colors":"10","texts":[["G"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"11","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"24":{"id":24,"type":"text","material":{"lit":false,"margin":2,"floating":true,"edge":[1,1,1]},"vertices":"12","colors":"13","texts":[["B"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"14","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"5":{"id":5,"type":"light","vertices":[[0,0,1]],"colors":[[1,1,1,1],[1,1,1,1],[1,1,1,1]],"viewpoint":true,"finite":false},"6":{"id":6,"type":"background","material":{"lit":false,"back":"lines"},"colors":"15","centers":"16","sphere":false,"fogtype":"none","fogscale":1,"flags":32768},"20":{"id":20,"type":"bboxdeco","material":{"front":"lines","back":"lines"},"vertices":"17","colors":"18","axes":{"mode":["pretty","pretty","pretty"],"step":[0.2000000029802322,0.2000000029802322,0.2000000029802322],"nticks":[5,5,5],"marklen":[15,15,15],"expand":[1.029999971389771,1.029999971389771,1.029999971389771]},"draw_front":true,"flags":32769},"1":{"id":1,"type":"subscene","par3d":{"antialias":8,"FOV":30,"ignoreExtent":false,"listeners":1,"mouseMode":{"none":"none","left":"trackball","right":"zoom","middle":"fov","wheel":"pull"},"observer":[0,0,3.777960538864136],"modelMatrix":[[1.002808213233948,0,0,-0.5355870127677917],[0,0.3417933881282806,0.937694787979126,-0.6778346300125122],[0,-0.9390696287155151,0.3412930071353912,-3.461941003799438],[0,0,0,1]],"projMatrix":[[3.732050657272339,0,0,0],[0,3.732050657272339,0,0],[0,0,-3.863703012466431,-13.61910820007324],[0,0,-1,0]],"skipRedraw":false,"userMatrix":[[1,0,0,0],[0,0.3420201433256682,0.9396926207859085,0],[0,-0.9396926207859085,0.3420201433256682,0],[0,0,0,1]],"userProjection":[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],"scale":[1.002808213233948,0.9993370175361633,0.9978739619255066],"viewport":{"x":0,"y":0,"width":1,"height":1},"zoom":1,"bbox":[0.0896475538611412,0.9785268306732178,0.08316178619861603,0.9751285910606384,0.08336060494184494,0.976635217666626],"windowRect":[0,0,256,256],"family":"sans","font":1,"cex":1,"useFreeType":true,"fontname":"NULL","maxClipPlanes":2147483647,"glVersion":"NA","activeSubscene":0},"embeddings":{"viewport":"replace","projection":"replace","model":"replace","mouse":"replace"},"objects":[6,20,19,21,22,23,24,5],"subscenes":[],"flags":36113}},"crosstalk":{"key":[],"group":[],"id":[],"options":[]},"width":480,"height":480,"buffer":{"accessors":[{"bufferView":0,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":1,"componentType":5121,"count":1000,"type":"VEC4","normalized":true},{"bufferView":2,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":3,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":4,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":5,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":7,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":8,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":10,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":11,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":12,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":13,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":14,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":15,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":16,"componentType":5121,"count":1,"type":"VEC3"},{"bufferView":17,"componentType":5126,"count":12,"type":"VEC3"},{"bufferView":18,"componentType":5121,"count":1,"type":"VEC4"}],"bufferViews":[{"buffer":0,"byteLength":12000,"byteOffset":0},{"buffer":0,"byteLength":4000,"byteOffset":12000},{"buffer":0,"byteLength":12000,"byteOffset":16000},{"buffer":0,"byteLength":12,"byteOffset":28000},{"buffer":0,"byteLength":4,"byteOffset":28012},{"buffer":0,"byteLength":12,"byteOffset":28016},{"buffer":0,"byteLength":12,"byteOffset":28028},{"buffer":0,"byteLength":4,"byteOffset":28040},{"buffer":0,"byteLength":12,"byteOffset":28044},{"buffer":0,"byteLength":12,"byteOffset":28056},{"buffer":0,"byteLength":4,"byteOffset":28068},{"buffer":0,"byteLength":12,"byteOffset":28072},{"buffer":0,"byteLength":12,"byteOffset":28084},{"buffer":0,"byteLength":4,"byteOffset":28096},{"buffer":0,"byteLength":12,"byteOffset":28100},{"buffer":0,"byteLength":4,"byteOffset":28112},{"buffer":0,"byteLength":3,"byteOffset":28116},{"buffer":0,"byteLength":144,"byteOffset":28120},{"buffer":0,"byteLength":4,"byteOffset":28264}],"buffers":[{"byteLength":28268,"bytes":"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\nAj+6v2Y/GHZDPz6r7T4uN1I/rVJkP8Trdz8JIVE/xmNpPyBVLD8HqR8//3USPqSGAz9Nb2Q/\nQoEGP0DIEj+Li1I/4Gu5/+XQpv8tLd//xYni/5TQG//IZGT/ud/y/0x4z//zwan/otCL/7gk\nuP/YzO//4+NY/80uY/+K8fH/tOXv/+g7WP+brzX/snrq/8x4vv/U9s3/5sKd/yND4f9/qtT/\ntTgf/4rSQf/loPP/TDW//+3chv/oy9r/V93H/4re1//JHmb/u7Rt/8W38P+2SMD/ve2a//Tb\n0/9dmOr/tbni/9qZPv83qy3/623L/6Zd2//q98D/35ie/yOo1f9Hvsj/8J+z/9fgb/9aFbv/\n78Dr/3DnSf+wb0D/hp/p/yp8tv/ufHL/1Oi4/7dB3/+qptr/1aor/9Zpqf/I9+v/ZNrB//jF\n4f/e0Z//PCbY/9m84f+V2UX/rUAx/3O06v84Urb/67KD/8fwvf/pRNX/onPb/6G1Gf+9UWj/\noOPt/5rI2//QJi3/tNde/+K/9v+xJYj/be5n/+fSrv82PeD/0t7z/+iGXv92t0//6Jrr/3xb\nyv/y8LP/44On/xnNxf/M7en/4ViX/8m2Mf+fivD/vE7M/8ryqP/JnJD/JWe0/6Cp6v/ljSX/\ngdR2//fS8P95Qqj/0ueC/+28wv9Fw+X/NsbM//CRrf/t79P/kWDi/9uH1/9Pwx//2H9K/6zB\n9f9wo8L/8sG//8Phkf+eH9P/qqfq/+WvLf+eNHb/bObC/6vp2P/jMp3/moA7/3hw5P+zZc//\n4PW//+aUjv8aj9v/lqXP/75jJ/92z1b/87Hv/3I2z//r8ZP/8NTc/2Hb4/82jaf/6HWB/9/t\nr/+ZNub/45nS/zTdIP/Go3b/yM30/zh91f/zrpn/oNd2/6Icsf/Mw+n/39BO/74vbv9/7uL/\nYcC+//Cwx//e3IL/URzF//PM9P+L61b/wmg//5Oz7f8vMaL/6Ltp/7fatv/WO6b/wpba/5jM\nJf/XWlz/uuP2/3nR4P/BGDj/tcZT/8uo8P+tS5//oOmQ//DbyP9Tbub/vdXr/+JeR/94vyn/\n2Xnw/35u2v/58c//36nF/zDZuf+k4tT/3CuO/97Daf/Szfn/mDC3/7rsev/mxMD/Spzc/8XP\n8f/okk7/W7dA/+uM4f+GYrT/5u2p/9p9kv8cqrv/XLLS//W5vf/E1Jr/iCjH/+Or1P893DP/\nqHUn/2Nr7P9Ec7r/66uT/9vyzP/fVuj/mHzj/8i9F//MU4P/rPLu/yzHsf/wgrf/7efF/2tQ\n4//Fg9D/ZrIh/89eRP+gx/L/c4DQ//bizP+m55v/4yC+/9K36//E40D/nUZT/37R5f/a7/D/\n4mmP/8nMP/+4mvH/2FPV/8n2tP/bqo//IlbJ/5XJ5f/eIR3/q8xv/+bF9f80Man/6cRx/+6t\n1/8z6K//jOfL/9sYlP/RsGL/v771/343nP+65W7/6qup/zCi5P/S2/H/5I9f/2LRM//yke//\ngFLR//H0r//SkaT/Jbq+/4O6yP/21Nn/2eal/4ss4P/vvOX/WudE/7B5O/+CkOn/M3nI/++f\njP/c7c7/zlvg/5OC2f+9qB7/1kWJ/6b16v9Bxr//75i5//Xz0f+EW+z/3Ibh/1/MG//FfmL/\ntczx/11k2v/34r//nd6Z/9Mkov/QtuD/rdk+/6kuNf9tx+r/wejt/+RLbf+Zo03/s4jn/8JO\ntP+x7qD/3aJy/xg7uP+kwtv/1j0p/6XXaP/tx/f/OSm3/+7Tcf/ottL/QOC9/7vm3v/cRZL/\ntJwu/4d17P/IZN3/4fnI/+Kpn/8lg9z/d4na/7dnG/9quFr/7aTh/3c8tf/b6of/8MDJ/0nP\n6P8rhK//7G1u/9XltP+mPdz/06bE/y3OK//Trmb/w8P2/12DzP/zybb/puSF/84Z0P/f1fT/\n6edg/7lQdP+d7Ov/RdHE//OixP/Sy4P/RCG1/+vG7v+Q5FH/ylEr/4Sy8f9DTKL/5rp//73s\nuf/kQb3/yJzo/7XkIf/Sc3z/zu33/4fQ1v+8IUn/ytRJ/8ep9P/GOLv/qO6P/+rc0v9fgN7/\nos3r/+Y1KP9xnTD/xWfm/29oxf/y5Lv/4Yy+/x3Qov9szrP/9cXi/+bPlP8mHN//2bDp/6Hi\nOP+ZRkH/drrk/zZj1P/zupf/3/PX/+Zk4P+tk9f/vcYl/9RWef+18vX/ndXl/+AiOP+4ynj/\n48z1/6w2kv+G6Xn/7tS0/ztO6P/L1+b/3HNW/3K8Nv/hhez/YUHR//Ppn//Tf6X/ILOi/8vz\n8v/qVIv/wLw+/62R7f+7YL7/xu+u/92dgf8cWcP/kJLe/86PIP9Y21P/97fk/3gppv/C6mT/\n4ayu/zSo2v9Eo7L/6Yyf/+rwxf+WT+f/4XXS/zjAFv/Jh07/pbTx/2+m1//41M7/xNqs/7Ux\n1f/Dven/38FH/7oseP947tf/MreX/+x8vP/m3cL/Wkzc/9Sm4v+R2y3/3XZr/87n+f9WcML/\n78ep/5Leev+/Gq//2MXz/9zrTf/AOVn/i+Tt/6vW5f/eMz//iKsm/7Vj7f/VXLX/v/W6/9e+\nmf8mMsr/f6fg/8dHGv+Gxlr/7K/x/2pSr//q45b/8c7d/1rm3P+Dyb7/qiJl/8u1Qv+qm/D/\nqS2//7Xvff/qycH/TJXg/7G66//kkDj/TKA8/+Z20v+fatL/7fbG/+iUof8av+L/TdXX//Wu\nxf/Z2or/WSHE/+7V7v+M4WP/y2s6/5Sy8P83ep3/5XBv/9Tqqv+qMOT/lZTg/9SaIf+7eqT/\nwfLi/1nPqv/0tN3/zL2c/ysowP/p1/H/t+Nl/888Ov+X0vH/Plyb/+SbdP/B6q7/4jbg/6+S\n5v/b3hv/z2mK/8P09f96xtf/tB00/7nVPP/JnPT/wDGm/5Lugv/q2Mb/Umff/7PP7f/mWjr/\nb6g4/9d46P+Je8X/9e7L/+Scv/8i3sL/sdvY/9Y3dv+gmC3/h2Xo/85W1v/Q9rb/2qWS/yNm\nyf9/hd7/wn8b/2PAXf/vrd7/izzB/9Ttj//zycz/UsLr/yiuvP/vdZL/5uq6/4dD4f/eptn/\nVNgs/9qbav/M1vj/T5LG//CspP+x33X/lhe9/8nF7//lwk//rEiB/4zo0/8/u6P/64+///Lq\nyP9nUen/yX/b/3K+HP+4bmL/rM/u/2yC3//65ND/suSm/90txP/Uwuj/yd1N/7swS/993e3/\nz+jz/+lZXf+YvUb/zZbs/81VqP+x86//5L9+/xcczP+csdb/y1oo/4nUXv/0vPX/UCes/+zp\nZv/lrcH/Nd3Z/5jm3v/hHXT/zcNx/9PI9f+OSZb/qeR7/+rAtP89huH/wsfs/+KgTf88wyz/\n8H7W/49Bzf/e8Z3/zYGJ/yGOrf9txMn/9MLQ/9/kkv9nHdn/7anp/2LnL/+lXzL/bpDo/z2T\nyv/wm5b/5e7Y/79m4P+Rjdn/xZgi/9ZQov+x9eL/jtPC/74kfv/SsFD/s630/6gw0f/J8o//\n8dPQ/12t5f+quuf/4Hkx/1aQQv/icNn/kGbL/+/zvf/kjaP/G8jW/0Wx1P/0paz/wteD/3kf\nuv/rzuT/Zt9b/8SINf+LlO7/NWu0/+uZfv/Q8Ln/2kDp/7Oh6P/i0if/z3qe/9L39P8tqaj/\n6muW/+HhtP9wPNn/3pfe/17TI//ahVv/vtH3/25vt//w3LX/id2I/8cekP/o0fT/u+pc/75H\nSP+Y0e3/tt/n/94/W/+dtCr/rnDt/9VoxP/P98b/2L6l/ypH0/9up+D/uDAV/4fHRv/hnu//\nUkGt/+jXhf/vvtf/R+bL/8Lx5//pSqH/up46/5SI6/+nVr3/ze6l/9yEeP8acbr/pLDf/9l6\nKv+B22n/+Mvz/2wzsf/d63n/4sHI/0rI2v9Vm7f/7J+j/+z01/+2Y+j/44jJ/yPSGv/LomD/\nuLvz/2OQ2P/30sT/ttyf/8wn1P/Cs+j/4Ng7/7UnXf9t7uj/c9TI//fP4v/q4J3/SiLl/+e6\n7f+O5UL/qFVA/4Cw5/8vQ8j/8L+H/87tyv/iVsX/rozM/5azJP/NS1z/peLy/47e4f/SHlP/\nwMNs/9C98v+dMpn/juZp/+q+pf8qYuX/0OPu/+JiXf+UzTP/04/w/0xH2P/136n/24S7/x7C\nkf/U8un/52C2/9SjM/+Wk/P/rVLV/931sv/XkY//I4rE/5Gr5v/cYBv/iM9o//XC8/9fPpn/\n4eRz/+mtwP813uL/NKG7/+yCkf/h58j/m1Pc/9N8wv8zsR//0Ys+/5yo8v90ncv/9dLJ/73l\nmv/FH+D/vLHu/+jPN/+rNGz/dunZ/1jb1P/3u9P/39uU/1Ih0v/fsOL/eNs4/6lNKv9om+v/\nQknB/+3Llf/R9M7/6ljC/7uF3v+Yxh3/vmVq/7Pf8P+w1tr/1jVZ/8zXc//j0vj/vi6u/5bu\nfP/q08H/TG3f/8nf8f/nZFT/hLVG/9aR6v9fUsn/8eGp/+F5s/8XxJ3/eN3K/7wZbf/ArFX/\nsabv/6Q5vP+87Ij/8srC/0ud6v/Ax+T/25BK/0m0Mf/redr/oWnc//L5zP/hpK//KsDb/1ii\nxf/wrK3/wN99/4gZxP/wzOX/WuVX/6qMU/+Ulen/JlOo/+yNYf+95Kn/2jDd/6uX1v/HxCb/\n01qE/7f19P88ppv/53qp/+zntP9kPOX/4Zfp/3PlHP/UhW3/yt33/0RSzf/xzp//i8yE/68i\njv/bxOz/v+FP/8QtPP+B1vD/refr/+U0Y/+bojf/oXLm/8F3vP/R88X/4rWX/yBR2f+NvNz/\nySwg/6LZUP/ksvb/RjzN//HZmP/w2Of/Z+LB/zDOof/xjcv/7+bO/2Fb4/+9j8//gbkl/89V\nT/+r1/P/X3/P//TQuf+j5Yj/1hjN/7+l6P/a4iz/lzhT/2re5P/L5Or/31dn/6TDM//Ah+7/\n1UG1/6v0ov/Xr3//HjG4/6HA5P/eTyj/oMOC//HQ9f9TO6v/6Nt+/+640P9A5tL/m9/c/9Yk\nZv/b1l//0sL4/6curP+d6m//4sS4/0B52v/T1fb/7bhd/0fIQv/vmtb/omPH/+Hyt//iiIv/\nG5rP/2nK2f/4ytP/1d2l/3sr1//puOP/YuBB/7psKf9zjO//S3yn/+eWiv/a7sP/xk3l/4J0\n3v+7mRj/w0+N/6Pv3/9UuJ//7J/M/9nAc/8nGrL/47zw/6PoRP+yRTr/gr7q/yxMsP/spHD/\nwuW3/9xAzv+4muH/ytoi/91ffv/D9Pj/gcrj/8wYJv+uy1j/17Hy/7lLnf+f65j/8+XQ/1xn\n6P+wxeT/3GI4/1+rKf/haOz/fmHS//Xxvv/ToLX/Kcu+/7vs5P/jRJP/opNH/5OC5v/DdtH/\n4/fQ/+isn/8kg+T/hpLS/1n1YD/ortc+Yus5P3sUZj/D9VA/UrgmPwgQNT4IEDU+y4hfP5NF\nRT/iyAk/6wNjP+eyFD/pS1E/HAfcPWS/SD/SGsg+0hrIPuV5OT+RCmA/51JzP7simT5y6fA+\nbztQP2micz8Q/UE/ZCopPwrXIj97FFE/UrgLP4XrOD8fhQ8+hes4P3mdWD+eC00/LsFvP5Ak\nZD+QJGQ/elCxPnQfTj9htTo+w6bHPif2Cj+m1nE/ptZxPyMcNT8+7WU/qrBvP4o3aT8KVW4+\nSAuxPoAdGz8NeS8/AE9VPmZmMj+Pc/Q+BZNqPzzPTD8h++8+Sqw+P5LgVD8lr3Y/px1OPyfw\nZj9mZkI/pdwdP1gjDD5ipYU+98NhP4ea/z5mZio/if9UP7xqNT8MjmE+iRD7PWZmCj9lKFM/\nz0iDPukgZj+iziA/K/5zP6iQmD4r5VQ+gpM/PwUKbj9701w/x8IGP98AaT/uy0s/ZmZaP3PJ\nrT4T6F0/hGdHPwM9Cj/Kj14/5IhXP7g+Sj+kcPQ9XC/MPq5nOz935zQ/PcraPnUIRj+Dqjc/\nSSJxP82cNj89SpA+rqdAP9pxPT+zIW4/GqsaPxROdT+P8ls/uH5TP/dkuz6/nRg/URprP2y1\nNT+WfTk/YRdjPygHWz8xgRk/khZ3Ps21XT6K9ys/C1UzPsGEaz8XkNo+lPVLP5T3Jj+l/rk+\n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\nwH8AAMB/mpkZPwAAwH8AAMB/zcxMPwAAAAE="}]},"context":{"shiny":false,"rmarkdown":null},"vertexShader":"#line 2 1\n// File 1 is the vertex shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\n\nattribute vec3 aPos;\nattribute vec4 aCol;\nuniform mat4 mvMatrix;\nuniform mat4 prMatrix;\nvarying vec4 vCol;\nvarying vec4 vPosition;\n\n#ifdef NEEDS_VNORMAL\nattribute vec3 aNorm;\nuniform mat4 normMatrix;\nvarying vec4 vNormal;\n#endif\n\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nattribute vec2 aTexcoord;\nvarying vec2 vTexcoord;\n#endif\n\n#ifdef FIXED_SIZE\nuniform vec3 textScale;\n#endif\n\n#ifdef FIXED_QUADS\nattribute vec3 aOfs;\n#endif\n\n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\nvarying float normz;\nuniform mat4 invPrMatrix;\n#else\nattribute vec3 aPos1;\nattribute vec3 aPos2;\nvarying float normz;\n#endif\n#endif // IS_TWOSIDED\n\n#ifdef FAT_LINES\nattribute vec3 aNext;\nattribute vec2 aPoint;\nvarying vec2 vPoint;\nvarying float vLength;\nuniform float uAspect;\nuniform float uLwd;\n#endif\n\n#ifdef USE_ENVMAP\nvarying vec3 vReflection;\n#endif\n\nvoid main(void) {\n  \n#ifndef IS_BRUSH\n#if defined(NCLIPPLANES) || !defined(FIXED_QUADS) || defined(HAS_FOG) || defined(USE_ENVMAP)\n  vPosition = mvMatrix * vec4(aPos, 1.);\n#endif\n  \n#ifndef FIXED_QUADS\n  gl_Position = prMatrix * vPosition;\n#endif\n#endif // !IS_BRUSH\n  \n#ifdef IS_POINTS\n  gl_PointSize = POINTSIZE;\n#endif\n  \n  vCol = aCol;\n  \n// USE_ENVMAP implies NEEDS_VNORMAL\n\n#ifdef NEEDS_VNORMAL\n  vNormal = normMatrix * vec4(-aNorm, dot(aNorm, aPos));\n#endif\n\n#ifdef USE_ENVMAP\n  vReflection = normalize(reflect(vPosition.xyz/vPosition.w, \n                        normalize(vNormal.xyz/vNormal.w)));\n#endif\n  \n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\n  /* normz should be calculated *after* projection */\n  normz = (invPrMatrix*vNormal).z;\n#else\n  vec4 pos1 = prMatrix*(mvMatrix*vec4(aPos1, 1.));\n  pos1 = pos1/pos1.w - gl_Position/gl_Position.w;\n  vec4 pos2 = prMatrix*(mvMatrix*vec4(aPos2, 1.));\n  pos2 = pos2/pos2.w - gl_Position/gl_Position.w;\n  normz = pos1.x*pos2.y - pos1.y*pos2.x;\n#endif\n#endif // IS_TWOSIDED\n  \n#ifdef NEEDS_VNORMAL\n  vNormal = vec4(normalize(vNormal.xyz), 1);\n#endif\n  \n#if defined(HAS_TEXTURE) || defined(IS_TEXT)\n  vTexcoord = aTexcoord;\n#endif\n  \n#if defined(FIXED_SIZE) && !defined(ROTATING)\n  vec4 pos = prMatrix * mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w;\n  gl_Position = pos + vec4(aOfs*textScale, 0.);\n#endif\n  \n#if defined(IS_SPRITES) && !defined(FIXED_SIZE)\n  vec4 pos = mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w + vec4(aOfs,  0.);\n  gl_Position = prMatrix*pos;\n#endif\n  \n#ifdef FAT_LINES\n  /* This code was inspired by Matt Deslauriers' code in \n   https://mattdesl.svbtle.com/drawing-lines-is-hard */\n  vec2 aspectVec = vec2(uAspect, 1.0);\n  mat4 projViewModel = prMatrix * mvMatrix;\n  vec4 currentProjected = projViewModel * vec4(aPos, 1.0);\n  currentProjected = currentProjected/currentProjected.w;\n  vec4 nextProjected = projViewModel * vec4(aNext, 1.0);\n  vec2 currentScreen = currentProjected.xy * aspectVec;\n  vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\n  float len = uLwd;\n  vec2 dir = vec2(1.0, 0.0);\n  vPoint = aPoint;\n  vLength = length(nextScreen - currentScreen)/2.0;\n  vLength = vLength/(vLength + len);\n  if (vLength > 0.0) {\n    dir = normalize(nextScreen - currentScreen);\n  }\n  vec2 normal = vec2(-dir.y, dir.x);\n  dir.x /= uAspect;\n  normal.x /= uAspect;\n  vec4 offset = vec4(len*(normal*aPoint.x*aPoint.y - dir), 0.0, 0.0);\n  gl_Position = currentProjected + offset;\n#endif\n  \n#ifdef IS_BRUSH\n  gl_Position = vec4(aPos, 1.);\n#endif\n}","fragmentShader":"#line 2 2\n// File 2 is the fragment shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\nvarying vec4 vCol; // carries alpha\nvarying vec4 vPosition;\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nvarying vec2 vTexcoord;\nuniform sampler2D uSampler;\n#endif\n\n#ifdef HAS_FOG\nuniform int uFogMode;\nuniform vec3 uFogColor;\nuniform vec4 uFogParms;\n#endif\n\n#if defined(IS_LIT) && !defined(FIXED_QUADS)\nvarying vec4 vNormal;\n#endif\n\n#if NCLIPPLANES > 0\nuniform vec4 vClipplane[NCLIPPLANES];\n#endif\n\n#if NLIGHTS > 0\nuniform mat4 mvMatrix;\n#endif\n\n#ifdef IS_LIT\nuniform vec3 emission;\nuniform float shininess;\n#if NLIGHTS > 0\nuniform vec3 ambient[NLIGHTS];\nuniform vec3 specular[NLIGHTS]; // light*material\nuniform vec3 diffuse[NLIGHTS];\nuniform vec3 lightDir[NLIGHTS];\nuniform bool viewpoint[NLIGHTS];\nuniform bool finite[NLIGHTS];\n#endif\n#endif // IS_LIT\n\n#ifdef IS_TWOSIDED\nuniform bool front;\nvarying float normz;\n#endif\n\n#ifdef FAT_LINES\nvarying vec2 vPoint;\nvarying float vLength;\n#endif\n\n#ifdef USE_ENVMAP\nvarying vec3 vReflection;\n#endif\n\nvoid main(void) {\n  vec4 fragColor;\n#ifdef FAT_LINES\n  vec2 point = vPoint;\n  bool neg = point.y < 0.0;\n  point.y = neg ? (point.y + vLength)/(1.0 - vLength) :\n                 -(point.y - vLength)/(1.0 - vLength);\n#if defined(IS_TRANSPARENT) && defined(IS_LINESTRIP)\n  if (neg && length(point) <= 1.0) discard;\n#endif\n  point.y = min(point.y, 0.0);\n  if (length(point) > 1.0) discard;\n#endif // FAT_LINES\n  \n#ifdef ROUND_POINTS\n  vec2 coord = gl_PointCoord - vec2(0.5);\n  if (length(coord) > 0.5) discard;\n#endif\n  \n#if NCLIPPLANES > 0\n  for (int i = 0; i < NCLIPPLANES; i++)\n    if (dot(vPosition, vClipplane[i]) < 0.0) discard;\n#endif\n    \n#ifdef FIXED_QUADS\n    vec3 n = vec3(0., 0., 1.);\n#elif defined(IS_LIT)\n    vec3 n = normalize(vNormal.xyz);\n#endif\n    \n#ifdef IS_TWOSIDED\n    if ((normz <= 0.) != front) discard;\n#endif\n\n#ifdef IS_LIT\n    vec3 eye = normalize(-vPosition.xyz/vPosition.w);\n    vec3 lightdir;\n    vec4 colDiff;\n    vec3 halfVec;\n    vec4 lighteffect = vec4(emission, 0.);\n    vec3 col;\n    float nDotL;\n#ifdef FIXED_QUADS\n    n = -faceforward(n, n, eye);\n#endif\n    \n#if NLIGHTS > 0\n    // Simulate two-sided lighting\n    if (n.z < 0.0)\n      n = -n;\n    for (int i=0;i<NLIGHTS;i++) {\n      colDiff = vec4(vCol.rgb * diffuse[i], vCol.a);\n      lightdir = lightDir[i];\n      if (!viewpoint[i]) {\n        if (finite[i]) {\n          lightdir = (mvMatrix * vec4(lightdir, 1.)).xyz;\n        } else {\n          lightdir = (mvMatrix * vec4(lightdir, 0.)).xyz;\n        }\n      }\n      if (!finite[i]) {\n        halfVec = normalize(lightdir + eye);\n      } else {\n        lightdir = normalize(lightdir - vPosition.xyz/vPosition.w);\n        halfVec = normalize(lightdir + eye);\n      }\n      col = ambient[i];\n      nDotL = dot(n, lightdir);\n      col = col + max(nDotL, 0.) * colDiff.rgb;\n      col = col + pow(max(dot(halfVec, n), 0.), shininess) * specular[i];\n      lighteffect = lighteffect + vec4(col, colDiff.a);\n    }\n#endif\n    \n#else // not IS_LIT\n    vec4 colDiff = vCol;\n    vec4 lighteffect = colDiff;\n#endif\n    \n#ifdef IS_TEXT\n    vec4 textureColor = lighteffect*texture2D(uSampler, vTexcoord);\n#endif\n    \n#ifdef HAS_TEXTURE\n\n// These calculations use the definitions from \n// https://docs.gl/gl3/glTexEnv\n\n#ifdef USE_ENVMAP\n    float m = 2.0 * sqrt(dot(vReflection, vReflection) + 2.0*vReflection.z + 1.0);\n    vec4 textureColor = texture2D(uSampler, vReflection.xy / m + vec2(0.5, 0.5));\n#else\n    vec4 textureColor = texture2D(uSampler, vTexcoord);\n#endif\n\n#ifdef TEXTURE_rgb\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(textureColor.rgb, lighteffect.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = lighteffect*vec4(textureColor.rgb, 1.);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - textureColor.rgb) * lighteffect.rgb, lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(lighteffect.rgb + textureColor.rgb, lighteffect.a);\n#endif\n\n#endif //TEXTURE_rgb\n        \n#ifdef TEXTURE_rgba\n\n#ifdef TEXMODE_replace\n// already done\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = lighteffect*textureColor;\n#endif\n\n#ifdef TEXMODE_decal\n    textureColor = vec4((1. - textureColor.a)*lighteffect.rgb) +\n                     textureColor.a*textureColor.rgb, \n                     lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - textureColor.rgb) * lighteffect.rgb,\n                    lighteffect.a*textureColor.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(lighteffect.rgb + textureColor.rgb,\n                    lighteffect.a*textureColor.a);\n#endif\n    \n#endif //TEXTURE_rgba\n    \n#ifdef TEXTURE_alpha\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(lighteffect.rgb, luminance);\n#endif \n\n#if defined(TEXMODE_modulate) || defined(TEXMODE_blend) || defined(TEXMODE_add)\n    textureColor = vec4(lighteffect.rgb, lighteffect.a*luminance);\n#endif\n \n#endif // TEXTURE_alpha\n    \n// The TEXTURE_luminance values are not from that reference    \n#ifdef TEXTURE_luminance\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(luminance, luminance, luminance, lighteffect.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = vec4(luminance*lighteffect.rgb, lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - luminance)*lighteffect.rgb,\n                        lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(luminance + lighteffect.rgb, lighteffect.a);\n#endif\n\n#endif // TEXTURE_luminance\n \n    \n#ifdef TEXTURE_luminance_alpha\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(luminance, luminance, luminance, textureColor.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = vec4(luminance*lighteffect.rgb, \n                        textureColor.a*lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - luminance)*lighteffect.rgb,\n                        textureColor.a*lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(luminance + lighteffect.rgb, \n                        textureColor.a*lighteffect.a);\n\n#endif\n\n#endif // TEXTURE_luminance_alpha\n    \n    fragColor = textureColor;\n\n#elif defined(IS_TEXT)\n    if (textureColor.a < 0.1)\n      discard;\n    else\n      fragColor = textureColor;\n#else\n    fragColor = lighteffect;\n#endif // HAS_TEXTURE\n    \n#ifdef HAS_FOG\n    // uFogParms elements: x = near, y = far, z = fogscale, w = (1-sin(FOV/2))/(1+sin(FOV/2))\n    // In Exp and Exp2: use density = density/far\n    // fogF will be the proportion of fog\n    // Initialize it to the linear value\n    float fogF;\n    if (uFogMode > 0) {\n      fogF = (uFogParms.y - vPosition.z/vPosition.w)/(uFogParms.y - uFogParms.x);\n      if (uFogMode > 1)\n        fogF = mix(uFogParms.w, 1.0, fogF);\n      fogF = fogF*uFogParms.z;\n      if (uFogMode == 2)\n        fogF = 1.0 - exp(-fogF);\n      // Docs are wrong: use (density*c)^2, not density*c^2\n      // https://gitlab.freedesktop.org/mesa/mesa/-/blob/master/src/mesa/swrast/s_fog.c#L58\n      else if (uFogMode == 3)\n        fogF = 1.0 - exp(-fogF*fogF);\n      fogF = clamp(fogF, 0.0, 1.0);\n      gl_FragColor = vec4(mix(fragColor.rgb, uFogColor, fogF), fragColor.a);\n    } else gl_FragColor = fragColor;\n#else\n    gl_FragColor = fragColor;\n#endif // HAS_FOG\n    \n}","players":[],"webGLoptions":{"preserveDrawingBuffer":true}},"evals":[],"jsHooks":[]}</script>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-rgb-space-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;5: The colors in the RGB space
</figcaption>
</figure>
</div>
</div>
<p>It then continues by projecting the colors, first into the XYZ space, then CIELab (not shown here), and then finally the DIN99d space (Figure&nbsp;6).</p>
<div class="cell page-columns page-full">
<div id="fig-din99d-space" class="cell-output-display quarto-float quarto-figure quarto-figure-center anchored no-overflow-x page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-din99d-space-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div id="rgl33817" style="width:100%;height:650px;" class="rglWebGL html-widget" aria-labelledby="rgl33817-aria"></div>
<script type="application/json" data-for="rgl33817">{"x":{"material":{"color":"#000000","alpha":1,"lit":true,"ambient":"#000000","specular":"#FFFFFF","emission":"#000000","shininess":50,"smooth":true,"front":"filled","back":"filled","size":3,"lwd":1,"fog":true,"point_antialias":false,"line_antialias":false,"texture":null,"textype":"rgb","texmode":"modulate","texmipmap":false,"texminfilter":"linear","texmagfilter":"linear","texenvmap":false,"depth_mask":true,"depth_test":"less","isTransparent":false,"polygon_offset":[0,0],"margin":"","floating":false,"tag":"","blend":["src_alpha","one_minus_src_alpha"]},"rootSubscene":1,"objects":{"31":{"id":31,"type":"points","material":{"lit":false},"vertices":"0","colors":"1","centers":"2","ignoreExtent":false,"flags":34816},"33":{"id":33,"type":"text","material":{"lit":false,"margin":0,"edge":[0,1,1]},"vertices":"3","colors":"4","texts":[["DIN99d"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"5","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"34":{"id":34,"type":"text","material":{"lit":false,"margin":0,"floating":true,"edge":[0,1,1]},"vertices":"6","colors":"7","texts":[["L99d"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"8","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"35":{"id":35,"type":"text","material":{"lit":false,"margin":1,"floating":true,"edge":[1,1,1]},"vertices":"9","colors":"10","texts":[["a99d"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"11","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"36":{"id":36,"type":"text","material":{"lit":false,"margin":2,"floating":true,"edge":[1,1,1]},"vertices":"12","colors":"13","texts":[["b99d"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"14","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"5":{"id":5,"type":"light","vertices":[[0,0,1]],"colors":[[1,1,1,1],[1,1,1,1],[1,1,1,1]],"viewpoint":true,"finite":false},"6":{"id":6,"type":"background","material":{"lit":false,"back":"lines"},"colors":"15","centers":"16","sphere":false,"fogtype":"none","fogscale":1,"flags":32768},"32":{"id":32,"type":"bboxdeco","material":{"front":"lines","back":"lines"},"vertices":"17","colors":"18","axes":{"mode":["pretty","pretty","pretty"],"step":[10,20,20],"nticks":[5,5,5],"marklen":[15,15,15],"expand":[1.029999971389771,1.029999971389771,1.029999971389771]},"draw_front":true,"flags":32769},"1":{"id":1,"type":"subscene","par3d":{"antialias":8,"FOV":30,"ignoreExtent":false,"listeners":1,"mouseMode":{"none":"none","left":"trackball","right":"zoom","middle":"fov","wheel":"pull"},"observer":[0,0,325.2228393554688],"modelMatrix":[[1.115169644355774,0,0,-69.56121826171875],[0,0.3409429788589478,0.8615754246711731,-0.06915782392024994],[0,-0.9367331266403198,0.3135878145694733,-322.4158325195312],[0,0,0,1]],"projMatrix":[[3.732050657272339,0,0,0],[0,3.732050657272339,0,0],[0,0,-3.863702774047852,-1172.390625],[0,0,-1,0]],"skipRedraw":false,"userMatrix":[[1,0,0,0],[0,0.3420201433256682,0.9396926207859085,0],[0,-0.9396926207859085,0.3420201433256682,0],[0,0,0,1]],"userProjection":[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],"scale":[1.115169644355774,0.9968505501747131,0.9168694019317627],"viewport":{"x":0,"y":0,"width":1,"height":1},"zoom":1,"bbox":[27.97290229797363,96.78160095214844,-35.81810760498047,41.15769577026367,-42.82153701782227,40.86908721923828],"windowRect":[0,0,256,256],"family":"sans","font":1,"cex":1,"useFreeType":true,"fontname":"NULL","maxClipPlanes":2147483647,"glVersion":"NA","activeSubscene":0},"embeddings":{"viewport":"replace","projection":"replace","model":"replace","mouse":"replace"},"objects":[6,32,31,33,34,35,36,5],"subscenes":[],"flags":36113}},"crosstalk":{"key":[],"group":[],"id":[],"options":[]},"width":480,"height":480,"buffer":{"accessors":[{"bufferView":0,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":1,"componentType":5121,"count":1000,"type":"VEC4","normalized":true},{"bufferView":2,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":3,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":4,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":5,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":7,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":8,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":10,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":11,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":12,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":13,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":14,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":15,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":16,"componentType":5121,"count":1,"type":"VEC3"},{"bufferView":17,"componentType":5126,"count":16,"type":"VEC3"},{"bufferView":18,"componentType":5121,"count":1,"type":"VEC4"}],"bufferViews":[{"buffer":0,"byteLength":12000,"byteOffset":0},{"buffer":0,"byteLength":4000,"byteOffset":12000},{"buffer":0,"byteLength":12000,"byteOffset":16000},{"buffer":0,"byteLength":12,"byteOffset":28000},{"buffer":0,"byteLength":4,"byteOffset":28012},{"buffer":0,"byteLength":12,"byteOffset":28016},{"buffer":0,"byteLength":12,"byteOffset":28028},{"buffer":0,"byteLength":4,"byteOffset":28040},{"buffer":0,"byteLength":12,"byteOffset":28044},{"buffer":0,"byteLength":12,"byteOffset":28056},{"buffer":0,"byteLength":4,"byteOffset":28068},{"buffer":0,"byteLength":12,"byteOffset":28072},{"buffer":0,"byteLength":12,"byteOffset":28084},{"buffer":0,"byteLength":4,"byteOffset":28096},{"buffer":0,"byteLength":12,"byteOffset":28100},{"buffer":0,"byteLength":4,"byteOffset":28112},{"buffer":0,"byteLength":3,"byteOffset":28116},{"buffer":0,"byteLength":192,"byteOffset":28120},{"buffer":0,"byteLength":4,"byteOffset":28312}],"buffers":[{"byteLength":28316,"bytes":"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\nCEFe8vrBBxmBQgn2v0FSGrnBJDi/QgHbQcGr3HZBGW+cQsi1oUExUTJBi/9nQlBRSsE6cwLC\nqOuCQiU2z7/mz83B4Gu5/+XQpv8tLd//xYni/5TQG//IZGT/ud/y/0x4z//zwan/otCL/7gk\nuP/YzO//4+NY/80uY/+K8fH/tOXv/+g7WP+brzX/snrq/8x4vv/U9s3/5sKd/yND4f9/qtT/\ntTgf/4rSQf/loPP/TDW//+3chv/oy9r/V93H/4re1//JHmb/u7Rt/8W38P+2SMD/ve2a//Tb\n0/9dmOr/tbni/9qZPv83qy3/623L/6Zd2//q98D/35ie/yOo1f9Hvsj/8J+z/9fgb/9aFbv/\n78Dr/3DnSf+wb0D/hp/p/yp8tv/ufHL/1Oi4/7dB3/+qptr/1aor/9Zpqf/I9+v/ZNrB//jF\n4f/e0Z//PCbY/9m84f+V2UX/rUAx/3O06v84Urb/67KD/8fwvf/pRNX/onPb/6G1Gf+9UWj/\noOPt/5rI2//QJi3/tNde/+K/9v+xJYj/be5n/+fSrv82PeD/0t7z/+iGXv92t0//6Jrr/3xb\nyv/y8LP/44On/xnNxf/M7en/4ViX/8m2Mf+fivD/vE7M/8ryqP/JnJD/JWe0/6Cp6v/ljSX/\ngdR2//fS8P95Qqj/0ueC/+28wv9Fw+X/NsbM//CRrf/t79P/kWDi/9uH1/9Pwx//2H9K/6zB\n9f9wo8L/8sG//8Phkf+eH9P/qqfq/+WvLf+eNHb/bObC/6vp2P/jMp3/moA7/3hw5P+zZc//\n4PW//+aUjv8aj9v/lqXP/75jJ/92z1b/87Hv/3I2z//r8ZP/8NTc/2Hb4/82jaf/6HWB/9/t\nr/+ZNub/45nS/zTdIP/Go3b/yM30/zh91f/zrpn/oNd2/6Icsf/Mw+n/39BO/74vbv9/7uL/\nYcC+//Cwx//e3IL/URzF//PM9P+L61b/wmg//5Oz7f8vMaL/6Ltp/7fatv/WO6b/wpba/5jM\nJf/XWlz/uuP2/3nR4P/BGDj/tcZT/8uo8P+tS5//oOmQ//DbyP9Tbub/vdXr/+JeR/94vyn/\n2Xnw/35u2v/58c//36nF/zDZuf+k4tT/3CuO/97Daf/Szfn/mDC3/7rsev/mxMD/Spzc/8XP\n8f/okk7/W7dA/+uM4f+GYrT/5u2p/9p9kv8cqrv/XLLS//W5vf/E1Jr/iCjH/+Or1P893DP/\nqHUn/2Nr7P9Ec7r/66uT/9vyzP/fVuj/mHzj/8i9F//MU4P/rPLu/yzHsf/wgrf/7efF/2tQ\n4//Fg9D/ZrIh/89eRP+gx/L/c4DQ//bizP+m55v/4yC+/9K36//E40D/nUZT/37R5f/a7/D/\n4mmP/8nMP/+4mvH/2FPV/8n2tP/bqo//IlbJ/5XJ5f/eIR3/q8xv/+bF9f80Man/6cRx/+6t\n1/8z6K//jOfL/9sYlP/RsGL/v771/343nP+65W7/6qup/zCi5P/S2/H/5I9f/2LRM//yke//\ngFLR//H0r//SkaT/Jbq+/4O6yP/21Nn/2eal/4ss4P/vvOX/WudE/7B5O/+CkOn/M3nI/++f\njP/c7c7/zlvg/5OC2f+9qB7/1kWJ/6b16v9Bxr//75i5//Xz0f+EW+z/3Ibh/1/MG//FfmL/\ntczx/11k2v/34r//nd6Z/9Mkov/QtuD/rdk+/6kuNf9tx+r/wejt/+RLbf+Zo03/s4jn/8JO\ntP+x7qD/3aJy/xg7uP+kwtv/1j0p/6XXaP/tx/f/OSm3/+7Tcf/ottL/QOC9/7vm3v/cRZL/\ntJwu/4d17P/IZN3/4fnI/+Kpn/8lg9z/d4na/7dnG/9quFr/7aTh/3c8tf/b6of/8MDJ/0nP\n6P8rhK//7G1u/9XltP+mPdz/06bE/y3OK//Trmb/w8P2/12DzP/zybb/puSF/84Z0P/f1fT/\n6edg/7lQdP+d7Ov/RdHE//OixP/Sy4P/RCG1/+vG7v+Q5FH/ylEr/4Sy8f9DTKL/5rp//73s\nuf/kQb3/yJzo/7XkIf/Sc3z/zu33/4fQ1v+8IUn/ytRJ/8ep9P/GOLv/qO6P/+rc0v9fgN7/\nos3r/+Y1KP9xnTD/xWfm/29oxf/y5Lv/4Yy+/x3Qov9szrP/9cXi/+bPlP8mHN//2bDp/6Hi\nOP+ZRkH/drrk/zZj1P/zupf/3/PX/+Zk4P+tk9f/vcYl/9RWef+18vX/ndXl/+AiOP+4ynj/\n48z1/6w2kv+G6Xn/7tS0/ztO6P/L1+b/3HNW/3K8Nv/hhez/YUHR//Ppn//Tf6X/ILOi/8vz\n8v/qVIv/wLw+/62R7f+7YL7/xu+u/92dgf8cWcP/kJLe/86PIP9Y21P/97fk/3gppv/C6mT/\n4ayu/zSo2v9Eo7L/6Yyf/+rwxf+WT+f/4XXS/zjAFv/Jh07/pbTx/2+m1//41M7/xNqs/7Ux\n1f/Dven/38FH/7oseP947tf/MreX/+x8vP/m3cL/Wkzc/9Sm4v+R2y3/3XZr/87n+f9WcML/\n78ep/5Leev+/Gq//2MXz/9zrTf/AOVn/i+Tt/6vW5f/eMz//iKsm/7Vj7f/VXLX/v/W6/9e+\nmf8mMsr/f6fg/8dHGv+Gxlr/7K/x/2pSr//q45b/8c7d/1rm3P+Dyb7/qiJl/8u1Qv+qm/D/\nqS2//7Xvff/qycH/TJXg/7G66//kkDj/TKA8/+Z20v+fatL/7fbG/+iUof8av+L/TdXX//Wu\nxf/Z2or/WSHE/+7V7v+M4WP/y2s6/5Sy8P83ep3/5XBv/9Tqqv+qMOT/lZTg/9SaIf+7eqT/\nwfLi/1nPqv/0tN3/zL2c/ysowP/p1/H/t+Nl/888Ov+X0vH/Plyb/+SbdP/B6q7/4jbg/6+S\n5v/b3hv/z2mK/8P09f96xtf/tB00/7nVPP/JnPT/wDGm/5Lugv/q2Mb/Umff/7PP7f/mWjr/\nb6g4/9d46P+Je8X/9e7L/+Scv/8i3sL/sdvY/9Y3dv+gmC3/h2Xo/85W1v/Q9rb/2qWS/yNm\nyf9/hd7/wn8b/2PAXf/vrd7/izzB/9Ttj//zycz/UsLr/yiuvP/vdZL/5uq6/4dD4f/eptn/\nVNgs/9qbav/M1vj/T5LG//CspP+x33X/lhe9/8nF7//lwk//rEiB/4zo0/8/u6P/64+///Lq\nyP9nUen/yX/b/3K+HP+4bmL/rM/u/2yC3//65ND/suSm/90txP/Uwuj/yd1N/7swS/993e3/\nz+jz/+lZXf+YvUb/zZbs/81VqP+x86//5L9+/xcczP+csdb/y1oo/4nUXv/0vPX/UCes/+zp\nZv/lrcH/Nd3Z/5jm3v/hHXT/zcNx/9PI9f+OSZb/qeR7/+rAtP89huH/wsfs/+KgTf88wyz/\n8H7W/49Bzf/e8Z3/zYGJ/yGOrf9txMn/9MLQ/9/kkv9nHdn/7anp/2LnL/+lXzL/bpDo/z2T\nyv/wm5b/5e7Y/79m4P+Rjdn/xZgi/9ZQov+x9eL/jtPC/74kfv/SsFD/s630/6gw0f/J8o//\n8dPQ/12t5f+quuf/4Hkx/1aQQv/icNn/kGbL/+/zvf/kjaP/G8jW/0Wx1P/0paz/wteD/3kf\nuv/rzuT/Zt9b/8SINf+LlO7/NWu0/+uZfv/Q8Ln/2kDp/7Oh6P/i0if/z3qe/9L39P8tqaj/\n6muW/+HhtP9wPNn/3pfe/17TI//ahVv/vtH3/25vt//w3LX/id2I/8cekP/o0fT/u+pc/75H\nSP+Y0e3/tt/n/94/W/+dtCr/rnDt/9VoxP/P98b/2L6l/ypH0/9up+D/uDAV/4fHRv/hnu//\nUkGt/+jXhf/vvtf/R+bL/8Lx5//pSqH/up46/5SI6/+nVr3/ze6l/9yEeP8acbr/pLDf/9l6\nKv+B22n/+Mvz/2wzsf/d63n/4sHI/0rI2v9Vm7f/7J+j/+z01/+2Y+j/44jJ/yPSGv/LomD/\nuLvz/2OQ2P/30sT/ttyf/8wn1P/Cs+j/4Ng7/7UnXf9t7uj/c9TI//fP4v/q4J3/SiLl/+e6\n7f+O5UL/qFVA/4Cw5/8vQ8j/8L+H/87tyv/iVsX/rozM/5azJP/NS1z/peLy/47e4f/SHlP/\nwMNs/9C98v+dMpn/juZp/+q+pf8qYuX/0OPu/+JiXf+UzTP/04/w/0xH2P/136n/24S7/x7C\nkf/U8un/52C2/9SjM/+Wk/P/rVLV/931sv/XkY//I4rE/5Gr5v/cYBv/iM9o//XC8/9fPpn/\n4eRz/+mtwP813uL/NKG7/+yCkf/h58j/m1Pc/9N8wv8zsR//0Ys+/5yo8v90ncv/9dLJ/73l\nmv/FH+D/vLHu/+jPN/+rNGz/dunZ/1jb1P/3u9P/39uU/1Ih0v/fsOL/eNs4/6lNKv9om+v/\nQknB/+3Llf/R9M7/6ljC/7uF3v+Yxh3/vmVq/7Pf8P+w1tr/1jVZ/8zXc//j0vj/vi6u/5bu\nfP/q08H/TG3f/8nf8f/nZFT/hLVG/9aR6v9fUsn/8eGp/+F5s/8XxJ3/eN3K/7wZbf/ArFX/\nsabv/6Q5vP+87Ij/8srC/0ud6v/Ax+T/25BK/0m0Mf/redr/oWnc//L5zP/hpK//KsDb/1ii\nxf/wrK3/wN99/4gZxP/wzOX/WuVX/6qMU/+Ulen/JlOo/+yNYf+95Kn/2jDd/6uX1v/HxCb/\n01qE/7f19P88ppv/53qp/+zntP9kPOX/4Zfp/3PlHP/UhW3/yt33/0RSzf/xzp//i8yE/68i\njv/bxOz/v+FP/8QtPP+B1vD/refr/+U0Y/+bojf/oXLm/8F3vP/R88X/4rWX/yBR2f+NvNz/\nySwg/6LZUP/ksvb/RjzN//HZmP/w2Of/Z+LB/zDOof/xjcv/7+bO/2Fb4/+9j8//gbkl/89V\nT/+r1/P/X3/P//TQuf+j5Yj/1hjN/7+l6P/a4iz/lzhT/2re5P/L5Or/31dn/6TDM//Ah+7/\n1UG1/6v0ov/Xr3//HjG4/6HA5P/eTyj/oMOC//HQ9f9TO6v/6Nt+/+640P9A5tL/m9/c/9Yk\nZv/b1l//0sL4/6curP+d6m//4sS4/0B52v/T1fb/7bhd/0fIQv/vmtb/omPH/+Hyt//iiIv/\nG5rP/2nK2f/4ytP/1d2l/3sr1//puOP/YuBB/7psKf9zjO//S3yn/+eWiv/a7sP/xk3l/4J0\n3v+7mRj/w0+N/6Pv3/9UuJ//7J/M/9nAc/8nGrL/47zw/6PoRP+yRTr/gr7q/yxMsP/spHD/\nwuW3/9xAzv+4muH/ytoi/91ffv/D9Pj/gcrj/8wYJv+uy1j/17Hy/7lLnf+f65j/8+XQ/1xn\n6P+wxeT/3GI4/1+rKf/haOz/fmHS//Xxvv/ToLX/Kcu+/7vs5P/jRJP/opNH/5OC5v/DdtH/\n4/fQ/+isn/8kg+T/hpLS/2dYgkLNkAJC5ClbwRdgrEK7/GhA+S2iQffzE0LDIwpByv8nwpKR\nikJB5Z1B4NjGwVJdn0Jcp5XBRTQUQl0yaEKq4PFB0A8iQTLZsEIidHbB5IIzwQtWXELnSdPA\nE4z7wZIKqELWTndBF5BzQSitokIlhJbBQwyvQf1+RUKuUgZCBiHCwSvfq0LIJZNALAB3wZgV\ns0IQnbTAcV8TQpLiSkLsJxpC4NINQIgqtUK0/eXBbEmVwFf3skLdJ5jB83XswE9UY0IiYBxC\nPLAhQR0hjkKG5ivBLqQHQmgPgUK1/pFBJjTvwUgsgkIZF9dBCNOEwYDAvEIBx4PBSTJmQRao\npUJN3x9BmkWYQZA/JEIYi3JAOFYkwoRkjkIgI1DB4+WfwTOmOEKwx/xBVqaoQeuxn0I6H63B\n4lEFQv2Tm0K1ULBBbCWwwam1FEKy0zNBF8wUwtMEskKZZHw8vNDvQVymrEJeFDxBtBCNwBbN\npUJU2gLC6PUJQIE5qkIIltXBlQ7cv3hzQkLJRR1C66KPvhjFlkKEbSnAlKjZQW23n0Jp9sxA\n9fCwwdYAV0JGjOxBQRHFwYl5tELW0ZbBmI/EQYo4tUIofQdBReDuQMqeg0IvQS/B447uwZ1Z\nnUJBJ2O/20GYwYCCjkJBf2dBq7n/QW/HgkLwWt/BghrmQUflhkLnCgVCcMSFwVELY0KsrK9B\n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\nAACgQQAAwH8AAMB/AAAgQgAAAAE="}]},"context":{"shiny":false,"rmarkdown":null},"vertexShader":"#line 2 1\n// File 1 is the vertex shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\n\nattribute vec3 aPos;\nattribute vec4 aCol;\nuniform mat4 mvMatrix;\nuniform mat4 prMatrix;\nvarying vec4 vCol;\nvarying vec4 vPosition;\n\n#ifdef NEEDS_VNORMAL\nattribute vec3 aNorm;\nuniform mat4 normMatrix;\nvarying vec4 vNormal;\n#endif\n\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nattribute vec2 aTexcoord;\nvarying vec2 vTexcoord;\n#endif\n\n#ifdef FIXED_SIZE\nuniform vec3 textScale;\n#endif\n\n#ifdef FIXED_QUADS\nattribute vec3 aOfs;\n#endif\n\n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\nvarying float normz;\nuniform mat4 invPrMatrix;\n#else\nattribute vec3 aPos1;\nattribute vec3 aPos2;\nvarying float normz;\n#endif\n#endif // IS_TWOSIDED\n\n#ifdef FAT_LINES\nattribute vec3 aNext;\nattribute vec2 aPoint;\nvarying vec2 vPoint;\nvarying float vLength;\nuniform float uAspect;\nuniform float uLwd;\n#endif\n\n#ifdef USE_ENVMAP\nvarying vec3 vReflection;\n#endif\n\nvoid main(void) {\n  \n#ifndef IS_BRUSH\n#if defined(NCLIPPLANES) || !defined(FIXED_QUADS) || defined(HAS_FOG) || defined(USE_ENVMAP)\n  vPosition = mvMatrix * vec4(aPos, 1.);\n#endif\n  \n#ifndef FIXED_QUADS\n  gl_Position = prMatrix * vPosition;\n#endif\n#endif // !IS_BRUSH\n  \n#ifdef IS_POINTS\n  gl_PointSize = POINTSIZE;\n#endif\n  \n  vCol = aCol;\n  \n// USE_ENVMAP implies NEEDS_VNORMAL\n\n#ifdef NEEDS_VNORMAL\n  vNormal = normMatrix * vec4(-aNorm, dot(aNorm, aPos));\n#endif\n\n#ifdef USE_ENVMAP\n  vReflection = normalize(reflect(vPosition.xyz/vPosition.w, \n                        normalize(vNormal.xyz/vNormal.w)));\n#endif\n  \n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\n  /* normz should be calculated *after* projection */\n  normz = (invPrMatrix*vNormal).z;\n#else\n  vec4 pos1 = prMatrix*(mvMatrix*vec4(aPos1, 1.));\n  pos1 = pos1/pos1.w - gl_Position/gl_Position.w;\n  vec4 pos2 = prMatrix*(mvMatrix*vec4(aPos2, 1.));\n  pos2 = pos2/pos2.w - gl_Position/gl_Position.w;\n  normz = pos1.x*pos2.y - pos1.y*pos2.x;\n#endif\n#endif // IS_TWOSIDED\n  \n#ifdef NEEDS_VNORMAL\n  vNormal = vec4(normalize(vNormal.xyz), 1);\n#endif\n  \n#if defined(HAS_TEXTURE) || defined(IS_TEXT)\n  vTexcoord = aTexcoord;\n#endif\n  \n#if defined(FIXED_SIZE) && !defined(ROTATING)\n  vec4 pos = prMatrix * mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w;\n  gl_Position = pos + vec4(aOfs*textScale, 0.);\n#endif\n  \n#if defined(IS_SPRITES) && !defined(FIXED_SIZE)\n  vec4 pos = mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w + vec4(aOfs,  0.);\n  gl_Position = prMatrix*pos;\n#endif\n  \n#ifdef FAT_LINES\n  /* This code was inspired by Matt Deslauriers' code in \n   https://mattdesl.svbtle.com/drawing-lines-is-hard */\n  vec2 aspectVec = vec2(uAspect, 1.0);\n  mat4 projViewModel = prMatrix * mvMatrix;\n  vec4 currentProjected = projViewModel * vec4(aPos, 1.0);\n  currentProjected = currentProjected/currentProjected.w;\n  vec4 nextProjected = projViewModel * vec4(aNext, 1.0);\n  vec2 currentScreen = currentProjected.xy * aspectVec;\n  vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\n  float len = uLwd;\n  vec2 dir = vec2(1.0, 0.0);\n  vPoint = aPoint;\n  vLength = length(nextScreen - currentScreen)/2.0;\n  vLength = vLength/(vLength + len);\n  if (vLength > 0.0) {\n    dir = normalize(nextScreen - currentScreen);\n  }\n  vec2 normal = vec2(-dir.y, dir.x);\n  dir.x /= uAspect;\n  normal.x /= uAspect;\n  vec4 offset = vec4(len*(normal*aPoint.x*aPoint.y - dir), 0.0, 0.0);\n  gl_Position = currentProjected + offset;\n#endif\n  \n#ifdef IS_BRUSH\n  gl_Position = vec4(aPos, 1.);\n#endif\n}","fragmentShader":"#line 2 2\n// File 2 is the fragment shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\nvarying vec4 vCol; // carries alpha\nvarying vec4 vPosition;\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nvarying vec2 vTexcoord;\nuniform sampler2D uSampler;\n#endif\n\n#ifdef HAS_FOG\nuniform int uFogMode;\nuniform vec3 uFogColor;\nuniform vec4 uFogParms;\n#endif\n\n#if defined(IS_LIT) && !defined(FIXED_QUADS)\nvarying vec4 vNormal;\n#endif\n\n#if NCLIPPLANES > 0\nuniform vec4 vClipplane[NCLIPPLANES];\n#endif\n\n#if NLIGHTS > 0\nuniform mat4 mvMatrix;\n#endif\n\n#ifdef IS_LIT\nuniform vec3 emission;\nuniform float shininess;\n#if NLIGHTS > 0\nuniform vec3 ambient[NLIGHTS];\nuniform vec3 specular[NLIGHTS]; // light*material\nuniform vec3 diffuse[NLIGHTS];\nuniform vec3 lightDir[NLIGHTS];\nuniform bool viewpoint[NLIGHTS];\nuniform bool finite[NLIGHTS];\n#endif\n#endif // IS_LIT\n\n#ifdef IS_TWOSIDED\nuniform bool front;\nvarying float normz;\n#endif\n\n#ifdef FAT_LINES\nvarying vec2 vPoint;\nvarying float vLength;\n#endif\n\n#ifdef USE_ENVMAP\nvarying vec3 vReflection;\n#endif\n\nvoid main(void) {\n  vec4 fragColor;\n#ifdef FAT_LINES\n  vec2 point = vPoint;\n  bool neg = point.y < 0.0;\n  point.y = neg ? (point.y + vLength)/(1.0 - vLength) :\n                 -(point.y - vLength)/(1.0 - vLength);\n#if defined(IS_TRANSPARENT) && defined(IS_LINESTRIP)\n  if (neg && length(point) <= 1.0) discard;\n#endif\n  point.y = min(point.y, 0.0);\n  if (length(point) > 1.0) discard;\n#endif // FAT_LINES\n  \n#ifdef ROUND_POINTS\n  vec2 coord = gl_PointCoord - vec2(0.5);\n  if (length(coord) > 0.5) discard;\n#endif\n  \n#if NCLIPPLANES > 0\n  for (int i = 0; i < NCLIPPLANES; i++)\n    if (dot(vPosition, vClipplane[i]) < 0.0) discard;\n#endif\n    \n#ifdef FIXED_QUADS\n    vec3 n = vec3(0., 0., 1.);\n#elif defined(IS_LIT)\n    vec3 n = normalize(vNormal.xyz);\n#endif\n    \n#ifdef IS_TWOSIDED\n    if ((normz <= 0.) != front) discard;\n#endif\n\n#ifdef IS_LIT\n    vec3 eye = normalize(-vPosition.xyz/vPosition.w);\n    vec3 lightdir;\n    vec4 colDiff;\n    vec3 halfVec;\n    vec4 lighteffect = vec4(emission, 0.);\n    vec3 col;\n    float nDotL;\n#ifdef FIXED_QUADS\n    n = -faceforward(n, n, eye);\n#endif\n    \n#if NLIGHTS > 0\n    // Simulate two-sided lighting\n    if (n.z < 0.0)\n      n = -n;\n    for (int i=0;i<NLIGHTS;i++) {\n      colDiff = vec4(vCol.rgb * diffuse[i], vCol.a);\n      lightdir = lightDir[i];\n      if (!viewpoint[i]) {\n        if (finite[i]) {\n          lightdir = (mvMatrix * vec4(lightdir, 1.)).xyz;\n        } else {\n          lightdir = (mvMatrix * vec4(lightdir, 0.)).xyz;\n        }\n      }\n      if (!finite[i]) {\n        halfVec = normalize(lightdir + eye);\n      } else {\n        lightdir = normalize(lightdir - vPosition.xyz/vPosition.w);\n        halfVec = normalize(lightdir + eye);\n      }\n      col = ambient[i];\n      nDotL = dot(n, lightdir);\n      col = col + max(nDotL, 0.) * colDiff.rgb;\n      col = col + pow(max(dot(halfVec, n), 0.), shininess) * specular[i];\n      lighteffect = lighteffect + vec4(col, colDiff.a);\n    }\n#endif\n    \n#else // not IS_LIT\n    vec4 colDiff = vCol;\n    vec4 lighteffect = colDiff;\n#endif\n    \n#ifdef IS_TEXT\n    vec4 textureColor = lighteffect*texture2D(uSampler, vTexcoord);\n#endif\n    \n#ifdef HAS_TEXTURE\n\n// These calculations use the definitions from \n// https://docs.gl/gl3/glTexEnv\n\n#ifdef USE_ENVMAP\n    float m = 2.0 * sqrt(dot(vReflection, vReflection) + 2.0*vReflection.z + 1.0);\n    vec4 textureColor = texture2D(uSampler, vReflection.xy / m + vec2(0.5, 0.5));\n#else\n    vec4 textureColor = texture2D(uSampler, vTexcoord);\n#endif\n\n#ifdef TEXTURE_rgb\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(textureColor.rgb, lighteffect.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = lighteffect*vec4(textureColor.rgb, 1.);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - textureColor.rgb) * lighteffect.rgb, lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(lighteffect.rgb + textureColor.rgb, lighteffect.a);\n#endif\n\n#endif //TEXTURE_rgb\n        \n#ifdef TEXTURE_rgba\n\n#ifdef TEXMODE_replace\n// already done\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = lighteffect*textureColor;\n#endif\n\n#ifdef TEXMODE_decal\n    textureColor = vec4((1. - textureColor.a)*lighteffect.rgb) +\n                     textureColor.a*textureColor.rgb, \n                     lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - textureColor.rgb) * lighteffect.rgb,\n                    lighteffect.a*textureColor.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(lighteffect.rgb + textureColor.rgb,\n                    lighteffect.a*textureColor.a);\n#endif\n    \n#endif //TEXTURE_rgba\n    \n#ifdef TEXTURE_alpha\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(lighteffect.rgb, luminance);\n#endif \n\n#if defined(TEXMODE_modulate) || defined(TEXMODE_blend) || defined(TEXMODE_add)\n    textureColor = vec4(lighteffect.rgb, lighteffect.a*luminance);\n#endif\n \n#endif // TEXTURE_alpha\n    \n// The TEXTURE_luminance values are not from that reference    \n#ifdef TEXTURE_luminance\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(luminance, luminance, luminance, lighteffect.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = vec4(luminance*lighteffect.rgb, lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - luminance)*lighteffect.rgb,\n                        lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(luminance + lighteffect.rgb, lighteffect.a);\n#endif\n\n#endif // TEXTURE_luminance\n \n    \n#ifdef TEXTURE_luminance_alpha\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(luminance, luminance, luminance, textureColor.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = vec4(luminance*lighteffect.rgb, \n                        textureColor.a*lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - luminance)*lighteffect.rgb,\n                        textureColor.a*lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(luminance + lighteffect.rgb, \n                        textureColor.a*lighteffect.a);\n\n#endif\n\n#endif // TEXTURE_luminance_alpha\n    \n    fragColor = textureColor;\n\n#elif defined(IS_TEXT)\n    if (textureColor.a < 0.1)\n      discard;\n    else\n      fragColor = textureColor;\n#else\n    fragColor = lighteffect;\n#endif // HAS_TEXTURE\n    \n#ifdef HAS_FOG\n    // uFogParms elements: x = near, y = far, z = fogscale, w = (1-sin(FOV/2))/(1+sin(FOV/2))\n    // In Exp and Exp2: use density = density/far\n    // fogF will be the proportion of fog\n    // Initialize it to the linear value\n    float fogF;\n    if (uFogMode > 0) {\n      fogF = (uFogParms.y - vPosition.z/vPosition.w)/(uFogParms.y - uFogParms.x);\n      if (uFogMode > 1)\n        fogF = mix(uFogParms.w, 1.0, fogF);\n      fogF = fogF*uFogParms.z;\n      if (uFogMode == 2)\n        fogF = 1.0 - exp(-fogF);\n      // Docs are wrong: use (density*c)^2, not density*c^2\n      // https://gitlab.freedesktop.org/mesa/mesa/-/blob/master/src/mesa/swrast/s_fog.c#L58\n      else if (uFogMode == 3)\n        fogF = 1.0 - exp(-fogF*fogF);\n      fogF = clamp(fogF, 0.0, 1.0);\n      gl_FragColor = vec4(mix(fragColor.rgb, uFogColor, fogF), fragColor.a);\n    } else gl_FragColor = fragColor;\n#else\n    gl_FragColor = fragColor;\n#endif // HAS_FOG\n    \n}","players":[],"webGLoptions":{"preserveDrawingBuffer":true}},"evals":[],"jsHooks":[]}</script>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-din99d-space-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;6: Colors in DIN99d space
</figcaption>
</figure>
</div>
</div>
<p>The DIN99d color space <span class="citation" data-cites="cui2002">(Cui et al. 2002)</span> is a euclidean, perceptually uniform color space. This means that the difference between two colors is equal to the euclidean distance between them. We take advantage of this by computing a distance matrix on all the colors in the subset, finding their pairwise color differences. We then apply a power transformation <span class="citation" data-cites="huang2015">(Huang et al. 2015)</span> to fine tune these differences.</p>
<p>To select the <code>n</code> colors that the user wanted, we proceed greedily: first, we find the two most distant points, then we find the third point that maximizes the minimum distance to the previously selected points. This is repeated until <code>n</code> points are selected. These points are then returned to the user; below is an example using <code>n = 5.</code></p>
<div class="cell page-columns page-full">
<div id="fig-selected" class="cell-output-display quarto-float quarto-figure quarto-figure-center anchored no-overflow-x page-columns page-full">
<figure class="quarto-float quarto-float-fig figure page-columns page-full">
<div aria-describedby="fig-selected-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div id="rgl10112" style="width:100%;height:650px;" class="rglWebGL html-widget" aria-labelledby="rgl10112-aria"></div>
<script type="application/json" data-for="rgl10112">{"x":{"material":{"color":"#000000","alpha":1,"lit":true,"ambient":"#000000","specular":"#FFFFFF","emission":"#000000","shininess":50,"smooth":true,"front":"filled","back":"filled","size":3,"lwd":1,"fog":true,"point_antialias":false,"line_antialias":false,"texture":null,"textype":"rgb","texmode":"modulate","texmipmap":false,"texminfilter":"linear","texmagfilter":"linear","texenvmap":false,"depth_mask":true,"depth_test":"less","isTransparent":false,"polygon_offset":[0,0],"margin":"","floating":false,"tag":"","blend":["src_alpha","one_minus_src_alpha"]},"rootSubscene":1,"objects":{"43":{"id":43,"type":"points","material":{"alpha":[0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248,0.09803921729326248],"lit":false,"isTransparent":true},"vertices":"0","colors":"1","centers":"2","ignoreExtent":false,"flags":34848},"45":{"id":45,"type":"text","material":{"lit":false,"margin":0,"edge":[0,1,1]},"vertices":"3","colors":"4","texts":[["DIN99d"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"5","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"46":{"id":46,"type":"text","material":{"lit":false,"margin":0,"floating":true,"edge":[0,1,1]},"vertices":"6","colors":"7","texts":[["L99d"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"8","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"47":{"id":47,"type":"text","material":{"lit":false,"margin":1,"floating":true,"edge":[1,1,1]},"vertices":"9","colors":"10","texts":[["a99d"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"11","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"48":{"id":48,"type":"text","material":{"lit":false,"margin":2,"floating":true,"edge":[1,1,1]},"vertices":"12","colors":"13","texts":[["b99d"]],"cex":[[1]],"adj":[[0.5,0.5,0.5]],"centers":"14","family":[["sans"]],"font":[[1]],"ignoreExtent":true,"flags":33808},"49":{"id":49,"type":"points","material":{"lit":false,"size":5},"vertices":"15","colors":"16","centers":"17","ignoreExtent":false,"flags":34816},"5":{"id":5,"type":"light","vertices":[[0,0,1]],"colors":[[1,1,1,1],[1,1,1,1],[1,1,1,1]],"viewpoint":true,"finite":false},"6":{"id":6,"type":"background","material":{"lit":false,"back":"lines"},"colors":"18","centers":"19","sphere":false,"fogtype":"none","fogscale":1,"flags":32768},"44":{"id":44,"type":"bboxdeco","material":{"front":"lines","back":"lines"},"vertices":"20","colors":"21","axes":{"mode":["pretty","pretty","pretty"],"step":[10,20,20],"nticks":[5,5,5],"marklen":[15,15,15],"expand":[1.029999971389771,1.029999971389771,1.029999971389771]},"draw_front":true,"flags":32769},"1":{"id":1,"type":"subscene","par3d":{"antialias":8,"FOV":30,"ignoreExtent":false,"listeners":1,"mouseMode":{"none":"none","left":"trackball","right":"zoom","middle":"fov","wheel":"pull"},"observer":[0,0,325.3004455566406],"modelMatrix":[[1.115169644355774,0,0,-69.56121826171875],[0,0.3409429788589478,0.8615754246711731,-0.09495473653078079],[0,-0.9367331266403198,0.3135878145694733,-322.5028076171875],[0,0,0,1]],"projMatrix":[[3.732050895690918,0,0,0],[0,3.732050895690918,0,0],[0,0,-3.863703727722168,-1172.670654296875],[0,0,-1,0]],"skipRedraw":false,"userMatrix":[[1,0,0,0],[0,0.3420201433256682,0.9396926207859085,0],[0,-0.9396926207859085,0.3420201433256682,0],[0,0,0,1]],"userProjection":[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],"scale":[1.115169644355774,0.9968505501747131,0.9168694019317627],"viewport":{"x":0,"y":0,"width":1,"height":1},"zoom":1,"bbox":[27.97290229797363,96.78160095214844,-35.81810760498047,41.15769577026367,-42.82153701782227,40.92897415161133],"windowRect":[0,0,256,256],"family":"sans","font":1,"cex":1,"useFreeType":true,"fontname":"NULL","maxClipPlanes":2147483647,"glVersion":"NA","activeSubscene":0},"embeddings":{"viewport":"replace","projection":"replace","model":"replace","mouse":"replace"},"objects":[6,44,43,45,46,47,48,49,5],"subscenes":[],"flags":36145}},"crosstalk":{"key":[],"group":[],"id":[],"options":[]},"width":480,"height":480,"buffer":{"accessors":[{"bufferView":0,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":1,"componentType":5121,"count":1000,"type":"VEC4","normalized":true},{"bufferView":2,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":3,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":4,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":5,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":7,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":8,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":10,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":11,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":12,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":13,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":14,"componentType":5126,"count":1,"type":"VEC3"},{"bufferView":15,"componentType":5126,"count":5,"type":"VEC3"},{"bufferView":16,"componentType":5121,"count":5,"type":"VEC4","normalized":true},{"bufferView":17,"componentType":5126,"count":5,"type":"VEC3"},{"bufferView":18,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":19,"componentType":5121,"count":1,"type":"VEC3"},{"bufferView":20,"componentType":5126,"count":16,"type":"VEC3"},{"bufferView":21,"componentType":5121,"count":1,"type":"VEC4"}],"bufferViews":[{"buffer":0,"byteLength":12000,"byteOffset":0},{"buffer":0,"byteLength":4000,"byteOffset":12000},{"buffer":0,"byteLength":12000,"byteOffset":16000},{"buffer":0,"byteLength":12,"byteOffset":28000},{"buffer":0,"byteLength":4,"byteOffset":28012},{"buffer":0,"byteLength":12,"byteOffset":28016},{"buffer":0,"byteLength":12,"byteOffset":28028},{"buffer":0,"byteLength":4,"byteOffset":28040},{"buffer":0,"byteLength":12,"byteOffset":28044},{"buffer":0,"byteLength":12,"byteOffset":28056},{"buffer":0,"byteLength":4,"byteOffset":28068},{"buffer":0,"byteLength":12,"byteOffset":28072},{"buffer":0,"byteLength":12,"byteOffset":28084},{"buffer":0,"byteLength":4,"byteOffset":28096},{"buffer":0,"byteLength":12,"byteOffset":28100},{"buffer":0,"byteLength":60,"byteOffset":28112},{"buffer":0,"byteLength":20,"byteOffset":28172},{"buffer":0,"byteLength":60,"byteOffset":28192},{"buffer":0,"byteLength":4,"byteOffset":28252},{"buffer":0,"byteLength":3,"byteOffset":28256},{"buffer":0,"byteLength":192,"byteOffset":28260},{"buffer":0,"byteLength":4,"byteOffset":28452}],"buffers":[{"byteLength":28456,"bytes":"Z1iCQs2QAkLkKVvBF2CsQrv8aED5LaJB9/MTQsMjCkHK/yfCkpGKQkHlnUHg2MbBUl2fQlyn\nlcFFNBRCXTJoQqrg8UHQDyJBMtmwQiJ0dsHkgjPBC1ZcQudJ08ATjPvBkgqoQtZOd0EXkHNB\nKK2iQiWElsFDDK9B/X5FQq5SBkIGIcLBK9+rQsglk0AsAHfBmBWzQhCdtMBxXxNCkuJKQuwn\nGkLg0g1AiCq1QrT95cFsSZXAV/eyQt0nmMHzdezAT1RjQiJgHEI8sCFBHSGOQobmK8EupAdC\naA+BQrX+kUEmNO/BSCyCQhkX10EI04TBgMC8QgHHg8FJMmZBFqilQk3fH0GaRZhBkD8kQhiL\nckA4ViTChGSOQiAjUMHj5Z/BM6Y4QrDH/EFWpqhB67GfQjofrcHiUQVC/ZObQrVQsEFsJbDB\nqbUUQrLTM0EXzBTC0wSyQplkfDy80O9BXKasQl4UPEG0EI3AFs2lQlTaAsLo9QlAgTmqQgiW\n1cGVDty/eHNCQslFHULroo++GMWWQoRtKcCUqNlBbbefQmn2zED18LDB1gBXQkaM7EFBEcXB\niXm0QtbRlsGYj8RBiji1Qih9B0FF4O5Ayp6DQi9BL8Hjju7BnVmdQkEnY7/bQZjBgIKOQkF/\nZ0Gruf9Bb8eCQvBa38GCGuZBR+WGQucKBUJwxIXBUQtjQqysr0HdwfTBx6C/Ql9vH8FcVqlB\nayWSQibuwUFEEZBA26+HQvt8y8E0k5nBkBmUQpTm6MED8wXB25iZQkKn1EENwl++ZWCwQjxj\nAcG4agRClCEGQoIjlUEZZBPCT/OpQkTNi0Hd6VrBUc+oQiL73sHguwBCyV5iQiMHmUHET7pB\ngVmLQrOvjcBCPODBozJWQj7DhcEQMsXBIVqIQk83+EFWNlpBGmK1QqLWQMHWPZRBFutaQphW\n5UHiLfPBBzuSQh0ZPEBWoa3BfGWVQkqJ0UBFEhJCMbp7QugdAEK5TyvBT0C9QliEoMHh/PA/\nJqKkQmn9+sG08lRA5RytQn6UlUEYteLAuJCrQsPzMD7pfbFBXcUPQnseNkElASXCVEOkQqBf\nOkH2M2DB82ekQibYpcGLFAhCNTo5Qh3C9EGxp4pBifWTQkk6gsE5RrfBTSkoQlZcJb+y3gfC\nwtKeQm+7dEHiFrVBC0e4QpTekMHl1IlB0QN4QoxQEUKO9K3B/StzQl3ghEFd4+vBYSqSQq09\nJMHMIxRCTbVTQiUQAkICVGNA3yuwQqWZtsFxTPTAciShQmoIisHA5TPBXMlFQg4rFEL2ao1B\nHDCnQocQacErrgJCkNSnQgBcU0HG8JrBs7M2QriQDULGRGHBdhKtQjth78GwwONBPrGtQtJ4\niEB2tJJBFlIiQqpa20BM7CPCAn+zQu1B18BfSjfBhiaKQi77z0EmtatBz6WOQmXWrMEeHeBB\nURqZQqfxwUHLYKPBBcRLQrBGT0EDqQHCyc68QuV8iMCIjb5BQ4SKQg/U70HOMnfAoPWaQoeV\nB8JPZy/ASQW5QhdrgsFlEwm/A050QqUFEUJX6r/AWuWYQqpR1r1nUxNChx+FQiGbGEGpcfrB\nl7tgQmPx6UHR887BiGi4Qrr/iMFm67lB5uCOQjDggUFwuCRBGM08QoBbKMFmcu7B8WeTQrjK\nYb/zYszBD1aLQo4cmUG8JAVCmLigQkeAycFoqcNBO++yQqRKakEOHyHBdBEsQusImUE5yuTB\nsmGzQte0NcGRqvJBCs+lQvXjkkGtdg1A2u+YQq0w3MFez3jBex6YQupu+8EG4N3AJqKTQshW\n7EGUi5y/bMa8QpbkpMB4gl1Bl7ddQsDYgkG45gbCYE+NQm4V0EE6eJvBYzaSQvUg2sFEqgJC\n0NWCQk5fukHH/cVB02KhQuY5wcC2zrPBrvOHQs3+hcEKNoDB0MOoQgUTjUFVV79AHsuuQpbe\nY8H6vM1B8jI6Qj2m4UFS8AHCONmTQiuDRkChG83BL5qaQnM3EkFKQRNCdIYtQk+hAELtbi7B\noVmrQkF6/8FG2s5AlfmyQjpju8F+zD5AwuRiQoe+HUJl7izB18ZpQqA1m0BF+eBBshdkQjC2\n1EB7VgrC8OxsQkB9vUHl39LBYCG9QqPhRMG0GZ5Bbw6RQgaQz0G+xBxBTHxyQg+Lj8F8YuHB\nbF2OQs66ncAysZ3Ba3FeQkN7vkHhcNRB9ZGcQp3PyMGJ9uhBRq+kQpbBsEEn/IbBYW8rQpWh\nkkFRcRDCxry7QssE58DslPBBPNOxQm3BMkGsX2O/WdWmQpGw8ME6cfDAkrJpQjB5ucFVWmHB\npJSEQj96AkIArNlALdu4QhcNHsGvUrVBoQNHQjhowkGtAA3CUx2WQq5wxUEbZmTBMSygQi0B\n+cGukgRCEbWQQl8pE0GuhK9BJB+qQqGux78CTJLBbm5fQhg1K8GkM/vBaSafQsRhrEFErmZB\nvB2lQnORncHoHNlBhLYxQtNb+UEaj9PBdqClQq3GbkDTeIjBsQypQkUjur8grBFCvaNAQgww\nFEJlRk7Alh+yQmrT8sE7U3C/HSaWQt0w3sERN23AApuhQg59tEFge1LAsMyvQqD5l8C7AvBB\nihMJQpR8f0GPYhrClESwQryZaUH53VfBtgetQr5YysFQKQFCIellQmL/yEF2zbFBFxCXQqoY\nBMGg7sHBbzX5Qb15vkATEA7C+mmhQhiaEEEo4fFBM4KrQncyicG1U1BBUTNfQpyDFELi4WbB\nyhCPQjLQgUFaXq7BvpOdQtwoi8FgWxJCdTFpQmqwBkJNjzlBnx2zQjqPgcHIAzDBJ62iQnsm\nzMEDVhzBC501Qkf1FkI7zERBUjWeQkzoHsFXkQRCUPeZQhCUUEHjI77BM/9NQjPH7EFrYY/B\nmxavQjgbvcHSzb5BVi+0QrS7qkD5tUFBvf1YQgGguL5yNBHCCzGsQtfwKsHrHUbBAS1xQnzm\nAkK9rphBPj6TQoywq8Fk+QZC77yJQuQB1UFaD9LB7cRhQiEvCUFAAgTCdiu/QqGjy79bEIRB\noGKbQoyEoUFR09zAilGhQtwOC8Jl5oxABsuuQpinu8EPQgBAq6BZQoXDHkKFUAfBXlKjQged\nRUDcEfpB4emrQlU9DUC5f5jBCUo1QjI/3EE7MeLB6gOzQl0KkcHhFPNBkQ2oQneRTEGnhbtA\n3laDQi7ZisHtHMvB88aqQiP9hcCn2ITBgb+OQhfUqUEDKdxBCHKMQl+8zMGHu+ZB/2qTQg3k\n3kHNxpfBumtRQo8eZUHncNXBPtm5Qmih8sB5P8dBrd+EQk9z7EFAEZM/spSGQqzf6sFl4STB\nCMyPQmhYuMG9NX3BndalQlwhp0E254ZARTupQroXJsE50a9B7VwsQjcMxEEgCQXCbLKdQqQ+\npEF8KDnBNTKgQh+798G5Lv9BN71jQgbpSUH9wu9B2FVbQoH/dECL9BPCztxQQjohEMEwneLB\nbDWcQv2Fn0GoCHZBBt+7QuPZVMF5s25BHd5+Qs0yAULfrtDBYZ14QgxlN0HFEPnBqAOcQrdS\nJsAVGBxCIkxhQrDZCUJRGV7A60G4QvEdwsE/RRLA+R+XQkoUBsLTYBBA32qOQqCJAEIjafDA\nYA+5Qr+bEsCBpIZBt3RCQiwMPkFvCRfC9GOGQvOOsEEdtanB/K2JQo3LscEUIwNCSMVlQu/w\n8EFyd5lBQdCiQnnARcEYWZ3B/CBuQiXZDL+81+rBOS24Qqzvl0B7HFRB3kevQicstsHvqaxB\nxjRjQurcGkLhuZrBj3ihQuVSLEFzI5rBR2WuQkw1TMFE4BZCCwY1QhjU6kHxLKRAHsijQvkS\nxMFp7zTB7we8Qqf7RsGqzv6/BYd+QsnTCEKHuEq/cqqjQjI+x8A9qBNCN6aQQn7APkElJtzB\nEh51Qm2jAkLHUbzBKLu6QupzlcF87qRBPJ+YQnWpeEHCt4FBeMouQt9FQsBKnBHCjIqhQtgR\ni8EePGzBZqdOQvPjFEJ2ua1BKoGgQi5CasFjd+JBM1WrQtYXSEFodojBFEgBQi2l40DdxBDC\nUtSlQofp1EASu/BBoHCgQpeOtUH7MSnB1YKpQr5FD8JfTytBwIWuQqUU4sFXkKZAc6lUQp+h\nIEJMOi7BFQOYQqKUw0BpcutBiD+iQhkJ8z8D0LPB4AgjQuxLt0EFJtPBC/6uQvsFhMHTY/1B\nn8ecQkPzrEFHrt9Ao6uFQsQhpsHEisnBI+exQvrKq8D6LDzBRkSNQurjtEHlr7pBJnObQtyL\n1sH2XQJCu/SXQr1C3UGsi6bBKXxHQiU5gEFvowbCuGG+QsHwv8CsB8pBDnqMQqazvUH8IOO/\nh++PQhfD+sFyRs3AFqiWQhKBoMFZuhTBwMuyQhuSUUG4xLI/P6S0QlnnFsFUL75BlRU4QtC6\nukE2ZRDCqb+nQufPlkEDeEXBT9+nQjpK8MGfo/5BupNsQg5Ka0GePddBuSCDQk3KVr7g9ffB\n9vhXQlPQPsGHke3B2MmXQg97wEGTJmhBz4e5QnMDMcEdK1BBhM92QlZs7EHB4dTBxit7Qjyl\nBkEVZ+jByIqPQv7HRD9A9hNCOlhgQmMsFUIX6bXALNi4QshD0MHVtoa+aiCYQspW/cHCvCLA\n/bGWQtqf30HBn4vAYeS/QjGLgcDm2IFBAihXQo6HZ0FDOBLCPRuOQoKNzkFO/qzBqG2YQpJ/\n0cGvSwhCWtF8QuSbsUFn1oxBMZGnQg26BsEsWo/BqNJNQouCe0D4rg7CPsO3Qok+hUBxfo1B\n+GepQugtuMEOHp5BqjtUQhBYGUJxmXLBLSWgQlk1KEFAJoPBQsamQll1e8GHuBBCzCArQmdq\nBkJge05BAVWdQqz+usEdOYTBAdC1QltuicEzuaPA0NJpQr8sF0LjyrBAjquHQmxCAMGwcupB\nNB6HQuCUckG6/t/B9iRfQvcH/UGhVZ/Bjwe0QhlhrsH74rFBlFOUQo1ugkFOALdBjYMKQkVO\n5T+C3RbCv1CfQiq+RcHaCVzBynJUQu/uCkLM46hBCM+lQgThkcEkj/FByFutQiK1YUExrYTB\n+TIBQnHKIkHpUxjCiHutQoVCMEDCrwFColujQk0ol0FGUunAsdClQoJ5CsLzMaxAwiyzQux+\nl8ETg8g+3n1lQhscF0J+TO7AhzSIQhVpCUCZpQlCwt5uQp41FUHIpAnCptF4QnV020E/OdPB\ncWC/QpDYVMFIAZFBCyaaQvlkm0G2ZSJBOeVlQp3EV8FRH/vBOsZ5QuetzL9m+uzBkM5dQqiy\noUFAq+pBOlWOQs1AwsHw8clB1WOdQs5Ev0HAv3vBZBoqQhGAmkFa/fzBax62Qk2DHsHhEvRB\nUI2oQmjij0GdoHg/RK+fQpXq6sE9OE3BSzNfQruKpMF9+57BrzaCQimxBkJwlzZBghC0Qvhk\nKcFApJlBTSxOQtXX0UFqPQHC1KiXQtiWjUGpdBTBjqGXQugz98HuSPpBW3SXQpvN9kANBOJB\nglalQps2ST8PaqrBCUprQnmJ7cBevOLBfiusQjjpVUH+IVJBNsasQh0aq8FdNM9BMoFXQp7Z\nDUI3L8/Bqd2wQhfVcEAsumTB+8S1QhNtqcB2nxFClSJSQuHjAUL1pdC/K/KzQoQjzcEH53nA\nHS2eQkdoAsIuCzi/ziWcQvU01EHY8rLAmemlQvIGI8BVz9hBUe39QRWeV0E1OxbCXxGsQjbA\nX0HvJWLBddapQlQgvMHmKgNChKBYQmtH8kHGV7dBtmeVQvjSNsHDQMrBYWIeQiX4DkBiUPvB\ncDahQjE0J0Ez4cpBT/K0QpxqnMHNqYRBeAtvQiGWFEKsjYzB/9aTQngPfEEjGr7B/g6tQkz8\ne8FPWRxCCg58Qrxj7EH/4cFAjMS5QvBQaMHGut/A3x+jQr+YwcGSCcnA7RA2Qh2iFkK3wt1A\n7OKnQh+hAcEibBFC4/GZQoD5PEGzpcfBuQRXQr/NB0LqarHBoICyQnxyuMHYNcpBLya0QmPs\nXEAAC/NAzthrQjtmgMAmuQHC5FylQtD+csGZqXPBJWJcQpCwEkKqM6xBI3p9QlBxiME8XfRB\nOlh7Qsa4z0F4+uHBsv9RQtVWwUDKGvrBKLa3QlvdZz9R05tBoBSPQpeL3EEuaCfBr2CbQtzX\nC8JLLQ1BekGeQh316cGfpKZAZGesQkEokUHzWgbBHV2rQtXDU0D2ycZBkW4IQoTeI0FBSSvC\nPYSfQgFxdUGEQJjBuQ2qQvCgncGuWBFC/PQwQryM3UHGljdBhMqWQhYnlcEsVJrBmopCQpkY\nRsB9hg/Ca3ikQtjXg0Hy+pdBBQq9Qq5QScGuyTlB03uEQs41AkK9irXBxJyJQmeXLkFpe8DB\ny+2eQsOt8cCrTRlCxXZnQhmNDEKQVp4/xD25Qsg3scHR/qDAwnioQhbZoMHkZSDBtd1RQrmy\nG0KAv35BOYShQmPMKsGiH9tBX9utQu1fEEFSiILBE449QhAMA0LLPoTBi2ysQrzl2MFuqNNB\nMOavQgi2zEDVwIpBo281Qm5NhEB8YCHC9s+uQonM6MBfnA7B9NN+QgcN40ESuphBNC6RQqdd\nssEMcf9BJ7qPQh9K0kF6vr3BnMssQlq1TEHfDxXCnVe5QtH2CsCPutlBDkGFQq3U3UFAaMHA\naDyKQkAPAcJjEGE/Rfi7QtsmkMFaZxPA77p1QiJfF0KqjdG/Ms2aQlX0hcBXyQ5CjZOKQrgZ\nMkGmaObBDwtqQtBV2kH9qa3BqRe3QjS8jcFK96ZBZn2SQgcHn0EqhIdBIf8uQs6vosD1UAzC\nUy2FQvEUCkDlfuDBt/OGQvxHVUHCZAlCLv2gQhsJ78GlaOZBtAunQux5sUEkojrBneEYQpbP\nukE2bu7BB3SyQvxbeMFYTwhCfQecQtELm0FxWJVAyWmIQuW4u8GuEKjBwbCDQuQ/0MFxnR7B\n/4aPQgVI6UEIQf0/aaa8Qmoz5MBRjZJBWdxUQh1so0EdFAvCZ8mHQmB+8UGpvpjBREyPQgj2\n5sGP9QBCJ+WCQvZdjEH2as1B8f+ZQu32RcAGj8TB2SGLQq2vcMFZH63BHEGyQudYT0Hqa9dA\nan2sQt1kSMFBUY5B9mpPQg0d8kFQM+3BX6ihQsXIOEDrG5jBuRWiQnpCPEDZhw9C0w8+QmXq\nEkJ1vebA/CixQkQs+8E93itADp+MQuhbAMK1t7xA/2+LQjCzAEISnyrBjUezQtE3Ob89DWhB\nDms3Ql+9EkEsuhjCs4aZQltohkE8e5rBlgalQtrzqcEHbxBCM0OBQtgR7UGJwlVBMTS3QuGU\nNsGOwynBUOlSQlG5ScBX5fLBj9apQvAuP0Hy3odBtKGnQqDKvsFig89BQk5FQoc2DkKhsK3B\nm/SoQj+2/kDT05HBZZG1QgELFMEPYBhCMaVEQjNXEEKmtJtAOnKuQtoD0sE8q/XA0SaqQo6B\niMH4PRrB2yRWQp2kF0LfynJBfkOJQiN4Y8HwtwhCFLFyQlbVt0EhhvvBuhZzQor1A0LZ7H/B\nzJ65Qj6/qcE0VY9BpU+hQvtEwEDjMplBgekMQpkByUA6RyDCD9WNQgueIsFPIMLBhrVOQhle\n9UGw3cVBJSqZQsvbqsG0yeVB36CiQj1ipUEDu5XBHMY0Qlw3MkFvZ/LBIlK1QszNVMBNPN5B\nx6ivQgeAXEF3pVjAfserQhVEA8Jnze2/e7OdQjkkxcEV2cM++v4qQqyxEEIC+pjArPKYQksM\nZz+S7wtCoSmPQkQ+7UBry+PBVnBAQq2V8EFC+dzBY6WzQlzBnsHk8exBodOqQuQGQ0FumO1A\nSOZ+QhcVYMFv9OLB9sydQjc+DcCY+KvBZpyMQo2YmUH4hvlBIth6QohwyMFvz9JBKFyJQqkJ\n90FumJHBw7xoQpVPkEEFXufBtpK/QsV2AsH1l5xBal6SQhKY2UFWMjtAtuiUQpi56MG9Q3/B\nYx+iQlIB/cFK56zA472hQlNZwkF+NSHAFDuuQoS1r8DIHeBBLXEOQmXBhkF3+BbC4g2zQonm\nHkHfwhzByCuoQpJPw8FKYe5BSoxtQugOyUGOt8JBKKqWQkrN5cAR38nBKmlRQuMjlMFp/o7B\nBW+BQq0fAUISWTZBOyC2Qkv9RcFS8bVB6axMQnjP3UHV+QTCs/iGQtByRUA6f97B/0qNQrdj\nMkFtfA5Co3Z6QqLKvUGdyyzB3um5QsWCpMHZDVxApwmdQmih+sGQX/FAeiOlQsLBsEGEdijB\n5BSfQvjUDEAnC4lBbHEBQoGEBUF0oR7CzhazQj1W3EAggzXBocitQgXXg8F0AwNCJkNPQrN7\nDULmd4NBwdKmQv3slMGiXXzBIvQsQsY2sMDfzdfBb+CSQrUNpkHPGaRBd9OzQj6pjcHmFZxB\nyDdwQurwDkLpisrBdLeKQntfO0E8TtrBDcCuQir+zMDyeSNCyAN0QklH/EEIzrG/2du7Qvui\nn8HuT2bALYmcQkYCvMFjbijBL/0rQjNgEUKuTkdBd/ClQiODRME6ixNCJPCUQlKmfEGqhNDB\ngsdLQmzCC0IFXpTBbTuwQvg30cFU79BBTPyxQhbGekC9jjpBCE1PQkR5hD+AXhHCU0eoQgkG\nL8FcNXjBW9dwQlbzA0KK061B0DqFQmhhn8HELvBB8F2IQkdO1UFfXMjBiG1tQlS650CAFNfB\ntGG9QkQOD8A8/IdBK3iWQjhHxUFts9zAYZakQpkxDsL60ElAEJqsQqlGk8Fmcda/Mb1WQiTd\nGkKs79G/42GCQltrIsCw2AVC14ZfQoeSU0H8OgzCgJtxQlcD9UEOVMjBDW67Qv5kh8HdAaZB\n+VKWQtKwi0Fj/lVB7KVAQn5u8cCbQgfCQrh4Qvz/7j8AqPXBBi55QqYbc0EvWANCrKaSQr7/\n08EVV8lBtiyhQli2tkGHi0vByKw3QsVBuEGuFPnBr6W2Qq6cSMF/jOVB9s2sQnKofEHof0RA\nYyaZQj5aysHjP4/BBCCJQsqg6sFgLBvBDWiHQiJ2CEJSQwNAHAG5Qnxc2MAGDaJB/vVDQiuf\nnUFpCBDCVtKaQue3okFHdm/BRQKfQogx5sFoEwRCfiyQQuQrjEH4QLhBVi+vQmixi8D+zITB\nZx14QhlehcGeJ7TBFRaeQlmRskE4KhdB3lqrQta0i8Fmx+tBtTUsQpVk5kHVFvLBsmSmQrBj\nBUCtsJXB4t6jQqJhkUCYpwxC0TpGQtQZ+0EBehbB0YivQnAU48EwBklAXT+QQpI7+8ERZVhA\nuJaSQpbE50FNYAzBuui6QggEsL+VSoZBbiVEQlnRKkHBcxrCFdiGQma7ukF057zBFfORQu5g\nsMErgwlCOYtoQh9zxEEc90ZBtV6nQuSiS8GSvH/Bu7lwQswF3b+X0P/Buqq5Qigls0CzW0VB\nnBSvQtKPn8EzNJpBoEhkQsWhFEJSbKjB/GemQj/N6kBa3n3B9KesQsjuKcGyARJC8xo7QrGK\nEEK1Q/pA+TyqQp011cE+7h/B8me3QozUSMEFn+/AYEV0QoPRDkKQW0xByoaVQjCPbsGpywFC\nc1qSQkW4lEGxF8XBgMVoQk39BEKismXBW5K2QmRyucGwGZlBNUqjQgP/8UC1JNVB8iX3QTxN\nC0Gd2SbC6XOVQjq37cCAIZPBiSNfQra94UGO58dBCeKgQmZVuMHQV+pBb+KpQmadmkGB7IbB\nfDoEQg2fbUGfMw3CaSG3Qh/4l8D6FhBCS/adQqrFpEHS20/AQIGlQtWpB8Lyn3TA1OyvQmLx\nzsGB+3m/+lRWQnuhJEJurIa/cy2hQqQF7L/tNutBpNOpQq5Ji0AeI5fByAc4QlikvkFGO6jB\nUtKsQhUtosG5r+FBj4OmQlgtZkEEGx9B1A5tQrcbNMENqf7BICamQmkq178CtozBP2iTQvG7\ncEG5W/RBOGSRQm7n6cGX3PZBUP6OQiA++kGFQIjBJsxAQpyrskEcqgHChVK6QtnhMMF2xdpB\nGUODQn/Xz0HSPYlAWCxqQs9Aw8EYkHzBjdeZQhqZ0cHjnsjAhjyqQieClEFQoKC/vNWzQgY3\n2MALUd9BUb0cQlvBlkH46xvCkAKgQlb4skGtzojB1QOoQrnR5cFMSwhCsDdNQn5Hp0FRDrpB\nNMaAQufhoMC4/PjBj9J2QtKhlsE1sbnBELCWQrhW0UGOmhpBs8S7QpPPBMFJPyxBC3V2Qkwu\nyUFWY+DBk4KCQph3iEDtIt7BUEmJQhBdAEG0Yw1C+hNqQq2eDUKc7DrBO7q5QqzcxME9rlRA\nfFOkQizPxsHX80BAoG4+QrbxFULAyxPBgyKYQhZhn0DooAJCtNaYQuFWfkAc58/BcWxGQiu7\n5EGMLPXB/863QtGQg8FJneJBSeuwQmUhMEHRDphA3hCOQiIolcFd4rjBSpGcQk9kmMDOCKXB\n4BuCQjiWw0EdfeRBjUFoQtcHqsFZub9B/Y6GQgr89UFYFajBg7xdQslOdEG4Ne3Bnn6+Qj7q\n08AFCa1BotKOQuKy4EHe5Nm9swyZQqZn/8GCHRnBh+WNQpaMycHQ94PBx22cQomgzUGAxpRA\nem+pQoWMN8FU8ttBoWIbQk6lukEwWwXCbOeuQrQuOEE7fvzAWh+kQtqO58EX9OVB49SBQsZg\nZEG2sPVBIr+GQnsSkj+TbffBrXBDQnoJGsFdxefBn6qTQgnwv0EzQodBOfe4QpCAeMHO5JlB\nilxxQuTGBUKRFd7BX2ySQgdz+kBuDsrBWS+qQrfEyb/DyR9CKeqBQhFc4UF7IafAq5O+Qiis\nicF4ppe/AT+FQryl78EbyovANM+CQrrPC0KfOOK/2NmzQjdLmMD+W51ByfYxQs4WhEEsehXC\nPPSUQo5xuUGOlpbB9kKcQvW62MEtZgdCGUaGQm6WvEHEma1B5CGrQifw4sBoJJHBarlVQnui\nRUCoft3BKb6zQn1bT0Abh5hB3KemQj0Ez8FFy7BBcpNGQvFjGELhzUvBtZOwQvM/EkEff2HB\n9DWxQhwPhcFNcwpCDjFLQpcJBEKOIEtBQF6mQusblcG+FWfBJtqvQpGaisE8D87A1RZeQr0h\nGEJBawxB0wORQuFJMcEO/Q1CinJ5QjH5mUGmYPzBuDR+QpMx9kEohpPB+ky8QkX7j8HkEoJB\n8ReiQqut6ECzuG9BDGMkQkNfJUCX2RzCrSqMQv+hZ8Fcwb3B13s2QtdmAkL+5rJBbH2ZQhph\npMGzUf9BUIWZQivGrkEx0q/BQLsaQs3tG0EYjQbCAxmvQsVNJz4PZ+tBvxKoQudtkkHC9MXA\nIiSqQj0yCsLUqUFA97y5Qi+aocEhiJc/IZdyQgJgGUKfahPBLJKKQtGRW0CdywRC4gmCQtU3\n40AzwvzBEFRYQvk5xkGpn8nBp+e2Qg9BdMGZHL1BIAOHQo5q1kFRvEpBulRJQsPWY8GhPOLB\n1NmWQloQZMAG0ajBZViAQnFTtUFDau5BExykQmK3zMH6St1Bb02wQk0/h0HFVEzBCu4bQtRg\nlUEBKAPCWz62QiM2FMFkmANCEYCmQpl9T0Hx2ZU8ch2bQssa68GbuyfBsCaAQv88psE6jW/B\nbuaXQtX2yUE6WrtAaia/QsCIAsFTPVdB+cxxQjsnu0G/XvXB5K+OQjIk4kFwb2HB0SyZQgU2\n+cHDKQJCyJGQQgISFEHQEdpBtb6fQl315j6F/LfB9yl8QrYmEsE7MeDBqkmxQhmFQkGNzC1B\nUGmrQtRch8Fv76JBdp5aQqBoCULnNdXBg8qcQrmZ2EBnzqjBvDKtQhrnbcCldBtCWDs0QhfS\nE0KffLQ8tZaxQr3R/sFMp0TAieOiQsSp5cE07OK8QVqxQjuBd0E1EJjAh9azQuOB4L+PC85B\nXYgYQmCqWkGUYyfC6PmlQiFGhEH8HYbBsBiqQhdovMHZrwlC3MZGQq8x0EEKQYZBs/OSQkO2\nScFM5bvBtyYdQnwLYECJQBnCTKGlQlx8P0HF+sRBkfO3QjbRe8HVbFBB4pd5QoPsCkI1EJTB\neI2FQtR4WkEpO7HBDOiPQghzS8EmUQ5CY6tYQkQyDEIADgRBKD6wQgALqMFJXh3B3TCrQjFR\ny8FcoarAvgVHQuqCHkIqAM5AWxGfQp/ew8DznexB9hykQsy7AkEf8KPBk+QyQjWy8UHBuKjB\nwjWrQlYBx8HeXutB+jelQj/FW0E7MnlBmxdFQrsxKMAQRhnCtki1QsINJ8HPhenAiNh1Qn/W\nBUIqUVtBXX+dQqxElMHK2QxCYOqQQrvaq0HZnsvBcZgtQoT8+kCXNxvCHI+1Qr16T0Ad97lB\nOXKKQlEw30ERhzjBKjiSQqd9B8KYuiBBwVa8QtHJfMFX5wlAHDiAQuQ/DEJrplLBy5uRQr5n\nEkG/OAtCiaWIQuKrikBeLfrBgzpdQnejyUG5cOrBwGa8Qo/iTsGet7hBMsiMQqBzvkEynfVA\nQ4VpQvhlncHd+77B5jGSQhB9z8BAtMTBY2NuQqAL40Hk0eFBI0CeQjf/tsG5rNhBtQisQj+d\nkkFmG3TBvpwXQtX/XEFOZOvBtnOzQpfoysAfigVCWLSeQmf6rUEktwHA0kSmQoO1BcKds9DA\nHhiCQp6cz8EqvFvBqpeLQuSk+kH/VpZAuSW3Qqk048Ax2mZBBKpWQmwQqkHfbgLCcciFQs7V\n2kG7c4HBxcaFQrw04sFjYfRBz22GQhJGh0EazO1BdUCTQqeciL/kpt3B9eSFQnBWQcGA46jB\nn9+wQodNRkGa2P9AkIywQvzJh8GrKL5BA39WQsgCBEJA3u3BVlWbQpYdt0BqcLvBCeWpQgvo\nNj/xJxtC9XA1Qv+zCUKaDKzA8KGuQlde98HIWgM/pISlQvQw/8ETYyDAQQioQm9/rEHNTYXA\nMcCvQnWabcAJbNRBbQESQqJVdEFsxx7CKRigQot3i0EkvX/BpY+iQvgTysHrpAZC8eA/Qtm4\n0kExKatBMTeGQiMoHsG5pOrBpa4kQnOGkkDtYBHCcbOqQtxV8UDUwr1BR4S7QgDOh8HtlVJB\n5rp/QqRkD0LxXYLB/YSGQvcWlEHgTszBbESaQqklgsGJcRNCqiZkQipB5kH9xulA7/CvQiC6\nicErQyXBmxSqQrOXhcE+7pvA3hpTQslTGUK46uJA3cGqQkNPCMEgcPhBp4qwQo6B10CuHoDB\ndoJKQl6ECUJcTqfBi4GwQiw4y8FnZttB8j+vQuxEzkBFVD1B2vlTQkFe0b84QQ/CmcqyQqNK\nK8GEAibB63p5Qp9KBEJCkohBn8uOQms+kMHezPJBPqqRQixcsEFkpbzBNjQ4QqgWCUH6uQzC\n8dO1QkF2Rj/mB7xB6VCHQhin+EExgB7BKemTQiHoCMK8JuBAhNWnQtnq6sGS4eM/2RM3QhhN\nGUIOtKDAgDGTQjMUoj8cRvdB39KUQlOeukD6WtHB8fZCQuGI40FLndnBuWqzQrXOkcGEvt9B\nU5CsQtH/ZUE2z/ZA9teEQvNXd8GCkuPBJZylQhEnTMDATW7Bn/eKQo6/k0GGmuFBr/GIQmnd\n18ENv+xBvWqMQsRp+EFR1pjBRFxqQqyAkkGlFfXBLpDBQpOM8cB5LZZBADeYQtsisEH0gKM/\nY2uVQsey7MFw8FXBfaSFQjr4pMHS7ofBAMCeQmbDt0HGRcJAoGitQp+TYcHhzOlBH8IlQlBj\nz0EwDATCQequQkqyZEFuBgLBCxKnQnWw9MG4vulB1DR9Qk704EBczslBgziIQs1mMkCEs+rB\nzJ4hQvhkr8DkTf3BhxyOQsUyyEFplbJB3WiwQl+kicFv/Z1B/FFoQoKxDEJQhtDBGQ2LQrjx\nDEE7y7rBKo2fQolGmsBJAxpCwX5qQhs7CkKPw+S/7AG7QpJotMEW1lTAQhSDQqjn6MHAdxS/\nBECIQt+iAELL+LfAryG4QowEO8DaJ61BDRIxQoQLYkExDh7Cg4+WQsdcukGwGanBXfGnQkY6\n08FU1g9CjmeFQm8BwEFyNYNBVQ2yQoOpC8FWlFrBWZExQijcVEBvTBPCLP6sQui7B0H1GK9B\n+hSeQh1Mt8EziqdBdGY1QvcqDEKECIHBeKmoQk/bEUGDXn/BJG6tQmd4XcE0fg9Cn/0/Qof7\nEUJvoFJBMtKmQo1Nv8EDRljBmhuzQiSLrMFQsarAIK1eQqCYH0IFkr1AkEmHQvC+5MCDNANC\nnZp0QvcCgEGYPv3B8SR/Ql4UxUG2mI7BUPy6QuHSgsFl9YFBQ/KeQvwlW0FPK4lBlfMuQiRw\n5L4CjRvCIsSZQgoofsFvaIHBMclBQiSIDEJekKRBAJulQjLKk8EAsQVC4wejQtpui0HFuqXB\nbCgeQrARDUEoWxrCU7uxQh57P0AMMdFB4jO0QoneF0E8m6HAwCepQnIrAMJe0rNAK3KaQhJ1\nCMJ+2AxBnb2TQicu70Fw7z3BxuO4Qh3AcL77kVFBq9tIQpKs7ECqMRbCb+KJQi8BiUHDVqXB\npKyQQhk8lcFV0wlCnQJgQkIxA0KdpmNBH6arQlSbfsGiJWbBfJhnQrzApMDoMe/BHy6vQuct\nNEHRk19BiICtQjrpssF/MMpBvYZbQm5jEkJwEMLBYSKWQuSWJkHacb3BN/ewQolm9sC01iBC\nb9UlQh0s+kHmeJU/8cKoQs+p7cFB59XAOKC0QmQgT8Grh6zA3ShtQsJKDkI89gZBCyiaQpUp\nUcH+BQ9CGtqJQqyZk0EYruDBmhBkQpaADkKfYJDBNTy2QnFfv8HufbFBV4CZQtZUJUHixbVB\nz+gBQr1im0DpUxrCRkieQhq/KcFNio/BLi1kQgDnAkKls75BNG2bQmakgcGCKbBBII6xQsaD\nS0Fx01nBpqIVQpsJNUECmwbCosKwQvFfxL/4o/lB2MakQlxioEHAAq3ArlOqQiALC8I+BUE/\n1IesQgcmv8GMIjzAAA5OQo/uH0IRIvE/m8OrQgGvh8CrugpCSNymQiof2UBSeafBBZQ7QvXR\n+EHvYL/BiuSuQskFusHpcu5BvRinQgapIkGr8RtBtr5dQjLY+MB0MwTCpHCvQhBKWb9nYX7B\n2qKgQnxjLEGn8/1B/gWVQsQt68GJVedBRUyZQp4H10G7JFfBs55hQrdMpkFMjtjBNcq7QtJu\nMMHDD61BlAyLQiVs4EH9HOlAD918QlbFtsGqZrDBQrmdQsyv1cFj7hnB3j+uQncIikH8Ja0+\nkeGvQnz38cDcxrBBCxMsQpPFp0EvyxLCMTKlQu9/kUFymVPBn3WkQgau5cGouv9BVetjQohf\nokEvNN9BgBuAQsYgCsBLMgPCoyRZQiX1YMGPvKXBuUiSQnjjyEETEUFBp6q5QkuOQsFdd4dB\nvSdrQmdk60HemOjBP+loQhCt/0DqJgPCPPKHQvvCjUA2BRFCNphaQqi4BkKRagvBzki1QofX\nzsEQ8dM/b06PQvhk6cEeWYpAcR6aQjGZzEEKlxrB4HihQiRBS0BWxudBgcjfQSvPH0GxnhzC\nk2+mQqY+a0H2+ozBi4atQsf4o8H+jg5CxjhAQpdo+EGaJHpBnoKaQqFXiMEg+p7BJJcdQkcE\nsb9QxAjCi0eYQqXrl0GWoL9BXuSxQs8IhcHNUIJBkbNsQj1ADUJ6ALTB6LCPQk4sPkE3kcHB\nHpuqQgyoFsHFAx9CFjRzQr2UC0LBixlA37e7Qnd5m8GSTqfAShSgQt4EscEUEFfBPiw+QgdS\nFUIhQJFBY1igQnGqU8EYQAFC7h+gQqkSZEFGmazB1UdUQp5F/0HspnfBf5awQqGhxcFslbFB\nyxy5Qrf0DEDMC0RBltNTQv4YY0BQSxXC7aCiQuJxCcF7VH3BbxJwQpEV8EET4blBL0OFQjDE\ntsEdbPlBPKSFQi1X9EFeCczB5x5UQiwdQEED7APCqiq+QrLFXsA8jqtBPnCUQudxnUE3jInA\nzaOZQk/yBcIELoO/XZG2QrFLo8EZ1Qw+VhtqQtEcGkJ10tbAYpeAQq4W3D4llOhBY1Z9QmP8\nCEFe8vrBBxmBQgn2v0FSGrnBJDi/QgHbQcGr3HZBGW+cQsi1oUExUTJBi/9nQlBRSsE6cwLC\nqOuCQiU2z7/mz83B4Gu5GeXQphktLd8ZxYniGZTQGxnIZGQZud/yGUx4zxnzwakZotCLGbgk\nuBnYzO8Z4+NYGc0uYxmK8fEZtOXvGeg7WBmbrzUZsnrqGcx4vhnU9s0Z5sKdGSND4Rl/qtQZ\ntTgfGYrSQRnloPMZTDW/Ge3chhnoy9oZV93HGYre1xnJHmYZu7RtGcW38Bm2SMAZve2aGfTb\n0xldmOoZtbniGdqZPhk3qy0Z623LGaZd2xnq98AZ35ieGSOo1RlHvsgZ8J+zGdfgbxlaFbsZ\n78DrGXDnSRmwb0AZhp/pGSp8thnufHIZ1Oi4GbdB3xmqptoZ1aorGdZpqRnI9+sZZNrBGfjF\n4Rne0Z8ZPCbYGdm84RmV2UUZrUAxGXO06hk4UrYZ67KDGcfwvRnpRNUZonPbGaG1GRm9UWgZ\noOPtGZrI2xnQJi0ZtNdeGeK/9hmxJYgZbe5nGefSrhk2PeAZ0t7zGeiGXhl2t08Z6JrrGXxb\nyhny8LMZ44OnGRnNxRnM7ekZ4ViXGcm2MRmfivAZvE7MGcryqBnJnJAZJWe0GaCp6hnljSUZ\ngdR2GffS8Bl5QqgZ0ueCGe28whlFw+UZNsbMGfCRrRnt79MZkWDiGduH1xlPwx8Z2H9KGazB\n9Rlwo8IZ8sG/GcPhkRmeH9MZqqfqGeWvLRmeNHYZbObCGavp2BnjMp0ZmoA7GXhw5BmzZc8Z\n4PW/GeaUjhkaj9sZlqXPGb5jJxl2z1YZ87HvGXI2zxnr8ZMZ8NTcGWHb4xk2jacZ6HWBGd/t\nrxmZNuYZ45nSGTTdIBnGo3YZyM30GTh91RnzrpkZoNd2GaIcsRnMw+kZ39BOGb4vbhl/7uIZ\nYcC+GfCwxxne3IIZURzFGfPM9BmL61YZwmg/GZOz7RkvMaIZ6LtpGbfathnWO6YZwpbaGZjM\nJRnXWlwZuuP2GXnR4BnBGDgZtcZTGcuo8BmtS58ZoOmQGfDbyBlTbuYZvdXrGeJeRxl4vykZ\n2XnwGX5u2hn58c8Z36nFGTDZuRmk4tQZ3CuOGd7DaRnSzfkZmDC3GbrsehnmxMAZSpzcGcXP\n8Rnokk4ZW7dAGeuM4RmGYrQZ5u2pGdp9khkcqrsZXLLSGfW5vRnE1JoZiCjHGeOr1Bk93DMZ\nqHUnGWNr7BlEc7oZ66uTGdvyzBnfVugZmHzjGci9FxnMU4MZrPLuGSzHsRnwgrcZ7efFGWtQ\n4xnFg9AZZrIhGc9eRBmgx/IZc4DQGfbizBmm55sZ4yC+GdK36xnE40AZnUZTGX7R5Rna7/AZ\n4mmPGcnMPxm4mvEZ2FPVGcn2tBnbqo8ZIlbJGZXJ5RneIR0Zq8xvGebF9Rk0MakZ6cRxGe6t\n1xkz6K8ZjOfLGdsYlBnRsGIZv771GX43nBm65W4Z6qupGTCi5BnS2/EZ5I9fGWLRMxnyke8Z\ngFLRGfH0rxnSkaQZJbq+GYO6yBn21NkZ2ealGYss4BnvvOUZWudEGbB5OxmCkOkZM3nIGe+f\njBnc7c4ZzlvgGZOC2Rm9qB4Z1kWJGab16hlBxr8Z75i5GfXz0RmEW+wZ3IbhGV/MGxnFfmIZ\ntczxGV1k2hn34r8Znd6ZGdMkohnQtuAZrdk+GakuNRltx+oZwejtGeRLbRmZo00Zs4jnGcJO\ntBmx7qAZ3aJyGRg7uBmkwtsZ1j0pGaXXaBntx/cZOSm3Ge7TcRnottIZQOC9Gbvm3hncRZIZ\ntJwuGYd17BnIZN0Z4fnIGeKpnxklg9wZd4naGbdnGxlquFoZ7aThGXc8tRnb6ocZ8MDJGUnP\n6BkrhK8Z7G1uGdXltBmmPdwZ06bEGS3OKxnTrmYZw8P2GV2DzBnzybYZpuSFGc4Z0Bnf1fQZ\n6edgGblQdBmd7OsZRdHEGfOixBnSy4MZRCG1GevG7hmQ5FEZylErGYSy8RlDTKIZ5rp/Gb3s\nuRnkQb0ZyJzoGbXkIRnSc3wZzu33GYfQ1hm8IUkZytRJGcep9BnGOLsZqO6PGerc0hlfgN4Z\nos3rGeY1KBlxnTAZxWfmGW9oxRny5LsZ4Yy+GR3QohlszrMZ9cXiGebPlBkmHN8Z2bDpGaHi\nOBmZRkEZdrrkGTZj1BnzupcZ3/PXGeZk4Bmtk9cZvcYlGdRWeRm18vUZndXlGeAiOBm4yngZ\n48z1Gaw2khmG6XkZ7tS0GTtO6BnL1+YZ3HNWGXK8NhnhhewZYUHRGfPpnxnTf6UZILOiGcvz\n8hnqVIsZwLw+Ga2R7Rm7YL4Zxu+uGd2dgRkcWcMZkJLeGc6PIBlY21MZ97fkGXgpphnC6mQZ\n4ayuGTSo2hlEo7IZ6YyfGerwxRmWT+cZ4XXSGTjAFhnJh04ZpbTxGW+m1xn41M4ZxNqsGbUx\n1RnDvekZ38FHGboseBl47tcZMreXGex8vBnm3cIZWkzcGdSm4hmR2y0Z3XZrGc7n+RlWcMIZ\n78epGZLeehm/Gq8Z2MXzGdzrTRnAOVkZi+TtGavW5RneMz8ZiKsmGbVj7RnVXLUZv/W6Gde+\nmRkmMsoZf6fgGcdHGhmGxloZ7K/xGWpSrxnq45YZ8c7dGVrm3BmDyb4ZqiJlGcu1Qhmqm/AZ\nqS2/GbXvfRnqycEZTJXgGbG66xnkkDgZTKA8GeZ20hmfatIZ7fbGGeiUoRkav+IZTdXXGfWu\nxRnZ2ooZWSHEGe7V7hmM4WMZy2s6GZSy8Bk3ep0Z5XBvGdTqqhmqMOQZlZTgGdSaIRm7eqQZ\nwfLiGVnPqhn0tN0ZzL2cGSsowBnp1/EZt+NlGc88OhmX0vEZPlybGeSbdBnB6q4Z4jbgGa+S\n5hnb3hsZz2mKGcP09Rl6xtcZtB00GbnVPBnJnPQZwDGmGZLughnq2MYZUmffGbPP7RnmWjoZ\nb6g4Gdd46BmJe8UZ9e7LGeScvxki3sIZsdvYGdY3dhmgmC0Zh2XoGc5W1hnQ9rYZ2qWSGSNm\nyRl/hd4Zwn8bGWPAXRnvrd4ZizzBGdTtjxnzycwZUsLrGSiuvBnvdZIZ5uq6GYdD4RneptkZ\nVNgsGdqbahnM1vgZT5LGGfCspBmx33UZlhe9GcnF7xnlwk8ZrEiBGYzo0xk/u6MZ64+/GfLq\nyBlnUekZyX/bGXK+HBm4bmIZrM/uGWyC3xn65NAZsuSmGd0txBnUwugZyd1NGbswSxl93e0Z\nz+jzGelZXRmYvUYZzZbsGc1VqBmx868Z5L9+GRcczBmcsdYZy1ooGYnUXhn0vPUZUCesGezp\nZhnlrcEZNd3ZGZjm3hnhHXQZzcNxGdPI9RmOSZYZqeR7GerAtBk9huEZwsfsGeKgTRk8wywZ\n8H7WGY9BzRne8Z0ZzYGJGSGOrRltxMkZ9MLQGd/kkhlnHdkZ7anpGWLnLxmlXzIZbpDoGT2T\nyhnwm5YZ5e7YGb9m4BmRjdkZxZgiGdZQohmx9eIZjtPCGb4kfhnSsFAZs630Gagw0RnJ8o8Z\n8dPQGV2t5RmquucZ4HkxGVaQQhnicNkZkGbLGe/zvRnkjaMZG8jWGUWx1Bn0pawZwteDGXkf\nuhnrzuQZZt9bGcSINRmLlO4ZNWu0GeuZfhnQ8LkZ2kDpGbOh6Bni0icZz3qeGdL39BktqagZ\n6muWGeHhtBlwPNkZ3pfeGV7TIxnahVsZvtH3GW5vtxnw3LUZid2IGccekBno0fQZu+pcGb5H\nSBmY0e0Ztt/nGd4/WxmdtCoZrnDtGdVoxBnP98YZ2L6lGSpH0xlup+AZuDAVGYfHRhnhnu8Z\nUkGtGejXhRnvvtcZR+bLGcLx5xnpSqEZup46GZSI6xmnVr0Zze6lGdyEeBkacboZpLDfGdl6\nKhmB22kZ+MvzGWwzsRnd63kZ4sHIGUrI2hlVm7cZ7J+jGez01xm2Y+gZ44jJGSPSGhnLomAZ\nuLvzGWOQ2Bn30sQZttyfGcwn1BnCs+gZ4Ng7GbUnXRlt7ugZc9TIGffP4hnq4J0ZSiLlGee6\n7RmO5UIZqFVAGYCw5xkvQ8gZ8L+HGc7tyhniVsUZrozMGZazJBnNS1wZpeLyGY7e4RnSHlMZ\nwMNsGdC98hmdMpkZjuZpGeq+pRkqYuUZ0OPuGeJiXRmUzTMZ04/wGUxH2Bn136kZ24S7GR7C\nkRnU8ukZ52C2GdSjMxmWk/MZrVLVGd31shnXkY8ZI4rEGZGr5hncYBsZiM9oGfXC8xlfPpkZ\n4eRzGemtwBk13uIZNKG7GeyCkRnh58gZm1PcGdN8whkzsR8Z0Ys+GZyo8hl0ncsZ9dLJGb3l\nmhnFH+AZvLHuGejPNxmrNGwZdunZGVjb1Bn3u9MZ39uUGVIh0hnfsOIZeNs4GalNKhlom+sZ\nQknBGe3LlRnR9M4Z6ljCGbuF3hmYxh0ZvmVqGbPf8Bmw1toZ1jVZGczXcxnj0vgZvi6uGZbu\nfBnq08EZTG3fGcnf8RnnZFQZhLVGGdaR6hlfUskZ8eGpGeF5sxkXxJ0ZeN3KGbwZbRnArFUZ\nsabvGaQ5vBm87IgZ8srCGUud6hnAx+QZ25BKGUm0MRnredoZoWncGfL5zBnhpK8ZKsDbGVii\nxRnwrK0ZwN99GYgZxBnwzOUZWuVXGaqMUxmUlekZJlOoGeyNYRm95KkZ2jDdGauX1hnHxCYZ\n01qEGbf19Bk8ppsZ53qpGezntBlkPOUZ4ZfpGXPlHBnUhW0Zyt33GURSzRnxzp8Zi8yEGa8i\njhnbxOwZv+FPGcQtPBmB1vAZrefrGeU0YxmbojcZoXLmGcF3vBnR88UZ4rWXGSBR2RmNvNwZ\nySwgGaLZUBnksvYZRjzNGfHZmBnw2OcZZ+LBGTDOoRnxjcsZ7+bOGWFb4xm9j88ZgbklGc9V\nTxmr1/MZX3/PGfTQuRmj5YgZ1hjNGb+l6Bna4iwZlzhTGWre5BnL5OoZ31dnGaTDMxnAh+4Z\n1UG1Gav0ohnXr38ZHjG4GaHA5BneTygZoMOCGfHQ9RlTO6sZ6Nt+Ge640BlA5tIZm9/cGdYk\nZhnb1l8Z0sL4GacurBmd6m8Z4sS4GUB52hnT1fYZ7bhdGUfIQhnvmtYZomPHGeHytxniiIsZ\nG5rPGWnK2Rn4ytMZ1d2lGXsr1xnpuOMZYuBBGbpsKRlzjO8ZS3ynGeeWihna7sMZxk3lGYJ0\n3hm7mRgZw0+NGaPv3xlUuJ8Z7J/MGdnAcxknGrIZ47zwGaPoRBmyRToZgr7qGSxMsBnspHAZ\nwuW3GdxAzhm4muEZytoiGd1ffhnD9PgZgcrjGcwYJhmuy1gZ17HyGblLnRmf65gZ8+XQGVxn\n6BmwxeQZ3GI4GV+rKRnhaOwZfmHSGfXxvhnToLUZKcu+Gbvs5BnjRJMZopNHGZOC5hnDdtEZ\n4/fQGeisnxkkg+QZhpLSGWdYgkLNkAJC5ClbwRdgrEK7/GhA+S2iQffzE0LDIwpByv8nwpKR\nikJB5Z1B4NjGwVJdn0Jcp5XBRTQUQl0yaEKq4PFB0A8iQTLZsEIidHbB5IIzwQtWXELnSdPA\nE4z7wZIKqELWTndBF5BzQSitokIlhJbBQwyvQf1+RUKuUgZCBiHCwSvfq0LIJZNALAB3wZgV\ns0IQnbTAcV8TQpLiSkLsJxpC4NINQIgqtUK0/eXBbEmVwFf3skLdJ5jB83XswE9UY0IiYBxC\nPLAhQR0hjkKG5ivBLqQHQmgPgUK1/pFBJjTvwUgsgkIZF9dBCNOEwYDAvEIBx4PBSTJmQRao\npUJN3x9BmkWYQZA/JEIYi3JAOFYkwoRkjkIgI1DB4+WfwTOmOEKwx/xBVqaoQeuxn0I6H63B\n4lEFQv2Tm0K1ULBBbCWwwam1FEKy0zNBF8wUwtMEskKZZHw8vNDvQVymrEJeFDxBtBCNwBbN\npUJU2gLC6PUJQIE5qkIIltXBlQ7cv3hzQkLJRR1C66KPvhjFlkKEbSnAlKjZQW23n0Jp9sxA\n9fCwwdYAV0JGjOxBQRHFwYl5tELW0ZbBmI/EQYo4tUIofQdBReDuQMqeg0IvQS/B447uwZ1Z\nnUJBJ2O/20GYwYCCjkJBf2dBq7n/QW/HgkLwWt/BghrmQUflhkLnCgVCcMSFwVELY0KsrK9B\n3cH0wcegv0Jfbx/BXFapQWslkkIm7sFBRBGQQNuvh0L7fMvBNJOZwZAZlEKU5ujBA/MFwduY\nmUJCp9RBDcJfvmVgsEI8YwHBuGoEQpQhBkKCI5VBGWQTwk/zqUJEzYtB3elawVHPqEIi+97B\n4LsAQsleYkIjB5lBxE+6QYFZi0Kzr43AQjzgwaMyVkI+w4XBEDLFwSFaiEJPN/hBVjZaQRpi\ntUKi1kDB1j2UQRbrWkKYVuVB4i3zwQc7kkIdGTxAVqGtwXxllUJKidFARRISQjG6e0LoHQBC\nuU8rwU9AvUJYhKDB4fzwPyaipEJp/frBtPJUQOUcrUJ+lJVBGLXiwLiQq0LD8zA+6X2xQV3F\nD0J7HjZBJQElwlRDpEKgXzpB9jNgwfNnpEIm2KXBixQIQjU6OUIdwvRBsaeKQYn1k0JJOoLB\nOUa3wU0pKEJWXCW/st4HwsLSnkJvu3RB4ha1QQtHuEKU3pDB5dSJQdEDeEKMUBFCjvStwf0r\nc0Jd4IRBXePrwWEqkkKtPSTBzCMUQk21U0IlEAJCAlRjQN8rsEKlmbbBcUz0wHIkoUJqCIrB\nwOUzwVzJRUIOKxRC9mqNQRwwp0KHEGnBK64CQpDUp0IAXFNBxvCawbOzNkK4kA1CxkRhwXYS\nrUI7Ye/BsMDjQT6xrULSeIhAdrSSQRZSIkKqWttATOwjwgJ/s0LtQdfAX0o3wYYmikIu+89B\nJrWrQc+ljkJl1qzBHh3gQVEamUKn8cFBy2CjwQXES0KwRk9BA6kBwsnOvELlfIjAiI2+QUOE\nikIP1O9BzjJ3wKD1mkKHlQfCT2cvwEkFuUIXa4LBZRMJvwNOdEKlBRFCV+q/wFrlmEKqUda9\nZ1MTQocfhUIhmxhBqXH6wZe7YEJj8elB0fPOwYhouEK6/4jBZuu5QebgjkIw4IFBcLgkQRjN\nPEKAWyjBZnLuwfFnk0K4ymG/82LMwQ9Wi0KOHJlBvCQFQpi4oEJHgMnBaKnDQTvvskKkSmpB\nDh8hwXQRLELrCJlBOcrkwbJhs0LXtDXBkaryQQrPpUL145JBrXYNQNrvmEKtMNzBXs94wXse\nmELqbvvBBuDdwCaik0LIVuxBlIucv2zGvEKW5KTAeIJdQZe3XULA2IJBuOYGwmBPjUJuFdBB\nOnibwWM2kkL1INrBRKoCQtDVgkJOX7pBx/3FQdNioULmOcHAts6zwa7zh0LN/oXBCjaAwdDD\nqEIFE41BVVe/QB7LrkKW3mPB+rzNQfIyOkI9puFBUvABwjjZk0Irg0ZAoRvNwS+amkJzNxJB\nSkETQnSGLUJPoQBC7W4uwaFZq0JBev/BRtrOQJX5skI6Y7vBfsw+QMLkYkKHvh1CZe4swdfG\naUKgNZtARfngQbIXZEIwttRAe1YKwvDsbEJAfb1B5d/SwWAhvUKj4UTBtBmeQW8OkUIGkM9B\nvsQcQUx8ckIPi4/BfGLhwWxdjkLOup3AMrGdwWtxXkJDe75B4XDUQfWRnEKdz8jBifboQUav\npEKWwbBBJ/yGwWFvK0KVoZJBUXEQwsa8u0LLBOfA7JTwQTzTsUJtwTJBrF9jv1nVpkKRsPDB\nOnHwwJKyaUIwebnBVVphwaSUhEI/egJCAKzZQC3buEIXDR7Br1K1QaEDR0I4aMJBrQANwlMd\nlkKucMVBG2ZkwTEsoEItAfnBrpIEQhG1kEJfKRNBroSvQSQfqkKhrse/AkySwW5uX0IYNSvB\npDP7wWkmn0LEYaxBRK5mQbwdpUJzkZ3B6BzZQYS2MULTW/lBGo/TwXagpUKtxm5A03iIwbEM\nqUJFI7q/IKwRQr2jQEIMMBRCZUZOwJYfskJq0/LBO1Nwvx0mlkLdMN7BETdtwAKboUIOfbRB\nYHtSwLDMr0Kg+ZfAuwLwQYoTCUKUfH9Bj2IawpREsEK8mWlB+d1XwbYHrUK+WMrBUCkBQiHp\nZUJi/8hBds2xQRcQl0KqGATBoO7BwW81+UG9eb5AExAOwvppoUIYmhBBKOHxQTOCq0J3MonB\ntVNQQVEzX0KcgxRC4uFmwcoQj0Iy0IFBWl6uwb6TnULcKIvBYFsSQnUxaUJqsAZCTY85QZ8d\ns0I6j4HByAMwwSetokJ7JszBA1YcwQudNUJH9RZCO8xEQVI1nkJM6B7BV5EEQlD3mUIQlFBB\n4yO+wTP/TUIzx+xBa2GPwZsWr0I4G73B0s2+QVYvtEK0u6pA+bVBQb39WEIBoLi+cjQRwgsx\nrELX8CrB6x1GwQEtcUJ85gJCva6YQT4+k0KMsKvBZPkGQu+8iULkAdVBWg/Swe3EYUIhLwlB\nQAIEwnYrv0Kho8u/WxCEQaBim0KMhKFBUdPcwIpRoULcDgvCZeaMQAbLrkKYp7vBD0IAQKug\nWUKFwx5ChVAHwV5So0IHnUVA3BH6QeHpq0JVPQ1AuX+YwQlKNUIyP9xBOzHiweoDs0JdCpHB\n4RTzQZENqEJ3kUxBp4W7QN5Wg0Iu2YrB7RzLwfPGqkIj/YXAp9iEwYG/jkIX1KlBAyncQQhy\njEJfvMzBh7vmQf9qk0IN5N5BzcaXwbprUUKPHmVB53DVwT7ZuUJoofLAeT/HQa3fhEJPc+xB\nQBGTP7KUhkKs3+rBZeEkwQjMj0JoWLjBvTV9wZ3WpUJcIadBNueGQEU7qUK6FybBOdGvQe1c\nLEI3DMRBIAkFwmyynUKkPqRBfCg5wTUyoEIfu/fBuS7/QTe9Y0IG6UlB/cLvQdhVW0KB/3RA\ni/QTws7cUEI6IRDBMJ3iwWw1nEL9hZ9BqAh2QQbfu0Lj2VTBebNuQR3efkLNMgFC367QwWGd\neEIMZTdBxRD5wagDnEK3UibAFRgcQiJMYUKw2QlCURlewOtBuELxHcLBP0USwPkfl0JKFAbC\n02AQQN9qjkKgiQBCI2nwwGAPuUK/mxLAgaSGQbd0QkIsDD5BbwkXwvRjhkLzjrBBHbWpwfyt\niUKNy7HBFCMDQkjFZULv8PBBcneZQUHQokJ5wEXBGFmdwfwgbkIl2Qy/vNfqwTktuEKs75dA\nexxUQd5Hr0InLLbB76msQcY0Y0Lq3BpC4bmawY94oULlUixBcyOawUdlrkJMNUzBROAWQgsG\nNUIY1OpB8SykQB7Io0L5EsTBae80we8HvEKn+0bBqs7+vwWHfkLJ0whCh7hKv3Kqo0IyPsfA\nPagTQjemkEJ+wD5BJSbcwRIedUJtowJCx1G8wSi7ukLqc5XBfO6kQTyfmEJ1qXhBwreBQXjK\nLkLfRULASpwRwoyKoULYEYvBHjxswWanTkLz4xRCdrmtQSqBoEIuQmrBY3fiQTNVq0LWF0hB\naHaIwRRIAUItpeNA3cQQwlLUpUKH6dRAErvwQaBwoEKXjrVB+zEpwdWCqUK+RQ/CX08rQcCF\nrkKlFOLBV5CmQHOpVEKfoSBCTDouwRUDmEKilMNAaXLrQYg/okIZCfM/A9CzweAII0LsS7dB\nBSbTwQv+rkL7BYTB02P9QZ/HnEJD86xBR67fQKOrhULEIabBxIrJwSPnsUL6yqvA+iw8wUZE\njULq47RB5a+6QSZzm0Lci9bB9l0CQrv0l0K9Qt1BrIumwSl8R0IlOYBBb6MGwrhhvkLB8L/A\nrAfKQQ56jEKms71B/CDjv4fvj0IXw/rBckbNwBaolkISgaDBWboUwcDLskIbklFBuMSyPz+k\ntEJZ5xbBVC++QZUVOELQurpBNmUQwqm/p0Lnz5ZBA3hFwU/fp0I6SvDBn6P+QbqTbEIOSmtB\nnj3XQbkgg0JNyla+4PX3wfb4V0JT0D7Bh5HtwdjJl0IPe8BBkyZoQc+HuUJzAzHBHStQQYTP\ndkJWbOxBweHUwcYre0I8pQZBFWfowciKj0L+x0Q/QPYTQjpYYEJjLBVCF+m1wCzYuELIQ9DB\n1baGvmogmELKVv3BwrwiwP2xlkLan99BwZ+LwGHkv0Ixi4HA5tiBQQIoV0KOh2dBQzgSwj0b\njkKCjc5BTv6swahtmEKSf9HBr0sIQlrRfELkm7FBZ9aMQTGRp0INugbBLFqPwajSTUKLgntA\n+K4Owj7Dt0KJPoVAcX6NQfhnqULoLbjBDh6eQao7VEIQWBlCcZlywS0loEJZNShBQCaDwULG\npkJZdXvBh7gQQswgK0JnagZCYHtOQQFVnUKs/rrBHTmEwQHQtUJbbonBM7mjwNDSaUK/LBdC\n48qwQI6rh0JsQgDBsHLqQTQeh0LglHJBuv7fwfYkX0L3B/1BoVWfwY8HtEIZYa7B++KxQZRT\nlEKNboJBTgC3QY2DCkJFTuU/gt0Wwr9Qn0IqvkXB2glcwcpyVELv7gpCzOOoQQjPpUIE4ZHB\nJI/xQchbrUIitWFBMa2EwfkyAUJxyiJB6VMYwoh7rUKFQjBAwq8BQqJbo0JNKJdBRlLpwLHQ\npUKCeQrC8zGsQMIss0LsfpfBE4PIPt59ZUIbHBdCfkzuwIc0iEIVaQlAmaUJQsLebkKeNRVB\nyKQJwqbReEJ1dNtBPznTwXFgv0KQ2FTBSAGRQQsmmkL5ZJtBtmUiQTnlZUKdxFfBUR/7wTrG\neULnrcy/ZvrswZDOXUKosqFBQKvqQTpVjkLNQMLB8PHJQdVjnULORL9BwL97wWQaKkIRgJpB\nWv38wWsetkJNgx7B4RL0QVCNqEJo4o9BnaB4P0Svn0KV6urBPThNwUszX0K7iqTBffuewa82\ngkIpsQZCcJc2QYIQtEL4ZCnBQKSZQU0sTkLV19FBaj0BwtSol0LYlo1BqXQUwY6hl0LoM/fB\n7kj6QVt0l0KbzfZADQTiQYJWpUKbNkk/D2qqwQlKa0J5ie3AXrziwX4rrEI46VVB/iFSQTbG\nrEIdGqvBXTTPQTKBV0Ke2Q1CNy/PwandsEIX1XBALLpkwfvEtUITbanAdp8RQpUiUkLh4wFC\n9aXQvyvys0KEI83BB+d5wB0tnkJHaALCLgs4v84lnEL1NNRB2PKywJnppULyBiPAVc/YQVHt\n/UEVnldBNTsWwl8RrEI2wF9B7yViwXXWqUJUILzB5ioDQoSgWEJrR/JBxle3QbZnlUL40jbB\nw0DKwWFiHkIl+A5AYlD7wXA2oUIxNCdBM+HKQU/ytEKcapzBzamEQXgLb0IhlhRCrI2Mwf/W\nk0J4D3xBIxq+wf4OrUJM/HvBT1kcQgoOfEK8Y+xB/+HBQIzEuULwUGjBxrrfwN8fo0K/mMHB\nkgnJwO0QNkIdohZCt8LdQOzip0IfoQHBImwRQuPxmUKA+TxBs6XHwbkEV0K/zQdC6mqxwaCA\nskJ8crjB2DXKQS8mtEJj7FxAAAvzQM7Ya0I7ZoDAJrkBwuRcpULQ/nLBmalzwSViXEKQsBJC\nqjOsQSN6fUJQcYjBPF30QTpYe0LGuM9BePrhwbL/UULVVsFAyhr6wSi2t0Jb3Wc/UdObQaAU\nj0KXi9xBLmgnwa9gm0Lc1wvCSy0NQXpBnkId9enBn6SmQGRnrEJBKJFB81oGwR1dq0LVw1NA\n9snGQZFuCEKE3iNBQUkrwj2En0IBcXVBhECYwbkNqkLwoJ3BrlgRQvz0MEK8jN1BxpY3QYTK\nlkIWJ5XBLFSawZqKQkKZGEbAfYYPwmt4pELY14NB8vqXQQUKvUKuUEnBrsk5QdN7hELONQJC\nvYq1wcSciUJnly5BaXvAwcvtnkLDrfHAq00ZQsV2Z0IZjQxCkFaeP8Q9uULIN7HB0f6gwMJ4\nqEIW2aDB5GUgwbXdUUK5shtCgL9+QTmEoUJjzCrBoh/bQV/brULtXxBBUoiCwROOPUIQDANC\nyz6EwYtsrEK85djBbqjTQTDmr0IItsxA1cCKQaNvNUJuTYRAfGAhwvbPrkKJzOjAX5wOwfTT\nfkIHDeNBErqYQTQukUKnXbLBDHH/QSe6j0IfStJBer69wZzLLEJatUxB3w8Vwp1XuULR9grA\nj7rZQQ5BhUKt1N1BQGjBwGg8ikJADwHCYxBhP0X4u0LbJpDBWmcTwO+6dUIiXxdCqo3RvzLN\nmkJV9IXAV8kOQo2TikK4GTJBpmjmwQ8LakLQVdpB/amtwakXt0I0vI3BSvemQWZ9kkIHB59B\nKoSHQSH/LkLOr6LA9VAMwlMthULxFApA5X7gwbfzhkL8R1VBwmQJQi79oEIbCe/BpWjmQbQL\np0LsebFBJKI6wZ3hGEKWz7pBNm7uwQd0skL8W3jBWE8IQn0HnELRC5tBcViVQMlpiELluLvB\nrhCowcGwg0LkP9DBcZ0ewf+Gj0IFSOlBCEH9P2mmvEJqM+TAUY2SQVncVEIdbKNBHRQLwmfJ\nh0JgfvFBqb6YwURMj0II9ubBj/UAQiflgkL2XYxB9mrNQfH/mULt9kXABo/Ewdkhi0Ktr3DB\nWR+twRxBskLnWE9B6mvXQGp9rELdZEjBQVGOQfZqT0INHfJBUDPtwV+ooULFyDhA6xuYwbkV\nokJ6QjxA2YcPQtMPPkJl6hJCdb3mwPwosUJELPvBPd4rQA6fjELoWwDCtbe8QP9vi0IwswBC\nEp8qwY1Hs0LRNzm/PQ1oQQ5rN0JfvRJBLLoYwrOGmUJbaIZBPHuawZYGpULa86nBB28QQjND\ngULYEe1BicJVQTE0t0LhlDbBjsMpwVDpUkJRuUnAV+XywY/WqULwLj9B8t6HQbShp0Kgyr7B\nYoPPQUJORUKHNg5CobCtwZv0qEI/tv5A09ORwWWRtUIBCxTBD2AYQjGlREIzVxBCprSbQDpy\nrkLaA9LBPKv1wNEmqkKOgYjB+D0awdskVkKdpBdC38pyQX5DiUIjeGPB8LcIQhSxckJW1bdB\nIYb7wboWc0KK9QNC2ex/wcyeuUI+v6nBNFWPQaVPoUL7RMBA4zKZQYHpDEKZAclAOkcgwg/V\njUILniLBTyDCwYa1TkIZXvVBsN3FQSUqmULL26rBtMnlQd+gokI9YqVBA7uVwRzGNEJcNzJB\nb2fywSJStULMzVTATTzeQceor0IHgFxBd6VYwH7Hq0IVRAPCZ83tv3uznUI5JMXBFdnDPvr+\nKkKssRBCAvqYwKzymEJLDGc/ku8LQqEpj0JEPu1Aa8vjwVZwQEKtlfBBQvncwWOls0JcwZ7B\n5PHsQaHTqkLkBkNBbpjtQEjmfkIXFWDBb/TiwfbMnUI3Pg3AmPirwWacjEKNmJlB+Ib5QSLY\nekKIcMjBb8/SQShciUKpCfdBbpiRwcO8aEKVT5BBBV7nwbaSv0LFdgLB9ZecQWpekkISmNlB\nVjI7QLbolEKYuejBvUN/wWMfokJSAf3BSueswOO9oUJTWcJBfjUhwBQ7rkKEta/AyB3gQS1x\nDkJlwYZBd/gWwuINs0KJ5h5B38IcwcgrqEKST8PBSmHuQUqMbULoDslBjrfCQSiqlkJKzeXA\nEd/JwSppUULjI5TBaf6OwQVvgUKtHwFCElk2QTsgtkJL/UXBUvG1QemsTEJ4z91B1fkEwrP4\nhkLQckVAOn/ewf9KjUK3YzJBbXwOQqN2ekKiyr1Bncsswd7puULFgqTB2Q1cQKcJnUJoofrB\nkF/xQHojpULCwbBBhHYoweQUn0L41AxAJwuJQWxxAUKBhAVBdKEews4Ws0I9VtxAIIM1waHI\nrUIF14PBdAMDQiZDT0Kzew1C5neDQcHSpkL97JTBol18wSL0LELGNrDA383XwW/gkkK1DaZB\nzxmkQXfTs0I+qY3B5hWcQcg3cELq8A5C6YrKwXS3ikJ7XztBPE7awQ3ArkIq/szA8nkjQsgD\ndEJJR/xBCM6xv9nbu0L7op/B7k9mwC2JnEJGArzBY24owS/9K0IzYBFCrk5HQXfwpUIjg0TB\nOosTQiTwlEJSpnxBqoTQwYLHS0JswgtCBV6UwW07sEL4N9HBVO/QQUz8sUIWxnpAvY46QQhN\nT0JEeYQ/gF4RwlNHqEIJBi/BXDV4wVvXcEJW8wNCitOtQdA6hUJoYZ/BxC7wQfBdiEJHTtVB\nX1zIwYhtbUJUuudAgBTXwbRhvUJEDg/APPyHQSt4lkI4R8VBbbPcwGGWpEKZMQ7C+tBJQBCa\nrEKpRpPBZnHWvzG9VkIk3RpCrO/Rv+NhgkJbayLAsNgFQteGX0KHklNB/DoMwoCbcUJXA/VB\nDlTIwQ1uu0L+ZIfB3QGmQflSlkLSsItBY/5VQeylQEJ+bvHAm0IHwkK4eEL8/+4/AKj1wQYu\neUKmG3NBL1gDQqymkkK+/9PBFVfJQbYsoUJYtrZBh4tLwcisN0LFQbhBrhT5wa+ltkKunEjB\nf4zlQfbNrEJyqHxB6H9EQGMmmUI+WsrB4z+PwQQgiULKoOrBYCwbwQ1oh0IidghCUkMDQBwB\nuUJ8XNjABg2iQf71Q0Irn51BaQgQwlbSmkLnt6JBR3ZvwUUCn0KIMebBaBMEQn4skELkK4xB\n+EC4QVYvr0JosYvA/syEwWcdeEIZXoXBnie0wRUWnkJZkbJBOCoXQd5aq0LWtIvBZsfrQbU1\nLEKVZOZB1RbywbJkpkKwYwVArbCVweLeo0KiYZFAmKcMQtE6RkLUGftBAXoWwdGIr0JwFOPB\nMAZJQF0/kEKSO/vBEWVYQLiWkkKWxOdBTWAMwbroukIIBLC/lUqGQW4lREJZ0SpBwXMawhXY\nhkJmu7pBdOe8wRXzkULuYLDBK4MJQjmLaEIfc8RBHPdGQbVep0LkokvBkrx/wbu5cELMBd2/\nl9D/wbqquUIoJbNAs1tFQZwUr0LSj5/BMzSaQaBIZELFoRRCUmyowfxnpkI/zepAWt59wfSn\nrELI7inBsgESQvMaO0KxihBCtUP6QPk8qkKdNdXBPu4fwfJnt0KM1EjBBZ/vwGBFdEKD0Q5C\nkFtMQcqGlUIwj27BqcsBQnNakkJFuJRBsRfFwYDFaEJN/QRCorJlwVuStkJkcrnBsBmZQTVK\no0ID//FAtSTVQfIl90E8TQtBndkmwulzlUI6t+3AgCGTwYkjX0K2veFBjufHQQnioEJmVbjB\n0FfqQW/iqUJmnZpBgeyGwXw6BEINn21BnzMNwmkht0If+JfA+hYQQkv2nUKqxaRB0ttPwECB\npULVqQfC8p90wNTsr0Ji8c7Bgft5v/pUVkJ7oSRCbqyGv3MtoUKkBey/7TbrQaTTqUKuSYtA\nHiOXwcgHOEJYpL5BRjuowVLSrEIVLaLBua/hQY+DpkJYLWZBBBsfQdQObUK3GzTBDan+wSAm\npkJpKte/AraMwT9ok0Lxu3BBuVv0QThkkUJu5+nBl9z2QVD+jkIgPvpBhUCIwSbMQEKcq7JB\nHKoBwoVSukLZ4TDBdsXaQRlDg0J/189B0j2JQFgsakLPQMPBGJB8wY3XmUIamdHB457IwIY8\nqkIngpRBUKCgv7zVs0IGN9jAC1HfQVG9HEJbwZZB+OsbwpACoEJW+LJBrc6IwdUDqEK50eXB\nTEsIQrA3TUJ+R6dBUQ66QTTGgELn4aDAuPz4wY/SdkLSoZbBNbG5wRCwlkK4VtFBjpoaQbPE\nu0KTzwTBST8sQQt1dkJMLslBVmPgwZOCgkKYd4hA7SLewVBJiUIQXQBBtGMNQvoTakKtng1C\nnOw6wTu6uUKs3MTBPa5UQHxTpEIsz8bB1/NAQKBuPkK28RVCwMsTwYMimEIWYZ9A6KACQrTW\nmELhVn5AHOfPwXFsRkIru+RBjCz1wf/Ot0LRkIPBSZ3iQUnrsEJlITBB0Q6YQN4QjkIiKJXB\nXeK4wUqRnEJPZJjAzgilweAbgkI4lsNBHX3kQY1BaELXB6rBWbm/Qf2OhkIK/PVBWBWowYO8\nXULJTnRBuDXtwZ5+vkI+6tPABQmtQaLSjkLisuBB3uTZvbMMmUKmZ//Bgh0ZwYfljUKWjMnB\n0PeDwcdtnEKJoM1BgMaUQHpvqUKFjDfBVPLbQaFiG0JOpbpBMFsFwmznrkK0LjhBO378wFof\npELajufBF/TlQePUgULGYGRBtrD1QSK/hkJ7EpI/k233wa1wQ0J6CRrBXcXnwZ+qk0IJ8L9B\nM0KHQTn3uEKQgHjBzuSZQYpccULkxgVCkRXewV9skkIHc/pAbg7KwVkvqkK3xMm/w8kfQinq\ngUIRXOFBeyGnwKuTvkIorInBeKaXvwE/hUK8pe/BG8qLwDTPgkK6zwtCnzjiv9jZs0I3S5jA\n/ludQcn2MULOFoRBLHoVwjz0lEKOcblBjpaWwfZCnEL1utjBLWYHQhlGhkJulrxBxJmtQeQh\nq0In8OLAaCSRwWq5VUJ7okVAqH7dwSm+s0J9W09AG4eYQdynpkI9BM/BRcuwQXKTRkLxYxhC\n4c1LwbWTsELzPxJBH39hwfQ1sUIcD4XBTXMKQg4xS0KXCQRCjiBLQUBepkLrG5XBvhVnwSba\nr0KRmorBPA/OwNUWXkK9IRhCQWsMQdMDkULhSTHBDv0NQopyeUIx+ZlBpmD8wbg0fkKTMfZB\nKIaTwfpMvEJF+4/B5BKCQfEXokKrrehAs7hvQQxjJEJDXyVAl9kcwq0qjEL/oWfBXMG9wdd7\nNkLXZgJC/uayQWx9mUIaYaTBs1H/QVCFmUIrxq5BMdKvwUC7GkLN7RtBGI0GwgMZr0LFTSc+\nD2frQb8SqELnbZJBwvTFwCIkqkI9MgrC1KlBQPe8uUIvmqHBIYiXPyGXckICYBlCn2oTwSyS\nikLRkVtAncsEQuIJgkLVN+NAM8L8wRBUWEL5OcZBqZ/JwafntkIPQXTBmRy9QSADh0KOatZB\nUbxKQbpUSULD1mPBoTziwdTZlkJaEGTABtGowWVYgEJxU7VBQ2ruQRMcpEJit8zB+krdQW9N\nsEJNP4dBxVRMwQruG0LUYJVBASgDwls+tkIjNhTBZJgDQhGApkKZfU9B8dmVPHIdm0LLGuvB\nm7snwbAmgEL/PKbBOo1vwW7ml0LV9slBOlq7QGomv0LAiALBUz1XQfnMcUI7J7tBv171weSv\njkIyJOJBcG9hwdEsmUIFNvnBwykCQsiRkEICEhRB0BHaQbW+n0Jd9eY+hfy3wfcpfEK2JhLB\nOzHgwapJsUIZhUJBjcwtQVBpq0LUXIfBb++iQXaeWkKgaAlC5zXVwYPKnEK5mdhAZ86owbwy\nrUIa523ApXQbQlg7NEIX0hNCn3y0PLWWsUK90f7BTKdEwInjokLEqeXBNOzivEFasUI7gXdB\nNRCYwIfWs0LjgeC/jwvOQV2IGEJgqlpBlGMnwuj5pUIhRoRB/B2GwbAYqkIXaLzB2a8JQtzG\nRkKvMdBBCkGGQbPzkkJDtknBTOW7wbcmHUJ8C2BAiUAZwkyhpUJcfD9BxfrEQZHzt0I20XvB\n1WxQQeKXeUKD7ApCNRCUwXiNhULUeFpBKTuxwQzoj0IIc0vBJlEOQmOrWEJEMgxCAA4EQSg+\nsEIAC6jBSV4dwd0wq0IxUcvBXKGqwL4FR0Lqgh5CKgDOQFsRn0Kf3sPA853sQfYcpELMuwJB\nH/CjwZPkMkI1svFBwbiowcI1q0JWAcfB3l7rQfo3pUI/xVtBOzJ5QZsXRUK7MSjAEEYZwrZI\ntULCDSfBz4XpwIjYdUJ/1gVCKlFbQV1/nUKsRJTBytkMQmDqkEK72qtB2Z7LwXGYLUKE/PpA\nlzcbwhyPtUK9ek9AHfe5QTlyikJRMN9BEYc4wSo4kkKnfQfCmLogQcFWvELRyXzBV+cJQBw4\ngELkPwxCa6ZSwcubkUK+ZxJBvzgLQomliELiq4pAXi36wYM6XUJ3o8lBuXDqwcBmvEKP4k7B\nnre4QTLIjEKgc75BMp31QEOFaUL4ZZ3B3fu+weYxkkIQfc/AQLTEwWNjbkKgC+NB5NHhQSNA\nnkI3/7bBuazYQbUIrEI/nZJBZht0wb6cF0LV/1xBTmTrwbZzs0KX6MrAH4oFQli0nkJn+q1B\nJLcBwNJEpkKDtQXCnbPQwB4YgkKenM/BKrxbwaqXi0LkpPpB/1aWQLklt0KpNOPAMdpmQQSq\nVkJsEKpB324CwnHIhULO1dpBu3OBwcXGhUK8NOLBY2H0Qc9thkISRodBGsztQXVAk0KnnIi/\n5KbdwfXkhUJwVkHBgOOowZ/fsEKHTUZBmtj/QJCMsEL8yYfBqyi+QQN/VkLIAgRCQN7twVZV\nm0KWHbdAanC7wQnlqUIL6DY/8ScbQvVwNUL/swlCmgyswPChrkJXXvfByFoDP6SEpUL0MP/B\nE2MgwEEIqEJvf6xBzU2FwDHAr0J1mm3ACWzUQW0BEkKiVXRBbMcewikYoEKLd4tBJL1/waWP\nokL4E8rB66QGQvHgP0LZuNJBMSmrQTE3hkIjKB7BuaTqwaWuJEJzhpJA7WARwnGzqkLcVfFA\n1MK9QUeEu0IAzofB7ZVSQea6f0KkZA9C8V2Cwf2EhkL3FpRB4E7MwWxEmkKpJYLBiXETQqom\nZEIqQeZB/cbpQO/wr0IguonBK0MlwZsUqkKzl4XBPu6bwN4aU0LJUxlCuOriQN3BqkJDTwjB\nIHD4QaeKsEKOgddArh6AwXaCSkJehAlCXE6nwYuBsEIsOMvBZ2bbQfI/r0LsRM5ARVQ9Qdr5\nU0JBXtG/OEEPwpnKskKjSivBhAImwet6eUKfSgRCQpKIQZ/LjkJrPpDB3szyQT6qkUIsXLBB\nZKW8wTY0OEKoFglB+rkMwvHTtUJBdkY/5ge8QelQh0IYp/hBMYAewSnpk0Ih6AjCvCbgQITV\np0LZ6urBkuHjP9kTN0IYTRlCDrSgwIAxk0IzFKI/HEb3Qd/SlEJTnrpA+lrRwfH2QkLhiONB\nS53Zwblqs0K1zpHBhL7fQVOQrELR/2VBNs/2QPbXhELzV3fBgpLjwSWcpUIRJ0zAwE1uwZ/3\nikKOv5NBhprhQa/xiEJp3dfBDb/sQb1qjELEafhBUdaYwURcakKsgJJBpRX1wS6QwUKTjPHA\neS2WQQA3mELbIrBB9ICjP2NrlULHsuzBcPBVwX2khUI6+KTB0u6HwQDAnkJmw7dBxkXCQKBo\nrUKfk2HB4czpQR/CJUJQY89BMAwEwkHqrkJKsmRBbgYCwQsSp0J1sPTBuL7pQdQ0fUJO9OBA\nXM7JQYM4iELNZjJAhLPqwcyeIUL4ZK/A5E39wYccjkLFMshBaZWyQd1osEJfpInBb/2dQfxR\naEKCsQxCUIbQwRkNi0K48QxBO8u6wSqNn0KJRprASQMaQsF+akIbOwpCj8Pkv+wBu0KSaLTB\nFtZUwEIUg0Ko5+jBwHcUvwRAiELfogBCy/i3wK8huEKMBDvA2ietQQ0SMUKEC2JBMQ4ewoOP\nlkLHXLpBsBmpwV3xp0JGOtPBVNYPQo5nhUJvAcBBcjWDQVUNskKDqQvBVpRawVmRMUIo3FRA\nb0wTwiz+rELouwdB9RivQfoUnkIdTLfBM4qnQXRmNUL3KgxChAiBwXipqEJP2xFBg15/wSRu\nrUJneF3BNH4PQp/9P0KH+xFCb6BSQTLSpkKNTb/BA0ZYwZobs0Iki6zBULGqwCCtXkKgmB9C\nBZK9QJBJh0LwvuTAgzQDQp2adEL3AoBBmD79wfEkf0JeFMVBtpiOwVD8ukLh0oLBZfWBQUPy\nnkL8JVtBTyuJQZXzLkIkcOS+Ao0bwiLEmUIKKH7Bb2iBwTHJQUIkiAxCXpCkQQCbpUIyypPB\nALEFQuMHo0LabotBxbqlwWwoHkKwEQ1BKFsawlO7sUIeez9ADDHRQeIztEKJ3hdBPJuhwMAn\nqUJyKwDCXtKzQCtymkISdQjCftgMQZ29k0InLu9BcO89wcbjuEIdwHC++5FRQavbSEKSrOxA\nqjEWwm/iiUIvAYlBw1alwaSskEIZPJXBVdMJQp0CYEJCMQNCnaZjQR+mq0JUm37BoiVmwXyY\nZ0K8wKTA6DHvwR8ur0LnLTRB0ZNfQYiArUI66bLBfzDKQb2GW0JuYxJCcBDCwWEilkLkliZB\n2nG9wTf3sEKJZvbAtNYgQm/VJUIdLPpB5niVP/HCqELPqe3BQefVwDigtEJkIE/Bq4eswN0o\nbULCSg5CPPYGQQsomkKVKVHB/gUPQhraiUKsmZNBGK7gwZoQZEKWgA5Cn2CQwTU8tkJxX7/B\n7n2xQVeAmULWVCVB4sW1Qc/oAUK9YptA6VMawkZInkIavynBTYqPwS4tZEIA5wJCpbO+QTRt\nm0JmpIHBgimwQSCOsULGg0tBcdNZwaaiFUKbCTVBApsGwqLCsELxX8S/+KP5QdjGpEJcYqBB\nwAKtwK5TqkIgCwvCPgVBP9SHrEIHJr/BjCI8wAAOTkKP7h9CESLxP5vDq0IBr4fAq7oKQkjc\npkIqH9lAUnmnwQWUO0L10fhB72C/wYrkrkLJBbrB6XLuQb0Yp0IGqSJBq/EbQba+XUIy2PjA\ndDMEwqRwr0IQSlm/Z2F+wdqioEJ8YyxBp/P9Qf4FlULELevBiVXnQUVMmUKeB9dBuyRXwbOe\nYUK3TKZBTI7YwTXKu0LSbjDBww+tQZQMi0IlbOBB/RzpQA/dfEJWxbbBqmawwUK5nULMr9XB\nY+4Zwd4/rkJ3CIpB/CWtPpHhr0J89/HA3MawQQsTLEKTxadBL8sSwjEypULvf5FBcplTwZ91\npEIGruXBqLr/QVXrY0KIX6JBLzTfQYAbgELGIArASzIDwqMkWUIl9WDBj7ylwblIkkJ448hB\nExFBQaequUJLjkLBXXeHQb0na0JnZOtB3pjowT/paEIQrf9A6iYDwjzyh0L7wo1ANgURQjaY\nWkKouAZCkWoLwc5ItUKH187BEPHTP29Oj0L4ZOnBHlmKQHEemkIxmcxBCpcaweB4oUIkQUtA\nVsbnQYHI30Erzx9BsZ4cwpNvpkKmPmtB9vqMwYuGrULH+KPB/o4OQsY4QEKXaPhBmiR6QZ6C\nmkKhV4jBIPqewSSXHUJHBLG/UMQIwotHmEKl65dBlqC/QV7ksULPCIXBzVCCQZGzbEI9QA1C\negC0weiwj0JOLD5BN5HBwR6bqkIMqBbBxQMfQhY0c0K9lAtCwYsZQN+3u0J3eZvBkk6nwEoU\noELeBLHBFBBXwT4sPkIHUhVCIUCRQWNYoEJxqlPBGEABQu4foEKpEmRBRpmswdVHVEKeRf9B\n7KZ3wX+WsEKhocXBbJWxQcscuUK39AxAzAtEQZbTU0L+GGNAUEsVwu2gokLicQnBe1R9wW8S\ncEKRFfBBE+G5QS9DhUIwxLbBHWz5QTykhUItV/RBXgnMweceVEIsHUBBA+wDwqoqvkKyxV7A\nPI6rQT5wlELncZ1BN4yJwM2jmUJP8gXCBC6Dv12RtkKxS6PBGdUMPlYbakLRHBpCddLWwGKX\ngEKuFtw+JZToQWNWfUJj/AhBXvL6wQcZgUIJ9r9BUhq5wSQ4v0IB20HBq9x2QRlvnELItaFB\nMVEyQYv/Z0JQUUrBOnMCwqjrgkIlNs+/5s/NwQAAwH8AAABAAAAAQAAAAAEAAMB/AAAAQAAA\nAEAAAMB/AACAQAAAgD8AAAABAADAfwAAgEAAAIA/AADAfwAAgEAAAIA/AAAAAQAAwH8AAIBA\nAACAPwAAwH8AAIBAAACAPwAAAAEAAMB/AACAQAAAgD+Pi+hB0nj2QBTzH8Jfd61CC7AywEW3\nI0Jo7DVCJTYLQiELqEEaKJ1ClDUGwuoKqMCjd6FCcfGlQbI3l8EYILr/5Ngb/78jGP8c0ND/\n6q3w/4+L6EHSePZAFPMfwl93rUILsDLARbcjQmjsNUIlNgtCIQuoQRoonUKUNQbC6gqowKN3\noUJx8aVBsjeXwQEBAQEAAAAAAADwQQAAwH8AAMB/AAAgQgAAwH8AAMB/AABIQgAAwH8AAMB/\nAABwQgAAwH8AAMB/AACMQgAAwH8AAMB/AACgQgAAwH8AAMB/AAC0QgAAwH8AAMB/AADAfwAA\noMEAAMB/AADAfwAAAAAAAMB/AADAfwAAoEEAAMB/AADAfwAAIEIAAMB/AADAfwAAwH8AACDC\nAADAfwAAwH8AAKDBAADAfwAAwH8AAAAAAADAfwAAwH8AAKBBAADAfwAAwH8AACBCAAAAAQ=="}]},"context":{"shiny":false,"rmarkdown":null},"vertexShader":"#line 2 1\n// File 1 is the vertex shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\n\nattribute vec3 aPos;\nattribute vec4 aCol;\nuniform mat4 mvMatrix;\nuniform mat4 prMatrix;\nvarying vec4 vCol;\nvarying vec4 vPosition;\n\n#ifdef NEEDS_VNORMAL\nattribute vec3 aNorm;\nuniform mat4 normMatrix;\nvarying vec4 vNormal;\n#endif\n\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nattribute vec2 aTexcoord;\nvarying vec2 vTexcoord;\n#endif\n\n#ifdef FIXED_SIZE\nuniform vec3 textScale;\n#endif\n\n#ifdef FIXED_QUADS\nattribute vec3 aOfs;\n#endif\n\n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\nvarying float normz;\nuniform mat4 invPrMatrix;\n#else\nattribute vec3 aPos1;\nattribute vec3 aPos2;\nvarying float normz;\n#endif\n#endif // IS_TWOSIDED\n\n#ifdef FAT_LINES\nattribute vec3 aNext;\nattribute vec2 aPoint;\nvarying vec2 vPoint;\nvarying float vLength;\nuniform float uAspect;\nuniform float uLwd;\n#endif\n\n#ifdef USE_ENVMAP\nvarying vec3 vReflection;\n#endif\n\nvoid main(void) {\n  \n#ifndef IS_BRUSH\n#if defined(NCLIPPLANES) || !defined(FIXED_QUADS) || defined(HAS_FOG) || defined(USE_ENVMAP)\n  vPosition = mvMatrix * vec4(aPos, 1.);\n#endif\n  \n#ifndef FIXED_QUADS\n  gl_Position = prMatrix * vPosition;\n#endif\n#endif // !IS_BRUSH\n  \n#ifdef IS_POINTS\n  gl_PointSize = POINTSIZE;\n#endif\n  \n  vCol = aCol;\n  \n// USE_ENVMAP implies NEEDS_VNORMAL\n\n#ifdef NEEDS_VNORMAL\n  vNormal = normMatrix * vec4(-aNorm, dot(aNorm, aPos));\n#endif\n\n#ifdef USE_ENVMAP\n  vReflection = normalize(reflect(vPosition.xyz/vPosition.w, \n                        normalize(vNormal.xyz/vNormal.w)));\n#endif\n  \n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\n  /* normz should be calculated *after* projection */\n  normz = (invPrMatrix*vNormal).z;\n#else\n  vec4 pos1 = prMatrix*(mvMatrix*vec4(aPos1, 1.));\n  pos1 = pos1/pos1.w - gl_Position/gl_Position.w;\n  vec4 pos2 = prMatrix*(mvMatrix*vec4(aPos2, 1.));\n  pos2 = pos2/pos2.w - gl_Position/gl_Position.w;\n  normz = pos1.x*pos2.y - pos1.y*pos2.x;\n#endif\n#endif // IS_TWOSIDED\n  \n#ifdef NEEDS_VNORMAL\n  vNormal = vec4(normalize(vNormal.xyz), 1);\n#endif\n  \n#if defined(HAS_TEXTURE) || defined(IS_TEXT)\n  vTexcoord = aTexcoord;\n#endif\n  \n#if defined(FIXED_SIZE) && !defined(ROTATING)\n  vec4 pos = prMatrix * mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w;\n  gl_Position = pos + vec4(aOfs*textScale, 0.);\n#endif\n  \n#if defined(IS_SPRITES) && !defined(FIXED_SIZE)\n  vec4 pos = mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w + vec4(aOfs,  0.);\n  gl_Position = prMatrix*pos;\n#endif\n  \n#ifdef FAT_LINES\n  /* This code was inspired by Matt Deslauriers' code in \n   https://mattdesl.svbtle.com/drawing-lines-is-hard */\n  vec2 aspectVec = vec2(uAspect, 1.0);\n  mat4 projViewModel = prMatrix * mvMatrix;\n  vec4 currentProjected = projViewModel * vec4(aPos, 1.0);\n  currentProjected = currentProjected/currentProjected.w;\n  vec4 nextProjected = projViewModel * vec4(aNext, 1.0);\n  vec2 currentScreen = currentProjected.xy * aspectVec;\n  vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\n  float len = uLwd;\n  vec2 dir = vec2(1.0, 0.0);\n  vPoint = aPoint;\n  vLength = length(nextScreen - currentScreen)/2.0;\n  vLength = vLength/(vLength + len);\n  if (vLength > 0.0) {\n    dir = normalize(nextScreen - currentScreen);\n  }\n  vec2 normal = vec2(-dir.y, dir.x);\n  dir.x /= uAspect;\n  normal.x /= uAspect;\n  vec4 offset = vec4(len*(normal*aPoint.x*aPoint.y - dir), 0.0, 0.0);\n  gl_Position = currentProjected + offset;\n#endif\n  \n#ifdef IS_BRUSH\n  gl_Position = vec4(aPos, 1.);\n#endif\n}","fragmentShader":"#line 2 2\n// File 2 is the fragment shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\nvarying vec4 vCol; // carries alpha\nvarying vec4 vPosition;\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nvarying vec2 vTexcoord;\nuniform sampler2D uSampler;\n#endif\n\n#ifdef HAS_FOG\nuniform int uFogMode;\nuniform vec3 uFogColor;\nuniform vec4 uFogParms;\n#endif\n\n#if defined(IS_LIT) && !defined(FIXED_QUADS)\nvarying vec4 vNormal;\n#endif\n\n#if NCLIPPLANES > 0\nuniform vec4 vClipplane[NCLIPPLANES];\n#endif\n\n#if NLIGHTS > 0\nuniform mat4 mvMatrix;\n#endif\n\n#ifdef IS_LIT\nuniform vec3 emission;\nuniform float shininess;\n#if NLIGHTS > 0\nuniform vec3 ambient[NLIGHTS];\nuniform vec3 specular[NLIGHTS]; // light*material\nuniform vec3 diffuse[NLIGHTS];\nuniform vec3 lightDir[NLIGHTS];\nuniform bool viewpoint[NLIGHTS];\nuniform bool finite[NLIGHTS];\n#endif\n#endif // IS_LIT\n\n#ifdef IS_TWOSIDED\nuniform bool front;\nvarying float normz;\n#endif\n\n#ifdef FAT_LINES\nvarying vec2 vPoint;\nvarying float vLength;\n#endif\n\n#ifdef USE_ENVMAP\nvarying vec3 vReflection;\n#endif\n\nvoid main(void) {\n  vec4 fragColor;\n#ifdef FAT_LINES\n  vec2 point = vPoint;\n  bool neg = point.y < 0.0;\n  point.y = neg ? (point.y + vLength)/(1.0 - vLength) :\n                 -(point.y - vLength)/(1.0 - vLength);\n#if defined(IS_TRANSPARENT) && defined(IS_LINESTRIP)\n  if (neg && length(point) <= 1.0) discard;\n#endif\n  point.y = min(point.y, 0.0);\n  if (length(point) > 1.0) discard;\n#endif // FAT_LINES\n  \n#ifdef ROUND_POINTS\n  vec2 coord = gl_PointCoord - vec2(0.5);\n  if (length(coord) > 0.5) discard;\n#endif\n  \n#if NCLIPPLANES > 0\n  for (int i = 0; i < NCLIPPLANES; i++)\n    if (dot(vPosition, vClipplane[i]) < 0.0) discard;\n#endif\n    \n#ifdef FIXED_QUADS\n    vec3 n = vec3(0., 0., 1.);\n#elif defined(IS_LIT)\n    vec3 n = normalize(vNormal.xyz);\n#endif\n    \n#ifdef IS_TWOSIDED\n    if ((normz <= 0.) != front) discard;\n#endif\n\n#ifdef IS_LIT\n    vec3 eye = normalize(-vPosition.xyz/vPosition.w);\n    vec3 lightdir;\n    vec4 colDiff;\n    vec3 halfVec;\n    vec4 lighteffect = vec4(emission, 0.);\n    vec3 col;\n    float nDotL;\n#ifdef FIXED_QUADS\n    n = -faceforward(n, n, eye);\n#endif\n    \n#if NLIGHTS > 0\n    // Simulate two-sided lighting\n    if (n.z < 0.0)\n      n = -n;\n    for (int i=0;i<NLIGHTS;i++) {\n      colDiff = vec4(vCol.rgb * diffuse[i], vCol.a);\n      lightdir = lightDir[i];\n      if (!viewpoint[i]) {\n        if (finite[i]) {\n          lightdir = (mvMatrix * vec4(lightdir, 1.)).xyz;\n        } else {\n          lightdir = (mvMatrix * vec4(lightdir, 0.)).xyz;\n        }\n      }\n      if (!finite[i]) {\n        halfVec = normalize(lightdir + eye);\n      } else {\n        lightdir = normalize(lightdir - vPosition.xyz/vPosition.w);\n        halfVec = normalize(lightdir + eye);\n      }\n      col = ambient[i];\n      nDotL = dot(n, lightdir);\n      col = col + max(nDotL, 0.) * colDiff.rgb;\n      col = col + pow(max(dot(halfVec, n), 0.), shininess) * specular[i];\n      lighteffect = lighteffect + vec4(col, colDiff.a);\n    }\n#endif\n    \n#else // not IS_LIT\n    vec4 colDiff = vCol;\n    vec4 lighteffect = colDiff;\n#endif\n    \n#ifdef IS_TEXT\n    vec4 textureColor = lighteffect*texture2D(uSampler, vTexcoord);\n#endif\n    \n#ifdef HAS_TEXTURE\n\n// These calculations use the definitions from \n// https://docs.gl/gl3/glTexEnv\n\n#ifdef USE_ENVMAP\n    float m = 2.0 * sqrt(dot(vReflection, vReflection) + 2.0*vReflection.z + 1.0);\n    vec4 textureColor = texture2D(uSampler, vReflection.xy / m + vec2(0.5, 0.5));\n#else\n    vec4 textureColor = texture2D(uSampler, vTexcoord);\n#endif\n\n#ifdef TEXTURE_rgb\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(textureColor.rgb, lighteffect.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = lighteffect*vec4(textureColor.rgb, 1.);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - textureColor.rgb) * lighteffect.rgb, lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(lighteffect.rgb + textureColor.rgb, lighteffect.a);\n#endif\n\n#endif //TEXTURE_rgb\n        \n#ifdef TEXTURE_rgba\n\n#ifdef TEXMODE_replace\n// already done\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = lighteffect*textureColor;\n#endif\n\n#ifdef TEXMODE_decal\n    textureColor = vec4((1. - textureColor.a)*lighteffect.rgb) +\n                     textureColor.a*textureColor.rgb, \n                     lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - textureColor.rgb) * lighteffect.rgb,\n                    lighteffect.a*textureColor.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(lighteffect.rgb + textureColor.rgb,\n                    lighteffect.a*textureColor.a);\n#endif\n    \n#endif //TEXTURE_rgba\n    \n#ifdef TEXTURE_alpha\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(lighteffect.rgb, luminance);\n#endif \n\n#if defined(TEXMODE_modulate) || defined(TEXMODE_blend) || defined(TEXMODE_add)\n    textureColor = vec4(lighteffect.rgb, lighteffect.a*luminance);\n#endif\n \n#endif // TEXTURE_alpha\n    \n// The TEXTURE_luminance values are not from that reference    \n#ifdef TEXTURE_luminance\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(luminance, luminance, luminance, lighteffect.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = vec4(luminance*lighteffect.rgb, lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - luminance)*lighteffect.rgb,\n                        lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(luminance + lighteffect.rgb, lighteffect.a);\n#endif\n\n#endif // TEXTURE_luminance\n \n    \n#ifdef TEXTURE_luminance_alpha\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(luminance, luminance, luminance, textureColor.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = vec4(luminance*lighteffect.rgb, \n                        textureColor.a*lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - luminance)*lighteffect.rgb,\n                        textureColor.a*lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(luminance + lighteffect.rgb, \n                        textureColor.a*lighteffect.a);\n\n#endif\n\n#endif // TEXTURE_luminance_alpha\n    \n    fragColor = textureColor;\n\n#elif defined(IS_TEXT)\n    if (textureColor.a < 0.1)\n      discard;\n    else\n      fragColor = textureColor;\n#else\n    fragColor = lighteffect;\n#endif // HAS_TEXTURE\n    \n#ifdef HAS_FOG\n    // uFogParms elements: x = near, y = far, z = fogscale, w = (1-sin(FOV/2))/(1+sin(FOV/2))\n    // In Exp and Exp2: use density = density/far\n    // fogF will be the proportion of fog\n    // Initialize it to the linear value\n    float fogF;\n    if (uFogMode > 0) {\n      fogF = (uFogParms.y - vPosition.z/vPosition.w)/(uFogParms.y - uFogParms.x);\n      if (uFogMode > 1)\n        fogF = mix(uFogParms.w, 1.0, fogF);\n      fogF = fogF*uFogParms.z;\n      if (uFogMode == 2)\n        fogF = 1.0 - exp(-fogF);\n      // Docs are wrong: use (density*c)^2, not density*c^2\n      // https://gitlab.freedesktop.org/mesa/mesa/-/blob/master/src/mesa/swrast/s_fog.c#L58\n      else if (uFogMode == 3)\n        fogF = 1.0 - exp(-fogF*fogF);\n      fogF = clamp(fogF, 0.0, 1.0);\n      gl_FragColor = vec4(mix(fragColor.rgb, uFogColor, fogF), fragColor.a);\n    } else gl_FragColor = fragColor;\n#else\n    gl_FragColor = fragColor;\n#endif // HAS_FOG\n    \n}","players":[],"webGLoptions":{"preserveDrawingBuffer":true}},"evals":[],"jsHooks":[]}</script>
</div>
<figcaption class="quarto-float-caption-margin quarto-float-caption quarto-float-fig margin-caption" id="fig-selected-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;7: Final selected colors using the qualpalr algorithm
</figcaption>
</figure>
</div>
</div>
<section id="color-specifications" class="level3">
<h3 class="anchored" data-anchor-id="color-specifications">Color specifications</h3>
<p>At the time of writing, qualpalr only works in the sRGB color space with the CIE Standard Illuminant D65 reference white.</p>
</section>
</section>
<section id="future-directions" class="level2">
<h2 class="anchored" data-anchor-id="future-directions">Future directions</h2>
<p>The greedy search to find distinct colors is crude. Particularly when searching for few colors, the greedy algorithm will lead to sub-optimal results. Other solutions to finding points that maximize the smallest pairwise distance among them are welcome.</p>
</section>
<section id="thanks" class="level2">
<h2 class="anchored" data-anchor-id="thanks">Thanks</h2>
<p><a href="http://www.brucelindbloom.com/">Bruce Lindbloom’s webpage</a> has been instrumental in making qualpalr. Also thanks to <a href="http://tools.medialab.sciences-po.fr/iwanthue/">i want hue</a>, which inspired me to make qualpalr.</p>
</section>
<section id="references" class="level2">




</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-cui2002" class="csl-entry">
Cui, G., M. R. Luo, B. Rigg, G. Roesler, and K. Witt. 2002. <span>“Uniform Colour Spaces Based on the <span>DIN99</span> Colour-Difference Formula.”</span> <em>Color Research &amp; Application</em> 27 (4): 282–90. <a href="https://doi.org/10/cz7764">https://doi.org/10/cz7764</a>.
</div>
<div id="ref-huang2015" class="csl-entry">
Huang, Min, Guihua Cui, Manuel Melgosa, Manuel Sánchez-Marañón, Changjun Li, M. Ronnier Luo, and Haoxue Liu. 2015. <span>“Power Functions Improving the Performance of Color-Difference Formulas.”</span> <em>Optics Express</em> 23 (1): 597. <a href="https://doi.org/10/gcsk6f">https://doi.org/10/gcsk6f</a>.
</div>
</div></section></div> ]]></description>
  <category>R</category>
  <category>Data visualization</category>
  <category>Qualpalr</category>
  <guid>https://jolars.co/blog/2016-10-15-introducing-qualpalr/</guid>
  <pubDate>Sat, 15 Oct 2016 00:00:00 GMT</pubDate>
  <media:content url="https://jolars.co/blog/2016-10-15-introducing-qualpalr/qualpalr.png" medium="image" type="image/png" height="148" width="144"/>
</item>
</channel>
</rss>
