Hero Image
Tags:

Drawing a Sunflower with Code

The first time I encountered Generative Art was through Daniel Eden’s blog post on Drawing with Numbers. I used to be an avid drawer as a child and seeing that you can create visually interesting pieces with numbers, algorithms, and randomness was fascinating to me. You can see more of his generative art pieces here. In his post, you will see that each piece has an instruction that he followed to generate a piece. In Matt Deslauriers’ video on Canvas WebGL from Frontend Masters, he mentions the conceptual artist, Sol LeWitt, and its relevance to Generative Art. What Sol LeWitt did was to provide an instruction and let other people to draw it. His instructions are very much like code and they never reached the exact precision to allow some randomness to it so that it would be unique.

I followed Dan’s advice from his blog post to use Processing instead of JavaScript since Processing is faster and I wanted to write code in a new syntax ā€“ in this case, Java syntax. I later appreciated Java’s strict rule ā€“ for example, 1. How its array is a fixed size ā€“ which means I cannot just randomly increase my array later in my code, 2. Unlike JavaScript, its constructor must match its class name, 3. When passing an argument or declaring a variable, you also have to pass its data type next to it.

I was inspired to create this after watching many video tutorials of Daniel Shiffman (another Daniel in the same post?!) on The Coding Train. I have always admired Creative Coders’ ability to create something so visually exciting by using math and physics. It was intimidating at first to implement or recall all the math related subjects (especially, the trignometry). But with Daniel Shiffman’s video tutorials, it has been a fun journey šŸ¤—. So thank you, Daniel!

Let’s dive into the process of creating a sunflower šŸŒ»

1. Creating an Individual Sunflower Seed

As you can see in the picture of a sunflower head, not all seeds have the same shape. I simplified and grouped them into 3. The core inner part has the triangular shape seeds that are pointing towards the center (I made it less pointy in the code because I liked the look of it better), its surrounding inner area has a rounded square shape, and its border seeds have starfish shapes. In order to mimic these variety of shapes with just numbers, I used Supershapes Formula by Paul Bourke. Given certain input number value, this formula can mimic natural forms. If you would like to know more about 2D Supershapes, the link to the video tutorial by Daniel Shiffman is here.

class SunflowerSeed {
  PVector position;
  float n1 = 1;
  float n2 = 1;
  float n3 = 1;
  float m = 1;
  float a = 1;
  float b = 1;
  float t = 0;

  SunflowerSeed(float tempN1, float tempN2, float tempN3, float tempM) {
    n1 = tempN1;
    n2 = tempN2;
    n3 = tempN3;
    m = tempM;
  }

  void toUpdateShape(float tempX, float tempY) {
    int radius = 10;
    int total = 200;
    float increment = TWO_PI / total; // Higher the total, smoother the line

    translate(tempX, tempY);
    beginShape();

    for (float angle = 0; angle < TWO_PI; angle += increment) {
      float r = supershape(angle);
      float deformedShape = 0.6;
      
      position = new PVector(radius * deformedShape * r * cos(angle), radius * r * sin(angle));
      vertex(position.x, position.y);
    }
    endShape();
  }

  float supershape(float theta) {
    // supershape() receives an angle and needs to return back r
    // The formula is coming from http://paulbourke.net/geometry/supershape/
    // Theta represents an angle

    float part1 = (1 / a) * cos(theta * m / 4);
    part1 = abs(part1);
    part1 = pow(part1, n2); // raise to an exponent

    float part2 = (1 / b) * sin(theta * m / 4);
    part2 = abs(part2);
    part2 = pow(part2, n3); // raise to an exponent

    float part3 = pow(part1 + part2, 1 / n1);

    if (part3 == 0) {
      return 0;
    }

    return (1 / part3);
  }
}

In the constructor, there are 4 variables ā€“ n1, n2, n3, m. This is where we assign real numbers to determine the shape of the seed. toUpdateShape() is where we call supershape() to determine its shape and its position of the seed to mimic the Sunflower’s spiral pattern. This is the blue print for each individual seed for its Particle System to define and use later.

2. Creating Sunflower Seeds Particle System

As I mentioned earlier, array is fixed in Java so I set the maximum size 2800 when creating the array. toCreateInnerHead() is where I am storing a new instance of the SunflowerSeed class as each array’s value ā€“ a total of 2800 seeds. We are passing number values as parameters to create the shape that we want. Its core inner seeds are using asymmetric forms with a value of n1 = 60, n2 = 55, n3 = 30, and m = 1. Its surrounding inner area is using a value of n1 = n2 = n3 = 1 and m = 2 to add rotational symmetry to the shape. Its border seeds are using values that create starfish shapes n1 = 0.1, n2 = 1.7, n3 = 1.7, and m = 5. You can see more shapes using different numbers in Paul Bourke’s article that I mentioned previously.

class SunflowerSeedSystem {
  SunflowerSeed[] seeds = new SunflowerSeed[2800];
  PVector HeadPosition;
  
  void toCreateInnerHead() {
    int coreSeeds = 300;
    int headSeeds = seeds.length - coreSeeds;
    
    for(int i=0; i<seeds.length; i++) {
      if(i < coreSeeds) {
        seeds[i] = new SunflowerSeed(60, 55, 30, 1);
      } else if(i > coreSeeds && i <= headSeeds) {
        seeds[i] = new SunflowerSeed(1, 1, 1, 2); 
      } else {
        seeds[i] = new SunflowerSeed(0.1, 1.7, 1.7, 5);
      }
    } 
  }
  
