This is the 8th day of my participation in the August More Text Challenge

rendering

In fact, the core idea of the effect is to combine multiple “layers” of heart-shaped cigarettes. First of all, we define the implementation code of one “layer” of heart-shaped cigarettes.

Heart.pde

class Heart{
  float seed = random(10.1000);
  float offset = random(TWO_PI);
  
  float ff = random(0.5.2.5);
  float sw = random(0.8.1.8);
  
  float part = 0.1+0.5*pow(random(1),2.0);
  float rf = random(0.5.1.15);
  
  float d = random(10.120);
  int m = 500; / / / < 500 points
  OpenSimplexNoise noise = new OpenSimplexNoise(); ///< noise, used to disturb the heart
  
  float R = 150;
  float xh(float p){
    return R/15.0*16*pow(sin(p),3);
  }
  float yh(float p){
    return  R/15.0* (-13*cos(p) +5*cos(2*p) + 2*cos(3*p) + cos(4*p));
  }

  void show(float q){
    for(int i=0; i<m; i++){float p = 1.0*i/m;
      float theta = offset + part*TWO_PI*i/m;
      
      float rad = 1.3 f;
      int per = 2;
      
      float xx = rf*xh(theta) + pow(p,3.0 f)*d*(float)noise.eval(seed + rad*cos(TWO_PI*(per*p-q)),rad*sin(TWO_PI*(per*p-q)));
      float yy = rf*yh(theta) + pow(p,3.0 f)*d*(float)noise.eval(2*seed + rad*cos(TWO_PI*(per*p-q)),rad*sin(TWO_PI*(per*p-q)));
      
      strokeWeight(sw);
      stroke(255,ff*18*sin(PI*p)); point(xx,yy); }}}Copy the code

The key code (shown below) represents the formula to draw the heart (divide 360° into m parts, then place a point in each position, as long as the partition is fine enough, it can look like a full ♥)

  float R = 150;
  float xh(float p){
    return R/15.0*16*pow(sin(p),3);
  }
  float yh(float p){
    return  R/15.0* (-13*cos(p) +5*cos(2*p) + 2*cos(3*p) + cos(4*p));
  }
Copy the code

The schematic diagram of corresponding functions after simplification is shown below

We can then simulate the effect of soot by superimposing a random perturbation of Simplex noise.

Heartsmoke.pde (Main entrance)

From there, we then construct multiple layers (250 in this case) of heart-shaped smoke and paint them layer by layer

float t, c;

int samplesPerFrame = 5;
int numFrames = 100;        
float shutterAngle = 6.;

int n = 250; / / / < 250 layer has

Heart[] array = new Heart[n];

void setup(a){
  size(600.600,P3D);

  for(int i=0; i<n; i++){ array[i] =newHeart(); }}void show(a){
  background(0);
  push();
  translate(width/2,height/2);
  
  for(int i=0; i<n; i++){ array[i].show(t); } pop(); }void draw(a) {

    t = mouseX*1.0/width;
    c = mouseY*1.0/height;
    if (mousePressed)
      println(c);
      
    frameCount = 0;
    show();

    /// @note save sequence GIF, later use gifsicle synthesis of real GIF
    // saveFrame("fr###.gif");
}

void keyPressed(a){
  if(key == 'r' || key == 'R'){ recording = ! recording; println("recording:"+ recording); }}Copy the code

The last part is OpenSimplexnoise.pde (because the code is too long to read, so I upload it to Baidu web site). For a detailed explanation of noise, you can see my previous article ShaderJoy — The Beauty of Noise, Let’s make noise together.

Link: pan.baidu.com/s/1uKqedakx… Extraction code: U8RU

How to compose GIF

Gifsicle download link: www.lcdf.org/gifsicle/

After the download and installation is complete, open the command line tool and run the following command (the default naming mode for each frame is FR *.gif) to generate anim.gif. The delay for each frame is 10/100 s

gifsicle --delay=10 --loop fr*.gif > anim.gif
Copy the code

Gifsicle supports more complex command lines in its official manual