<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Iterative Tangents</title>
  <link href="https://iterativetangents.com/atom.xml" rel="self"/>
  <link href="https://iterativetangents.com/"/>
  <updated>2026-04-28T20:28:47+00:00</updated>
  <id>https://iterativetangents.com/</id>
  <author>
    <name>exupero</name>
  </author>
  <entry>
    <id>https://iterativetangents.com/talk-on-software-development-as-a-career</id>
    <link href="https://iterativetangents.com/talk-on-software-development-as-a-career"/>
    <title>Talk on software development as a career</title>
    <updated>2026-04-21T08:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Recently I discussed software engineering as a career with a few high school juniors and seniors. I worked from a loose visual outline, and I drew on the whiteboard quite a bit, so here's my synthesis of the two, with a few added drawings. More details below the sketchnotes.</p><p><div class="text-center"> <img src="/images/sketchnotes-software-engineering.svg" alt="Sketchnotes on talk about software engineering career" /> </div></p><p>Originally I planned to walk through the various jobs I've held and compare them to higher and higher parts of the <a href='https://rogermartin.medium.com/heuristics-management-strategy-bdc744acdfab'>knowledge funnel</a>, moving from junior-level "code" up to principal-level "mystery", but it struck even me as dry and not very helpful for guiding career decisions, so I pivoted to talk more about turning ambiguous ideas into concrete realities. To illustrate, I started with an activity. I paired up the kids and asked teams to design an app that would help people eat healthier. Remembering my own high school class, I expected they'd need elaboration or cajoling, but I was pleasantly surprised how readily they jumped into the task without further instruction. Digital natives, I guess.</p><p>After about five minutes each team described their idea. They were all different, though they had the same underlying goal. As a software engineer, my job is often to surface those kinds of differences in how stakeholders, designers, and devs imagine a product or interface.</p><p>When I was fresh out of college, I noticed how different the working world was from school. Rather than being given closed-ended problems and having to find the right answer, I was given open-ended problems which often required asking more questions. And there were no right answers. Instead, every choice had tradeoffs. My job is frequently to identify those tradeoffs and make either a recommendation or a decision.</p><p>I'm frequently an intermediary between parties, translating concepts and language between stakeholders, other developers, the computer, and (now) AI. Contrary to stereotypes of computer programmers, that role requires a lot of communication skills.</p><p>I concluded with some of my personal experiences:</p><p><div class="text-center"> <img src="/images/sketchnotes-software-engineer.svg" alt="Sketchnotes on talk about being a software engineerer" /> </div></p><p>I work remotely, and the students were old enough to remember COVID and doing school from home, so they had context for how WFH can be lonely but also less distracting. Personally, I prefer working from home. I can live almost anywhere (thanks now to Starlink), I'm paid much more than I was when I worked for a local software agency (thanks to having more employment options), and I get to spend way more time with my wife, kids, and the house and property that are my favorite place in the world.</p><p>I also explained some of the differences between big companies and smaller startups. Small organizations provide more opportunity to make an impact on the business, but huge FAANG corporations provide more opportunity to make an impact on the world. The compensation is different too. Startups sometimes offer equity to make up for less salary, but beyond a few famous examples, not many people get rich off startup equity, though it can occasionally provide a moderate windfall.</p><p>Job-hopping is common in the software industry. People often struggle to get promotions and raises without changing jobs every couple of years, but my experience is mixed. I've had jobs where I got raises and promotions, but also jobs where I didn't. My bigger gripe is having to change jobs to continue growing professionally, regardless of promotions or pay. It's very easy to get pigeon-holed. The better I've been at a job, the harder it's been for the business to replace me in a given role. Sometimes it's easier to jump into a new role at a new job than to convince your boss to take a gamble on your capabilities.</p><p>One student asked what "bad stories" I had. Overall my career has been reasonably positive, and I don't really have horror stories. Certainly there are experiences I didn't prefer at the time, but those experiences were also stepping stones to where I am now, which is a stepping stone to wherever I'll be in the future. I've had bosses I didn't like and coworkers who annoyed me, but the incidents were all fairly forgettable, and so far I'm free of tales about dropped production databases, unsecured S3 buckets, and expectations of working a <a href='https://en.wikipedia.org/wiki/996_working_hour_system'>996 schedule</a>.</p>]]></content>
  </entry>
  <entry>
    <id>https://iterativetangents.com/learning-dvorak</id>
    <link href="https://iterativetangents.com/learning-dvorak"/>
    <title>Learning Dvorak</title>
    <updated>2026-04-13T08:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I saw a blog post today about someone who's been using Dvorak for 20 years, and a commenter asked how long it took to learn. I learned Dvorak in my first job after college, when I had a lot of downtime and wanted to give it a try. I practiced by transcribing Wikipedia articles, and I remember it as a three-month process.</p><p>In the first month, I typed very slowly and made lots of mistakes, all with the much higher cognitive burden of having to remember where a character was and which finger should press the key before I could type anything. A very frustrating month. In the second month, my fingers began to know where they needed to go, but by then I had developed the habit of thinking about each key before I hit it. Many times a finger found a character before I could remember where it was. At that point, I knew I could drop the mental load, and I spent the third month learning not to think about each key. After that, it just took some time to get back up to my old Qwerty speed.</p><p>I related that three-month learning curve to my grandfather not long afterward, and he thought it felt very similar to his experience learning Morse code in the Navy after he was drafted for World War II.</p><p>I don't know if I'm faster using Dvorak than I was with Qwerty. I don't have any data, but qualitatively I feel like I type about the same speed. What has changed is the amount of effort. At the end of a long day of typing, my hands were less tired using Dvorak than they were using Qwerty. When a coworker found out I used Dvorak, he exclaimed, "Oh, that's why your hands don't move when you type!"</p><p>Despite using Dvorak as my main keyboard for more than 15 years, I can still type Qwerty, though not as fast and I have to look at the keyboard as I type. I can't touch type Qwerty anymore. I knew a guy who used Dvorak when in Emacs and Qwerty everywhere else (or vice versa), and he had learned to switch between layouts fluently, but I never tried. The closest I've come is typing on an iPad, where I still use the default Qwerty layout. An iPad, of course, has no tactile feedback, so I have to look anyway, and I don't feel that I would be as fast looking at a Dvorak keyboard. It's as if my eyes know one layout and my fingers another.</p>]]></content>
  </entry>
  <entry>
    <id>https://iterativetangents.com/synchronized-blinking</id>
    <link href="https://iterativetangents.com/synchronized-blinking"/>
    <title>Synchronized blinking</title>
    <updated>2026-04-10T08:00:00+00:00</updated>
    <content type="html"><![CDATA[
<svg viewBox="0 0 100 20" width="100%"><rect width="100%" height="100%" fill="white"></rect><rect fill="black" height="20" width="100"></rect><g><circle cx="10.0" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="11.600000023841858" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="13.200000047683716" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="14.800000071525574" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="16.40000009536743" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="18.00000011920929" cy="2.0" fill="red" r="0.3"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="19.600000143051147" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="21.200000166893005" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="22.800000190734863" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="24.40000021457672" cy="5.5" fill="red" r="0.12499999739229678"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="26.00000023841858" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="27.600000262260437" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="29.200000286102295" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="30.800000309944153" cy="4.0" fill="red" r="0.19999999850988387"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="32.40000033378601" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="34.00000035762787" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="35.60000038146973" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="37.200000405311584" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="38.80000042915344" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="40.4000004529953" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="42.00000047683716" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="43.600000500679016" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="45.200000524520874" cy="2.0" fill="red" r="0.3"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="46.80000054836273" cy="4.0" fill="red" r="0.19999999850988387"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="48.40000057220459" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="50.00000059604645" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="51.600000619888306" cy="4.0" fill="red" r="0.19999999850988387"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="53.200000643730164" cy="4.0" fill="red" r="0.19999999850988387"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="54.80000066757202" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="56.40000069141388" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="58.00000071525574" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="59.600000739097595" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="61.20000076293945" cy="2.0" fill="red" r="0.3"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="62.80000078678131" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="64.40000081062317" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="66.00000083446503" cy="4.0" fill="red" r="0.19999999850988387"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="67.60000085830688" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="69.20000088214874" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="70.8000009059906" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="72.40000092983246" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="74.00000095367432" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="75.60000097751617" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="77.20000100135803" cy="5.5" fill="red" r="0.12499999739229678"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="78.80000102519989" cy="2.0" fill="red" r="0.3"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="80.40000104904175" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="82.0000010728836" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="83.60000109672546" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="85.20000112056732" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="86.80000114440918" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="88.40000116825104" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle></g></svg>
<p>The first time I saw this sight, I was riding across northern Indiana in the early morning hours, awoken by my wife as she drove. Neither of us knew what it was. The eeriness of it is the closest I've felt to a UFO encounter, but now, 15 years later, these sights are common. On that drive across Indiana, she woke me again near dawn to tell me what they were. Windmills.</p><p>The red lights, of course, are safety lights to warn planes, and recently while driving across Kansas in the dark and thinking how glad I was not to have this blinking array out my bedroom window, I supposed it could be important for the lights to all blink together. If each light blinked on its own schedule, it would create a continuous scintillation, which could be even more disruptive:</p>
<svg viewBox="0 0 100 20" width="100%"><rect width="100%" height="100%" fill="white"></rect><rect fill="black" height="20" width="100"></rect><g><circle cx="10.0" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" begin="1.230242434476605s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="11.600000023841858" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" begin="0.9981511678785335s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="13.200000047683716" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" begin="0.018351546797283902s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="14.800000071525574" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" begin="2.8195961663457294s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="16.40000009536743" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" begin="2.811246446687909s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="18.00000011920929" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" begin="1.0425540876093309s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="19.600000143051147" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" begin="1.5194508819787051s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="21.200000166893005" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" begin="2.311607640237533s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="22.800000190734863" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" begin="0.47024067170953876s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="24.40000021457672" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" begin="0.41928804871125347s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="26.00000023841858" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" begin="2.415683314421141s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="27.600000262260437" cy="2.0" fill="red" r="0.3"><animate attributeName="fill-opacity" begin="1.569405467364999s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="29.200000286102295" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" begin="0.4260681096477784s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="30.800000309944153" cy="4.0" fill="red" r="0.19999999850988387"><animate attributeName="fill-opacity" begin="1.6336644266810212s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="32.40000033378601" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" begin="0.6147406372756847s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="34.00000035762787" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" begin="0.5541212708338459s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="35.60000038146973" cy="2.0" fill="red" r="0.3"><animate attributeName="fill-opacity" begin="0.4831299701151067s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="37.200000405311584" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" begin="1.6211911943513893s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="38.80000042915344" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" begin="0.7362796607438868s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="40.4000004529953" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" begin="0.6528063747230545s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="42.00000047683716" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" begin="0.6994673842540002s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="43.600000500679016" cy="5.5" fill="red" r="0.12499999739229678"><animate attributeName="fill-opacity" begin="0.11498067995782624s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="45.200000524520874" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" begin="1.9655208093882492s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="46.80000054836273" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" begin="1.9574301206878595s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="48.40000057220459" cy="6.0" fill="red" r="0.09999999701976775"><animate attributeName="fill-opacity" begin="0.620212522081522s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="50.00000059604645" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" begin="1.3900493627084272s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="51.600000619888306" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" begin="1.3296373509518071s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="53.200000643730164" cy="4.0" fill="red" r="0.19999999850988387"><animate attributeName="fill-opacity" begin="2.9969424443037864s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="54.80000066757202" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" begin="2.7287521067670726s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="56.40000069141388" cy="4.0" fill="red" r="0.19999999850988387"><animate attributeName="fill-opacity" begin="1.4743536166848377s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="58.00000071525574" cy="3.5" fill="red" r="0.2249999988824129"><animate attributeName="fill-opacity" begin="0.9242787456254394s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="59.600000739097595" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" begin="2.8872437782012392s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="61.20000076293945" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" begin="0.5179464627420157s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="62.80000078678131" cy="4.0" fill="red" r="0.19999999850988387"><animate attributeName="fill-opacity" begin="1.6664051462638847s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="64.40000081062317" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" begin="2.3675719735052874s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="66.00000083446503" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" begin="0.6139066566170566s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="67.60000085830688" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" begin="2.3337490785585837s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="69.20000088214874" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" begin="2.9493095545011094s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="70.8000009059906" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" begin="2.508944843268986s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="72.40000092983246" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" begin="1.912492289578219s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="74.00000095367432" cy="2.0" fill="red" r="0.3"><animate attributeName="fill-opacity" begin="1.8935696649345748s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="75.60000097751617" cy="3.0" fill="red" r="0.24999999925494193"><animate attributeName="fill-opacity" begin="2.641071854721127s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="77.20000100135803" cy="5.0" fill="red" r="0.1499999977648258"><animate attributeName="fill-opacity" begin="2.1701149386474077s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="78.80000102519989" cy="4.0" fill="red" r="0.19999999850988387"><animate attributeName="fill-opacity" begin="2.9613064142888996s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="80.40000104904175" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" begin="2.1317243254450036s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="82.0000010728836" cy="5.5" fill="red" r="0.12499999739229678"><animate attributeName="fill-opacity" begin="0.3710136855087727s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="83.60000109672546" cy="4.5" fill="red" r="0.17499999813735484"><animate attributeName="fill-opacity" begin="1.4455870657817944s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="85.20000112056732" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" begin="1.8514376695842953s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="86.80000114440918" cy="2.0" fill="red" r="0.3"><animate attributeName="fill-opacity" begin="1.7743173178639142s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle><circle cx="88.40000116825104" cy="2.5" fill="red" r="0.27499999962747096"><animate attributeName="fill-opacity" begin="0.22214378182003724s" calcMode="discrete" dur="3s" repeatCount="indefinite" values="1;0;0;1"></animate></circle></g></svg>
<p>So I have some questions:</p><ol><li>Is it a safety consideration for the lights to blink together, or aesthetic?</li><li>Is synchronized blinking required when building a wind farm?</li><li>What's the mechanism for synchronizing the lights?</li></ol><p>If you know any of the answers, I'd be happy to <a href="mailto:eric@iterativetangents.com?subject=Synchronized blinking">hear from you</a>.</p>
]]></content>
  </entry>
  <entry>
    <id>https://iterativetangents.com/annotating-changes-in-diff-files</id>
    <link href="https://iterativetangents.com/annotating-changes-in-diff-files"/>
    <title>Annotating changes in diff files</title>
    <updated>2026-04-07T08:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Besides <a href='/diff-files-for-troubleshooting-git-branches'>troubleshooting branches</a> and <a href='/splitting-branches-using-diff-files'>splitting branches</a>, I've also used diff files to annotate changes in a branch. That's a less common analytical task, not a regular part of my workflow, but having already used diff files for other purposes, using one to summarize some repetitive changes was natural.</p><p>In this particular case, I introduced a new vertical slice of functionality, which required adding a lot of new classes. I became curious how many classes had to be added, how many methods, and how many imports, to have a better idea how much similar work might be required for future vertical slices. I tried going through the changes and counting, but the counts were difficult to track. Instead, I added comments to the diff.</p><p>The unified diff format output by Git doesn't natively support comments, but since I wasn't using the diff afterward, it didn't matter. I used <code>#</code> in the first column to denote a comment, then added comments for any changes of interest, such as <code>class added</code>, <code>method changed</code>, <code>import added</code>, <code>external interface changed</code>, <code>unit test added</code>, etc. Afterward, it was simple to tally the changes with a Bash command:</p><pre><code class="bash">cat annotated-changes.diff | grep '&#94;#' | sort | uniq -c | sort -nk1r
