# Triage: Generative Series Breakdown

By [ocm5965](https://paragraph.com/@ocm5965) · 2023-08-31

---

[https://highlight.xyz/mint/64eec29adb2d22cad96e3151](https://highlight.xyz/mint/64eec29adb2d22cad96e3151)

     The basis of Triage is Delaunay triangulation (DT) from the d3-delaunay library which is based on the Delaunator by Volodymyr Agafonkin. It is a “technique for creating a mesh of contiguous, nonoverlapping triangles from a dataset of points.”

![Liu Yonghe, Feng Jinming, Shao Yuehong](https://storage.googleapis.com/papyrus_images/74880dfbe8adae11743ab5dd0281eec432d0c6ceab226d4b46cccd69c08b1634.png)

Liu Yonghe, Feng Jinming, Shao Yuehong

The algorithm is based on ideas from the following papers:

*   [A simple sweep-line Delaunay triangulation algorithm](http://www.academicpub.org/jao/paperInfo.aspx?paperid=15630), 2013, Liu Yonghe, Feng Jinming and Shao Yuehong
    
*   [S-hull: a fast radial sweep-hull routine for Delaunay triangulation](http://www.s-hull.org/paper/s_hull.pdf), 2010, David Sinclair
    
*   [A faster circle-sweep Delaunay triangulation algorithm](http://cglab.ca/~biniaz/papers/Sweep%20Circle.pdf), 2011, Ahmad Biniaz and Gholamhossein Dastghaibyfard
    

![Early test using points from a simplex noise field](https://storage.googleapis.com/papyrus_images/d64cd2480a364fc3f62a1d179fb98a0cd58bafec65967968ffd47fd8908681bd.png)

Early test using points from a simplex noise field

![Another example with circle packing](https://storage.googleapis.com/papyrus_images/5efe0fc0fcc7eccf081092183c38bc9816072d24c6b59abad6ff82f1e577f33c.png)

Another example with circle packing

     The first step of drawing an iteration of Triage is the creation of a small random vector path in the center of the canvas containing about twenty segments. Then, the center of the shape is moved to the center of the canvas using geometric.js.

     In order to fill the canvas efficiently, an optimal rotation for the shape must be found. A bounding box will be used to compare the shape W x L ratio to the canvas ratio. By rotating the shape and its bounding box 360 degrees in 30 degree increments and recording the ratio for each turn, a sample of 12 possible rotations with corresponding ratios can be used to find the optimal rotation.

![Ready to be scaled up](https://storage.googleapis.com/papyrus_images/d2c0b22fde368ae98cb2ec9df2eda92032e349144c731c9c914369c1f060deb2.png)

Ready to be scaled up

     The array of points is given to the function geometric.polygonScaleArea() which scales up the shape from the center until the edge of the canvas is detected. The next step loops through the paths of the shape to create Bezier paths using the bezier.js function new Bezier(p1.x,p1.y,p2.x,p2.y,p3.x,p3.y,p4.x,p4.y). The second part of the loop “generates a curve’s outline at distance d along the curve normal and anti-normal. The result is an array of curves that taken together form the outline path for this curve.”

[https://pomax.github.io/bezierjs/](https://pomax.github.io/bezierjs/)

        for(b=3;b<rotPoly_s.length;b+=3){
          //generate curve
          const bcurve = new Bezier(
            rotPoly_s[b][0], rotPoly_s[b][1],
            rotPoly_s[b-1][0], rotPoly_s[b-1][1],
            rotPoly_s[b-2][0], rotPoly_s[b-2][1],
            rotPoly_s[b-3][0], rotPoly_s[b-3][1]);
    
          //curve outlines
          or=hl.random(lo,hi)
          om=hl.random(1.2,1.3)
          for(var toffo=0; toffo<=1; toffo+=0.4) {
            var doc = c => drawCurve(c);
            var outline = bcurve.outline(or);
            outline.curves.forEach(doc);
            or=or*om
          }
        }
    

![Offset outlines of the curves](https://storage.googleapis.com/papyrus_images/01a9e4cb3a45e53c1d5e6c52d6eedd32791647e5d2ee66d928835e8430039cb6.png)

Offset outlines of the curves

     Another great thing about bezier.js is the ability to “generate a LookUp Table of coordinates on the curve, spaced at parametrically equidistance intervals.” Inside the drawCurve function, the LUTs are found for each curve and stored to be used for the triangle mesh input.

    function drawCurve(curve, offset){
    ...
      var LUT = zcurve.getLUT(LUTsteps);
      LUT.forEach(p => dlc.push([p.x,p.y]));
    }
    

![LUT points along the curves](https://storage.googleapis.com/papyrus_images/988127b2f2ea3eb4d4cfd55c2f02f278845ed58d2821894d80ee829f6d4b2d26.png)

LUT points along the curves

     The triangle mesh is generated by d3.js, then the resulting points are sorted into groups of three so that each triangle can be edited individually.

      let verts = newVerts
      voronoi = d3.voronoi();
      let triangles = voronoi(verts).triangles();
      triangles.map(t => t.map(v => tris.push([v[0], v[1]])))
    
      count=0
      t=[]
      for(t=0;t<tris.length;t++){
          if(count==3){
              t.push([
                tris[t-3][0], tris[t-3][1],
                tris[t-2][0], tris[t-2][1],
                tris[t-1][0], tris[t-1][1]
              ])
          count=0
        }
        count++
      }
    

     Some filters are used to cull triangles that have at least one angle of less than 4 degrees, triangles that get too close to the canvas edge, and triangles that are over a certain size. Each triangle is then scaled down slightly (to create small gaps) and filled with hatch lines. Noise is mapped to the stroke weight, number of hatch lines, and angle of hatch lines to create interesting patterns in the texture of the mesh.

     That’s about it, thank you for reading this far.

![Triage #1](https://storage.googleapis.com/papyrus_images/66af497e2eec4fb54404962ba6943bc0c4978916dcf1fc3bacfe7785dee383bd.png)

Triage #1

    //the end

---

*Originally published on [ocm5965](https://paragraph.com/@ocm5965/triage-generative-series-breakdown)*
