<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jeff Lowery</title>
    <description>The latest articles on DEV Community by Jeff Lowery (@jeffml).</description>
    <link>https://dev.to/jeffml</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F58098%2F418b8827-facc-49cd-8561-e9d39d544001.jpeg</url>
      <title>DEV Community: Jeff Lowery</title>
      <link>https://dev.to/jeffml</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeffml"/>
    <language>en</language>
    <item>
      <title>The Transposition Trap and 3,600 "Fake" Chess Positions</title>
      <dc:creator>Jeff Lowery</dc:creator>
      <pubDate>Tue, 14 Jul 2026 22:08:18 +0000</pubDate>
      <link>https://dev.to/jeffml/the-transposition-trap-and-3600-fake-chess-positions-1igd</link>
      <guid>https://dev.to/jeffml/the-transposition-trap-and-3600-fake-chess-positions-1igd</guid>
      <description>&lt;h2&gt;
  
  
  &lt;em&gt;Part 1: The Graph, The Orphans, and the Synthetic Bridge&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;If you ask a casual chess player what an opening is, they’ll give you a simple answer: it’s a name, an ECO (Encyclopedia of Chess Openings) code, and a sequence of moves. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;1. e4 e5 2. Nf3 Nc6 3. Bb5&lt;/code&gt; is the Ruy Lopez. Clean. Elegant. Linear.&lt;/p&gt;

&lt;p&gt;If you ask a software engineer who maintains a chess opening database how hard it could be to map these openings, they’ll likely utter the famous last words of every data engineer: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It’s just a flat list of moves. How hard could it be?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I know this because I am that engineer. I maintain &lt;code&gt;@chess-openings/eco.json&lt;/code&gt;, an open-source data package on npm. And during a recent extensive rewrite of my data pipeline, I was violently reminded that in the world of chess data, linearity is an illusion, FEN strings are slippery shapes, and the data structure isn’t a list at all.&lt;/p&gt;

&lt;p&gt;It’s a directed nightmare of a graph.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Transposition Trap
&lt;/h3&gt;

&lt;p&gt;Let’s start with the fundamental lie of chess data: that a position’s identity is tied to how you got there.&lt;/p&gt;

&lt;p&gt;In chess, different move orders can lead to the exact same layout of pieces on the board. This is known as a &lt;strong&gt;transposition&lt;/strong&gt;. Consider the Benoni Defense. You can reach a classic Benoni position via the canonical move order:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;1. d4 Nf6 2. c4 g6 3. Nc3 Bg7 4. e4 d6&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0sszhtweqq58bjg8hgc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0sszhtweqq58bjg8hgc0.png" alt="The Benoni Opening via move order 1" width="473" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what happens if Black plays a slightly different order to achieve the exact same strategic setup?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;1. d4 Nf6 2. c4 c5 3. d5 d6 4. Nc3 g6&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0sszhtweqq58bjg8hgc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0sszhtweqq58bjg8hgc0.png" alt="The same opening via a different move order" width="473" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To the human eye, the board is identical. To a database relying strictly on Forsythe-Edwards Notation (FEN) as a strict unique identifier, they can look like entirely different entities. A standard FEN string doesn't just track where the pieces are; it tracks whose turn it is, castling rights, and potential &lt;em&gt;en passant&lt;/em&gt; squares.&lt;/p&gt;

&lt;p&gt;If White changes the order of their pawn pushes, castling rights or &lt;em&gt;en passant&lt;/em&gt; availability might technically differ for a fleeting move, even if the piece layout converges. &lt;em&gt;(Yes, I hear you, chess purists: technically, an active en passant option makes identical piece layouts mathematically distinct game states. But for the sake of navigation mapping, we must occasionally fly close to the sun).&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;If your data pipeline treats a full, standard FEN string as an absolute, immutable identity, you will instantly split your data universe in two.&lt;/p&gt;

&lt;p&gt;The fix? I had to implement a strict position-only FEN matching fallback across the entire ingestion engine. In chess data engineering, you quickly learn that a full FEN is merely a suggestion; the raw piece placement layout is the true identity.&lt;/p&gt;




&lt;h3&gt;
  
  
  A Directed Nightmare: The &lt;code&gt;fromTo&lt;/code&gt; Graph
&lt;/h3&gt;

&lt;p&gt;Once you accept that positions transpose, your flat file of chess openings explodes into a massive directed graph. In my latest build of the pipeline, this graph ballooned to 17,500 edges connecting over 16,000 unique nodes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Position A] --(Move: c5)--&amp;gt; [Position B] --(Move: d5)--&amp;gt; [Position C]
    \                                                         /
     \-----------------(Alternative Path)--------------------/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every single opening and variation in the database must know where it came from (&lt;code&gt;from&lt;/code&gt;) and where it can go (&lt;code&gt;to&lt;/code&gt;). If a single link breaks, that entire branch of chess history becomes unreachable noise in the application layer.&lt;/p&gt;