</code></pre><p>That finds all the comment lines, sorts them, counts unique lines, then sorts by count, with the most common changes listed first. With those results, I got a quick bird's-eye view of the vertical slice. It took some manual tagging within the diff, but I didn't want to write even a rudimentary parser for a one-off task I could do the hard way in a few minutes. And that annotated diff may be useful in the future, such as if a stakeholder asks why there's so much friction for a new feature, I can provide specifics on how a dozen new classes were needed (for transporting data between different layers) and how three times as many imports were needed to make those classes available in the expected places.</p><p>If you have other suggestions for using diff files as part of your workflow, please <a href="mailto:eric@iterativetangents.com?subject=Annotating changes in diff files">email me</a>.</p>
]]></content>
  </entry>
  <entry>
    <id>https://iterativetangents.com/splitting-branches-using-diff-files</id>
    <link href="https://iterativetangents.com/splitting-branches-using-diff-files"/>
    <title>Splitting branches using diff files</title>
    <updated>2026-04-03T08:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Besides using diff files to <a href='/diff-files-for-troubleshooting-git-branches'>troubleshoot Git branches</a>, I've also used them to split a branch into multiple branches. Often when starting some exploratory work, it's hard to know the full breadth of the changes that are needed, so foreseeing tactical cleanups and refactors can be difficult. I'll often make changes as I explore the code, whether they're relevant or not. But as a reviewer, I appreciate when a pull request is small, focused, and contains only non-functional changes <i>or</i> changes that are visible to the end user, not both, so when I've written a branch that's a big bundle of mixed concerns, I try to take the time to split it up. It can feel like starting over to break working changes into smaller sets, so here's how I use diff files to jumpstart new branches and avoid some rework.</p><p>In my oversized branch, I write the full diff to a file with something like <code>git diff main... &gt; changes.diff</code>. Then I review the changes, looking for what parts are trivial cleanup that could go in a separate branch and be merged without any of the other work I've done. I copy those files out into a new file, say, <code>cleanup.diff</code>, remove irrelevant chunks, and delete from <code>changes.diff</code> the chunks that got copied. Sometimes chunks themselves need to be edited, which can be tricky, since Git's diffs include information about what line changes start at and how many lines were changed, information that has to be updated if I add or remove lines from a chunk.</p><p>Once I've pulled out all the simple cleanup changes, I look for refactors. Those chunks go in another file, e.g. <code>refactor.diff</code>. Sometimes an oversized branch has multiple refactors, and multiple diff files are needed to untangle them into separate threads. When I'm done, the chunks remaining in <code>changes.diff</code> should reflect functional changes.</p><p>Once I have all the diff files, I figure out how to structure my pull requests. I create a new branch for the first set of changes, run <code>cat something.diff | patch -p1</code>, test, and if all is well, open a PR. After that, I create a new branch off the first branch and apply the next diff. It may or may not work. Sometimes the subsequent diff files don't align perfectly with a subset of the original changes, but even if the diff applies cleanly, the code might not function correctly, because often changes are tough to eyeball from a diff. Tweaking is frequently necessary. Once I've tested and gotten the second branch working, I continue on through any other diffs I have, branching from the appropriate base branches. Usually only two branches are necessary; in extreme cases, three. Any more than that and I tend to break out cleanups and refactors before finishing exploratory work, so that exploration is clearer and without as many distractions.</p><p>Whether I create PRs for each branch immediately depends on team culture. For some teams, I've only created branches one at a time, keeping others in reserve until the PRs they depend on get merged. That simplifies the mental landscape for reviewers, since PRs targeting other PRs can be hard to reason about, but it can also be simpler for me as the code author. When reviewers have feedback, I can make changes to the branch and rebase downstream branches without worrying that others have already pulled those branches. If I do open subsequent PRs before the first one is merged, I target the open branch and note in the PR description that it depends on another PR, then I rely on GitHub to automatically retarget a PR at the main development branch when its base branch is merged.</p>
]]></content>
  </entry>
  <entry>
    <id>https://iterativetangents.com/diff-files-for-troubleshooting-git-branches</id>
    <link href="https://iterativetangents.com/diff-files-for-troubleshooting-git-branches"/>
    <title>Diff files for troubleshooting Git branches</title>
    <updated>2026-03-10T08:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>A couple months ago I wrote about using <a href='/local-pr-review'>diff files to review code</a> in Neovim, instead of using GitHub's UI. That's a recent innovation, but I've long used diffs to troubleshoot problems in unfamiliar code, to isolate the cause of a bug or test failure when neither inspection of the code nor step-by-step debugging has uncovered it. When commits are granular and atomic, I can use <code>git bisect</code> to isolate the problem, but that's not always an option. Sometimes the branch has messy commits; maybe mine, maybe someone else's. So when bisecting doesn't work, I've applied partial reverse diff files to find what causes a problem.</p><p>For a long time I would revert the whole branch without committing, then stage reverted chunks one at a time, clear the unstaged changes, and check for the bug or test failure. That worked alright, but it could often get confusing. It can be a little tricky to think in terms of staging reversions, and if the set of changes didn't fix the problem, or introduced a new problem, such as by not undoing some related changes, then I had to reset my git index and start over. Now I stage changes via diff files, instead of using Git's staging area. Here's how.</p><p>I start on a branch off the main development branch (<code>main</code>), then clear the working directory of uncommitted changes, either by committing them, stashing them, or discarding them. Then I create a reverse diff and save it to a file with <code>git diff main... -R &gt; changes.diff</code>. That diff captures the changes that will undo the entire branch and make it identical to <code>main</code>. Since I only want to undo specific changes, I open a new file, say, <code>candidate.diff</code>, and copy sections of <code>changes.diff</code> over to it one at a time. Diff files are arranged as chunks within files, so it's easiest to copy across the full file then delete any unwanted chunks. Once I have the changes I want undone in <code>candidate.diff</code>, I apply it with <code>cat candidate.diff | patch -p1</code>, then check for the bug or test failure. If the problem isn't fixed, or if some additional reversions are needed, I try again by clearing changes with <code>git checkout -- .</code>, then I edit <code>candidate.diff</code>, run the new patch, and test again.</p><p>I've been using much smaller, more atomic commits lately, so <code>git bisect</code> works well, and it's been a while since I needed this workflow. Next time I do need it, though, I plan to write some helper scripts. For example, here's a small script to alleviate the confusion of patching reversions by first reverting the whole branch, then applying the positive diff:</p><pre><code class="bash">#!/bin/bash

