<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Braden Eliason</title>
    <subtitle>Braden Eliason&#x27;s personal blog and résumé — engineer, inventor, designer, entrepreneur.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://bradeneliason.com/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://bradeneliason.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-05-10T00:00:00+00:00</updated>
    <id>https://bradeneliason.com/atom.xml</id>
    <entry xml:lang="en">
        <title>Chopstick Fruit Bowl</title>
        <published>2026-05-10T00:00:00+00:00</published>
        <updated>2026-05-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/fruit-bowl-01/"/>
        <id>https://bradeneliason.com/blog/fruit-bowl-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/fruit-bowl-01/">&lt;p&gt;What do you do when you have 11 pairs of extra chopsticks?&lt;&#x2F;p&gt;
&lt;p&gt;Apparently, you design a 3D printed connector and turn them into a fruit bowl. The connector holds each chopstick at a slightly different angle, so the whole set fans out into a bowl shape once they&#x27;re all inserted.&lt;&#x2F;p&gt;
&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;fruit-bowl-01&#x2F;fruit_bowl_01_fig1a.png&quot; alt=&quot;Top-down view of the connector body with all 22 chopsticks radiating outward&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;fruit-bowl-01&#x2F;fruit_bowl_01_fig1b.jpg&quot; alt=&quot;Top-down view of the printed connector with all 22 chopsticks inserted&quot;&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;fruit-bowl-01&#x2F;fruit_bowl_01_fig2a.png&quot; alt=&quot;Angled view of the connector body showing the chopsticks fanning outward and upward&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;fruit-bowl-01&#x2F;fruit_bowl_01_fig2b.png&quot; alt=&quot;Close-up of a single connector joint holding a subset of chopsticks&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;fruit-bowl-01&#x2F;fruit_bowl_01_fig2c.jpg&quot; alt=&quot;Angled view of the printed connector and chopsticks forming the bowl shape&quot;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;And here it is doing its job, holding a small haul of onions and a mandarin.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;fruit-bowl-01&#x2F;fruit_bowl_01_fig3.jpg&quot; alt=&quot;The finished chopstick fruit bowl holding onions and a mandarin&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Circle Mesh Extrude: Vibe-Coding a Real FeatureScript Feature with Claude</title>
        <published>2025-10-18T00:00:00+00:00</published>
        <updated>2025-10-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/onshape-circle-mesh-extrude-01/"/>
        <id>https://bradeneliason.com/blog/onshape-circle-mesh-extrude-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/onshape-circle-mesh-extrude-01/">&lt;p&gt;Follow-up to &lt;a href=&quot;&#x2F;blog&#x2F;onshape-zonohedron-mcp-01&#x2F;&quot;&gt;my last post&lt;&#x2F;a&gt; on Onshape&#x27;s FeatureScript MCP server. This time I built something a little more complex: a feature I&#x27;m calling Circle Mesh Extrude.&lt;&#x2F;p&gt;
&lt;p&gt;The input to this feature is a sketch with a network of line segments with circles centered at the endpoints. The feature will create a lattice structure with the width of each segment driven by the circles in the sketch. The feature interpolates the width linearly across each segment between its endpoints, and if a segment doesn&#x27;t have a circle at one end, it falls back to a default width. The result is a skeletonized structure with tapered segments that follows whatever line network you feed it — thick where you&#x27;ve drawn big circles, thin where you haven&#x27;t drawn anything at all.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;onshape-circle-mesh-extrude-01&#x2F;onshape_circle_mesh_extrude_01_fig1.jpg&quot; alt=&quot;Tapered skeletal structure generated from a line network with width-setting circles at the endpoints&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;onshape-circle-mesh-extrude-01&#x2F;onshape_circle_mesh_extrude_01_fig2.jpg&quot; alt=&quot;Example of the circle mesh extrude with a driving sketch&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;swapping-copilot-for-claude-code&quot;&gt;Swapping Copilot for Claude Code&lt;&#x2F;h2&gt;
&lt;p&gt;With the &lt;a href=&quot;&#x2F;blog&#x2F;onshape-zonohedron-mcp-01&#x2F;&quot;&gt;Zonohedron feature&lt;&#x2F;a&gt;, I mostly hand-coded the FeatureScript myself, with Copilot&#x27;s free tier mainly helping me push through rough patches. This one is a lot more vibe-coded: I wrote the sketch-geometry handling for a single segment by hand, then handed the harder part off to Claude — extruding that logic across an entire mesh of segments and giving the result all the settings you&#x27;d expect from the built-in Extrude feature (draft angle, symmetric&#x2F;one-sided options, and so on).&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve wanted a feature like this for a while, and it didn&#x27;t take long to get working with the MCP-Claude connection. Now that it&#x27;s built and tested, the AI&#x27;s job is done. It behaves like any other feature in the toolbar: reusable, editable, shareable, with no dependency on the AI that helped write it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-motivated-this&quot;&gt;What Motivated This&lt;&#x2F;h2&gt;
&lt;p&gt;I don&#x27;t have a particular need for this feature currently. I was motivated to create this because I noticed a common geometric pattern emerging from structural topology optimization. Topology optimization takes a design space, a set of loads, and a stiffness or weight target, and iteratively removes material to produce the lightest structure that still holds up — the output is usually a branching, organic mesh that looks more like bone than a machined part. The more complicated outputs of topology optimization are often only possible to manufacture with additive manufacturing processes. Often engineers have to take the result of the topology optimization and manually create a shape that can be manufactured with a laser cutter or a mill. I added an input for a fillet radius which sets the radius of inside corners of the lattice structure to alleviate stress concentrations and to allow the lattice structure to be manufacturable with an end mill.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-future&quot;&gt;The Future&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s still early — I&#x27;m stress testing it now, so if you want to try breaking it, I&#x27;d be curious to hear how you managed it. Try it out here: &lt;a href=&quot;https:&#x2F;&#x2F;cad.onshape.com&#x2F;documents&#x2F;ada8454ceed4a56122b326f2&#x2F;w&#x2F;e578df3e64d26f494e6a34cd&#x2F;e&#x2F;7809dba24f87d4f8ddf8523e&quot;&gt;Circle Mesh Extrude&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Onshape has hinted at adding topology optimization. When that capability is released I would like to try out a workflow of optimizing the part and recreating the geometry quickly with Circle Mesh Extrude. It&#x27;s perhaps too much to ask for now, but I would be really interested if I could directly optimize the parameters of a part design iteratively. That is, instead of recreating a part based on the results of topology optimization, directly update a part&#x27;s design within a simulation loop. If you cleverly select the features used to generate the part being simulated, the end result could be both a topology optimized and manufacturable component.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Vibe-Coding My First FeatureScript Feature: Zonohedron</title>
        <published>2025-10-02T00:00:00+00:00</published>
        <updated>2025-10-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/onshape-zonohedron-mcp-01/"/>
        <id>https://bradeneliason.com/blog/onshape-zonohedron-mcp-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/onshape-zonohedron-mcp-01/">&lt;p&gt;I&#x27;ve wanted to learn &lt;a href=&quot;https:&#x2F;&#x2F;cad.onshape.com&#x2F;FsDoc&#x2F;intro.html&quot;&gt;FeatureScript&lt;&#x2F;a&gt; for a while now. It&#x27;s Onshape&#x27;s own built-in language — the same one their standard toolbar features like Extrude and Fillet are written in — and it&#x27;s genuinely the &quot;real&quot; way to build custom, reusable, parametric CAD features. In a &lt;a href=&quot;&#x2F;blog&#x2F;onshape-loop-pattern-01&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt; I explored how a &lt;code&gt;for&lt;&#x2F;code&gt; loop can be created in Onshape using only the feature tree, no code. I just never had enough reason or motivation to push through the initial learning hurdles.&lt;&#x2F;p&gt;
&lt;p&gt;What finally got me over that hump was Onshape&#x27;s newly released FeatureScript MCP server, which launched through Onshape Labs. MCP, short for &lt;a href=&quot;https:&#x2F;&#x2F;www.anthropic.com&#x2F;news&#x2F;model-context-protocol&quot;&gt;Model Context Protocol&lt;&#x2F;a&gt;, is an open standard that lets an AI agent call external tools through a common interface instead of every integration being a one-off. Onshape&#x27;s version of this hands an AI agent a specific set of tools for working with FeatureScript: generate code from a natural-language description.&lt;&#x2F;p&gt;
&lt;p&gt;I used the free tier of Copilot for this first attempt. It wasn&#x27;t doing all the work — more like clearing away just enough of the syntax friction that the hand-coding part stayed fun. I&#x27;ll give Claude a try soon for something more ambitious.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zonohedra&quot;&gt;Zonohedra&lt;&#x2F;h2&gt;
&lt;p&gt;In a short amount of time I had my first real custom feature working: a Zonohedron generator.&lt;&#x2F;p&gt;
&lt;p&gt;A zonohedron is a convex polyhedron built out of centrally symmetric faces. Zonohedra can be generated from a set of lines in 3D space each pointing in their own direction. Sweep the first segment along the second, and you get a parallelogram. Sweep that parallelogram along a third segment, and you get a parallelepiped — a squished 3D box. Keep going, sweeping the resulting volume along each additional segment in turn, and each new sweep adds a &quot;zone&quot; to the shape. More formally this is a Minkowski sum of line segments, but &quot;sweep, sweep, sweep&quot; is a perfectly accurate way to visualize it.&lt;&#x2F;p&gt;
&lt;p&gt;To create zonohedra in FeatureScript, I&#x27;m actually computing each face of the zonohedra as separate quadrilaterals and then enclosing the final volume of these separate faces.&lt;&#x2F;p&gt;
&lt;p&gt;Many named polyhedra are also zonohedra, including a cube. Four segments along a cube&#x27;s long diagonals give you a rhombic dodecahedron. If you carefully select five different line segments, the resulting zonohedra is a rhombic icosahedron, and a special set of six segments gives a rhombic triacontahedron.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;onshape-zonohedron-mcp-01&#x2F;onshape_zonohedron_mcp_01_fig1.jpg&quot; alt=&quot;Rhombic triacontahedron (purple), rhombic dodecahedron (blue), and rhombic icosahedron (green) generated with the zonohedron feature&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I also played with using this to build zonohedron-based dome (ie, zomes) structures. There are some interesting architectural projects that use this zome geometry to build houses and garden structures.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;onshape-zonohedron-mcp-01&#x2F;onshape_zonohedron_mcp_01_fig2.jpg&quot; alt=&quot;Zonohedron dome built from a partial sweep sequence&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A For Loop Hiding in Onshape&#x27;s Linear Pattern</title>
        <published>2025-09-14T00:00:00+00:00</published>
        <updated>2025-09-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/onshape-loop-pattern-01/"/>
        <id>https://bradeneliason.com/blog/onshape-loop-pattern-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/onshape-loop-pattern-01/">&lt;p&gt;Onshape&#x27;s Linear Pattern feature has a checkbox called &quot;Reapply features.&quot; Normally when you pattern a feature (rather than just a body), Onshape copies the &lt;em&gt;result&lt;&#x2F;em&gt; of that feature to each new location instead of re-running the feature&#x27;s logic from scratch. &lt;em&gt;Reapply features&lt;&#x2F;em&gt; tells Onshape to actually regenerate the seed feature at every single instance, dependencies and all. Most of the time you&#x27;d only reach for this if an &quot;up to next&quot; extrude or similar end condition needs to re-evaluate against each instance&#x27;s own local geometry.&lt;&#x2F;p&gt;
