Project 11: Generative Landscape

For this week’s project, I decided to visualize an NYC subway line I used often (pre-pandemic) and the views of the skyline you can see passing by when the subway tracks are outdoors. I first roughly sketched out the scene on paper and then used Professor Cortina’s sample code as a baseline and customized it further to meet my vision. I displayed the scene to be at night since that’s the time of day I liked most when watching the skyline. Also, although nighttime, the lights would always be on in the buildings, so I decided to have lit up windows on all the buildings as well as the light reflections.

sketch
var frontBuildings = [];
var backBuildings = [];
var moonAndStars;

function preload(){
  moonAndStars = loadImage("https://i.imgur.com/0JJlsGR.png");
}

function setup() {
  createCanvas(480, 270);
  // make initial array of buildings
  for(var i = 0; i < 20; i++) {
    var x = random(width);
    backBuildings[i] = makeBackBuilding(x);
  }
  for(var i = 0; i < 10; i++) {
    var x = random(width);
    frontBuildings[i] = makeFrontBuilding(x);
  }
  frameRate(50);
}

function draw() {
  // gradient night background
  setGradient(color("#655BCE"), color("#1A1449"));
  // moon
  image(moonAndStars, 0, 0, 480, 350);

  updateAndDisplayBuildings();
  removeBuildingsThatHaveSlippedOutOfView();
  addNewBuildingsWithSomeRandomProbability(); 
  // tracks
  drawTrack();
  // train
  drawTrain();

}

function drawTrain() {
  strokeWeight(1);
  stroke("#4A4954");
  // train body
  fill("#65656D");
  rect(0, 180, 220, 80);
  strokeWeight(5);
  line(0, 240, 218, 240);
  strokeWeight(2);
  line(0, 245, 218, 245);
  strokeWeight(5);
  line(0, 190, 218, 190);
  strokeWeight(2);
  line(0, 196, 218, 195);
  noStroke();
  // doors
  fill("#5D5C66");
  rect(10, 200, 36, 60);
  rect(130, 200, 36, 60);
  // windows
  fill("#383664");
  rect(12, 202, 32, 30);
  rect(132, 202, 32, 30);
  rect(58, 200, 60, 30);
  rect(200, 200, 20, 40);
  // train number
  fill("purple");
  circle(182, 220, 25);
  fill(255);
  textSize(20);
  text("7", 177, 227);
}

function drawTrack() {
  fill(20);
  rect(0, 220, width, 100);
  fill("#8F8F91");
  rect(0, 220, width, 30);
  fill("#F0D933");
  rect(0, 240, width, 5);
}

function removeBuildingsThatHaveSlippedOutOfView(){
  // If a building has dropped off the left edge,
  // remove it from the array.  This is quite tricky, but
  // we've seen something like this before with particles.
  // The easy part is scanning the array to find buildings
  // to remove. The tricky part is if we remove them
  // immediately, we'll alter the array, and our plan to
  // step through each item in the array might not work.
  //     Our solution is to just copy all the buildings
  // we want to keep into a new array.
  var frontBuildingsToKeep = [];
  var backBuildingsToKeep = [];
  for (var i = 0; i < frontBuildings.length; i++){
      if (frontBuildings[i].x + frontBuildings[i].width > 0) {
          frontBuildingsToKeep.push(frontBuildings[i]);
      }
  }
  for (var i = 0; i < backBuildings.length; i++){
      if (backBuildings[i].x + backBuildings[i].width > 0) {
          backBuildingsToKeep.push(backBuildings[i]);
      }
  }
  frontBuildings = frontBuildingsToKeep; // remember the surviving buildings
  backBuildings = backBuildingsToKeep;
}


function addNewBuildingsWithSomeRandomProbability() {
  // With a very tiny probability, add a new building to the end.
  var newBuildingLikelihood = 0.08; 
  if (random(0,1) < newBuildingLikelihood) {
      backBuildings.push(makeBackBuilding(width));
      frontBuildings.push(makeFrontBuilding(width));
  }
}

function updateAndDisplayBuildings(){
  // Update the building's positions, and display them.
  for (var i = 0; i < backBuildings.length; i++){
      backBuildings[i].move();
      backBuildings[i].display();
  }
  for (var i = 0; i < frontBuildings.length; i++){
      frontBuildings[i].move();
      frontBuildings[i].display();
  }
    
}

// method to update position of building every frame
function frontBuildingMove() {
    this.x += this.speed;
}
    
// draw the building and some windows
function frontBuildingDisplay() {
  noStroke();
  // building
  fill(this.color);
  var y = 220 - this.height;
  rect(this.x, y, this.width, this.height);
  // windows
  fill("#FFE9AD");
  for(var i = 0; i < 5; i ++) {
    rect(this.x + 5, y + 10 + 15*i, this.width/6, 8);
    rect(this.x + 20, y + 10 + 15*i, this.width/6, 8);
    rect(this.x + 35, y + 10 + 15*i, this.width/6, 8);
  }
}

function makeFrontBuilding(xlocation) {
  var building = {x: xlocation, 
                  width: random(40, 50), 
                  height: random(80, 120),
                  color: color(random(20, 70), random(20, 70), 130),
                  speed: -3,
                  move: frontBuildingMove, 
                  display: frontBuildingDisplay}
  return building;
}


// method to update position of building every frame
function backBuildingMove() {
  this.x += this.speed;
}
    
// draw the back building and some windows
function backBuildingDisplay() {
  noStroke();
  var y = 220 - this.height;
  // light reflections
  fill(255, 243, 180, 20);
  ellipse(this.x + this.width/2, y + 20, this.width*2);
  // building
  fill(this.color);
  rect(this.x, y, this.width, this.height);
  // windows
  fill("#FFE9AD");
  for(var i = 0; i < 8; i ++) {
    rect(this.x + 5, y + 10 + 15*i, this.width/6, 8);
    rect(this.x + 20, y + 10 + 15*i, this.width/6, 8);
    rect(this.x + 35, y + 10 + 15*i, this.width/6, 8);
  }
}

function makeBackBuilding(xlocation) {
  var building = {x: xlocation, 
                  width: random(40, 50), 
                  height: random(130, 160),
                  color: color(random(60, 80), random(60, 80), 180),
                  speed: -3,
                  move: backBuildingMove, 
                  display: backBuildingDisplay}
  return building;
}

// code from https://editor.p5js.org/REAS/sketches/S1TNUPzim
function setGradient(c1, c2) {
  noFill();
  for (var y = 0; y < height; y++) {
    var inter = map(y, 0, height, 0, 1);
    var c = lerpColor(c1, c2, inter);
    stroke(c);
    line(0, y, width, y);
  }
}



Leave a Reply