patch=$1

git diff main... -R | patch -p1
cat $patch | patch -p1
</code></pre><p>If you have other tips for troubleshooting changes on a Git branch, <a href="mailto:eric@iterativetangents.com?subject=Diff files for troubleshooting Git branches">email me</a>.</p>
]]></content>
  </entry>
  <entry>
    <id>https://iterativetangents.com/local-pr-review</id>
    <link href="https://iterativetangents.com/local-pr-review"/>
    <title>Local PR review</title>
    <updated>2026-01-13T08:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>At work I've been reviewing some large pull requests, ranging from thousands of lines of new code to tens of thousands, and in the process I've discovered how limiting GitHub's web interface for code review is, both the default UI and the beta UI. To handle these reviews, I started reviewing PRs on my local machine. Here's what my workflow looks like, with lots of links to NeoVim code and small scripts for manipulating diffs.</p><p>I briefly considered building my own web UI. I <a href='https://github.com/exupero/review'>did that</a> a few years ago when working on a team that hosted their code on BitBucket after I'd gotten used to GitHub reviews and treating review comments as notes to myself, coming back to delete questions answered by later code or clarifing thoughts before publishing anything. But before diving into a frontend application, I took a lesson from <a href='/drafting-pull-requests'>drafting pull requests locally</a> and decided to see how far I could get with a diff, NeoVim, and some scripts.</p><p>A big advantage of reviewing code in a local text editor is that it can handle large diffs without requiring me to click "Load diff" on particular files or only showing one file at a time, which allows searching the whole set of changes for particular patterns, such as <code>TODO</code> comments or the catching of too broad exceptions. I can also <a href='https://github.com/exupero/scripts/blob/main/diff-filter'>exclude files in bulk</a>, like generated code, documentation, and tests, depending on what my focus is for a given review. It's also a lot easier to review changes made since the last review, just by creating a diff with <code>git diff &lt;last-reviewed-commit&gt;...HEAD</code>.</p><p>But it's not a perfect experience, and I did a lot of customization to NeoVim to make it more diff-friendly. For starters, GitHub allows collapsing files. To support collapsing both files and chunks within a file, I defined a fold method and expression:</p><pre><code class="vimscript">setlocal foldmethod=expr