&lt;p&gt;It turns out this checkbox also gives you a loop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-trick&quot;&gt;The Trick&lt;&#x2F;h2&gt;
&lt;p&gt;If you define a variable feature that updates a variable of the same name (eg, &lt;code&gt;#i = #i + 1&lt;&#x2F;code&gt;) and that variable-setting feature is one of features being patterned, something interesting happens with &lt;em&gt;Reapply features&lt;&#x2F;em&gt; turned on. Each pattern instance re-executes the variable update and everything downstream of it. In effect you get a &lt;code&gt;for&lt;&#x2F;code&gt; loop without writing any FeatureScript.&lt;&#x2F;p&gt;
&lt;p&gt;The Linear Pattern itself isn&#x27;t doing any of the actual patterning on its own in this setup — it&#x27;s not moving copies around in the usual sense. It&#x27;s just the container that forces N repeated evaluations to happen. All the real geometric work happens in the features living inside it, driven entirely by the equations and features that depend on &lt;code&gt;i&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Much credit for this technique goes to &lt;a href=&quot;https:&#x2F;&#x2F;www.theonsherpa.com&#x2F;&quot;&gt;Evan Reese&lt;&#x2F;a&gt;, who runs &lt;a href=&quot;https:&#x2F;&#x2F;www.theonsherpa.com&#x2F;&quot;&gt;The Onsherpa&lt;&#x2F;a&gt; and has been building and teaching Onshape workflows like this for a while.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;square-disc-and-a-spherical-fibonacci-lattice&quot;&gt;Square, Disc, and a Spherical Fibonacci Lattice&lt;&#x2F;h2&gt;
&lt;p&gt;I tried this out on three examples of equation-driven lattices.&lt;&#x2F;p&gt;
&lt;p&gt;The square pattern is the simplest case: each iteration creates a transformed copy of the initial part and shifts the copy&#x27;s position in x and y as a function of variable &lt;code&gt;i&lt;&#x2F;code&gt;. If the x-position is outside of the square bounds on one side, it wraps around to the other side.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;onshape-loop-pattern-01&#x2F;onshape_loop_pattern_01_fig1.png&quot; alt=&quot;Square pattern built by incrementing a variable inside a Reapply-features Linear Pattern&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The disc pattern does something similar but radially — each iteration adds a copy of the initial part at a radius and angle that&#x27;s a function of &lt;code&gt;i&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;onshape-loop-pattern-01&#x2F;onshape_loop_pattern_01_fig2.jpg&quot; alt=&quot;Disc pattern with radius driven by the loop variable&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The one I&#x27;m most pleased with is a Fibonacci sphere pattern. A Fibonacci sphere is a way of distributing points roughly evenly across the surface of a sphere using the golden angle. The points are positioned on rings spaced evenly along the z-axis of the sphere, one ring for each point. On each ring, the point is positioned the golden angle around the z-axis from the previous point (approximately 137.5 degrees).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;onshape-loop-pattern-01&#x2F;onshape_loop_pattern_01_fig3.png&quot; alt=&quot;Spherical Fibonacci lattice of bodies, each placed by an equation-driven loop iteration&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;cad.onshape.com&#x2F;documents&#x2F;c42b33d6f8229732b2aaccea&#x2F;&quot;&gt;Demo here&lt;&#x2F;a&gt; if you want to see it in action.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;driving-sketches-with-i&quot;&gt;Driving Sketches with &lt;code&gt;i&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The demos above were using simple disks and spheres with a transform-copy feature to demonstrate the loop. It&#x27;s also possible to drive sketch dimensions with the variable &lt;code&gt;i&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For this to work, the linear pattern feature needs to include the variable feature which increments &lt;code&gt;i&lt;&#x2F;code&gt; (ie, &lt;code&gt;#i = #i + 1&lt;&#x2F;code&gt;), the sketch, and any extrudes or other features that make use of the sketch.&lt;&#x2F;p&gt;
&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;onshape-loop-pattern-01&#x2F;onshape_loop_pattern_01_fig4a.png&quot; alt=&quot;Sketch dimension driven by `i`&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;onshape-loop-pattern-01&#x2F;onshape_loop_pattern_01_fig4b.png&quot; alt=&quot;Result of a loop with a sketch driven by `i`&quot;&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;featurescript-would-be-the-real-way-to-do-this&quot;&gt;FeatureScript Would Be the &quot;Real&quot; Way to Do This&lt;&#x2F;h2&gt;
&lt;p&gt;To be clear, this is not the idiomatic way to write procedural CAD in Onshape. &lt;a href=&quot;https:&#x2F;&#x2F;cad.onshape.com&#x2F;FsDoc&#x2F;intro.html&quot;&gt;FeatureScript&lt;&#x2F;a&gt; — Onshape&#x27;s own built-in language, the same one their standard toolbar features are written in — has actual &lt;code&gt;for&lt;&#x2F;code&gt; loops and pattern-generation functions like &lt;code&gt;opPattern&lt;&#x2F;code&gt; for exactly this kind of equation-driven geometry. If I wanted this to be robust, reusable, and fast, I&#x27;d write it in FeatureScript.&lt;&#x2F;p&gt;
&lt;p&gt;But there&#x27;s something fun about finding a way to implement such loop without touching FeatureScript.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Kroki + Pluto = ❤</title>
        <published>2021-09-28T00:00:00+00:00</published>
        <updated>2021-09-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/kroki-pluto-01/"/>
        <id>https://bradeneliason.com/blog/kroki-pluto-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/kroki-pluto-01/">&lt;!-- # Kroki + Pluto = ❤ --&gt;
&lt;h2 id=&quot;intro-to-pluto-jl&quot;&gt;Intro to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Pluto.jl&amp;amp;type=Repositories&quot;&gt;Pluto.jl&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Pluto.jl&amp;amp;type=Repositories&quot;&gt;Pluto.jl&lt;&#x2F;a&gt; is a package for Julia for creating reactive notebooks in your browser. A notebook is a series of cells of code which display their outputs inline. When a function or variable is changed in the Pluto, all affected cells are updated automatically. When you add user interface elements such as sliders, buttons, and text fields with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=PlutoUI.jl&amp;amp;type=Repositories&quot;&gt;PlutoUI.jl&lt;&#x2F;a&gt;, a Pluto notebook becomes a powerful way to explore code interactively.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;intro-to-kroki&quot;&gt;Intro to Kroki&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;kroki.io&#x2F;&quot;&gt;Kroki&lt;&#x2F;a&gt; is a unified API to create a panoply of diagrams from a variety of diagram specification languages. Kroki supports 22 different diagram specification languages and many of these languages like &lt;a href=&quot;https:&#x2F;&#x2F;mermaid-js.github.io&#x2F;mermaid&#x2F;#&#x2F;&quot;&gt;Mermaid&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;vega.github.io&#x2F;vega&#x2F;&quot;&gt;Vega&lt;&#x2F;a&gt; can support many more diagram types. All the diagrams supported can be defined simply from text descriptions. Kroki is run as a free service where you can send the text description of the diagram to the server and get an image back. If you want to run Kroki without an internet connection or keep your diagrams private, you can install a self-managed service on a local machine. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Kroki.jl&amp;amp;type=Repositories&quot;&gt;Kroki.jl&lt;&#x2F;a&gt; is a wrapper around the Kroki API.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;kroki-pluto&quot;&gt;Kroki + Pluto&lt;&#x2F;h2&gt;
&lt;p&gt;Static diagrams can be made using a variety of string macros provided by &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Kroki.jl&amp;amp;type=Repositories&quot;&gt;Kroki.jl&lt;&#x2F;a&gt;. For example a PlantUML sequence diagram can be made using the &lt;code&gt;plantuml&lt;&#x2F;code&gt; string macro:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;plantuml&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Kroki -&amp;gt; Julia: Hello Julia!&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;kroki-pluto-01&#x2F;kroki_pluto_01_fig1.png&quot; alt=&quot;PlantUML Example Hello Julia&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Making diagrams interactively in Pluto is as simple as updating the string which defines the diagram. For example a text box could be used to update the name in the diagram above with a simple string interpolation. Note: it&#x27;s simpler to interpolate the string and send it to the &lt;code&gt;Kroki.diagram&lt;&#x2F;code&gt; function than interpolating in a &lt;code&gt;plantuml&quot;...&quot;&lt;&#x2F;code&gt; macro string.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;kroki-pluto-01&#x2F;kroki_pluto_01_fig2.png&quot; alt=&quot;PlantUML Example with interactivity&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The demo on the right shows a Vega Lite diagram being interactively updated from data in a textbox. The raw input data is split into individual numbers and parsed as numbers. Then a vector of named tuples is created in the format of &lt;code&gt;(category=1, value=15)&lt;&#x2F;code&gt;. This named data is then converted directly into JSON inside the piechart specification string.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;data = parse.(Float&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;64&lt;&#x2F;span&gt;&lt;span&gt;, split(replace(input_data, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; =&amp;gt; &amp;#39; &amp;#39;)))
&lt;&#x2F;span&gt;&lt;span&gt;named_data = [(category=i, value=d) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(i,d) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; enumerate(data)]
&lt;&#x2F;span&gt;&lt;span&gt;vl_piechart = &amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  &amp;quot;\&lt;&#x2F;span&gt;&lt;span&gt;$schema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;quot;: &amp;quot;https:&#x2F;&#x2F;vega.github.io&#x2F;schema&#x2F;vega-lite&#x2F;v5.json&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  &amp;quot;description&amp;quot;: &amp;quot;A simple donut chart with embedded data.&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  &amp;quot;data&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;values&amp;quot;: &lt;&#x2F;span&gt;&lt;span&gt;$(JSON.json(named_data))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  &amp;quot;mark&amp;quot;: {&amp;quot;type&amp;quot;: &amp;quot;arc&amp;quot;, &amp;quot;innerRadius&amp;quot;: 50},
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  &amp;quot;encoding&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;theta&amp;quot;: {&amp;quot;field&amp;quot;: &amp;quot;value&amp;quot;, &amp;quot;type&amp;quot;: &amp;quot;quantitative&amp;quot;},
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;color&amp;quot;: {&amp;quot;field&amp;quot;: &amp;quot;category&amp;quot;, &amp;quot;type&amp;quot;: &amp;quot;nominal&amp;quot;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  &amp;quot;view&amp;quot;: {&amp;quot;stroke&amp;quot;: null}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;kroki-pluto-01&#x2F;kroki_pluto_01_fig3.gif&quot; alt=&quot;Vega Lite diagram being interacted with&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to explore these experiments for yourself, I&#x27;ve made a repository on my Github: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bradeneliason&#x2F;PlutoKrokiPlayground&quot;&gt;PlutoKrokiPlayground&lt;&#x2F;a&gt;. Leave me a star on Github if you enjoyed this project.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Graph Vizualization in Pluto Notebooks</title>
        <published>2021-09-28T00:00:00+00:00</published>
        <updated>2021-09-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/pluto-graphviz-01/"/>
        <id>https://bradeneliason.com/blog/pluto-graphviz-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/pluto-graphviz-01/">&lt;!-- # Graph Vizualization in Pluto Notebooks --&gt;
&lt;p&gt;I made an experimental &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Pluto.jl&amp;amp;type=Repositories&quot;&gt;Pluto.jl&lt;&#x2F;a&gt; notebook to display graphs using &lt;a href=&quot;https:&#x2F;&#x2F;js.cytoscape.org&#x2F;&quot;&gt;Cytoscape.js&lt;&#x2F;a&gt;. Cytoscape is a graph visualization library written in pure JavaScript. I used the package &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=HypertextLiteral.jl&amp;amp;type=Repositories&quot;&gt;HypertextLiteral.jl&lt;&#x2F;a&gt; to generate HTML output inside of a Pluto notebook. The output HTML pane has four buttons to fit, center, redraw, and download the graph. You can also swap out different graphs, styles, and layout methods interactively.&lt;&#x2F;p&gt;
&lt;p&gt;Currently this project is just a prototype which you can view and download on my Github: PlutoGraphViz. There are many features of Cytoscape which I have not implemented yet. If you found this project interesting, leave me a star on Github.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;animated-demo&quot;&gt;Animated Demo&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;pluto-graphviz-01&#x2F;pluto_graphviz_01_fig1.gif&quot; alt=&quot;Interactive demo of graph visualization&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Isometric Pixel Art in Blender</title>
        <published>2021-04-11T00:00:00+00:00</published>
        <updated>2021-04-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/blender-isometric-pixel-art-01/"/>
        <id>https://bradeneliason.com/blog/blender-isometric-pixel-art-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/blender-isometric-pixel-art-01/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig1.gif&quot; alt=&quot;Demo Animation&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Limitation spurs creativity. I think this is why sonnets and haiku continue to be popular modes of expression. In the same spirit, pixel art is a creative limitation that has bred incredible creativity and attention to detail. Let&#x27;s automate it!&lt;&#x2F;p&gt;
&lt;p&gt;Okay... there&#x27;s no replacement for hand-made pixel art. A single pixel can dramatically change the look and feel of pixel art. That being said, pixel art stylized render could serve as an excellent starting point. I&#x27;ve found quite a few people creating incredible pixel art using Blender. I&#x27;m neither an artist nor a game designer, and I don&#x27;t claim that the results below are pixel perfect. I just like the pixel art aesthetic and wanted to see how far I could stretch Blender to create stylistic renders. The results I have so far are pretty promising and figured I&#x27;d share them.&lt;&#x2F;p&gt;
&lt;p&gt;If this was as simple as pixelating a 3D render, then this would be over and done with in a matter of seconds. The Blender compositor already has a pixelate node. To apply a simple pixelation effect image must first be scaled down, pixelated, and then scaled up again by the reciprocal amount (nodes show below).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig2.png&quot; alt=&quot;Pixelate node setup&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Pixel art typically uses outlines around objects to offset them from the background. There may also be lines within the object to demarcate hard edges. It&#x27;s also common in pixel art to have a limited palette. Both of the outlines and color limitation effects can be replicated with a little bit of extra effort in the Blender compositor. To clean up the compositing tree, I grouped together the nodes for the pixelation effect with a division node to form my own pixelate node group.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig3.png&quot; alt=&quot;Pixelate node group&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;camera-setup&quot;&gt;Camera Setup&lt;&#x2F;h2&gt;
&lt;p&gt;If you want your scene to be isometric, set your camera to be &lt;strong&gt;Orthographic&lt;&#x2F;strong&gt; in the camera object data properties tab. The rotation of the camera should also be set to &lt;strong&gt;60°&lt;&#x2F;strong&gt; around the x axis and &lt;strong&gt;45°&lt;&#x2F;strong&gt; around the z axis. Actually any angle 45° off-axis from the x and y axes will work, viz. 45°, 135°, 225°, or 315°. With this camera mode and orientation, lines parallel to the x or y axes will have a slope of exactly $\frac{1}{2}$. That is, they run horizontally two pixels before jumping up one pixel. To adjust what&#x27;s in and out of frame, you can move the camera around and adjust the orthographic scale of the camera.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig4.png&quot; alt=&quot;Camera settings for isometric rendering&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compositing-nodes&quot;&gt;Compositing Nodes&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig5.png&quot; alt=&quot;Overview of compositing nodes&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;outlines&quot;&gt;Outlines&lt;&#x2F;h2&gt;
&lt;p&gt;Early in my experimentation I had used a cryptomatte workflow to isolate a single object and create an outline around it. The problem with this workflow is that it only worked for the object I happen to select, not automatically for all objects within my scene. If you only wanted to outline a single object such as a character sprite, then cryptomatte workflow is your friend.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig6.png&quot; alt=&quot;Outline nodes&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I am creating two sets of linework. One set of lines outlines an object from the background based on a difference in depth. The other set of lines occur where there are hard edges. The general pattern I am using to create outlines is to use an edge detection filter on some input, dilate the detected edges, and then apply the less than operator with some threshold to force the value to either black or white. For the depth outline, I use the depth data from the render layers as an input. I found it helps to correct the depth data with a power math node. For the hard edge outlines, I&#x27;m using the normal data from the render layers. The final touch is to make the hard edge outlines a shade of grey (set to 25% grey here). Since the hard edge outlines can occur within an object I don&#x27;t want to make the output cluttered with dark lines. The thickness of the edges can be modified by adjusting the dilate&#x2F;erode node and less than nodes. I just adjusted the settings on these nodes until I achieved a single pixel wide line.&lt;&#x2F;p&gt;
&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig7a.png&quot; alt=&quot;Depth outlines&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig7b.png&quot; alt=&quot;Hard edge outlines&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig7c.png&quot; alt=&quot;Combined outlines&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig7d.png&quot; alt=&quot;Combined outlines pixelated&quot;&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;color-simplification&quot;&gt;Color Simplification&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;simple-method&quot;&gt;Simple Method&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig8.png&quot; alt=&quot;Color Pallete Simplification&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Simplifying the colors can be quite simple. We can separate the color channels of an input and then round each pixel to a subset of values using the snap math node. This cuts down the number of possible colors. Unfortunately, this results in ugly banding that I discussed in a previous post. To summarize, instead of the typical 256 different values a pixel can have for each channel, we round the values for channel to just a handful of steps. This produces large jumps between adjacent colors. Areas where the color is meant to transition slowly (like on the ball shown below) produce large bands of color with large jumps in color between the bands.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig9.png&quot; alt=&quot;Color banding&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dithered-method&quot;&gt;Dithered Method&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig10.png&quot; alt=&quot;Color Pallete Simplification, dithering&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There is a rather simple fix. We don&#x27;t perceive each pixel in isolation; instead we tend to perceive the average of a block of pixels. If we add a little bit of noise to the image before we round it to discrete steps, this breaks up the monotonous banding. The two spheres above and below have the same number of colors, but the sphere below has noise added to it to break up the banding. This process of adding noise to an image to break up color banding is called dithering.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig11.png&quot; alt=&quot;Dithering applied&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I created another node group called &lt;strong&gt;Dither and Snap&lt;&#x2F;strong&gt; (details shown below). We first multiply the noise input by the increment used to round values. This means that the noise we add only can only change a pixel&#x27;s value by at most the width of the step sizes. We add this noise to the image before snapping the value to increments. This is done for each color channel with the same increment and input noise.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig12.png&quot; alt=&quot;Dither and Snap node group&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dithering&quot;&gt;Dithering&lt;&#x2F;h2&gt;
&lt;p&gt;The remaining question is what noise should we add to our image? We want the light and dark pixels to be spread out and intermingled across the input noise. This way we can avoid creating patches that are accidentally a little darker or lighter. Another way of saying this is we want the noise to average close to zero not only across the whole noise pattern but also small blocks of pixels within the pattern. Well, fortunately this has been figured out for us already. A Bayer matrix is an excellent choice because it balances out the average value across the whole matrix. If you want to learn how to make these matrices yourself or download images that are ready to use: &lt;a href=&quot;&#x2F;blog&#x2F;blender_dithering_01&#x2F;&quot;&gt;check out my previous post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig13.png&quot; alt=&quot;Dither Noise Input&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The final touch to this the dithering is to stretch out the dithering noise with a scale node. This is entirely optional and some of the images above are produced without this scaling. The images below show different settings for this final noise scaling: without scaling, vertical scaling by four pixels, and horizontal scaling by four pixels. I personally like the vertical scaling by 4 pixels. It complements the isometric style nicely.&lt;&#x2F;p&gt;
&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig11.png&quot; alt=&quot;No scaling of dithering noise&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig14.png&quot; alt=&quot;Four-pixel vertical scaling&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig15.png&quot; alt=&quot;Four-pixel horizontal scaling&quot;&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;more-results&quot;&gt;More Results&lt;&#x2F;h2&gt;
&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig16.gif&quot; alt=&quot;Spiral staircase animation with a Freestyle line pass&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-isometric-pixel-art-01&#x2F;blender_isometric_pixel_art_01_fig17.gif&quot; alt=&quot;Rotating torus&quot;&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Monte Carlo Light Transport</title>
        <published>2021-03-18T00:00:00+00:00</published>
        <updated>2021-03-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/light-transport-01/"/>
        <id>https://bradeneliason.com/blog/light-transport-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/light-transport-01/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;light-transport-01&#x2F;light_transport_01_fig1.gif&quot; alt=&quot;Monte Carlo light transport animation&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Monte Carlo methods are a class of algorithms that rely on random sampling to generate a result. These methods can obtain results for both random and deterministic processes. For example, the value of $\pi$ is deterministic, but by sampling random points within a square and testing if they fall within a circle you can estimate the value of $\pi$ (albeit quite slowly). For random processes like light scattering, Monte Carlo methods are a natural choice and can be quite versatile.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mclighttransport-jl&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=MCLightTransport.jl&amp;amp;type=Repositories&quot;&gt;MCLightTransport.jl&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I wrote a small package in Julia called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=MCLightTransport.jl&amp;amp;type=Repositories&quot;&gt;MCLightTransport.jl&lt;&#x2F;a&gt; to simulate the propagation of photons through a turbid media. This is currently a very experimental package with many possible improvements and changes. It is currently only able to simulate light propagating through a uniform volume, but I&#x27;m hoping to add more capabilities soon. I&#x27;m not going to go into the details of how it works right now, but if you want to learn the basics Wikipedia has &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Monte_Carlo_method_for_photon_transport#Implementation_of_photon_transport_in_a_scattering_medium&quot;&gt;a pretty good summary&lt;&#x2F;a&gt; of how this algorithm works. For a more technical discussion, I highly recommend &lt;a href=&quot;https:&#x2F;&#x2F;omlc.org&#x2F;&quot;&gt;Oregon Medical Laser Center&#x27;s website&lt;&#x2F;a&gt;. They have several good resources including book chapters, class notes, and software for Monte Carlo light transport simulation. I based my work on the mc123.c script they&#x27;ve released.&lt;&#x2F;p&gt;
&lt;p&gt;The animation above and the images below represent the energy deposited in a turbid volume by a beam of light. Think of a laser beam submersed in a vat of milk. Simulated photons are launched from the same point and in the same direction each time. Each photon has a trajectory and at each step it jumps to a new location some distance along that trajectory. At each location the photon&#x27;s trajectory can be altered (ie, scattered). As a simulated photon bumbles through the volume it deposits a portion of its energy at each location it visits. When the photon loses most of its weight, it has a chance of dying. Once a photon is dead, another is launched and the process repeats many, many times. Over time the estimate of the energy deposited at each location improves. The white spaces in the graphics are locations where no photons have visited.&lt;&#x2F;p&gt;
&lt;p&gt;The image below shows the contribution of a single photon. You can somewhat make out the random trajectory this particular photon took through the volume.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;light-transport-01&#x2F;light_transport_01_fig2.png&quot; alt=&quot;Path of a single photon&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m really pleased with the resulting plots. There are certainly more succinct and informative ways to display this information. However, I quite like that it looks like a lightsaber illuminating smoke or fog. Even the graininess from the inherent randomness of this algorithm looks intentional.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;light-transport-01&#x2F;light_transport_01_fig3.png&quot; alt=&quot;3D volume slices&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;light-transport-01&#x2F;light_transport_01_fig4.png&quot; alt=&quot;3D volume perspective&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Solving Beam Deflection</title>
        <published>2021-03-16T00:00:00+00:00</published>
        <updated>2021-03-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/beam-deflection-01/"/>
        <id>https://bradeneliason.com/blog/beam-deflection-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/beam-deflection-01/">&lt;p&gt;This started as a challenge on the Julia Discourse website to come up with a compelling application for Julia in seven lines of code. I had wanted a reason to learn more about the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=ApproxFun.jl&amp;amp;type=Repositories&quot;&gt;ApproxFun.jl&lt;&#x2F;a&gt; package and took this as a fun opportunity.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=ApproxFun.jl&amp;amp;type=Repositories&quot;&gt;ApproxFun.jl&lt;&#x2F;a&gt; is well suited to finding the solutions of ordinary differential equations (ODEs) with boundary conditions. One application for this is solving beam deflection under various loading conditions. As a test case, here&#x27;s a simple beam deflection problem: a cantilevered beam with uniform loading. The beam has length, L, and its stiffness is determined by both the elastic modulus, E, and the moment of inertia, I.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;beam-deflection-01&#x2F;beam_deflection_01_fig1.png&quot; alt=&quot;Uniformly loaded cantilever beam&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-seven-lines-excluding-imports&quot;&gt;My Seven Lines (excluding imports)&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; ApproxFun, Plots
&lt;&#x2F;span&gt;&lt;span&gt;L, E, I = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;12.0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1.0
&lt;&#x2F;span&gt;&lt;span&gt;d = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span&gt;.L
&lt;&#x2F;span&gt;&lt;span&gt;z = Fun(identity, d)
&lt;&#x2F;span&gt;&lt;span&gt;B = [[Evaluation(d,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,k) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; k=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]... ; [Evaluation(d,L,k) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; k=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]... ;]
&lt;&#x2F;span&gt;&lt;span&gt;v = [B; E*I*Derivative()^&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;] \ [ zeros(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;)...; one(z)]
&lt;&#x2F;span&gt;&lt;span&gt;func_name = zip([v, v&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;, v&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&amp;#39;, v&amp;#39;&amp;#39;&amp;#39;], [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Deflection&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Angle&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Moment&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Shear&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;])
&lt;&#x2F;span&gt;&lt;span&gt;plot([plot(z, f, title=n, label=&amp;quot;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(f,n) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; func_name]..., lw=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;beam-deflection-01&#x2F;beam_deflection_fig1a.svg&quot; alt=&quot;Deflection, angle, moment, and shear of beam with a uniform loading&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-it-s-doing&quot;&gt;What It&#x27;s Doing&lt;&#x2F;h2&gt;
&lt;p&gt;The seven lines are bit compressed to fit within the constraints of the challenge so let&#x27;s walk through it.&lt;&#x2F;p&gt;
&lt;p&gt;These four lines are the simplest. The first line imports the required packages: &lt;code&gt;ApproxFun&lt;&#x2F;code&gt;, &lt;code&gt;Plots&lt;&#x2F;code&gt;. Next we define variables for the length, elastic modulus, and moment of inertia for the beam. The variable, d, is the domain of the ODE, in this case from &lt;code&gt;0&lt;&#x2F;code&gt; to &lt;code&gt;L&lt;&#x2F;code&gt;. The last line in this group creates an &lt;code&gt;ApproxFun&lt;&#x2F;code&gt; function called &lt;code&gt;z&lt;&#x2F;code&gt; across the domain. This function is simply the distance along the beam.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; ApproxFun, Plots
&lt;&#x2F;span&gt;&lt;span&gt;L, E, I = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;12.0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1.0
&lt;&#x2F;span&gt;&lt;span&gt;d = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span&gt;.L
&lt;&#x2F;span&gt;&lt;span&gt;z = Fun(identity, d)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;B&lt;&#x2F;code&gt; is an array of boundary conditions for our ODE. It&#x27;s a fourth order ODE, so there are four boundary conditions. The vertical displacement and first derivative (ie, angle) are both zero at the fixed end of the beam. The moment and shear in the beam (2nd and 3rd) derivatives are zero at the free end of the beam.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;B = [[Evaluation(d,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,k) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; k=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]... ; 
&lt;&#x2F;span&gt;&lt;span&gt;     [Evaluation(d,L,k) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; k=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]... ;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The solution is generated on the next line. The boundary conditions, B, are all set to 0 using &lt;code&gt;zeros(4)...&lt;&#x2F;code&gt;. The differential equation for a beam is defined (&lt;code&gt;E*I*Derivative()^4&lt;&#x2F;code&gt;) and gets set to a uniform load &lt;code&gt;one(z)&lt;&#x2F;code&gt; (shown as &lt;strong&gt;q&lt;&#x2F;strong&gt; in the image above).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;v = [B; E*I*Derivative()^&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;] \ [ zeros(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;)..., one(z)]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These two lines plot the results. I&#x27;ve zipped together the solution and its derivatives with their corresponding labels. The last line uses list comprehension to plot the results. Note: I wouldn&#x27;t typically use syntax like this for plotting but I had to fit this whole thing into seven lines. A little bit of clarity went out the window.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;func_name = zip([v, v&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;, v&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&amp;#39;, v&amp;#39;&amp;#39;&amp;#39;], [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Deflection&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Angle&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Moment&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Shear&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;])
&lt;&#x2F;span&gt;&lt;span&gt;plot([plot(z, f, title=n, label=&amp;quot;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(f,n) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; func_name]..., lw=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;beam-deflection-more-breathing-room&quot;&gt;Beam Deflection - More Breathing Room&lt;&#x2F;h2&gt;
&lt;p&gt;When not constrained to seven lines of code, it&#x27;s much easier to document and read this Julia code. The code below show a 2 meter long beam with load which increases from 0 at the fixed end to 1 kN&#x2F;m at the end of the beam. Here&#x27;s a diagram of the problem setup.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;beam-deflection-01&#x2F;beam_deflection_01_fig2.png&quot; alt=&quot;Uniformly varying load on a cantilever beam&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; ApproxFun, Plots
&lt;&#x2F;span&gt;&lt;span&gt;° = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;π&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;180&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Setting up problem
&lt;&#x2F;span&gt;&lt;span&gt;L = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2.0                 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Length in m
&lt;&#x2F;span&gt;&lt;span&gt;E = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;82.74e9             &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Elasticin Pa
&lt;&#x2F;span&gt;&lt;span&gt;I = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;444.0 &lt;&#x2F;span&gt;&lt;span&gt;* (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.01&lt;&#x2F;span&gt;&lt;span&gt;)^&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Moment of interia m⁴
&lt;&#x2F;span&gt;&lt;span&gt;d = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span&gt;.L                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Domain of beam
&lt;&#x2F;span&gt;&lt;span&gt;z = Fun(identity, d)    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Length along beam in m
&lt;&#x2F;span&gt;&lt;span&gt;D = Derivative()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Problem Definition
&lt;&#x2F;span&gt;&lt;span&gt;q = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span&gt;*(z&#x2F;L)          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Triangular loading in N&#x2F;m
&lt;&#x2F;span&gt;&lt;span&gt;w = E*I*D^&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4             &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# DiffEq for beam deflection
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Boundary Conditions
&lt;&#x2F;span&gt;&lt;span&gt;B= [Evaluation(d, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Beam vertically constrained at z = 0
&lt;&#x2F;span&gt;&lt;span&gt;    Evaluation(d, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Beam&amp;#39;s angle is constrained at z = 0
&lt;&#x2F;span&gt;&lt;span&gt;    Evaluation(d, L, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Beam&amp;#39;s moment is 0 at z = L
&lt;&#x2F;span&gt;&lt;span&gt;    Evaluation(d, L, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;)]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Beam&amp;#39;s shear is 0 at z = L
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Solving for vertical displacement
&lt;&#x2F;span&gt;&lt;span&gt;v = [B; w] \ [ zeros(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;)...; -q]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Renaming and scaling variables
&lt;&#x2F;span&gt;&lt;span&gt;θ, M, V = (v&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;°),(v&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&amp;#39;*E*I), (v&amp;#39;&amp;#39;&amp;#39;*E*I)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Plotting
&lt;&#x2F;span&gt;&lt;span&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;= plot(z, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span&gt;v, legend=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:none&lt;&#x2F;span&gt;&lt;span&gt;, 
&lt;&#x2F;span&gt;&lt;span&gt;    title=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Deflection [mm]&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, lc=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:blue&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;= plot(z, θ, legend=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:none&lt;&#x2F;span&gt;&lt;span&gt;, 
&lt;&#x2F;span&gt;&lt;span&gt;    title=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Angle [°]&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, lc=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:orange&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;plot(z, M&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span&gt;, label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Moment [kN⋅m]&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    fill = (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.15&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:blue&lt;&#x2F;span&gt;&lt;span&gt;),  lc=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:blue&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plot!(z, V&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span&gt;, label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Shear [kN]&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    fill = (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.15&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:green&lt;&#x2F;span&gt;&lt;span&gt;), lc=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:green&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3 &lt;&#x2F;span&gt;&lt;span&gt;= plot!(z, q&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span&gt;, label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Load [kN&#x2F;m]&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, 
&lt;&#x2F;span&gt;&lt;span&gt;    fill = (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.15&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:red&lt;&#x2F;span&gt;&lt;span&gt;),   lc=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:red&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    legend=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:bottomright&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;l = grid(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, heights=[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.2&lt;&#x2F;span&gt;&lt;span&gt; ,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.6&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;plot(p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, layout=l,  lw = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, size=(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;900&lt;&#x2F;span&gt;&lt;span&gt;), frame=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:zerolines&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;beam-deflection-01&#x2F;beam_deflection_fig2a.svg&quot; alt=&quot;Deflection, angle, moment, and shear of beam with a uniformly varying load&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;I would like to play around with more supporting and loading conditions. In particular, I want to figure out how to have a support in the middle of the beam. I believe I need to solve this problem piecewise when there are supports in the middle of the beam.&lt;&#x2F;li&gt;
&lt;li&gt;I want to see if ApproxFun can solve some more complicated examples. It should be able to solve the 2D version of this problem - plate deflection.&lt;&#x2F;li&gt;
&lt;li&gt;Adding &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Units.jl&amp;amp;type=Repositories&quot;&gt;Units.jl&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Measurements.jl&amp;amp;type=Repositories&quot;&gt;Measurements.jl&lt;&#x2F;a&gt; should allow me to calculate beam deflections with units and error propagation.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>My Favorite Shape</title>
        <published>2021-01-15T00:00:00+00:00</published>
        <updated>2021-01-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/favorite-shape-01/"/>
        <id>https://bradeneliason.com/blog/favorite-shape-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/favorite-shape-01/">&lt;p&gt;This is a love letter to my favorite shape, half of Euler&#x27;s figure-eight ... or &lt;em&gt;Fig. 8&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;favorite-shape-01&#x2F;favorite_shape_01_fig1.png&quot; alt=&quot;Euler&amp;#39;s Fig. 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I know... it&#x27;s a little weird to have a favorite shape. It&#x27;s even weirder to be so specific about one&#x27;s favorite shape. But bear with me, this is a really cool shape.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pre-history&quot;&gt;Pre-history&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;favorite-shape-01&#x2F;favorite_shape_01_fig2.jpg&quot; alt=&quot;Acheulean hand axe&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It looks similar to some of the earliest tools humans made, the Acheulean hand axe.&lt;&#x2F;p&gt;
&lt;p&gt;Many of these early tools resemble the Euler&#x27;s figure-eight. In particular cordiform (heart-shaped) and amygdaloidal (almond-shaped).&lt;&#x2F;p&gt;
&lt;p&gt;Many such hand axes were crafted to a great degree of precision and symmetry. This has led many to wonder if these axes were works of art in addition to being useful tools.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;math&quot;&gt;Math&lt;&#x2F;h2&gt;
&lt;p&gt;The mathematical formulation for solving for this shape is somewhat long. Since others have discussed the solution at great length and for brevity I&#x27;ll simply place the parameterized solution here.&lt;&#x2F;p&gt;
&lt;p&gt;$$ x=a \sqrt{k+\cos t} $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ y=\frac{a}{2} \int_{0}^{t} \frac{\cos u} {\sqrt{k+\cos u}} du $$&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; Plots
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;k = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.65223
&lt;&#x2F;span&gt;&lt;span&gt;dt = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.000001
&lt;&#x2F;span&gt;&lt;span&gt;tlim = acos(-k)-dt
&lt;&#x2F;span&gt;&lt;span&gt;t = -tlim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:dt:tlim  
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;x = @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt; sqrt(k + cos(t))
&lt;&#x2F;span&gt;&lt;span&gt;y = cumsum(@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt; cos(t)&#x2F;sqrt(k + cos(t))*dt)&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;plot(x,y, aspect_ratio=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, lw=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;, legend=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:none&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;savefig(joinpath(@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;__DIR__&lt;&#x2F;span&gt;&lt;span&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;favorite_shape_01_fig2.png&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;favorite-shape-01&#x2F;favorite_shape_01_fig2.png&quot; alt=&quot;Elastica curve traced by the parametric solution&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;elastica-elastic-curves&quot;&gt;Elastica - Elastic Curves&lt;&#x2F;h2&gt;
&lt;p&gt;If take an elastic strip and curl it around so that the ends meet, it makes half of Euler&#x27;s figure-eight. This shape minimizes the total curvature while still allowing the line to meet up end-to-end.&lt;&#x2F;p&gt;
&lt;p&gt;This shape is just one curve belonging to a family of curves called elastica or elastic curves. The study of elastic curves has puzzled the minds of many famous mathematicians, notably Galileo, Bernoulli, and Euler. Euler built on the analysis of Bernoulli and was able to characterize this family of curves completely.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;favorite-shape-01&#x2F;favorite_shape_01_fig3.png&quot; alt=&quot;Elastic Curves&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lintearia-water-in-a-tarp&quot;&gt;Lintearia - Water in a tarp&lt;&#x2F;h2&gt;
&lt;p&gt;It turns out that there is another example in physics which produces this curve called lintearia (from the Latin for linen). Imagine taking a length of tarp and securing the ends to horizontal bars at the same height. Fill the tarp with water up to the bars. Imagine for a moment that the sides are capped magically or that the caps are sufficiently far away to not affect the behavior at the center of the tarp.&lt;&#x2F;p&gt;
&lt;p&gt;Without the water, the tarp would form a catenary curve. When you fill the tarp with water, the water pressure pushes outwards against the tarp. Importantly, the water pressure increases with depth causing the tarp to bulge out at the bottom. If the bars are drawn together, the shape created is half of Euler&#x27;s figure-eight.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;favorite-shape-01&#x2F;favorite_shape_01_fig4.png&quot; alt=&quot;Lintearia&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;similar-curves&quot;&gt;Similar Curves&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;d be remiss to end this article without mentioning some very similar curves.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mathcurve.com&#x2F;courbes2d.gb&#x2F;lemniscate&#x2F;lemniscate.shtml&quot;&gt;The lemniscate of Bernoulli&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mathcurve.com&#x2F;courbes2d.gb&#x2F;syntractrice&#x2F;syntractrice.shtml&quot;&gt;The convict curve&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References:&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mathcurve.com&#x2F;courbes2d.gb&#x2F;chainette&#x2F;bachette.shtml&quot;&gt;Mathcurve.com LINTEARIA&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mathcurve.com&#x2F;courbes2d.gb&#x2F;linteaire&#x2F;linteaire.shtml&quot;&gt;Mathcurve.com ELASTIC CURVE&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Diagnostic Test Visualization: Part 2</title>
        <published>2021-01-04T00:00:00+00:00</published>
        <updated>2021-01-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/diagnostic-tests-02/"/>
        <id>https://bradeneliason.com/blog/diagnostic-tests-02/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/diagnostic-tests-02/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;diagnostic-tests-02&#x2F;diagnostic_tests_02_fig1.png&quot; alt=&quot;Diagnostic Test Visualization with Linear Scale from previous post&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;diagnostic-tests-02&#x2F;diagnostic_tests_02.jl&quot;&gt;View the full Julia source for this post&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to revisit a visualization that I made in a &lt;a href=&quot;&#x2F;blog&#x2F;diagnostic_tests_01&quot;&gt;previous post&lt;&#x2F;a&gt; which shows various parameters of diagnostics tests. I glossed over an issue in that previous post that I hope to address here. For a typical screening test, the visualization would be nearly unreadable because important regions on the graph would be too small to see. The box in the visualization is divided vertically by the prevalence of the condition. People with the condition are on the left and people without the condition are on the right. If the prevalence of the condition is low (say 1%), then the people with the condition (both true positives and false negatives) take up just a tiny sliver of the graph. The same problem occurs with very high prevalence for people without the condition. Similarly if the sensitivity or specificity is close to either 0 or 1, one of the boxes vanishes into a sliver.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;to-a-new-scale&quot;&gt;To a New Scale&lt;&#x2F;h2&gt;
&lt;p&gt;Instead of fundamentally altering the diagram, I figured I could rescale the axes. Logarithmic scaling is for &lt;a href=&quot;https:&#x2F;&#x2F;xkcd.com&#x2F;1162&#x2F;&quot;&gt;quitters&lt;&#x2F;a&gt; and doesn&#x27;t meet my needs, so I need a new scaling function. Probability is bounded between 0 and 1. I want to expand the regions on the ends of this range nearest to 0 and 1. On the low end, I need to distinguish two anomalous events with probabilities like 1% and 0.1%. Similarly, I would like the scale to distinguish two events which are nearly certain with probabilities like 99% and 99.9%. The scaling function should have an inverted &quot;S&quot; shape that gets steeper near 0 and 1. The steep regions of the graph have the effect of mapping a narrow range of inputs (depicted on the x-axis) into a larger region of output (depicted on the y-axis).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; Plots
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;logit(x) = log(x&#x2F;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;-x))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;begin
&lt;&#x2F;span&gt;&lt;span&gt;	xx = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.001&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1.0
&lt;&#x2F;span&gt;&lt;span&gt;	plot( logit, xx, label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Logit function&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, lw=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	plot!( xlim=(-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1.1&lt;&#x2F;span&gt;&lt;span&gt;), ylim=(-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4.1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4.1&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;		legend=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:topleft&lt;&#x2F;span&gt;&lt;span&gt;, framestyle=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:zerolines&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;		size=(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;savefig(joinpath(@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;__DIR__&lt;&#x2F;span&gt;&lt;span&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;diagnostic_tests_02_fig2.svg&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;diagnostic-tests-02&#x2F;diagnostic_tests_02_fig2.svg&quot; alt=&quot;Plot of logit function with asymptotes at 0 and 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;At first it seems like &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Logit&quot;&gt;logit function&lt;&#x2F;a&gt; (the inverse of the logistic function) might be a good fit. This maps values between 0 and 1 to $-∞$ and $+∞$. The one annoyance is that it takes a bounded range and maps it to an unbounded range. I wanted all the relevant features of my visualization to stay within a box so having a rescaling capable of shooting points off to infinity was not ideal.&lt;&#x2F;p&gt;
&lt;p&gt;The function below satisfies all my requirements. The amount of scaling at the ends of the range is determined by a parameter $k$. One really convenient feature is that to invert the scaling, you only need to apply the same function with the reciprocal parameter.&lt;&#x2F;p&gt;
&lt;p&gt;$$ f(x; k) = \frac{1}{1 + \left(\frac{1}{x} - 1 \right)^k} $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ f(\ f(x; k);\ \frac{1}{k})= x $$&lt;&#x2F;p&gt;
&lt;p&gt;I have to credit an answer from &lt;a href=&quot;https:&#x2F;&#x2F;math.stackexchange.com&#x2F;questions&#x2F;1832177&#x2F;sigmoid-function-with-fixed-bounds-and-variable-steepness-partially-solved&#x2F;3253471#3253471&quot;&gt;user Ron on math.StackExhange.com&lt;&#x2F;a&gt; with this equation. The question posted in that thread was relating to a sigmoidal function for an AI application, but Ron&#x27;s equation is precisely what I was looking for.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve found that a scaling factor of $\frac{1}{5}$ is a really good balance. With this scaling factor probabilities of 0.1%, 1%, 10%, and their inverses get remapped to close to 10% divisions. This means I can create nearly regularly spaced ticks marks that fall on these preferred numbers.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; Plots
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;scale_fwd(x, k=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;) = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;+ ((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;x) - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)^(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;k))
&lt;&#x2F;span&gt;&lt;span&gt;scale_rev(x, k=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;) = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;+ ((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;x) - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)^(k))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;begin
&lt;&#x2F;span&gt;&lt;span&gt;	xx = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.001&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1.0
&lt;&#x2F;span&gt;&lt;span&gt;	plot( scale_fwd, xx, label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;k=5&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, color=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:dodgerblue1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	plot!(scale_rev, xx, label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;k=1&#x2F;5&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, ls=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:dash&lt;&#x2F;span&gt;&lt;span&gt;, color=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:dodgerblue1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	plot!(xx, xx, lw=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, ls=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:dot&lt;&#x2F;span&gt;&lt;span&gt;, color=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:gray&lt;&#x2F;span&gt;&lt;span&gt;, label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;x=y&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;	plot!(aspect_ratio=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:equal&lt;&#x2F;span&gt;&lt;span&gt;, 
&lt;&#x2F;span&gt;&lt;span&gt;		xlim=(-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1.1&lt;&#x2F;span&gt;&lt;span&gt;), 
&lt;&#x2F;span&gt;&lt;span&gt;		ylim=(-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1.1&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;		legend=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:topleft&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;		framestyle=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:zerolines&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;		size=(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;600&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;600&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;		lw=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;	)
&lt;&#x2F;span&gt;&lt;span&gt;    savefig(joinpath(@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;__DIR__&lt;&#x2F;span&gt;&lt;span&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;diagnostic_tests_02_fig3.svg&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;diagnostic-tests-02&#x2F;diagnostic_tests_02_fig3.svg&quot; alt=&quot;Plot of bounded scaling function and its inverse&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;new-version-of-the-visualization&quot;&gt;New Version of the Visualization&lt;&#x2F;h2&gt;
&lt;p&gt;I recreated my visualization from before but with axes rescaled. Take note of the light dashed lines and axis labels. The prevalence is only 2.2% but this doesn&#x27;t collapse the left side of the plot into a sliver. It goes without saying that this graph isn&#x27;t meant to be used as a quantitative comparison.&lt;&#x2F;p&gt;
&lt;p&gt;Logarithmic scales are ubiquitous for a reason; they have a very nice property that turns curves depicting exponential decays or growth into straight lines. I&#x27;m not sure if there is a deeper mathematical elegance to the scale I&#x27;ve chosen or if there is another scaling function that&#x27;s better suited. My primary goal is to have a readable chart that helps me better understand diagnostic tests.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;diagnostic-tests-02&#x2F;diagnostic_tests_02_fig4.jpg&quot; alt=&quot;Diagnostic Test Visualization&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I could have stopped with the image above, but I wasn&#x27;t quite satisfied. I wanted my visualization to be interactive. I fired up an interactive &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Pluto.jl&amp;amp;type=Repositories&quot;&gt;Pluto.jl&lt;&#x2F;a&gt; notebook and created a visualization which reacts to input from a few sliders. The graphic was made with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Luxor.jl&amp;amp;type=Repositories&quot;&gt;Luxor.jl&lt;&#x2F;a&gt;. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Luxor.jl&amp;amp;type=Repositories&quot;&gt;Luxor.jl&lt;&#x2F;a&gt; isn&#x27;t meant to be interactive, but &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Pluto.jl&amp;amp;type=Repositories&quot;&gt;Pluto.jl&lt;&#x2F;a&gt; notebooks are reactive. When I update the value of a slider, the notebook automatically updates all the affected code including the code which generates the visualization. I may explore using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Javis.jl&amp;amp;type=Repositories&quot;&gt;Javis.jl&lt;&#x2F;a&gt; in the future for smoother animations. I&#x27;m pretty pleased with the outcome and there&#x27;s still a lot more that could be done.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;diagnostic-tests-02&#x2F;diagnostic_tests_02_fig5.gif&quot; alt=&quot;Animated Diagnostic Test Visualization&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;&#x2F;h2&gt;
&lt;p&gt;This project is mostly a vehicle for me to better remember the details of diagnostic testing. I&#x27;ve been pretty unsatisfied with the descriptions and visualizations on this topic. Creating my own visualization gives me a motivation to dive into terminology of diagnostic tests and learn new skills (such as creating interactive visualizations).&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s still more work that could be done. For this first plot, I&#x27;ve chosen to divide the plot first by people with and without the condition (i.e. splitting vertically by the prevalence). The vertical segments are subsequently split based on the results of their test. I chose this order because the most commonly discussed parameters of a diagnostic test—sensitivity, specificity, and prevalence—can be read directly from the graph. You could just as easily flip the order of these cuts. That is, first cut the square horizontally by people&#x27;s test results (negative and positive) and then cut the horizontal segments by people with and without the condition. On this alternative plot, parameters including positive predictive value (PPV), false discovery rate (FDR), false omission rate (FOR), and negative predictive value (NPV) can be read off from the graph directly.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Household Prints</title>
        <published>2021-01-01T00:00:00+00:00</published>
        <updated>2021-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/household-prints-01/"/>
        <id>https://bradeneliason.com/blog/household-prints-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/household-prints-01/">&lt;p&gt;This is a catch-all post for various 3D printing projects that I&#x27;ve done around the house. None of these prints really warrant a post by themselves, but they were rewarding projects worthy of sharing in some form nonetheless.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tv-lift&quot;&gt;TV Lift&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;household-prints-01&#x2F;household_prints_01_fig1.jpg&quot; alt=&quot;3D printed TV lift feet&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I placed a soundbar in front of my TV. The speaker ended up being just high enough to block the IR receiver on the TV. The black printed feet lift up the TV enough to allow the soundbar to fit just beneath. Our Monstera deliciosa, Cookie, is making a guest appearance on the right.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;drawer-organizers&quot;&gt;Drawer Organizers&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;household-prints-01&#x2F;household_prints_01_fig2.jpg&quot; alt=&quot;3D printed drawer organizers&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;3D Printing is very enjoyable; organizing drawers is not. Solution: 3D print your own modular drawer organizers. The design was done by me, but it was inspired by the organization bins designed by Alexandre Chappel. They have a base dimension of 50 mm. The image on the right is a 2×2×1 bin. The tab at the front has a recess which is wide enough for a $\frac{1}{4}&quot;$ wide label. Print files available here.&lt;&#x2F;p&gt;
&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;household-prints-01&#x2F;household_prints_01_fig3a.png&quot; alt=&quot;1x1 drawer organizers&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;household-prints-01&#x2F;household_prints_01_fig3b.png&quot; alt=&quot;1x3 drawer organizers&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;household-prints-01&#x2F;household_prints_01_fig3c.png&quot; alt=&quot;2x2 drawer organizers&quot;&gt;
&lt;&#x2F;div&gt;
&lt;!-- ![3D printed drawer organizers](household_prints_01_fig3a.png)
![3D printed drawer organizers](household_prints_01_fig3b.png)
![3D printed drawer organizers](household_prints_01_fig3c.png) --&gt;
&lt;h2 id=&quot;wire-organizer&quot;&gt;Wire Organizer&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;household-prints-01&#x2F;household_prints_01_fig4.jpg&quot; alt=&quot;3D printed jumper wire organizer&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Speaking of organizers, a while ago I made a quick 3D printed organizer for my breadboard jumper wires. The wires came in a kit as a large bundle. Entropy took its course, and soon I had a multi-colored spaghetti pile of jumper wires. I&#x27;m realizing now fighting household entropy may be one of the killer apps for 3D printing—provided you have the design tools to make organizational devices to your heart&#x27;s content.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;beard-trimmer-attachments&quot;&gt;Beard Trimmer Attachments&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;household-prints-01&#x2F;household_prints_01_fig5.jpg&quot; alt=&quot;3D printed organizer for beard trimmer attachments&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Yet another organizer... this time for beard trimmer attachments. Extracting the shape of each attachment was a small exercise in reverse engineering. I laid each attachment on its side on my flat bed scanner with a ruler. Using an image analysis software called Fiji, I was able to get the precise shape of each attachment. This model is designed to print upside-down with most of the body shelled out.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tall-desk-organizer&quot;&gt;Tall Desk Organizer&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;household-prints-01&#x2F;household_prints_01_fig6.png&quot; alt=&quot;3D printed tall desk organizer&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Okay, last organizational device, I promise. This was designed and printed at the request of my partner. She was browsing desk organizers online and found one she liked. It ends up being cheaper to print such devices yourself; this device cost about $2.50 in filament and electricity. The added benefit is you can customize the dimensions. In this case, my partner wanted this organizer to be able to fit Post-it notes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-ornamental-forest&quot;&gt;An Ornamental Forest&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;household-prints-01&#x2F;household_prints_01_fig7.jpg&quot; alt=&quot;3D printed Christmas ornaments&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For the holidays, I went into factory mode and printed over a dozen little Christmas ornaments as gifts. The model is available here (not designed by me). I spent a bit of time optimizing the print settings and got the print time down to 22 minutes (from about 45 min). I&#x27;m sure the print time could be reduced further but probably not without compromising the quality or strength.
Power Disks&lt;&#x2F;p&gt;
&lt;h2 id=&quot;power-disk-ornaments&quot;&gt;Power Disk Ornaments&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;household-prints-01&#x2F;household_prints_01_fig8.jpg&quot; alt=&quot;3D printed Christmas ornaments for kids&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Last Christmas, I printed three custom power discs for my three little niblings (names blurred out). Power disks are from the children&#x27;s show Wild Kratts and give the main characters the form and powers of various creatures. The little rascals soon were trading disks and imbuing themselves with the powers of their siblings.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Cellphone Camera Microscope Adapter</title>
        <published>2020-07-24T00:00:00+00:00</published>
        <updated>2020-07-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/microscope-adapter-01/"/>
        <id>https://bradeneliason.com/blog/microscope-adapter-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/microscope-adapter-01/">&lt;p&gt;A while back I picked up an old microscope—a Zeiss Photomicroscope II to be precise. I&#x27;ve had a long list of projects that I&#x27;ve been meaning to do related to this microscope, and this adapter for my cellphone camera is hopefully the first in a series of posts detailing these projects.&lt;&#x2F;p&gt;
&lt;p&gt;The first set of projects that I want to check off the list related to special image processing algorithms. So the first step in this journey is acquiring the images. While I was shopping for a camera to attach to my microscope, I was floored by how expensive many of the options were. I&#x27;m sure that the precision, quality, and special features are required for many applications, but all I wanted were pretty, high-definition photos. In the past, I held my phone to the microscope eyepiece, but this gets old quickly.
My first prototype adapter&lt;&#x2F;p&gt;
&lt;p&gt;I have a set of Moment lenses for my phone that attach via a bayonet mount to a specialized case. My first iteration of an adapter consisted of just the mounting feature attached to a cone. The base of the adapter fits around the microscope eyepiece and can be locked into place with three screws.&lt;&#x2F;p&gt;
&lt;p&gt;As many engineers know, CAD designs live in a universe devoid of scale, gravity, and the nuisance of manufacturing limitations. This adapter was to live at the top of the microscope with the screen of my phone pointing upwards. In this orientation, the weight of my phone would torque on the small printed tabs of the adapter. The solution was to print supports which braced against the back of my phone. The supports lower the amount of force applied to the mounting tabs and prevents them from breaking.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;microscope-adapter-01&#x2F;microscope_adapter_01_fig1.jpg&quot; alt=&quot;Close up of first prototype&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Currently, the position and orientation of the adapter relative to the eyepiece are set with three screws with generous wiggle room. In the future, I&#x27;d like to print a 3rd version with hard stops that seat the adapter at the correct position.&lt;&#x2F;p&gt;
&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;microscope-adapter-01&#x2F;microscope_adapter_01_fig2.jpg&quot; alt=&quot;Close up of the Moment case interface with hard stops&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;microscope-adapter-01&#x2F;microscope_adapter_01_fig3.jpg&quot; alt=&quot;Adapter at Work&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;microscope-adapter-01&#x2F;microscope_adapter_01_fig4.jpg&quot; alt=&quot;Adapter mounted on eyepiece&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;microscope-adapter-01&#x2F;microscope_adapter_01_fig5.jpg&quot; alt=&quot;Image acquired from my cellphone&quot;&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Mechanical Rope Tensioner</title>
        <published>2020-06-24T00:00:00+00:00</published>
        <updated>2020-06-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/rope-tensioner-01/"/>
        <id>https://bradeneliason.com/blog/rope-tensioner-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/rope-tensioner-01/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;rope-tensioner-01&#x2F;rope_tensioner_01_fig3.png&quot; alt=&quot;&amp;quot;CAD Cross-section of the tensioner&amp;quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;rope-tensioner-01&#x2F;rope_tensioner_01_fig1.jpg&quot; alt=&quot;&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;rope-tensioner-01&#x2F;rope_tensioner_01_fig2.jpg&quot; alt=&quot;&quot;&gt;
&lt;&#x2F;div&gt;

&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;rope-tensioner-01&#x2F;rope_tensioner_01_fig4.jpg&quot; alt=&quot;&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;rope-tensioner-01&#x2F;rope_tensioner_01_fig5.jpg&quot; alt=&quot;&quot;&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going camping in a week equipped with a new tent. In anticipation, I&#x27;ve been designing and printing a completely unnecessary trinket to make setting up camp marginally easier. I&#x27;ve created a rope tensioner that works via a pair of geared cams. Early prototypes suggested that the gears wouldn&#x27;t be strictly necessary, but it keeps the cams in sync and makes the self-locking action more robust. The surfaces of the cams are knurled with large teeth to catch the rope. Some of my early prototypes had serrations going in just one direction or a small knurling pattern. These early teeth patterns didn&#x27;t work on the slippery surface of the paracord I was testing it on. The large teeth mesh together which forces the rope into a tortuous path when locked. This device is also designed to print in place. Once the print is complete, the cams can be gently torqued with a screwdriver to set them free.&lt;&#x2F;p&gt;
&lt;p&gt;To be completely honest, this was mostly a design exercise to see if I could create a self-locking cam mechanism. The idea turned out to be very sticky, and I couldn&#x27;t abandon it until I had a working prototype. Now that I&#x27;ve settled on this design, I&#x27;ll probably opt to batch print a more elegant design. The simpler design employed by many rope tensioners forces the rope into tight &quot;S&quot; or &quot;U&quot; bends with a simple, monolithic design. The rope is fed through holes or around various appendages of the device. As tension is applied, the rope presses against the tensioner at the bends and friction keeps the rope from slipping. It&#x27;s ultimately the better (albeit uninteresting) design.&lt;&#x2F;p&gt;
&lt;p&gt;If I were to spend more time with this trinket, I may add a spring mechanism to bias the cams closed. Currently the cams have to close enough to catch the rope to begin the self-locking action. I thought about replacing the hinge section with a compliant spring mechanism. Imagine a central post coming from the triangular base where the hinge currently resides with spiral spokes connecting it to the cam bodies. This would act as both hinge and spring, but I&#x27;m afraid that such a design would allow the cams to move apart when force is applied.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Dithering in Blender</title>
        <published>2020-06-13T00:00:00+00:00</published>
        <updated>2020-06-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/blender-dithering-01/"/>
        <id>https://bradeneliason.com/blog/blender-dithering-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/blender-dithering-01/">&lt;p&gt;I wanted to create a 3D render which emulated the retro video game aesthetic. To accomplish this I created a compositing node setup in Blender to apply ordered dithering to an image. Below is a description of what dithering is and how it can be applied to a rendering in Blender using compositing nodes. Note: I have the image files of the dither matrices needed for this effect free to download below so that you don&#x27;t need to recreate them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-dithering&quot;&gt;What is Dithering?&lt;&#x2F;h2&gt;
&lt;p&gt;Dithering an image is adding noise to it. Typically we want as little noise in an image as possible, so why would we want to add noise? Early computer displays had a limited number of colors that could be displayed. If you wanted to smoothly blend one color into another, this limited color palette would result in blocks colors with large jumps between colors. This is known as color banding. This color banding is the visual result of quantization error. Quantization error occurs when a smoothly varying input value is rounded to a limited number of output levels.&lt;&#x2F;p&gt;
&lt;p&gt;Images with this color banding are accurate at the pixel level. That is, every pixel is displayed with the color as close to the original as possible (minimal pixel quantization error). But this per-pixel accuracy often results in very ugly images. Instead consider treating a block of pixels as a unit instead of the single pixel. We perceive the average of a block of pixels and this pixel grouping allows for more possible perceived gradations. The dithered gradient in the image below is composed of only black and white pixels, but the blocks of pixels are perceived as gradations of grey. So while dither causes the quantization error of the image to increase at the pixel level, the result is perceived as closer to the input gradient. Thus dithering is adding noise to an image in such a way that it reduces the perceptual cost of quantization errors.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-dithering-01&#x2F;blender_dithering_01_fig1.png&quot; alt=&quot;Ordered Dithering&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There are two ways to think about this dithering noise: adding noise and then rounding or having a different threshold across a block of pixels. To dither a block of pixels we can add a block of noise and round up and down with a threshold of 50%. The noise we add is set up so that a block of 50% grey pixels would have half its pixels rounded up to white and half its pixels rounded down to black. Similarly, a block of 25% grey would have noise added such that rounding with a 50% threshold results in about 25% of the pixels being white. The other way to think about this having a different threshold for each pixel within a block. These methods are equivalent, but I wanted to make it clear that the threshold matrices that I am creating below have the effect of adding noise.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dithering-matrices&quot;&gt;Dithering Matrices&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; Images
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;bayer_matrix&lt;&#x2F;span&gt;&lt;span&gt;(size=1)
&lt;&#x2F;span&gt;&lt;span&gt;    M = [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0 2&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3 1&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; i=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;:size
&lt;&#x2F;span&gt;&lt;span&gt;        M = @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;. &lt;&#x2F;span&gt;&lt;span&gt;[(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;*M) (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;*M+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;); (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;*M+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;) (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;*M+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; M.&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;^(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;*size)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;img_size = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;128
&lt;&#x2F;span&gt;&lt;span&gt;imgs = []
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; n = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4
&lt;&#x2F;span&gt;&lt;span&gt;    bm = bayer_matrix(n)
&lt;&#x2F;span&gt;&lt;span&gt;    reps = convert(Int, img_size&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;^n)
&lt;&#x2F;span&gt;&lt;span&gt;    bm = repeat(bm, inner=(reps,reps))
&lt;&#x2F;span&gt;&lt;span&gt;    push!(imgs, bm)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;padimg = ones((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;128&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;output = hcat(imgs[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;], padimg, imgs[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;], padimg, imgs[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;], padimg, imgs[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;save(joinpath(@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;__DIR__&lt;&#x2F;span&gt;&lt;span&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;blender_dithering_fig2.png&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;), colorview(Gray, output))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-dithering-01&#x2F;blender_dithering_fig2.png&quot; alt=&quot;Bayer matrices of different sizes&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I created a quick function in Julia to create different threshold maps. These threshold maps (Bayer matrices) are square grey-scale images that are repeated to fill a region. The input image is compared pixel-by-pixel to the threshold map and set to white if it&#x27;s above the pixel in the map and black if it&#x27;s below the value in the map. The algorithm on the right can create threshold maps of many different sizes. This algorithm starts with a 2-by-2 matrix and iteratively expands the matrix by two in each dimension to reach a desired size. The final step is dividing the matrix by an appropriate factor of two to yield values between zero and one.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;blender-compositing-with-dithering&quot;&gt;Blender Compositing with Dithering&lt;&#x2F;h2&gt;
&lt;p&gt;Below are two images of the default Blender monkey Suzanne with and without dithering. The image on the left has no dithering, so any pixel above the threshold of 50% grey is set to white. The image on the right is still composed of only black and white pixels, but the dither produces the appearance of fine gradations of grey. I added a rendering pass to draw lines around certain contours of the model to give the model more form and produce a retro line art and shading appearance.&lt;&#x2F;p&gt;
&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-dithering-01&#x2F;blender_dithering_01_fig3a.png&quot; alt=&quot;Without Dithering&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-dithering-01&#x2F;blender_dithering_01_fig3b.png&quot; alt=&quot;With Dithering&quot;&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;send-nodes&quot;&gt;Send Nodes&lt;&#x2F;h2&gt;
&lt;p&gt;Here is the Blender image compositor node setup used to produce the results above.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-dithering-01&#x2F;blender_dithering_01_fig4.png&quot; alt=&quot;Blender node&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-dithering-01&#x2F;blender_dithering_01_fig5.png&quot; alt=&quot;Node Group&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;more-results&quot;&gt;More Results&lt;&#x2F;h2&gt;
&lt;div class=&quot;fig-group&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-dithering-01&#x2F;blender_dithering_01_fig6a.png&quot; alt=&quot;Color Wheel Input&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-dithering-01&#x2F;blender_dithering_01_fig6b.png&quot; alt=&quot;Color Wheel Quantized&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-dithering-01&#x2F;blender_dithering_01_fig6c.png&quot; alt=&quot;Color Wheel Dithered&quot;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-dithering-01&#x2F;blender_dithering_01_fig7.gif&quot; alt=&quot;Dithered Animation&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Pursuit Curves in Julia</title>
        <published>2020-06-10T00:00:00+00:00</published>
        <updated>2020-06-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/pursuit-curves-01/"/>
        <id>https://bradeneliason.com/blog/pursuit-curves-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/pursuit-curves-01/">&lt;p&gt;Imagine the path of a fox chasing a rabbit. A simple pursuit strategy (but by no means the best) would consist of the fox constantly moving in the direction of the rabbit at full speed. The path traced out by this fox belongs to a family of curves aptly named pursuit curves. The exact trajectory that the fox takes depends on the path that the rabbit takes, the starting positions of each animal, and the relative speed between the fox and rabbit.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;pursuit-curves-01&#x2F;pursuit_curves_01.jl&quot;&gt;View the full Julia source for this post&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The position of the rabbit through time is the vector $R(t)$, and the position of the fox through time is the vector $F(t)$. For the sake of simplicity, the rabbit takes a predefined trajectory and has a constant speed of 1. The fox&#x27;s speed is some multiple, $k$, of the rabbit&#x27;s speed. The governing differential equation of this pursuit curve is below. The differential equation tells us that every instant in time, we are updating the fox&#x27;s velocity, $F&#x27;$, to point in the direction of the rabbit. The unit vector pointing from the fox and to the rabbit is $R-F&#x2F;|R-F|$. We scale this unit vector by the fox&#x27;s speed (simplified to just $k$) to get an equation for the fox velocity vector at any instant in time.&lt;&#x2F;p&gt;
&lt;p&gt;$$ \left| R&#x27; \right| \equiv 1 $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ \left| F&#x27; \right| \equiv k \left| R&#x27; \right| = k $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ \frac{F&#x27;}{\left| F&#x27; \right|} = \frac{R-F}{\left| R-F \right|} $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ F&#x27; = \frac{k(R-F)}{\left| R-F \right|} $$&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m defining a custom plotting recipe in Julia. This creates the plots seen below containing lines with trailing widths. The last line of this code block shouldn&#x27;t be ignored. This single line defines the differential equation which governs the path of the fox. It takes the function pursued (the path that the rabbit takes) and spits out the velocity vector of the fox.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; Plots
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; DifferentialEquations, OrdinaryDiffEq
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; LinearAlgebra
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Creating a plotting recipe for these pursuit curves
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;userplot&lt;&#x2F;span&gt;&lt;span&gt; PursuitPlot
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;recipe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;(cp::PursuitPlot)
&lt;&#x2F;span&gt;&lt;span&gt;    t, u = cp.args
&lt;&#x2F;span&gt;&lt;span&gt;    u = hcat(u...)&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;    x, y = u[:,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;], u[:,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;    n = length(x)
&lt;&#x2F;span&gt;&lt;span&gt;    linewidth --&amp;gt; range(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.75&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, length = n)
&lt;&#x2F;span&gt;&lt;span&gt;    aspect_ratio --&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;    x, y
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Define the pursuit differential equations
&lt;&#x2F;span&gt;&lt;span&gt;pursuit(u, k, t) = k * (pursued(t) - u) &#x2F; norm(pursued(t)-u)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;rabbit-running-in-a-circle&quot;&gt;Rabbit Running in a Circle&lt;&#x2F;h2&gt;
&lt;p&gt;The following simulation has the rabbit run in a unit circle with the fox starting at [4, 0]. In this simulation the fox is only 80% as fast as the rabbit, so he&#x27;s never able to catch the rabbit.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;pursued(t) = [cos(t), sin(t)]
&lt;&#x2F;span&gt;&lt;span&gt;prob = ODEProblem(pursuit, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4.0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;], (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;17&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.8&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;sol = solve(prob, saveat=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;pursuitplot(sol.t, pursued.(sol.t), label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Rabbit&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;pursuitplot!(sol.t, sol.u, color=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:tomato2&lt;&#x2F;span&gt;&lt;span&gt;, label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Fox&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;pursuit-curves-01&#x2F;pursuit_curves_01_fig1b.gif&quot; alt=&quot;Animation: Rabbit running in a circle&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rabbit-running-in-a-line&quot;&gt;Rabbit Running in a Line&lt;&#x2F;h2&gt;
&lt;p&gt;The following simulation has the rabbit run straight north starting at [0, -3] with the fox starting at [4, 0] again. Once again, the fox is only 80% as fast as the rabbit, so he&#x27;s never able to catch the rabbit.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;pursued(t) = [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, (t - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;)]
&lt;&#x2F;span&gt;&lt;span&gt;prob = ODEProblem(pursuit, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4.0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;], (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;sol = solve(prob, saveat=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;pursuitplot(sol.t, pursued.(sol.t), label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Rabbit&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;pursuitplot!(sol.t, sol.u, color=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:tomato2&lt;&#x2F;span&gt;&lt;span&gt;, label=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Fox&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;pursuit-curves-01&#x2F;pursuit_curves_01_fig2b.gif&quot; alt=&quot;Animation: Rabbit Running in a Line&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Tolerance Analysis with Julia</title>
        <published>2020-05-20T00:00:00+00:00</published>
        <updated>2020-05-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/tolerance-analysis-01/"/>
        <id>https://bradeneliason.com/blog/tolerance-analysis-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/tolerance-analysis-01/">&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;&#x2F;h2&gt;
&lt;p&gt;This project started when I was reviewing the Excel engineering calculators found on &lt;a href=&quot;https:&#x2F;&#x2F;www.mitcalc.com&#x2F;&quot;&gt;MITCalc.com&lt;&#x2F;a&gt;. MITCalc has many useful calculators for sale for designing gears, springs, beams, and much more. They also have many generic engineering tools including a spreadsheet for performing tolerance analysis. Being both frugal and curious, I set out to see if I could replicate this function with Julia. I&#x27;ve posted the results here for other frugal, curious engineers.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;&#x2F;strong&gt; I&#x27;ve done this as a proof of concept to fulfill my own curiosity—use your own judgment to determine if this tool will work for you and your application.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;background-on-tolerances&quot;&gt;Background on Tolerances&lt;&#x2F;h2&gt;
&lt;p&gt;If you are making only a few parts and assembling the parts yourself, make the parts to fit. If you watch a master woodworker, they&#x27;ll often use layout calipers or use one piece to mark another (i.e. relative dimensioning). If there&#x27;s another party involved in the manufacturing or you are making many parts, you need to consider the tolerances of your dimension. Dimensions and tolerances are a way to communicate intent. There are several different ways a dimension may be presented on the drawing of a part:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Limit of size&lt;&#x2F;strong&gt; — This type of tolerance lists two dimensions: the smallest and the largest acceptable dimension. The dimensions are either written from smallest to largest left-to-right, or the larger dimension is on top of the smaller dimension.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Equal bilateral form&lt;&#x2F;strong&gt; — This is the type of tolerance &lt;em&gt;most people are familiar with&lt;&#x2F;em&gt;. The dimension has a nominal value followed by the acceptable variation. The dimension may be larger or smaller by equal amounts.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Unequal bilateral form&lt;&#x2F;strong&gt; — Like the equal bilateral form, the dimension has a nominal value followed by the acceptable variation, but in this case the variation is not symmetrical. The dimension may be larger or smaller on either side by a different amount.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Unilateral form&lt;&#x2F;strong&gt; — In this case the dimension is only allowed to be either larger or smaller than the nominal dimension.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Unless Otherwise Specified (UOS)&lt;&#x2F;strong&gt; — A part drawing will often have a table of tolerances in the title block. The tolerance of each dimension can be specified by the number of significant digits given.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Decimal Places&lt;&#x2F;th&gt;&lt;th&gt;Format&lt;&#x2F;th&gt;&lt;th&gt;Standard Tolerance&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;0.x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;± 0.2&quot;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;0.0x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;± 0.01&quot;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;0.00x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;± 0.005&quot;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;0.000x&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;± 0.0005&quot;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;In general, use equal bilateral tolerance, but there are exceptions to that rule. Remember, dimensions and tolerances communicate intent. Consider the case where you want an eighth-inch hole, but the application permits the hole to be larger more readily than it permits the hole to be smaller. You may want to specify the dimension as something like $\varnothing\ 0.125 + 0.005&#x2F;-0.001&quot;$. If instead you were to specify the dimension as $\varnothing\ 0.127 \pm .003&quot;$, this might signal to the manufacturer that they would need a special $0.127&quot;$ tool. In general, holes produced by standard drills are over-sized, so a wise machinist might know to use a quarter-inch drill when given a dimension of $\varnothing\ 0.127 \pm .003&quot;$. However, $\varnothing\ 0.125 +0.005&#x2F;-0.001&quot;$ more clearly communicates your intent: a $0.125&quot;$ hole.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tolerance-analysis&quot;&gt;Tolerance Analysis&lt;&#x2F;h2&gt;
&lt;p&gt;Tolerance analysis is the process of calculating how error might accumulate in dimensions with tolerances. There are two main ways to calculate tolerance: worst-case analysis and statistical analysis.&lt;&#x2F;p&gt;
&lt;p&gt;Worst-case analysis is just what it sounds like: you consider each dimension at its largest and smallest acceptable values and you calculate the upper and lower bounds for the combined dimensions. Since most manufacturing processes will produce real dimensions following a normal distribution, it&#x27;s extremely unlikely that anything close to the worst-case scenario will come true. The power of the worst-case analysis is in its simplicity. It boils down to just adding up the dimensions twice: once for the lower limit dimensions and again for the upper limit dimensions.&lt;&#x2F;p&gt;
&lt;p&gt;Statistical tolerance analysis is much more powerful. Instead of assuming each part is at the limits of its acceptable variation, statistical tolerance analysis assumes each dimension is sampled from a statistical distribution. For the sake of simplicity, we&#x27;ll assume that each dimension is sampled from a normal distribution centered on the nominal dimension and that the tolerance represents three times the standard deviation ($3\sigma$ on either side for a total of $6\sigma$). When we assume the dimensions are normal distributions, we can use the root sum squared (RSS) method to combine their tolerances as standard deviations.&lt;&#x2F;p&gt;
&lt;p&gt;Note: Not all manufacturing processes will produce dimensions with a normal distribution. The methods to calculate the tolerance stack-up get more complicated; special software has been developed to deal with these cases. I plan to look into various probabilistic programming packages in Julia (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Soss.jl&amp;amp;type=Repositories&quot;&gt;Soss.jl&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Turing.jl&amp;amp;type=Repositories&quot;&gt;Turing.jl&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Gen.jl&amp;amp;type=Repositories&quot;&gt;Gen.jl&lt;&#x2F;a&gt;) to see if they can be used for this type of tolerance analysis.&lt;&#x2F;p&gt;
&lt;p&gt;Consider the simple case of stacking two parts together.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The left, purple part has a dimension of $40 \pm 0.5 mm$.&lt;&#x2F;li&gt;
&lt;li&gt;The right, blue part has a dimension of $25 \pm 0.1 mm$.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;tolerance-analysis-01&#x2F;tolerance_analysis_01_fig1.png&quot; alt=&quot;Tolerance stack up&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;worst-case-tolerance-analysis&quot;&gt;Worst-Case Tolerance Analysis&lt;&#x2F;h3&gt;
&lt;p&gt;Using worst-case analysis, we can calculate the upper and lower bound of the stack. The smallest acceptable dimensions of each part are 39.5 and 24.9 for a total of 64.4. The largest acceptable dimensions of each part are 40.5 and 25.1 for a total of 65.6. The total stack will fall between 64.4 and 65.6—put another way, $64 \pm 0.6 mm$.&lt;&#x2F;p&gt;
&lt;p&gt;The worst-case analysis produces a more conservative (larger) tolerance compared with the RSS method.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;statistical-tolerance-analysis-rss-method&quot;&gt;Statistical Tolerance Analysis — RSS Method&lt;&#x2F;h3&gt;
&lt;p&gt;Using the RSS method, we sum the standard deviations using the following formula:&lt;&#x2F;p&gt;
&lt;p&gt;$$ \sigma_{total}=\sqrt{\sum_{i=1}^{n} \sigma_{i}^{2}}\ = \sqrt{0.5^2 + 0.1^2}\ =\ 0.5099.. $$&lt;&#x2F;p&gt;
&lt;p&gt;The nominal dimensions are summed normally to produce a tolerance stack up of $65 \pm 0.51 mm$.&lt;&#x2F;p&gt;
&lt;p&gt;These processes are not terribly complicated for simple geometries, but the complexity of these calculations explodes rapidly with more dimensions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;measurements-jl&quot;&gt;Measurements.jl&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; Measurements
&lt;&#x2F;span&gt;&lt;span&gt;a = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4.5 &lt;&#x2F;span&gt;&lt;span&gt;± &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;b = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3.8 &lt;&#x2F;span&gt;&lt;span&gt;± &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.4&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;show &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;a + b
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;a + b = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;12.8 &lt;&#x2F;span&gt;&lt;span&gt;± &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.45
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Measurements.jl&amp;amp;type=Repositories&quot;&gt;Measurements.jl&lt;&#x2F;a&gt; is a package in Julia that takes care of uncertainty propagation for you. Values with uncertainty can be specified with &lt;code&gt;±&lt;&#x2F;code&gt;, as we would hope. In this case, the &lt;code&gt;±&lt;&#x2F;code&gt; symbol is a custom infix operator that creates a new type called a Measurement. Through the unreasonable effectiveness of multiple dispatch, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Measurements.jl&amp;amp;type=Repositories&quot;&gt;Measurements.jl&lt;&#x2F;a&gt; extends the basic operations of Julia, so all calculations done on a Measurement will look the same as if they were just a float. More details about &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Measurements.jl&amp;amp;type=Repositories&quot;&gt;Measurements.jl&lt;&#x2F;a&gt; can be found in the package documentation. Here is a basic example of how &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Measurements.jl&amp;amp;type=Repositories&quot;&gt;Measurements.jl&lt;&#x2F;a&gt; works:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tolerance-analysis-with-measurements-jl&quot;&gt;Tolerance Analysis with Measurements.jl&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; Measurements
&lt;&#x2F;span&gt;&lt;span&gt;dim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;40 &lt;&#x2F;span&gt;&lt;span&gt;± &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;dim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;25 &lt;&#x2F;span&gt;&lt;span&gt;± &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;show&lt;&#x2F;span&gt;&lt;span&gt; dim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;+ dim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;dim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;+ dim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;65.0 &lt;&#x2F;span&gt;&lt;span&gt;± &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.51
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Consider our simple example above. The following code is able to replicate the statistical tolerance analysis method without the hassle. There&#x27;s no special methods to call. The complexities of propagating uncertainty though addition are handled by extending the definition of addition to quantities with uncertainty.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, that&#x27;s too easy. Let&#x27;s try a problem with more teeth.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;tolerance-analysis-01&#x2F;tolerance_analysis_01_fig2a.png&quot; alt=&quot;Tolerance analysis example&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This example was taken from an example in the &lt;a href=&quot;https:&#x2F;&#x2F;www.mitcalc.com&#x2F;doc&#x2F;tolanalysis3d&#x2F;help&#x2F;en&#x2F;tolanalysis3d.htm&quot;&gt;documentation for the MITCalc.com&lt;&#x2F;a&gt; tolerance analysis calculator. Imagine a part with two holes. The positions of the two holes are determined by chains of dimensions. We would like to know the center-to-center distance and tolerance between the two holes. Our simple calculation methods above quickly become unwieldy.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;tolerance-analysis-01&#x2F;tolerance_analysis_01_fig2b.png&quot; alt=&quot;Tolerance analysis example, variable names&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The MITCalc example suggests first deriving the equations below:&lt;&#x2F;p&gt;
&lt;p&gt;$$ X=B-D+(C-E) \cdot \cos (A)-G \cdot \sin (A) $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ Y=(C-E) \cdot \sin (A)+G \cdot \cos (A)-F $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ Z=\sqrt{X^{2}+Y^{2}} $$&lt;&#x2F;p&gt;
&lt;p&gt;Ignore those equations. &lt;strong&gt;There&#x27;s an easier way.&lt;&#x2F;strong&gt; Performing statistical tolerance analysis on this part becomes trivial with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Measurements.jl&amp;amp;type=Repositories&quot;&gt;Measurements.jl&lt;&#x2F;a&gt;. Simply define each of the dimensions as a vector. For example, dimension $F$ is really a vector with a Measurement for one of its values:&lt;&#x2F;p&gt;
&lt;p&gt;$$ F = \begin{bmatrix}0 \44.95 \pm 0.05\end{bmatrix} $$&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; Pkg; Pkg.add(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Measurements&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; Measurements, LinearAlgebra
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Define the degree symbol to convert from degree to radians
&lt;&#x2F;span&gt;&lt;span&gt;° = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;pi &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;180&lt;&#x2F;span&gt;&lt;span&gt;; 
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Create a quick function to rotate vectors
&lt;&#x2F;span&gt;&lt;span&gt;vec_rotate(θ, vector) = [cos(θ) -sin(θ); sin(θ) cos(θ)] * vector
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;A = (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;30 &lt;&#x2F;span&gt;&lt;span&gt;± &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.2&lt;&#x2F;span&gt;&lt;span&gt;)°;
&lt;&#x2F;span&gt;&lt;span&gt;B = [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;120&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;C = vec_rotate(A, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;120&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;D = [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;E = vec_rotate(A, [-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;F = [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;44.95 &lt;&#x2F;span&gt;&lt;span&gt;± &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.05&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;G = vec_rotate(A, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;44.95 &lt;&#x2F;span&gt;&lt;span&gt;± &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.05&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;= B + C + G + E;
&lt;&#x2F;span&gt;&lt;span&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;= F + D;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Z = path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;- path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;show&lt;&#x2F;span&gt;&lt;span&gt; norm(Z) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# The length of Z
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once all the vectors are defined, determining Z is easy. Z is just a vector derived from all the other dimension vectors. The length of Z can be found by taking its norm. The result is $160.26 \pm 0.24$.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;norm(Z) = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;160.26 &lt;&#x2F;span&gt;&lt;span&gt;± &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.24
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;smaller-details&quot;&gt;Smaller Details:&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;I&#x27;ve only demonstrated doing the RSS statistical tolerance analysis method. I may write a quick function to do worst-case analysis using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Measurements.jl&amp;amp;type=Repositories&quot;&gt;Measurements.jl&lt;&#x2F;a&gt;—stay tuned.&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;ve converted a unilateral dimension ($45 +0&#x2F;-0.1$) in the example above into equal bilateral dimensions ($44.95 \pm 0.05$).&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;ve bodged a little convenience function to rotate vectors. I would like to make a more generic function, but it wasn&#x27;t worth the hassle for this proof of concept. Let me know if you know of a better solution.&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;ve also defined the degree symbol as π&#x2F;180. I made it a habit to write that at the beginning of all Julia scripts, because it&#x27;s ridiculously convenient to just write &lt;code&gt;30°&lt;&#x2F;code&gt; instead of &lt;code&gt;30*π&#x2F;180&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;This method is also compatible with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Unitful.jl&amp;amp;type=Repositories&quot;&gt;Unitful.jl&lt;&#x2F;a&gt;. This means that you could give units to each dimension vector and the calculation proceeds without any additional monkey business. I haven&#x27;t shown this here, but it does work. Using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Unitful.jl&amp;amp;type=Repositories&quot;&gt;Unitful.jl&lt;&#x2F;a&gt; may be particularly useful if you&#x27;re compelled to use mixed imperial and metric dimensions.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;future-work&quot;&gt;Future Work&lt;&#x2F;h3&gt;
&lt;p&gt;There is still a lot that could be done. I&#x27;ve considered creating a package to perform tolerance analysis, but for now I&#x27;m happy with using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Measurements.jl&amp;amp;type=Repositories&quot;&gt;Measurements.jl&lt;&#x2F;a&gt;. More refined statistical tolerance analysis processes could probably still use Julia, but I believe I&#x27;ll need to use one of the probabilistic programming packages (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Soss.jl&amp;amp;type=Repositories&quot;&gt;Soss.jl&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Turing.jl&amp;amp;type=Repositories&quot;&gt;Turing.jl&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Gen.jl&amp;amp;type=Repositories&quot;&gt;Gen.jl&lt;&#x2F;a&gt;) to perform uncertainty propagation with arbitrary distributions. Here are some features that could be part of a Julia package specifically for tolerances:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Supporting all of the tolerance types above, including a way to parse a dimension written as a string.&lt;&#x2F;li&gt;
&lt;li&gt;Support for multiple different tolerance analysis methods, including worst-case, RSS, Monte Carlo simulations, and others.&lt;&#x2F;li&gt;
&lt;li&gt;Support for units (a must!).&lt;&#x2F;li&gt;
&lt;li&gt;Integration with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;search?q=Plots.jl&amp;amp;type=Repositories&quot;&gt;Plots.jl&lt;&#x2F;a&gt; (this would be interesting).&lt;&#x2F;li&gt;
&lt;li&gt;The ability to specify standard tolerances (e.g., ANSI or ISO hole tolerance standards)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you end up using this, let me know! I&#x27;d be really curious to hear what you think.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Assistive Bottle Opener</title>
        <published>2020-01-17T00:00:00+00:00</published>
        <updated>2020-01-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/bottle-opener-01/"/>
        <id>https://bradeneliason.com/blog/bottle-opener-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/bottle-opener-01/">&lt;p&gt;Biomedical engineering was a natural choice for me. It combines my knack for engineering with my desire to help people. Biomedical engineers are more or less guaranteed to work on projects intended to help people. The spark of altruism at the beginning of a project can slowly fade as a project gets longer. This project came out of a desire to work on a smaller project where I could see that spark through from beginning to end. Inspiration for this project comes from two places. First, I stumbled upon &lt;a href=&quot;https:&#x2F;&#x2F;www.ncbi.nlm.nih.gov&#x2F;pmc&#x2F;articles&#x2F;PMC6371113&#x2F;&quot;&gt;an article&lt;&#x2F;a&gt; discussing the cost savings from 3D printing assistive devices. Second, I stumbled across &lt;a href=&quot;https:&#x2F;&#x2F;makersmakingchange.com&#x2F;&quot;&gt;MakersMakingChange.com&lt;&#x2F;a&gt; where such devices are posted.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;starting-point&quot;&gt;Starting Point&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;bottle-opener-01&#x2F;bottle_opener_01_fig1.jpg&quot; alt=&quot;Bottle opener opener downloaded as a starting point&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I can&#x27;t claim credit for this design. I believe I downloaded this design from &lt;a href=&quot;https:&#x2F;&#x2F;www.myminifactory.com&#x2F;object&#x2F;3d-print-assistive-bottle-opener-74399&quot;&gt;MyMiniFactory&lt;&#x2F;a&gt;, though there appears to be several versions of this opener floating around on the web. Armed with my &lt;a href=&quot;https:&#x2F;&#x2F;www.myminifactory.com&#x2F;object&#x2F;3d-print-assistive-bottle-opener-74399&quot;&gt;Prusa i3 MK3&lt;&#x2F;a&gt;, I printed this design as a starting point for my prototyping.&lt;&#x2F;p&gt;
&lt;p&gt;This version works well for bottles within a small range of cap diameters. There were many bottle caps in my kitchen with smaller caps than this opener could accommodate.&lt;&#x2F;p&gt;
&lt;p&gt;I also wanted to improve the aesthetics of this device if I could. The hard surface transitions and utilitarian design doesn&#x27;t make for an inviting object to hold. I liked the curved profile of the handles. The outside of the curve finds the center of my palm nicely making for a secure grip.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prototype-1-complete-failure&quot;&gt;Prototype 1 - Complete Failure&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;bottle-opener-01&#x2F;bottle_opener_01_fig2.jpg&quot; alt=&quot;Prototype 1 of bottle opener&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;My next thought was to add a conical taper to the cap-gripping portion. I increased the height of the cap-gripping section to have a gently tapering cone. As I was designing this, I suspected that the opener would simply slide upward as the grips were closed. This was indeed the case.&lt;&#x2F;p&gt;
&lt;p&gt;If there was enough friction and the taper angle of the cone were shallow enough, I&#x27;m pretty sure this design would work. I wanted this design to be printable in a variety of different materials.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prototype-2-progress&quot;&gt;Prototype 2 - Progress&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;bottle-opener-01&#x2F;bottle_opener_01_fig3.jpg&quot; alt=&quot;Prototype 2 of bottle opener&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With this new prototype I added three different sections of different diameters. This opened most of the caps that I tested. I added a taper between each section of different diameters. This taper helps with 3D printing. The added benefit is that if the cap is slightly larger than one of the sections, a small amount of downward pressure will expand the opener to allow the cap to fit.&lt;&#x2F;p&gt;
&lt;p&gt;There were a few issues that I wanted to improve:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The handles could be a bit longer to get the right amount of leverage and to improve the grip comfort.&lt;&#x2F;li&gt;
&lt;li&gt;The force required to close the opener is higher than I would like. This is mostly because of how thick the plastic is around the cap-gripping portion. This section acts like a large live hinge.&lt;&#x2F;li&gt;
&lt;li&gt;The gaps between the three different diameter sections were too large. More diameters would work better.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;prototype-3-ready-for-testing&quot;&gt;Prototype 3 - Ready for Testing&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;bottle-opener-01&#x2F;bottle_opener_01_fig4.jpg&quot; alt=&quot;Prototype 3 of bottle opener&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I added several more sections of different diameters to the cap-gripping section. The handles were lengthened and material was taken away from around the cap-gripper. This prototype works wonderfully. There are only a few bottles that it doesn&#x27;t work on. If a bottle has a small cap and the neck of the bottle is much wider, the bottle opener can run into the bottle&#x27;s neck before the cap gets to the right section of the cap-gripper.&lt;&#x2F;p&gt;
&lt;p&gt;Next steps are to give it to a family member that has trouble opening bottles to get their feedback. Stay tuned.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;other-ideas-jamming&quot;&gt;Other Ideas - Jamming&lt;&#x2F;h2&gt;
&lt;p&gt;I would like to try a design based on a jamming principle next. That is, when the bottle opener is torqued against the cap, the applied torque works to close the grippers further. A small amount of pretension with a spring or rubber band should be applied to the jamming mechanism. This pretension keeps the jamming mechanism in contact with the cap. This design would have a clear advantage over the designs above. The amount of torque applied to the cap is not limited by a user&#x27;s grip strength. The user just needs to apply a force to the opener&#x27;s handle and that force would be translated into a gripping action by the jamming mechanism. A key test of this device would be to open a bottle without closing my hand.&lt;&#x2F;p&gt;
&lt;p&gt;With a jamming mechanism bottle opener, the limiting factor may become the user&#x27;s grip on the bottle itself. I&#x27;d like to build a jamming mechanism for holding the bottle secure to a kitchen countertop or table. Caps have a smaller range of sizes compared with bottles. I suspect the challenge will be to make a mechanism that can hold bottles of all different sizes securely.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;additional-articles&quot;&gt;Additional Articles:&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Gallup N, Bow JK, Pearce JM. &lt;a href=&quot;https:&#x2F;&#x2F;www.ncbi.nlm.nih.gov&#x2F;pmc&#x2F;articles&#x2F;PMC6371113&#x2F;&quot;&gt;Economic Potential for Distributed Manufacturing of Adaptive Aids for Arthritis Patients in the U.S.&lt;&#x2F;a&gt; Geriatrics (Basel). 2018;3(4):89. Published 2018 Dec 6. doi:10.3390&#x2F;geriatrics3040089&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Adventures in Non-photorealistic Rendering</title>
        <published>2020-01-15T00:00:00+00:00</published>
        <updated>2020-01-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/blender-npr-01/"/>
        <id>https://bradeneliason.com/blog/blender-npr-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/blender-npr-01/">&lt;!-- ~~~ 
&lt;div class=&quot;figgrid&quot;&gt;
~~~ --&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-npr-01&#x2F;blender_npr_01_fig1.png&quot; alt=&quot;Blender NPR, anatomy textbook style&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-npr-01&#x2F;blender_npr_01_fig2.png&quot; alt=&quot;Blender NPR, black and white line art&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;!-- ~~~ 
&lt;&#x2F;div&gt;
~~~ --&gt;
&lt;p&gt;Here&#x27;s a quick experiment with a non-photorealistic rendering (NPR) of a heart using Blender. I created a custom shader to render 3D objects in the style of intaglio printing. You&#x27;ve likely seen intaglio before without knowing the name. It&#x27;s the style of printmaking most associated with the portraits on currency.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;blender-npr-01&#x2F;blender_npr_01_fig3.jpg&quot; alt=&quot;Intaglio printing&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Inspiration for this project came from the Blender NPR YouTube channel and Blender community. This is very much a work in progress. I am working on gaining greater control over the line direction.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Diagnostic Tests, Visualized</title>
        <published>2020-01-15T00:00:00+00:00</published>
        <updated>2020-01-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/diagnostic-tests-01/"/>
        <id>https://bradeneliason.com/blog/diagnostic-tests-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/diagnostic-tests-01/">&lt;!-- # Diagnostic Tests, Visualized --&gt;
&lt;p&gt;Diagnostic tests are invaluable tools in our medical arsenal, but they are not without pitfalls. These tests can be used to identify diseases in their early states or eliminate competing diagnoses to find the correct treatment course. However, no test is perfect. When a healthy person receives a positive result, this can result in anxiety, additional (often invasive) tests, and occasionally misdiagnosis&#x2F;over-treatment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;let-s-look-at-an-example&quot;&gt;Let&#x27;s look at an example:&lt;&#x2F;h2&gt;
&lt;p&gt;The numbers for this example come from an open-access article.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;50% of people have the condition  * that we&#x27;re screening for ( * &quot;Prevalence&quot; or $\Pi$)&lt;&#x2F;li&gt;
&lt;li&gt;90% of people with the disease  * will be correctly identified as  * having the condition ( * &quot;Sensitivity&quot; or $S$)&lt;&#x2F;li&gt;
&lt;li&gt;30% of people without the disease will be correctly identified as being healthy (&quot;Specificity&quot; or $Sp$)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If we give the screening test to 10,000 people, we would expect the following results:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: right&quot;&gt;&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Have Condition&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Healthy&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Row Totals&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;Positive Test&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;4500 True Positive&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;3500 False Negative&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;8000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;Negative Test&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;500  False Negative&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1500 True Negative&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;Column Totals&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;5000&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;5000&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;10000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;For a given diagnostic test, there are many related parameters that we might care about. Some of these terms include prevalence, sensitivity, false negative rate, specificity, false positive rate, accuracy, likelihood ratio, and much more. I&#x27;m a visual person and wanted a way to represent these parameters and their relationships visually.&lt;&#x2F;p&gt;
&lt;p&gt;The visualization below demonstrates the performance of diagnostic tests with relevant parameters marked along the outside of the square.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The square represents all people that receive our diagnostic test&lt;&#x2F;li&gt;
&lt;li&gt;The left column represents the people with the condition.&lt;&#x2F;li&gt;
&lt;li&gt;The right column represents healthy people.&lt;&#x2F;li&gt;
&lt;li&gt;The green regions represent &lt;em&gt;correct&lt;&#x2F;em&gt; test results.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;diagnostic-tests-01&#x2F;diagnostic_tests_01_fig1.png&quot; alt=&quot;Diagnostic test visualization&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m working on an interactive version of this chart, so stay tuned. My hope is that being able to play with each parameter will result in a deeper intuition for how these parameters relate.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;referenced-articles&quot;&gt;Referenced Articles:&lt;&#x2F;h2&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.ncbi.nlm.nih.gov&#x2F;pmc&#x2F;articles&#x2F;PMC4389712&#x2F;&quot;&gt;Maxim LD, Niebo R, Utell MJ. Screening tests: a review with examples. Inhal Toxicol. 2014;26(13):811-828. doi:10.3109&#x2F;08958378.2014.955932&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hodgkin-Huxley Model Using Julia</title>
        <published>2020-01-15T00:00:00+00:00</published>
        <updated>2020-01-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Braden Eliason
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://bradeneliason.com/blog/hodkin-huxley-01/"/>
        <id>https://bradeneliason.com/blog/hodkin-huxley-01/</id>
        
        <content type="html" xml:base="https://bradeneliason.com/blog/hodkin-huxley-01/">&lt;!-- # Hodgkin-Huxley Model Using Julia --&gt;
&lt;p&gt;For a biomedical engineer, studying the Hodgkin-Huxley model is a rite of passage—and rightly so. &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Alan_Hodgkin&quot;&gt;Alan Hodgkin&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Andrew_Huxley&quot;&gt;Andrew Huxley&lt;&#x2F;a&gt; set out to reverse engineer the spiking behavior of our nerves, and their work won them the Nobel Prize. This apex of scientific achievement was only possible through the combination of exquisite biological technique, genius mathematical insight, and no small degree of patience.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve modeled their equation in several programming languages: first in Matlab while in college, second using Python (when I couldn&#x27;t afford a personal license of Matlab), and now here using Julia. It has become my &quot;Hello World!&quot; example for scientific computing. The winner?—Julia by a landslide!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;introduction-to-the-hodgkin-huxley-model&quot;&gt;Introduction to the Hodgkin-Huxley Model&lt;&#x2F;h2&gt;
&lt;p&gt;This introduction isn&#x27;t meant to be exhaustive. If you would like to learn more about the electrical behavior of a cell membrane, I highly recommend &lt;a href=&quot;http:&#x2F;&#x2F;www.bem.fi&#x2F;book&#x2F;&quot;&gt;Bioelectromagnetism - Principles and Applications of Bioelectric and Biomagnetic Fields by Jaakko Malmivuo &amp;amp; Robert Plonsey&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The electrical behavior of a cell is not dictated by the flow of electrons. Instead, electricity flows through the body by the movement of ions. Thus the electrical behavior of a cell is dictated by ion concentrations and ion conductance. Ions can pass through the cell membrane by way of ion channels. Hodgkin and Huxley predicted that the conductance of these ion channels was dictated by activating &quot;particles.&quot; Each of the particles could be active or inactive and the proportion of particles in the active state corresponds with the fraction of ion channels that are open.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;transfer-rate-coefficients&quot;&gt;Transfer Rate Coefficients&lt;&#x2F;h2&gt;
&lt;p&gt;In their model, there were three different activating particles: n, m, and h. The particles jump from their inactive state to their active state at a rate of $\alpha$. Similarly they become inactive again at a rate of $\beta$. Hodgkin and Huxley determined how these transfer rates changed as a function of the membrane voltage (or more accurately the difference between the membrane voltage and the resting potential). The particle kinetics and governing differential equation are shown below:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kinetics&quot;&gt;Kinetics:&lt;&#x2F;h3&gt;
&lt;p&gt;$$ n \overset{\alpha_n}{\underset{\beta_n}{\leftrightharpoons}} (1-n) $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ m \overset{\alpha_m}{\underset{\beta_m}{\leftrightharpoons}} (1-m) $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ h \overset{\alpha_n}{\underset{\beta_n}{\leftrightharpoons}} (1-h) $$&lt;&#x2F;p&gt;
&lt;h3 id=&quot;differential-equations&quot;&gt;Differential Equations:&lt;&#x2F;h3&gt;
&lt;p&gt;$$ \frac{d n}{d t}=\alpha_n(1-n)-\beta_n n $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ \frac{d m}{d t}=\alpha_m(1-m)-\beta_m m $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ \frac{d h}{d t}=\alpha_h(1-h)-\beta_h h $$&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-setup&quot;&gt;The Setup&lt;&#x2F;h2&gt;
&lt;p&gt;The Julia code below imports the necessary packages and defines functions for the transfer rate coefficients for n, m, and h. We also define the steady state values for each of these activation particles to help define our initial conditions. Finally we set a few constants that will be used throughout the script.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt; Plots, DifferentialEquations, LaTeXStrings
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;## Constants
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; C_m     = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# μF&#x2F;cm², membrane capacitance
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; G_NaMax = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;120     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# mS&#x2F;cm², max conductivity of Na channel
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; G_KMax  = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;36      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# mS&#x2F;cm², max conductivity of K channel
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; G_L     = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.3     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# mS&#x2F;cm², leak conductivity
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; V_r  = -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;65        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# mV, resting potential
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; V_Na =  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;50        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# mV, Nernst voltage for Na 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; V_K  = -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;77        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# mV, Nernst voltage for K
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; V_L  = -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;54.387    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# mV, Nernst voltage for leak
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;## Function Definitions
&lt;&#x2F;span&gt;&lt;span&gt;α_n(dV) = (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1 &lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.01&lt;&#x2F;span&gt;&lt;span&gt;*dV)&#x2F;(exp(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;*dV)- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;β_n(dV) = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.125&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;(exp(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.0125&lt;&#x2F;span&gt;&lt;span&gt;*dV))
&lt;&#x2F;span&gt;&lt;span&gt;α_m(dV) = (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2.5 &lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;*dV)&#x2F;(exp(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2.5 &lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;*dV)- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;β_m(dV) = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;(exp(dV&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;18&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;α_h(dV) = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.07&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;(exp(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.05&lt;&#x2F;span&gt;&lt;span&gt;*dV))
&lt;&#x2F;span&gt;&lt;span&gt;β_h(dV) = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;(exp(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3 &lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;*dV)+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Steady states
&lt;&#x2F;span&gt;&lt;span&gt;n_∞(dV) = α_n(dV)&#x2F;(α_n(dV)+β_n(dV))
&lt;&#x2F;span&gt;&lt;span&gt;m_∞(dV) = α_m(dV)&#x2F;(α_m(dV)+β_m(dV))
&lt;&#x2F;span&gt;&lt;span&gt;h_∞(dV) = α_h(dV)&#x2F;(α_h(dV)+β_h(dV))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;differential-equations-1&quot;&gt;Differential Equations&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;hodkin-huxley-01&#x2F;hodkin_huxley_01_fig1.png&quot; alt=&quot;Circuit diagram of neuron membrane&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This diagram shows the circuit model for a small patch of cell membrane. Each ion can only pass through a designated channel. The conductance of each ion channel is determined by the parameters G which is affected by the activation particles described above. Each ion also has a voltage associated with it called the Nernst voltage. In addition to the ion channel conductances, there are also leak currents and capacitive currents. From this circuit model, the final differential equation can be derived.&lt;&#x2F;p&gt;
&lt;p&gt;Hodgkin and Huxley solved for the membrane current per unit area as function of the ion conductance and membrane capacitance. Recall that the current across a capacitor depends on the capacitance and change in voltage over time. For my simulation, instead of solving for the membrane current, I am going to solve for the change in voltage over time. The membrane current will be a stimulus which I can adjust to cause the simulated cell to fire. Essentially this is the current that I am injecting into my simulated cell to cause an action potential.&lt;&#x2F;p&gt;
&lt;p&gt;$$ I_m=C_m \frac{d V_m}{d t}+(V_m-V_{Na}) G_{Na}+(V_m-V_K) G_K+(V_m-V_L) G_L $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ \frac{d V_m}{d t} = \frac{I_{inj}(t) + (V_{Na} - V_m)G_{Na} + (V_K - V_m)G_K + (V_L - V_m) G_L}{C_m} $$&lt;&#x2F;p&gt;
&lt;p&gt;Julia is a very concise and high level language. As a result, it can often look like pseudocode. The differential equations defined above for the transfer rate coefficients and the membrane voltage can be seen directly in the HH_model function.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Injected Current Function
&lt;&#x2F;span&gt;&lt;span&gt;I_inj(t) = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10 &lt;&#x2F;span&gt;&lt;span&gt;* (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5 &lt;&#x2F;span&gt;&lt;span&gt;&amp;lt; t &amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;HH_model&lt;&#x2F;span&gt;&lt;span&gt;(u,p,t)
&lt;&#x2F;span&gt;&lt;span&gt;    n, m, h, Vm = u 
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Update transfer rate coefficients, n, m, and h
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# V_diff: membrane voltage - rest voltage
&lt;&#x2F;span&gt;&lt;span&gt;    V_diff = Vm - V_r         
&lt;&#x2F;span&gt;&lt;span&gt;    dn = α_n(V_diff)*(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;-n) - β_n(V_diff)*n
&lt;&#x2F;span&gt;&lt;span&gt;    dm = α_m(V_diff)*(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;-m) - β_m(V_diff)*m
&lt;&#x2F;span&gt;&lt;span&gt;    dh = α_h(V_diff)*(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;-h) - β_h(V_diff)*h
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Update cell membrane voltage, Vm
&lt;&#x2F;span&gt;&lt;span&gt;    G_K  = G_KMax  * n^&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Sodium conductance
&lt;&#x2F;span&gt;&lt;span&gt;    G_Na = G_NaMax * h * m^&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Potasium conductance
&lt;&#x2F;span&gt;&lt;span&gt;    dVm = ( I_inj(t) + (V_Na - Vm)*G_Na + 
&lt;&#x2F;span&gt;&lt;span&gt;          (V_K - Vm)*G_K + (V_L - Vm)*G_L ) &#x2F; C_m 
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    [dn; dm; dh; dVm]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;## Run Model:
&lt;&#x2F;span&gt;&lt;span&gt;u&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;= [n_∞(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;); m_∞(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) ; h_∞(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;); -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;65.1&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;tspan = (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;50.0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;prob = ODEProblem(HH_model, u&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, tspan)
&lt;&#x2F;span&gt;&lt;span&gt;sol = solve(prob, saveat=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.01&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;julia&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-julia &quot;&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Plotting
&lt;&#x2F;span&gt;&lt;span&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;= plot(
&lt;&#x2F;span&gt;&lt;span&gt;    sol.t, sol[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;,:], 
&lt;&#x2F;span&gt;&lt;span&gt;    legend=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;, 
&lt;&#x2F;span&gt;&lt;span&gt;    ylabel=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Voltage [mV]&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;= plot(
&lt;&#x2F;span&gt;&lt;span&gt;    sol.t, I_inj.(sol.t), 
&lt;&#x2F;span&gt;&lt;span&gt;    legend=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;, lc=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:red&lt;&#x2F;span&gt;&lt;span&gt;, 
&lt;&#x2F;span&gt;&lt;span&gt;    ylabel=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Current&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3 &lt;&#x2F;span&gt;&lt;span&gt;= plot(
&lt;&#x2F;span&gt;&lt;span&gt;    sol.t, sol[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,:]&amp;#39;, 
&lt;&#x2F;span&gt;&lt;span&gt;    label=[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;m&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;h&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;],
&lt;&#x2F;span&gt;&lt;span&gt;    color=[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:blue :orange :green&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    legend=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:topright&lt;&#x2F;span&gt;&lt;span&gt;, 
&lt;&#x2F;span&gt;&lt;span&gt;    xlabel=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Time [ms]&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, 
&lt;&#x2F;span&gt;&lt;span&gt;    ylabel=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Fraction Active&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;l = grid(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, heights=[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.2&lt;&#x2F;span&gt;&lt;span&gt; ,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.4&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;plot(
&lt;&#x2F;span&gt;&lt;span&gt;    p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, 
&lt;&#x2F;span&gt;&lt;span&gt;    layout = l, 
&lt;&#x2F;span&gt;&lt;span&gt;    size=(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;600&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;600&lt;&#x2F;span&gt;&lt;span&gt;), 
&lt;&#x2F;span&gt;&lt;span&gt;    lw=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bradeneliason.com&#x2F;blog&#x2F;hodkin-huxley-01&#x2F;hodkin_huxley_01_fig2.png&quot; alt=&quot;Simulated neuron results. The light blue curve indicates the voltage across the cell membrane. The red curve is the injected current stimulus that causes the simulated cell to depolarize. The blue, orange, and green curve show the dynamics of the activating particles.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