&lt;p&gt;When I ran my newly rewritten pipeline for the very first time, it didn't gracefully compile. It screamed. The integrity report spat out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;45 disconnected interpolated openings&lt;/strong&gt; (floating in the void)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;87 duplicate edges&lt;/strong&gt; (circular logic loops)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;16 FEN collisions&lt;/strong&gt; (two different opening names claiming the exact same board state)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fixing this wasn’t a matter of tweaking a JSON file. It required rewriting four fundamental pipeline changes across three separate architecture files to enforce strict deduplication at the moment of generation, rather than trying to retrofit it after the fact.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Interpolation Rabbit Hole
&lt;/h3&gt;

&lt;p&gt;But the deepest psychological damage came from what I call "Orphan Openings."&lt;/p&gt;

&lt;p&gt;When you scrape, pull, and normalize chess data from ten different online sources, you will inevitably find records for deeply specific sub-variations. For instance, a source might list a highly complex line twenty moves deep into the King's Indian Defense.&lt;/p&gt;

&lt;p&gt;The problem? The source database gives you the start position and the end position, but it doesn't explicitly define the seventeen moves in between as distinct database entries. They are missing from the master list. In graph theory, this leaves your deep variation as an orphan—floating in space with no parent node to connect it back to the game's starting array.&lt;/p&gt;

&lt;p&gt;To make the graph navigable, I had to build an interpolation engine. When the pipeline encounters an orphan, it is forced to go down a recursive rabbit hole. It triggers a function called &lt;code&gt;lineOfDescent&lt;/code&gt;, which programmatically walks backward through the move sequence, move by move, generating synthetic bridge nodes until it finally collides with a known, recognized opening.&lt;/p&gt;

&lt;p&gt;Think about the absurdity of this: &lt;strong&gt;I had to programmatically generate 3,625 synthetic chess positions solely so that real chess openings could find each other.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These interpolated entries don't have historical flavor text or specific human names; they exist purely as the digital mortar holding the bricks of the graph together. If the recursion logic is off by even a single character, it triggers a recursion explosion, spinning out infinitely as it tries to resolve transpositions that loop back on themselves.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Joy of the Green Check
&lt;/h3&gt;

&lt;p&gt;After days of wrestling with the graph topology, adjusting fallback FEN algorithms, and ensuring the &lt;code&gt;lineOfDescent&lt;/code&gt; didn't eat my CPU, I finally ran the verification suite.&lt;/p&gt;

&lt;p&gt;There is a unique, quiet satisfaction that only a developer knows when a wall of red terminal text finally turns into a clean, green monolithic block of passing checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ fen-unique: 12509 unique in ecoA-E, 3625 in interpolated, 0 overlaps
✓ eco-prefix: all entries match their category file
✓ interp-isolation: interpolated entries correctly separated
✓ fromto-ref: all 17565 transitions reference existing FENs
✓ fromto-dup: no duplicate transitions
✓ valid-src: all source identifiers are valid
✓ rootsrc: all 3625 interpolated entries have rootSrc
✓ interp-connect: all 3625 interpolated entries connected in fromTo graph

