Search code examples
javascriptprocessingp5.js

Loop construction in Processing artistic rendition


The code is from here:

t=0
draw=_=>{t||createCanvas(W = 720,W)
t+=.01
B=blendMode
colorMode(HSB)
B(BLEND)
background(0,.1)
B(ADD)
for(y = 0; y < W; y += 7)
  for(x = 0; x < W; x+=7)
    dist(x, y, H = 360, H) +
      !fill(x * y % 360, W, W, 
            T=tan(noise(x, y) * 9 + t)) 
         < sin(atan2(y - H, x - H) * 2.5 + 84)**8 * 200 + 130?
circle(x, y + 30, 4/T) : 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>

I see that t is increased by 0.01 each iteration, but I am unsure whether for each t value the entire canvas is refreshed, or whether there is an endpoint somewhat set by :0}. Is this correct?

It also seems like there is a Boolean call in the middle basically comparing two distances to determine which circles are filled and how. If the < was instead > the circles would form a complementary pattern on the rendition.

The ! is explained here, as a way of saving space and calling a function as soon as it is declared. I presume it determines how points are filled with a certain variable color scheme depending on the Boolean operation result. The +!fill() is unfamiliar, as well as the ? at the end, and I guess that they amount to: if the x, y location is within the boundary of the star the circles are colored (filled) as such, but the negation in '!' is confusing.

Can I get an English explanation of the main structural points on this code (the loop and the Boolean) to match the syntax?


I have so far gathered that the basic loop is

for(y from 0 to the width of the canvas at increments of 7)
  for(x from... )
check if the distance from (x , y) to 360 is less than sin()^8 * 200 + 130
  If so fill (or not fill with the ! ????) with these colors
otherwise do nothing :0 

Solution

  • This is what it might look like if it were written normally

    let t = 0;
    const W = 720;
    // https://p5js.org/reference/#/p5/draw
    // `draw` needs to be in the global scope so p5 can use it
    draw = () => {
      // create a canvas if this is the first frame
      if (!t) createCanvas(W, W);
      t += 0.01;
      // Use HSB and blending to do the fancy effects
      // The black circles are because we're ignoring stroke and so using its defaults
      // The blending will eventually hide it anyway
      colorMode(HSB);
      blendMode(BLEND);
      background(0, 0.1);
      blendMode(ADD);
      // iterate over 7px grid
      for(y = 0; y < W; y += 7) {
        for(x = 0; x < W; x += 7) {
          // center
          const H = 360;
          // distance from center
          const D = dist(x, y, H, H);
          // pick an alpha based on location and time
          const T = tan(noise(x, y) * 9 + t);
          // set fill color
          fill(x * y % 360, W, W, T);
          // magic to calculate the star's boundary
          // sine wave in polar coords, I think
          const S = sin(atan2(y - H, x - H) * 2.5 + 84)**8 * 200 + 130;
          // draw a circle if we're within the star's area
          // circle's color, alpha, and radius depend on location and time
          if (D < S) circle(x, y + 30, 4/T);
        }
      }
    };
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>

    Note that there are some hacks in the original code that are solely to help make it a one-liner or to reduce the number of characters. They don't have any meaningful effect on the results.