setlocal foldexpr=v:lua.diff&#95;fold&#95;level&#40;&#41;
setlocal foldtext=v:lua.diff&#95;fold&#95;text&#40;&#41;
setlocal foldlevel=99
</code></pre><p>The Lua functions are implemented in <a href='https://fennel-lang.org/'>Fennel</a>:</p><pre><code class="fennel">&#40;fn file-start? &#91;line&#93;
 &#40;string.match line &quot;&#94;diff&quot;&#41;

&#40;fn chunk-start? &#91;line&#93;
  &#40;string.match line &quot;&#94;@@&quot;&#41;&#41;

&#40;fn &#95;G.diff&#95;fold&#95;level &#91;line&#93;
  &#40;let &#91;line &#40;vim.fn.getline &#40;or line vim.v.lnum&#41;&#41;&#93;
    &#40;if
      &#40;file-start? line&#41; :&gt;1
      &#40;chunk-start? line&#41; :&gt;2
      :=&#41;&#41;&#41;

&#40;fn &#95;G.diff&#95;fold&#95;text &#91;&#93;
  &#40;let &#91;line &#40;vim.fn.getline vim.v.foldstart&#41;
        count &#40;+ 1 &#40;- vim.v.foldend vim.v.foldstart&#41;&#41;
        level &#40;&#95;G.diff&#95;fold&#95;level vim.v.foldstart&#41;
        prefix &#40;case level
                 &#40;where :&gt;1&#41; &quot;&quot;
                 &#40;where :&gt;2&#41; &quot;▶ &quot;
                 &#95;  &quot;&quot;&#41;&#93;
    &#40;.. prefix line &quot; &#40;&quot; count &quot; lines&#41;&quot;&#41;&#41;&#41;
