Mini Project 4: Generative Landscapes & Patterns

 

Project Link

https://editor.p5js.org/King-Raphael/sketches/dnzQIlztr

 

Brief Description and Concept

This project generates a dynamic incense stick pattern using p5.js, inspired by the traditional art of arranging incense sticks and a blooming flower. The visual complexity of the pattern adapts based on the number of layers and sticks, creating either a dense or loose arrangement. The background gradient is randomly generated, which adds a unique touch to each output as well.

 

Visual Documentation

 

Coding

function setup() {
  createCanvas(800, 500); // Create a canvas with 800x500 dimensions
  noLoop(); // Stop draw from looping

  // Create random colors for the gradient
  let c1 = color(random(100, 255), random(100, 255), random(100, 255), random(50, 150)); // Random start color
  let c2 = color(random(0, 100), random(0, 100), random(150, 255),80);
  
  // Draw the gradient background
  drawGradient(0, 0, width, height, c1, c2, 1);
  
  translate(width / 2, height / 2); // Center the pattern
  
  let layers = floor(random(5, 10)); // Number of layers of incense sticks
  let sticksPerLayer = floor(random(10, 22)); // Sticks in each layer

  // Adjust maxRadius based on the number of layers and sticksPerLayer
  let densityFactor = map(layers * sticksPerLayer, 50, 220, 1.5, 0.5);
  let maxRadius = 200 * densityFactor; // Adjust max radius based on density factor
  let minRadius = 50 * densityFactor;  // Adjust min radius based on density factor

  for (let i = 0; i < layers; i++) {
    let layerRadius = map(i, 0, layers, minRadius, maxRadius); // Gradual increase in radius
    let hue = map(i, 0, layers, 0, 255); // Vary color along the layer
    let angleOffset = random(0, TWO_PI); // Random rotation for each layer
    
    drawIncenseLayerWithArcs(layerRadius, sticksPerLayer, hue, angleOffset);
  }
  saveCanvas('Raphael', 'png'); // saves the canvas as a png image
}

// Function to draw a single layer of incense sticks with connecting arcs
function drawIncenseLayerWithArcs(radius, numSticks, hueValue, angleOffset) {
  strokeWeight(2);
  let prevX, prevY; // Variables to store the previous stick's coordinates
  
  for (let j = 0; j < numSticks; j++) { let angle = map(j, 0, numSticks, 0, TWO_PI) + angleOffset; // Distribute around the circle let x = cos(angle) * radius; let y = sin(angle) * radius; let length = random(20, 120); // Random length for each stick let colorHue = hueValue + random(-20, 20); // Slight variation in color stroke(colorHue, 80, 200); // Stroke color with variation line(x, y, x + cos(angle) * length, y + sin(angle) * length); // Draw the incense stick // If there is a previous point, draw an arc between the previous and current points if (j > 0) {
      noFill();
      stroke(colorHue, 150, 255, 150); // Softer stroke for the arc
      let midX = (prevX + x) / 2;
      let midY = (prevY + y) / 2;
      arc(midX, midY, radius, radius, atan2(prevY - midY, prevX - midX), atan2(y - midY, x - midX));
    }
    
    // Store the current point as the previous point for the next iteration
    prevX = x;
    prevY = y;
    
    // Optional: Draw circles at the base for added texture
    fill(colorHue, 80, 100);
    noStroke();
    ellipse(x, y, random(5, 10));
  }
}

// Function to draw the gradient background with random colors
function drawGradient(x, y, w, h, c1, c2, axis) {
  noFill();
  
  for (let i = y; i <= y + h; i++) {
    let inter = map(i, y, y + h, 0, 1);
    let c = lerpColor(c1, c2, inter);
    stroke(c);
    line(x, i, x + w, i);
  }
}

Coding Explanation:

  1. Gradient Background:let c1 = color(random(100, 255), random(100, 255), random(100, 255), random(50, 150));
    let c2 = color(random(0, 100), random(0, 100), random(150, 255),80);
    drawGradient(0, 0, width, height, c1, c2, 1);
    This code generates a gradient background with two randomly chosen colors (c1 and c2). The drawGradientfunction fills the canvas with a smooth gradient transition.
  2. Pattern Density Control:let densityFactor = map(layers * sticksPerLayer, 50, 220, 1.5, 0.5);
    let maxRadius = 200 * densityFactor;
    let minRadius = 50 * densityFactor;
    The densityFactor scales the radius of the pattern based on the number of layers and sticks per layer. A higher density leads to a more compact pattern, while a lower density spreads it out.
  3. Incense Sticks and Arcs:stroke(colorHue, 80, 200);
    line(x, y, x + cos(angle) * length, y + sin(angle) * length);
    if (j > 0) {
    stroke(colorHue, 150, 255, 150);
    arc(midX, midY, radius, radius, atan2(prevY – midY, prevX – midX), atan2(y – midY, x – midX));
    }

    Each incense stick is drawn using the line function. If there is a previous stick, an arc is drawn between the current and previous stick to create a visual connection.

  4. Adjusting the Pattern: I can further experiment with the densityFactor, color variations, and lengths of the sticks to produce different visual styles.

 

Findings / Lessons Learned

  • Using randomness in colors and positions adds a unique flavor to each generation.
  • Managing visual density is crucial to maintaining the aesthetic appeal of generative patterns.
  • Experimenting with visual elements like arcs and gradient backgrounds can elevate a basic pattern to an intricate piece of art.

 

Reflection Prompts

1. How is drawing by hand from observation different from programming the computer to draw for you? Can you think of some commonalities as well?

Drawing by hand allows for intuitive and immediate adjustments based on visual feedback and personal expression. In contrast, programming requires logical structuring and pre-defined rules. However, both share a process of iterative refinement and require a keen sense of composition and aesthetics.

2. What properties have you manipulated in the repetition? Describe your observations and visual outcomes during the process.

The number of layers, sticks per layer, stick lengths, and angles were manipulated. Changing these properties affected the visual density, pattern complexity, and overall feel of the artwork. Denser configurations resulted in a more clustered pattern, while fewer layers created a minimalist, expansive look.

3. What makes a good generative pattern?

A good generative pattern balances randomness and order, creating a visually pleasing composition while maintaining a sense of unpredictability. It should have an underlying structure that can surprise the viewer with unexpected variations in color, form, or arrangement.

Leave a Reply

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