✗ 0 failure(s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The graph was whole. The orphans were adopted. The synthetic bridges held.&lt;/p&gt;

&lt;p&gt;But normalizing the graph topology was only half the battle. Once the architecture was solid, I had to plunge into the messy, politically charged, and deeply inconsistent world of human naming conventions—a data normalization war where there are no winners.&lt;/p&gt;

&lt;p&gt;In the next article, we’ll dive into "Source Wars," dealing with the chaos of ten conflicting databases, and why "King's Indian Attack" and "Reti: KIA" are a data engineer's worst nightmare.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>architecture</category>
      <category>database</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How to build an associative graph using React + p5</title>
      <dc:creator>Jeff Lowery</dc:creator>
      <pubDate>Sun, 12 May 2024 19:21:23 +0000</pubDate>
      <link>https://dev.to/jeffml/how-to-build-an-associative-graph-using-react-p5-25ic</link>
      <guid>https://dev.to/jeffml/how-to-build-an-associative-graph-using-react-p5-25ic</guid>
      <description>&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;I've been working on a &lt;a href="https://fensterchess.com"&gt;chess opening database&lt;/a&gt; in my copious spare time, and have begun experimenting with visualizations of said openings. One of those is a relationship graph (see above) that displays origin square to destination square of each opening move. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Farmer in the Dell
&lt;/h2&gt;

&lt;p&gt;Relationships (a type of association) can be found everywhere. In this post, I'll graph the relationships as described in a nursery rhyme called "The Farmer in the Dell".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The farmer in the dell&lt;br&gt;
The farmer in the dell&lt;br&gt;
Hi-ho, the derry-o&lt;br&gt;
The farmer in the dell&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After establishing that there's a farmer that lives in a dell, a series of new characters are introduced via the verb "takes".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The farmer takes a wife&lt;/li&gt;
&lt;li&gt;The wife takes the child&lt;/li&gt;
&lt;li&gt;The child takes the nurse&lt;/li&gt;
&lt;li&gt;The nurse takes the cow&lt;/li&gt;
&lt;li&gt;The cow takes the dog&lt;/li&gt;
&lt;li&gt;The dog takes the cat&lt;/li&gt;
&lt;li&gt;The cat takes the mouse&lt;/li&gt;
&lt;li&gt;The mouse takes the cheese&lt;/li&gt;
&lt;li&gt;The cheese stands alone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The word "takes" is way overloaded here, but that can be addressed later. From this list of relationships we can see that the farmer and the cheese have a single relation (terminal nodes in the graph to come), but all other characters have two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graphing the characters
&lt;/h2&gt;

&lt;p&gt;To display the relationships among the characters in the rhyme, we can start by arranging each in a circle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F97uenplpmwheuspviw8w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F97uenplpmwheuspviw8w.png" alt="Our cast of characters" width="800" height="804"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm using the JavaScript graphics library &lt;a href="https://p5js.org/"&gt;p5&lt;/a&gt; inside a react component, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* eslint-disable react/prop-types */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;p5&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;characters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;farmer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;wife&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;nurse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;cow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;cheese&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Version1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;p5&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;remove&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                    &lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWO_PI&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCanvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="c1"&gt;//move 0,0 to the center of the canvas&lt;/span&gt;
                &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ellipseMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CENTER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CENTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CENTER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Georgia&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ellipse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Version1"&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;renderRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without delving too deep into p5, it's worth pointing out some features of this code.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to render graphics inside a React component
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;const renderRef = useRef();&lt;/code&gt;&lt;br&gt;
&lt;code&gt;p.createCanvas(600, 600).parent(renderRef.current);&lt;/code&gt;&lt;br&gt;
&lt;code&gt;return &amp;lt;div id="Version1" ref={renderRef}&amp;gt;&amp;lt;/div&amp;gt;;&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  useEffect() cleanup function
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;p5&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;remove&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;the p5 remove function 'cleans up' the previous render before useEffect is called again. &lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up the character locations
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;         &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Note the two Object.entries() calls. This is done to get an index to calculate each character's angle from the center:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;     &lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWO_PI&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Drawing the relationships
&lt;/h2&gt;

&lt;p&gt;Now to draw a line from each taker to each taken character. First, I add a "takes" relation from farmer all the way down to cheese, then draw a line from the taker location to the taken location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doRelations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// for this version, all relations are the same.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;takes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cm"&gt;/* ... */&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;takes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;takes&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;taken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ellipse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uuseg78nr8mm4un3j02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uuseg78nr8mm4un3j02.png" alt="lines of relationships" width="800" height="801"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not terribly interesting, but it's a start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements analysis
&lt;/h2&gt;

&lt;p&gt;Let's reexamine the word "takes". It's usage is very ambiguous and often nonsensical. Here's one attempt to disambiguate the rhyme:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The farmer &lt;strong&gt;&lt;em&gt;married&lt;/em&gt;&lt;/strong&gt; a wife&lt;/li&gt;
&lt;li&gt;The wife &lt;strong&gt;&lt;em&gt;married&lt;/em&gt;&lt;/strong&gt; a farmer&lt;/li&gt;
&lt;li&gt;The wife &lt;strong&gt;&lt;em&gt;adopts&lt;/em&gt;&lt;/strong&gt; the child&lt;/li&gt;
&lt;li&gt;The wife &lt;strong&gt;&lt;em&gt;employs&lt;/em&gt;&lt;/strong&gt; the nurse&lt;/li&gt;
&lt;li&gt;The child &lt;strong&gt;&lt;em&gt;needs&lt;/em&gt;&lt;/strong&gt; the nurse&lt;/li&gt;
&lt;li&gt;The nurse &lt;strong&gt;&lt;em&gt;cares for&lt;/em&gt;&lt;/strong&gt; the child&lt;/li&gt;
&lt;li&gt;The farmer &lt;strong&gt;&lt;em&gt;owns&lt;/em&gt;&lt;/strong&gt; the cow&lt;/li&gt;
&lt;li&gt;The nurse &lt;strong&gt;&lt;em&gt;milks&lt;/em&gt;&lt;/strong&gt; the cow&lt;/li&gt;
&lt;li&gt;The farmer &lt;strong&gt;&lt;em&gt;owns&lt;/em&gt;&lt;/strong&gt; the dog&lt;/li&gt;
&lt;li&gt;The dog &lt;strong&gt;&lt;em&gt;guards&lt;/em&gt;&lt;/strong&gt; the cow&lt;/li&gt;
&lt;li&gt;The dog &lt;strong&gt;&lt;em&gt;befriends&lt;/em&gt;&lt;/strong&gt; the cat&lt;/li&gt;
&lt;li&gt;The cat &lt;strong&gt;&lt;em&gt;befriends&lt;/em&gt;&lt;/strong&gt; the dog&lt;/li&gt;
&lt;li&gt;The cat &lt;strong&gt;&lt;em&gt;adopts&lt;/em&gt;&lt;/strong&gt; the farmer&lt;/li&gt;
&lt;li&gt;The cat &lt;strong&gt;&lt;em&gt;hunts&lt;/em&gt;&lt;/strong&gt; the mouse&lt;/li&gt;
&lt;li&gt;The mouse &lt;strong&gt;&lt;em&gt;eats&lt;/em&gt;&lt;/strong&gt; the cheese&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see what the relationship graph looks like now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmk8xtymd2m3ztx2tc5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmk8xtymd2m3ztx2tc5t.png" alt="further relationships" width="774" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not bad, but now let's color-code the relationships and add a legend:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa9uan2jefdon3yobdegs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa9uan2jefdon3yobdegs.png" alt="relationships color-coded" width="800" height="651"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing curves instead of lines
&lt;/h2&gt;

&lt;p&gt;One problem with using lines is that in reciprocal relationships, such as "married", one line overwrites another, obscuring the former relationship. Bezier curves can be used instead of lines, and look better. Each bezier curve takes eight arguments, which are the (x,y) coordinates of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the start point&lt;/li&gt;
&lt;li&gt;the first control point&lt;/li&gt;
&lt;li&gt;the second control point&lt;/li&gt;
&lt;li&gt;the end point&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What are these control points? Here's an illustration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flejrrzql4cteparl1qdb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flejrrzql4cteparl1qdb.png" alt="bezier control points" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The shape of the curve can be adjusted by moving the control points. In this case, I'll set the first control point to be halfway between the start location and the center of the graph, and similarly for the end point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const cp1 = [x / 2, y / 2];
    const cp2 = [x2 / 2, y2 / 2];

    p.noFill();
    p.bezier(x, y, ...cp1, ...cp2, x2, y2);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4eqoj2i5pwntjdfnxsby.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4eqoj2i5pwntjdfnxsby.png" alt="curves instead of lines" width="800" height="809"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Though aesthetically better, the change didn't fix the overwrite problem. The control points can be adjusted for each character by adding a "fudge" factor to the control point equation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fudge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fudge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ny&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fudge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nx2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fudge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ny2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fudge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cp1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;nx&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ny&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cp2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;nx2&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ny2&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the variable &lt;strong&gt;i&lt;/strong&gt; is a counter that is increased as each character in the rhyme is rendered. The final output is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmut2kdg0kt0i2k60ioko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmut2kdg0kt0i2k60ioko.png" alt="further adjustment to curves" width="800" height="809"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Arrows? What about arrows?
&lt;/h2&gt;

&lt;p&gt;One thing missing from the diagram is an indication of the direction of the relationship, e.g. "farmer owns dog" and not "dog owns farmer". However, there is no native support for arrowheads in p5, so it takes quite a bit of trigonometry to draw them...another post in itself. See links below for more info on the subject.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;A complete program can be found &lt;a href="https://github.com/JeffML/farmer-in-the-dell"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful links:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://editor.p5js.org/kevinhb92/sketches/zLDDLrg_"&gt;This example&lt;/a&gt; gets halfway to an arrow feature.&lt;/p&gt;

&lt;p&gt;One thing is to have the tip of the arrow touch the edge of each character's circle, using the formula in the answer found &lt;a href="https://stackoverflow.com/questions/14096138/find-the-point-on-a-circle-with-given-center-point-radius-and-degree"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>graphical</category>
      <category>javascript</category>
      <category>p5</category>
    </item>
  </channel>
</rss>