</code></pre><p>It's often helpful to jump from the diff to a particular file, to see the full context of the current code, so <a href='https://github.com/exupero/vim/blob/main/fnl/diff.fnl#L41-L51'>here</a>'s a command that uses the cursor's current position to find the right file name and chunk header, then calculate where to go and open it in a new tab.</p><p>Some chunks are not interesting and can be <a href='https://github.com/exupero/vim/blob/main/fnl/diff.fnl#L86-L98'>deleted</a> from the diff, or <a href='https://github.com/exupero/vim/blob/main/fnl/diff.fnl#L126-L133'>whole files</a>. Sometimes part of a chunk can be deleted but not others, so I also have a <a href='https://github.com/exupero/vim/blob/main/fnl/diff.fnl#L86-L98'>command</a> to delete from the top of the chunk down to the cursor, which also updates the chunk header so jumping to the chunk's position in the file still works.</p><p>Often I start a review with the smallest changes, so <a href='https://github.com/exupero/scripts/blob/main/diff-sort'>here</a>'s a <a href='https://babashka.org/'>Babashka</a> script that sorts the files in a diff by how many lines each file has. Using it for a while, I've added the ability to sort by how many additions each file has, or deletions, or how close the ratio of additions to deletions is, or whether the file name contains the word "test". And <a href='https://github.com/exupero/scripts/blob/main/diff-rotate'>here</a>'s a script that moves the first file to the end of the diff, for cases where I don't want to delete the file but also don't want to review it yet.</p><p>Beyond shuffling code, I also needed to add comments. I opted to use markers in the first column of the dif, adding <code>v</code> and <code>&#94;</code> to indicate what range of lines I'm commenting on, and <code>x</code> to indicate the end of a comment, like this:</p><pre><code class="diff">diff --git a/old&#95;file.txt b/new&#95;file.txt
index 2a2e9f1..a48d7f5 100644
--- a/old&#95;file.txt
+++ b/new&#95;file.txt
@@ -1,1 +1,2 @@
-Notes on changes
+An ode
v
+To my code
&#94;
👏
x
</code></pre><p>I add those markers with a couple of <a href='https://github.com/exupero/vim/blob/6d95f503f12889e1358107bdbd34039160e287a6/fnl/diff.fnl#L152-L172'>NeoVim commands</a> that also put the highlighted lines into the copy register, which allows using them in an UltiSnips expansion, such as for <a href='https://github.com/exupero/snippets/blob/main/diff/markdown.snippets'>making a suggestion</a>. It's also trivial to adjust the range of lines being commented on by moving the markers, while to adjust the range of a comment on GitHub I have to copy a comment's content, delete the comment, select a new range, create a new comment, and paste the original comment.</p><p>I have a <a href='https://github.com/exupero/scripts/blob/main/diff-comments'>script</a> to collect those comments as Markdown, with file and line metadata in the header of the fenced code block:</p><pre><code>```diff new&#95;file.txt -2 +2
+To my code
```

👏</code></pre><p>Comments are separated by <code>---</code>.</p><p>I write those comments to a file, tweak them if necessary, and create a pending GitHub review with <a href='https://github.com/exupero/scripts/blob/main/lib/markdown/review.clj'>this code</a>. I keep that file of comments as a place to track what comments have been addressed and how, whether a requested change was made (and in what commit) or an answer was given for a question.</p><p>One final advantage of using NeoVim is how easy it is to define key bindings for all these actions, especially repeated actions using <a href='https://github.com/tpope/vim-repeat'>vim-repeat</a>, making code review very fluid.</p><p>There are, however, some pitfalls. One is that it's easy to delete chunks that have comments. To avoid losing work, I added NeoVim functions that write any deleted or truncated code that have comments into a separate file, which I later use to create the Markdown comments. Another problem is that I don't see anyone else's comments while reviewing, so I occasionally make duplicate comments or miss explanations that aren't in the code. The most annoying pitfall, though, is not having the latest revision locally, causingy my comments to end up on the wrong lines of the current revision. That's part of the reason the script which creates the review only creates a pending review, so I can check it before submitting (though finding pending comments in GitHub's review UI is still a challenge).</p><p>I volunteered to review these large PRs because I'm always curious where my personal workflow hits a breaking point, and finding the friction gives me an opportunity to improve my tools in ways that benefit small tasks too. If you have further suggestions, or ideas on how to mitigate the pitfalls, <a href="mailto:eric@iterativetangents.com?subject=Local PR review">email me</a>!</p>
]]></content>
  </entry>
  <entry>
    <id>https://iterativetangents.com/distance-shapes-playground</id>
    <link href="https://iterativetangents.com/distance-shapes-playground"/>
    <title>Distance shapes playground</title>
    <updated>2025-09-22T08:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>The <a href='/circles-of-apollonius'>previous</a> <a href='/point-line-sum-shapes'>four</a> <a href='/point-line-difference-shapes'>posts</a> <a href='/point-line-product-shapes'>about</a> distance shapes in Euclidean and taxicab geometries included illustrations that differed from the ones in my earlier posts about taxicab geometry. In those posts, I hard-coded points and lines in SVGs. Basic shapes like circles, ellipses, and parabolas are fairly easy to work out by hand. To draw <a href='/ovals-of-cassini-in-taxicab-geometry/'>ovals of Cassini</a> I used an algebraic approach, but it was limited to cases where the foci were aligned vertically with each other. For more complex shapes, and cover all possible variations of those shapes, I made a distance shape playground. You can find the latest version of it <a href='https://distance-shapes.exupero.org'>here</a>.</p><p>The buttons on the right add shapes to the both grids, the upper grid showing the shape in Euclidean geometry, while the lower uses the Manhattan distance to draw the shape in taxicab geometry. The same control points are used for both plots. Drag points to change the shapes. To reset the plots, click the "Clear" button. To export a plot as an SVG, click "Export SVG" on the upper right of each plot.</p><p>The current implementation defines each shape as a two-dimensional signed distance function, where negative values denote the inside of a shape, positive values the outside, and zero values the boundary. To draw the boundary, it uses a simple <a href='https://en.wikipedia.org/wiki/Marching_squares'>marching squares</a> algorithm. It's not particularly fast, especially not at the level of recursion needed for a smooth curve. Thus, when dragging points, you'll see the recursion level drop and shapes become less precise, which helps keep the visual responsive, though it's by no means as fast as one would hope.</p><p>Nor is the algorithm flawless. The boundary will occasionally spill into a square whose corners are all outside the shape, so the boundary inside that square is missing. It also misses cases where the boundary itself becomes two-dimensional, as it can for midsets and difference-based hyperbolas. But speed wasn't necessary for the few static illustrations I needed, and unhandled edge cases were easy to avoid.</p><p>I played with partial support for additional <a href='https://en.wikipedia.org/wiki/Minkowski_distance'>Minkowski distances</a>, but while the shapes were interesting, their meaning was much harder to intuit, and broken edge cases were more common.</p><p>The original prototype, with all the same functionality, was about 500 lines of ClojureScript code. The public playground is slightly shorter, thanks to offloading some functions I commonly use to a <a href='https://github.com/exupero/polymath'>library</a>.</p><p>If you have suggestions for improvements, <a href="mailto:eric@iterativetangents.com?subject=Distance shapes playground">I'm happy to hear them</a>.</p>
]]></content>
  </entry>
  <entry>
    <id>https://iterativetangents.com/point-line-product-shapes</id>
    <link href="https://iterativetangents.com/point-line-product-shapes"/>
    <title>Point-line product shapes</title>
    <updated>2025-09-18T08:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>The last of these unnamed shape definitions is the one that's a constant product of distances to a focus and a directrix:</p>
