1

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 
JAP
  • 405
  • 2
  • 11
  • 1
    `?:` is [conditional operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#conditional_ternary_operator), basically an inline if/then/else. `!` is a [logical not](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#logical_operators) operator. Here it's used in conjunction with [type coercion](https://developer.mozilla.org/en-US/docs/Glossary/Type_coercion). – Ouroborus Dec 27 '22 at 06:27
  • @Ouroborus Is the mathematical Boolean `<` just applied to the `dist()` and depending on the FALSE/TRUE output `fill()` is either applied or not? – JAP Dec 27 '22 at 16:13

2 Answers2

2

The main issue is the +! with ? and :0, which is terribly confusing, but clearly it is equivalent to

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)
    if(dist(x, y, H = 360, H) < sin(atan2(y - H, x - H) * 2.5 + 84)**8 * 200 + 130){
fill(x * y % 360, W, W, 
            T=tan(noise(x, y) * 9 + t)) 
circle(x, y + 30, 4/T)}else{0}}

The Boolean in the ? applies to the dist() part (in absolute values the angle has to be less than sin()... + 130.

The + part I still don't understand, and hopefully will be addressed by someone who knows Processing or JS (not me). However, it probably forces the execution to identify and throw out with regards to filling (hence !) values that are too low for the sin(atan2()), which will happen at around 0 and pi.

Because of arctan2() being (here)

enter image description here

the number of spikes in the star will be a multiple of 5 going from 0 to 2 pi. The fact that changing the code to < sin(2 * atan2(...)... doubles the spikes implies that the fill() part is also in Boolean operation of its own.

The result of the Boolean determines whether the colorful fill is applied or not (applied if less than).

The :0 at the end is the else do nothing.

JAP
  • 405
  • 2
  • 11
  • 1
    I think the `+!` is just so that `fill` can be called and `T` set without making either separate expressions. I don't think they actually effect `<`. – Ouroborus Dec 27 '22 at 20:07
  • @Ouroborus "Second," is a leftover from a prior edit that I overlooked. – JAP Dec 27 '22 at 20:14
2

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.

Ouroborus
  • 16,237
  • 4
  • 39
  • 62
  • (+1) As the frames succeed each other, I see the contents of prior frames are not discarded, but rather, new circles are added, how is this possible? The computer doesn't start at `t=0` for each frame, correct? Similarly, this seems to be an infinite loop: no ending $t$ value. I don't see any difference between `t += 0.01` or other values that I can plug in. Could you comment on this sort of "loop of loops" controlled by `t` (including the `if(!)`? – JAP Dec 27 '22 at 20:10
  • 1
    Yeah, a large part of how it works is keeping the old drawing, dimming it a little, then drawing over the top of that. This is also why the background starts out drawing black circle outlines but fades to black. `blendmode` makes that possible. `t` is time and how fast it progresses is tuned for effect (0.01 per frame in this case). The `+!` just makes it so `fill`'s return value has no significant contribution to `<` but is still executed. – Ouroborus Dec 27 '22 at 20:16
  • `blendMode` is between frames or within each frame? – JAP Dec 27 '22 at 20:19
  • 1
    Within each frame. Each time `draw` is called by p5, is one frame. `blendMode(BLEND)` is the normal way drawing works, an alpha-weighted color average. `background` would usually clear the canvas, but it is called with color black (0) and alpha 0.1 so it only dims the image. Then `blendMode(ADD)` changes how alpha mixing works, causing colors to add together instead of being alpha-weighted average. When combined with the circles being repeatedly drawn in certain places, this gives the bright pin-point glow effect. The "sparkle" is because the colors are randomized a bit. – Ouroborus Dec 27 '22 at 20:22