  void toCreateOuterHead() {
    for (int i = 0; i < seeds.length; i++) {
      float c = 11;
      float a = i * radians(137.5);
      float r = c * sqrt(i);
      HeadPosition = new PVector(r * cos(a), r * sin(a));
      
      // Updating Each Seed //<>//
      seeds[i].toUpdateShape(HeadPosition.x, HeadPosition.y);
    }
  }
  
  float getHeadPositionMag() {
    return HeadPosition.mag() + 40;
  }

  void toAddStyle() {
    float SatRange = random(0, 5);
    float BrightRange = random(60, 90);
    strokeWeight(0.07);
    stroke(0, SatRange, BrightRange);
    fill(0);
  }
}

toCreateOuterHead() is used to create the Sunflower’s spiral pattern. radians(137.5) is the magic number. This formula is the planar model that can be found in this document on page 2 to create Phyllotaxis. More in-depth tutorial on Phyllotaxis can be found here by Daniel Shiffman.

angle = n āˆ— 137.5, r = cāˆšn

n is the number of dots and c contributes to a scaling factor. If you change c from 11 to 1, you will see that our seeds get extremely small. Since this formula gives an angle and radius, we can convert it to x and y by using Polar Coordinates (r,Īø) to Cartesian Coordinates (x,y) transformation x = r * cos(angle); y = r * sin(angle).

3. Creating Sunflower Petals

class SunflowerPetals { 
  float r;
  float x;
  float y;

  SunflowerPetals(float headLength) {
    r = headLength / 2;
  }

  void createPetals() {
    for (float a = 0; a < TWO_PI; a += PI/13) {
      x = r * cos(a);
      y = r * sin(a);
      
      drawPetal(x, y, 2.5 - PI/13 / TWO_PI, a);
      drawPetal(x, y, 2.5 - PI/13 / TWO_PI, a + PI/3.75);
    }
  }

  void drawPetal(float x, float y, float lfSize, float dir) {
    pushMatrix();

    // Perform transformations in TRS order
    translate(x, y);            // Define origin for the drawing
    rotate(dir);                // rotate by 'dir' radians
    scale(lfSize);              // scale the image
    beginShape();
    vertex(0, 0);
    
    // CURVED SUNFLOWER PETALS
    bezierVertex(50, -60 * noise(frameCount + 1), 100, -30 * noise(frameCount + 1), +150, -50);
    bezierVertex(100, 30 * noise(frameCount + 2), 50, 30, 0, 0);
    
    endShape();
    popMatrix();
  }

  void toAddStyle() {
    float SatRange = random(0, 5);
    float BrightRange = random(60, 90);
    strokeWeight(0.07);
    stroke(0, SatRange, BrightRange);
    noFill();
  }
}

class SunflowerPetals is the blue print of our petals. Later in our setup(), we are going to pass the sunflower head’s magnitude (length) into our newly created SunflowerPetals instance to position the petals correctly around its sunflower head. In between beginShape() and endShape(), we can set any number of arbitrary vertices. You can set vertex or any kind of vertex over and over again. In createPetals(), we are calling drawPetal() to draw around the circle. We are converting from Polar Coordinates (r,Īø) to Cartesian Coordinates (x,y) to draw vertices around the circle. Curves require additional points that control the entry and exit of the curve. We have vertex(0, 0) in the beginning because all shapes created with beginShape() must start with a vertex() to define the starting point of the shape.

Having only one bezierVertex() can be enough as petals but I used two to give more volume to it. noise() returns the Perlin noise value, which is a random sequence generator that is more natural than the standard random() function. This creates the look of threads around the petals. I encourage you to play with bezierVertex() with random number values. I got some crazy looking shapes during my experiments. For example, the below bezierVertex() creates crazy circular + sunflower petals shape.

bezierVertex(50, -60 * noise(frameCount + 3), 100, -30 * noise(frameCount + 1), +150 * noise(frameCount + 1), -50);
bezierVertex(100, 30 * noise(frameCount + 3), 50, 30 * noise(frameCount + 1), 0, 0);

4. Its Setup

SunflowerSeedSystem SunflowerSeeds;
SunflowerPetals Petals;

float HeadMag;

void setup() {
  colorMode(HSB, 360, 100, 100);
  background(0);
  fullScreen();

  SunflowerSeeds = new SunflowerSeedSystem();
  SunflowerSeeds.toCreateInnerHead();
}

void draw() {
  translate(width / 2, height / 2);

  // Drawing Seeds
  pushMatrix();
  SunflowerSeeds.toAddStyle();
  SunflowerSeeds.toCreateOuterHead();
  HeadMag = SunflowerSeeds.getHeadPositionMag();
  popMatrix();

  // Drawing Petals
  Petals = new SunflowerPetals(HeadMag);
  Petals.toAddStyle();
  Petals.createPetals();
}

// For SAVING as an image
void keyPressed() {
  if (key == 's') {
    save("sunflower.png");
    saveHiRes(5);
    exit();
  }
}
 
void saveHiRes(int scaleFactor) {
  PGraphics hi_res = createGraphics(width*scaleFactor, height*scaleFactor, JAVA2D);
  beginRecord(hi_res);
  hi_res.scale(scaleFactor);
  setup();
  draw();
  endRecord();
  hi_res.save("hire_sunflower.png");
}

This is where we declare SunflowerSeeds and Petals variables and assign them a new instance of the classes that we have created ā€“ SunflowerSeedsSystem and SunflowerPetals. It’s important that we call Petals() after SunflowerSeeds() to pass in the correct Seeds (or Sunflower Head) magnitutde.

Write a Reply or Comment

Your email address will not be published. Required fields are marked *