<div class="formula"><svg xmlns:xlink="http://www.w3.org/1999/xlink" width="24.453ex" height="2.843ex" style="vertical-align: -0.838ex;" viewBox="0 -863.1 10528.2 1223.9" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" aria-labelledby="MathJax-SVG-1-Title">
<title id="MathJax-SVG-1-Title">d i s t left-parenthesis p comma f right-parenthesis dot d i s t left-parenthesis p comma l right-parenthesis equals c</title>
<defs aria-hidden="true">
<path stroke-width="1" id="E1-MJMATHI-64" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path>
<path stroke-width="1" id="E1-MJMATHI-69" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path>
<path stroke-width="1" id="E1-MJMATHI-73" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"></path>
<path stroke-width="1" id="E1-MJMATHI-74" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path>
<path stroke-width="1" id="E1-MJMAIN-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path>
<path stroke-width="1" id="E1-MJMATHI-70" d="M23 287Q24 290 25 295T30 317T40 348T55 381T75 411T101 433T134 442Q209 442 230 378L240 387Q302 442 358 442Q423 442 460 395T497 281Q497 173 421 82T249 -10Q227 -10 210 -4Q199 1 187 11T168 28L161 36Q160 35 139 -51T118 -138Q118 -144 126 -145T163 -148H188Q194 -155 194 -157T191 -175Q188 -187 185 -190T172 -194Q170 -194 161 -194T127 -193T65 -192Q-5 -192 -24 -194H-32Q-39 -187 -39 -183Q-37 -156 -26 -148H-6Q28 -147 33 -136Q36 -130 94 103T155 350Q156 355 156 364Q156 405 131 405Q109 405 94 377T71 316T59 280Q57 278 43 278H29Q23 284 23 287ZM178 102Q200 26 252 26Q282 26 310 49T356 107Q374 141 392 215T411 325V331Q411 405 350 405Q339 405 328 402T306 393T286 380T269 365T254 350T243 336T235 326L232 322Q232 321 229 308T218 264T204 212Q178 106 178 102Z"></path>
<path stroke-width="1" id="E1-MJMAIN-2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"></path>
<path stroke-width="1" id="E1-MJMATHI-66" d="M118 -162Q120 -162 124 -164T135 -167T147 -168Q160 -168 171 -155T187 -126Q197 -99 221 27T267 267T289 382V385H242Q195 385 192 387Q188 390 188 397L195 425Q197 430 203 430T250 431Q298 431 298 432Q298 434 307 482T319 540Q356 705 465 705Q502 703 526 683T550 630Q550 594 529 578T487 561Q443 561 443 603Q443 622 454 636T478 657L487 662Q471 668 457 668Q445 668 434 658T419 630Q412 601 403 552T387 469T380 433Q380 431 435 431Q480 431 487 430T498 424Q499 420 496 407T491 391Q489 386 482 386T428 385H372L349 263Q301 15 282 -47Q255 -132 212 -173Q175 -205 139 -205Q107 -205 81 -186T55 -132Q55 -95 76 -78T118 -61Q162 -61 162 -103Q162 -122 151 -136T127 -157L118 -162Z"></path>
<path stroke-width="1" id="E1-MJMAIN-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path>
<path stroke-width="1" id="E1-MJMAIN-22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path>
<path stroke-width="1" id="E1-MJMATHI-6C" d="M117 59Q117 26 142 26Q179 26 205 131Q211 151 215 152Q217 153 225 153H229Q238 153 241 153T246 151T248 144Q247 138 245 128T234 90T214 43T183 6T137 -11Q101 -11 70 11T38 85Q38 97 39 102L104 360Q167 615 167 623Q167 626 166 628T162 632T157 634T149 635T141 636T132 637T122 637Q112 637 109 637T101 638T95 641T94 647Q94 649 96 661Q101 680 107 682T179 688Q194 689 213 690T243 693T254 694Q266 694 266 686Q266 675 193 386T118 83Q118 81 118 75T117 65V59Z"></path>
<path stroke-width="1" id="E1-MJMAIN-3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path>
<path stroke-width="1" id="E1-MJMATHI-63" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path>
</defs>
<g stroke="currentColor" fill="currentColor" stroke-width="0" transform="matrix(1 0 0 -1 0 0)" aria-hidden="true">
 <use xlink:href="#E1-MJMATHI-64" x="0" y="0"></use>
 <use xlink:href="#E1-MJMATHI-69" x="523" y="0"></use>
 <use xlink:href="#E1-MJMATHI-73" x="869" y="0"></use>
 <use xlink:href="#E1-MJMATHI-74" x="1338" y="0"></use>
<g transform="translate(1866,0)">
 <use xlink:href="#E1-MJMAIN-28" x="0" y="0"></use>
 <use xlink:href="#E1-MJMATHI-70" x="389" y="0"></use>
 <use xlink:href="#E1-MJMAIN-2C" x="893" y="0"></use>
 <use xlink:href="#E1-MJMATHI-66" x="1338" y="0"></use>
 <use xlink:href="#E1-MJMAIN-29" x="1888" y="0"></use>
</g>
 <use xlink:href="#E1-MJMAIN-22C5" x="4367" y="0"></use>
 <use xlink:href="#E1-MJMATHI-64" x="4867" y="0"></use>
 <use xlink:href="#E1-MJMATHI-69" x="5391" y="0"></use>
 <use xlink:href="#E1-MJMATHI-73" x="5736" y="0"></use>
 <use xlink:href="#E1-MJMATHI-74" x="6206" y="0"></use>
<g transform="translate(6734,0)">
 <use xlink:href="#E1-MJMAIN-28" x="0" y="0"></use>
 <use xlink:href="#E1-MJMATHI-70" x="389" y="0"></use>
 <use xlink:href="#E1-MJMAIN-2C" x="893" y="0"></use>
 <use xlink:href="#E1-MJMATHI-6C" x="1338" y="0"></use>
 <use xlink:href="#E1-MJMAIN-29" x="1636" y="0"></use>
</g>
 <use xlink:href="#E1-MJMAIN-3D" x="9038" y="0"></use>
 <use xlink:href="#E1-MJMATHI-63" x="10094" y="0"></use>
</g>
</svg>
</div>
<p>These shapes are cousins of the <a href='/ovals-of-cassini-in-taxicab-geometry/'>ovals of Cassini</a>, which are a constant product of distances to two foci. Here, though, one of the foci is a line, so half the shape wraps a focus that extends to infinity:</p>
<figure class="figure-small"><img alt="A constant product of distances to a point and a line, in Euclidean geometry." src="/images/point-line-product-euclidean.svg" /><figcaption>A constant product of distances to a point and a line, in Euclidean geometry.</figcaption></figure>
<p>Similarly for taxicab geometry:</p>
<figure class="figure-small"><img alt="A constant product of distances to a point and a line, in taxicab geometry." src="/images/point-line-product-taxicab.svg" /><figcaption>A constant product of distances to a point and a line, in taxicab geometry.</figcaption></figure>
<p>Both can be fully disconnected:</p>
<figure class="figure-small"><img alt="Two disconnected components, in Euclidean geometry." src="/images/point-line-product-euclidean-2.svg" /><figcaption>Two disconnected components, in Euclidean geometry.</figcaption></figure>
<figure class="figure-small"><img alt="Two disconnected components, in taxicab geometry." src="/images/point-line-product-taxicab-2.svg" /><figcaption>Two disconnected components, in taxicab geometry.</figcaption></figure>
<p>or fully joined:</p>
<figure class="figure-small"><img alt="Two connected components, in Euclidean geometry." src="/images/point-line-product-euclidean-3.svg" /><figcaption>Two connected components, in Euclidean geometry.</figcaption></figure>
<figure class="figure-small"><img alt="Two connected components, in taxicab geometry." src="/images/point-line-product-taxicab-3.svg" /><figcaption>Two connected components, in taxicab geometry.</figcaption></figure>
<p>Interesting things happen to the taxicab shape when the directrix is neither horizontal or vertical. Here it is at 45° to the axes:</p>
<figure class="figure-small"><img alt="With the directrix at 45° to the axes, components just touching." src="/images/point-line-product-taxicab-4.svg" /><figcaption>With the directrix at 45° to the axes, components just touching.</figcaption></figure>
<figure class="figure-small"><img alt="Components disconnected." src="/images/point-line-product-taxicab-5.svg" /><figcaption>Components disconnected.</figcaption></figure>
<figure class="figure-small"><img alt="Components connected." src="/images/point-line-product-taxicab-6.svg" /><figcaption>Components connected.</figcaption></figure>
<p>With a sloped directrix, the shape becomes asymmetric (and, to my imagination, birdlike):</p>
<figure class="figure-small"><img alt="Asymmetric shape." src="/images/point-line-product-taxicab-7.svg" /><figcaption>Asymmetric shape.</figcaption></figure>
]]></content>
  </entry>
  <entry>
    <id>https://iterativetangents.com/point-line-difference-shapes</id>
    <link href="https://iterativetangents.com/point-line-difference-shapes"/>
    <title>Point-line difference shapes</title>
    <updated>2025-09-15T08:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Another shape without a common name is the one defined as a constant difference of distances to a focus and a directrix:</p>
<div class="formula"><svg xmlns:xlink="http://www.w3.org/1999/xlink" width="25.614ex" height="2.843ex" style="vertical-align: -0.838ex;" viewBox="0 -863.1 11028.2 1223.9" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" aria-labelledby="MathJax-SVG-1-Title">
<title id="MathJax-SVG-1-Title">d i s t left-parenthesis p comma f right-parenthesis minus d i s t left-parenthesis p comma l right-parenthesis equals c</title>
<defs aria-hidden="true">
<path stroke-width="1" id="E1-MJMATHI-64" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path>
<path stroke-width="1" id="E1-MJMATHI-69" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path>
<path stroke-width="1" id="E1-MJMATHI-73" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"></path>
<path stroke-width="1" id="E1-MJMATHI-74" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path>
<path stroke-width="1" id="E1-MJMAIN-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path>
<path stroke-width="1" id="E1-MJMATHI-70" d="M23 287Q24 290 25 295T30 317T40 348T55 381T75 411T101 433T134 442Q209 442 230 378L240 387Q302 442 358 442Q423 442 460 395T497 281Q497 173 421 82T249 -10Q227 -10 210 -4Q199 1 187 11T168 28L161 36Q160 35 139 -51T118 -138Q118 -144 126 -145T163 -148H188Q194 -155 194 -157T191 -175Q188 -187 185 -190T172 -194Q170 -194 161 -194T127 -193T65 -192Q-5 -192 -24 -194H-32Q-39 -187 -39 -183Q-37 -156 -26 -148H-6Q28 -147 33 -136Q36 -130 94 103T155 350Q156 355 156 364Q156 405 131 405Q109 405 94 377T71 316T59 280Q57 278 43 278H29Q23 284 23 287ZM178 102Q200 26 252 26Q282 26 310 49T356 107Q374 141 392 215T411 325V331Q411 405 350 405Q339 405 328 402T306 393T286 380T269 365T254 350T243 336T235 326L232 322Q232 321 229 308T218 264T204 212Q178 106 178 102Z"></path>
<path stroke-width="1" id="E1-MJMAIN-2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"></path>
<path stroke-width="1" id="E1-MJMATHI-66" d="M118 -162Q120 -162 124 -164T135 -167T147 -168Q160 -168 171 -155T187 -126Q197 -99 221 27T267 267T289 382V385H242Q195 385 192 387Q188 390 188 397L195 425Q197 430 203 430T250 431Q298 431 298 432Q298 434 307 482T319 540Q356 705 465 705Q502 703 526 683T550 630Q550 594 529 578T487 561Q443 561 443 603Q443 622 454 636T478 657L487 662Q471 668 457 668Q445 668 434 658T419 630Q412 601 403 552T387 469T380 433Q380 431 435 431Q480 431 487 430T498 424Q499 420 496 407T491 391Q489 386 482 386T428 385H372L349 263Q301 15 282 -47Q255 -132 212 -173Q175 -205 139 -205Q107 -205 81 -186T55 -132Q55 -95 76 -78T118 -61Q162 -61 162 -103Q162 -122 151 -136T127 -157L118 -162Z"></path>
<path stroke-width="1" id="E1-MJMAIN-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path>
<path stroke-width="1" id="E1-MJMAIN-2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path>
<path stroke-width="1" id="E1-MJMATHI-6C" d="M117 59Q117 26 142 26Q179 26 205 131Q211 151 215 152Q217 153 225 153H229Q238 153 241 153T246 151T248 144Q247 138 245 128T234 90T214 43T183 6T137 -11Q101 -11 70 11T38 85Q38 97 39 102L104 360Q167 615 167 623Q167 626 166 628T162 632T157 634T149 635T141 636T132 637T122 637Q112 637 109 637T101 638T95 641T94 647Q94 649 96 661Q101 680 107 682T179 688Q194 689 213 690T243 693T254 694Q266 694 266 686Q266 675 193 386T118 83Q118 81 118 75T117 65V59Z"></path>
<path stroke-width="1" id="E1-MJMAIN-3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path>
<path stroke-width="1" id="E1-MJMATHI-63" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path>
</defs>
<g stroke="currentColor" fill="currentColor" stroke-width="0" transform="matrix(1 0 0 -1 0 0)" aria-hidden="true">
 <use xlink:href="#E1-MJMATHI-64" x="0" y="0"></use>
 <use xlink:href="#E1-MJMATHI-69" x="523" y="0"></use>
 <use xlink:href="#E1-MJMATHI-73" x="869" y="0"></use>
 <use xlink:href="#E1-MJMATHI-74" x="1338" y="0"></use>
<g transform="translate(1866,0)">
 <use xlink:href="#E1-MJMAIN-28" x="0" y="0"></use>
 <use xlink:href="#E1-MJMATHI-70" x="389" y="0"></use>
 <use xlink:href="#E1-MJMAIN-2C" x="893" y="0"></use>
 <use xlink:href="#E1-MJMATHI-66" x="1338" y="0"></use>
 <use xlink:href="#E1-MJMAIN-29" x="1888" y="0"></use>
</g>
 <use xlink:href="#E1-MJMAIN-2212" x="4367" y="0"></use>
 <use xlink:href="#E1-MJMATHI-64" x="5367" y="0"></use>
 <use xlink:href="#E1-MJMATHI-69" x="5891" y="0"></use>
 <use xlink:href="#E1-MJMATHI-73" x="6236" y="0"></use>
 <use xlink:href="#E1-MJMATHI-74" x="6706" y="0"></use>
<g transform="translate(7234,0)">
 <use xlink:href="#E1-MJMAIN-28" x="0" y="0"></use>
 <use xlink:href="#E1-MJMATHI-70" x="389" y="0"></use>
 <use xlink:href="#E1-MJMAIN-2C" x="893" y="0"></use>
 <use xlink:href="#E1-MJMATHI-6C" x="1338" y="0"></use>
 <use xlink:href="#E1-MJMAIN-29" x="1636" y="0"></use>
</g>
 <use xlink:href="#E1-MJMAIN-3D" x="9538" y="0"></use>
 <use xlink:href="#E1-MJMATHI-63" x="10594" y="0"></use>
</g>
</svg>
</div>
<p>In Euclidean geometry, these appear to be a parabola:</p>
<figure class="figure-small"><img alt="A parabolic shape defined by a constant difference of distances to a point and a line, in Euclidean geometry." src="/images/point-line-difference-euclidean.svg" /><figcaption>A parabolic shape defined by a constant difference of distances to a point and a line, in Euclidean geometry.</figcaption></figure>
<p>That makes sense, since a parabola is the set of points equidistant from a focus and a directrix, or having zero difference in distances.</p><p>Interestingly, though, this definition can also produce a kind of double parabola:</p>
<figure class="figure-small"><img alt="A double parabolic shape, in Euclidean geometry." src="/images/point-line-difference-euclidean-2.svg" /><figcaption>A double parabolic shape, in Euclidean geometry.</figcaption></figure>
<p>Taxicab geometry has analogs for both:</p>
<figure class="figure-small"><img alt="A parabolic shape, in taxicab geometry." src="/images/point-line-difference-taxicab.svg" /><figcaption>A parabolic shape, in taxicab geometry.</figcaption></figure>
<figure class="figure-small"><img alt="A double parabolic shape." src="/images/point-line-difference-taxicab-2.svg" /><figcaption>A double parabolic shape.</figcaption></figure>
<p>The double parabola above just looks like two angled lines, and to see them as overlapping parabloas requires some foreknowledge of what you're looking for.</p><p>With the directrix at 45° to the axes, the shape still looks like a taxicab parabola with a similarly sloped directrix:</p>
<figure class="figure-small"><img alt="A parabolic shape with the directrix at 45°." src="/images/point-line-difference-taxicab-3.svg" /><figcaption>A parabolic shape with the directrix at 45°.</figcaption></figure>
<p>When forming a double parabola against a directrix at 45°, the shape becomes two right angles:</p>
<figure class="figure-small"><img alt="A double parabolic shape with the directrix at 45°." src="/images/point-line-difference-taxicab-4.svg" /><figcaption>A double parabolic shape with the directrix at 45°.</figcaption></figure>
<p>With a sloped line, some foreknowledge again helps to see the underlying pair of overlapping parabolas:</p>
<figure class="figure-small"><img alt="A double parabolic shape, offset with one another." src="/images/point-line-difference-taxicab-5.svg" /><figcaption>A double parabolic shape, offset with one another.</figcaption></figure>
]]></content>
  </entry>
</feed>
