Stage 2

Overview

Overviewvisual1

In the second stage of the Cityscape Challenge, we draw rows of windows in our buildings. Because we will be drawing lots of windows and rows, we will automate the process using for loops.

 

Draw a Window in an Office in a Building

When drawing an object inside of another object, it makes sense to use the context.translate() method to make the drawing easier by moving the origin of the context's coordinate system.

In this example, we draw a blue building with a yellow door with a red door knob.

We know that the top left corner of the building is positioned at (100, 50) on the canvas.

We know that the top left corner of the door is positioned at (140, 160) in the building.

We know that the top left corner of the door knob is positioned at (40, 80) in the door.

Do we know or care what the coordinates of the door knob are on the canvas or in the building? No. By translating the origin of the coordinate system to the appropriate frame of reference, we can focus on drawing and not worry about the math. And if we move the door, the door knob moves along with it.

Notice how we restore the drawing state so the origin of the coordinate system is back at the top left corner of the canvas? Remember, it is important to tidy up after moving things around.

To learn more about saving and restoring the drawing state and translating the origin of the coordinate system, visit the save() / restore() and translate() lessons.

Quick Reference: fillRect() fillStyle save() / restore() translate()

Editor (write code below)
var canvas = document.getElementById('basic_cityscape_stage2_example1'); var context = canvas.getContext('2d'); context.save(); // The origin is at the top left corner of the canvas context.translate(100, 50); // The origin is now at the top left corner of the building context.fillStyle = '#4169E1'; context.fillRect(0, 0, 200, 240); context.translate(140, 160); // The origin is now at the top left corner of the door context.fillStyle = '#FFFF00'; context.fillRect(0, 0, 40, 80); context.fillStyle = '#FF0000'; context.fillRect(26, 40, 8, 8); context.restore(); // The origin is back at the top left corner of the canvas
Message Log
This is a lesson, not a challenge, the code runs automatically.

But change it! Play with it! Click "Run" to see your changes.

Run
Run and Focus Canvas
Reset
Canvas (your drawing will display here)

Challenge 1

Challenge1visual1
What your drawing should look like

Copy the drawBuilding() function that you wrote in Stage 1 Challenge 6 and edit it to draw one window in the top left corner office.

Inside the function, after moving the coordinate system to the building's top left corner and drawing the building's gray rectangle, move the coordinate system to (4, 4) inside the building. This is the top left corner of the top left office. Remember, the building has outer walls 4 pixels thick around it.

Draw a window positioned in the office at (4, 2). The window is a dark gray rectangle (color '#666666') that is 8 pixels wide and 10 pixels tall.

At this point, your function will draw a gray building with one window in the top left corner office.

Use the drawBuilding() function to draw a building that is 6 office units across and 12 floors tall sitting on the ground at (100, 300).

If you need help saving and restoring the drawing state and translating the origin of the coordinate system, visit the save() / restore() and translate() lessons.

Quick Reference: Functions fillRect() fillStyle save() / restore() translate()

Previous Challenge: View your code from Stage 1 Challenge 6 to use on this challenge.

Code Missing: You have not yet entered any code in to the previous challenge: Stage 1 Challenge 6
Stage 1 Challenge 6
Editor (write code below)
var canvas = document.getElementById('basic_cityscape_stage2_challenge1'); var context = canvas.getContext('2d'); function drawBuilding(leftX, groundY, units, floors) { // PUT YOUR CODE FOR DRAWING A BUILDING HERE // AFTER TRANSLATING TO THE BUILDING'S TOP LEFT CORNER // AND DRAWING THE BUILDING'S RECTANGLE... // MOVE THE COORDINATE SYSTEM TO (4, 4) INSIDE THE BUILDING // CHANGE THE FILLSTYLE TO THE COLOR #666666 // DRAW A RECTANGLE AT (4, 2) THAT IS 8 PIXELS WIDE AND 10 PIXELS TALL } // DRAW A BUILDING AT (100, 300) WITH 6 UNITS AND 12 FLOORS HERE drawGround(300); // Draws the ground at y = 300 function drawGround(y) { context.save(); context.fillStyle = 'Black'; context.fillRect(0, y, canvas.width, 2); context.restore(); }
Message Log
This is a lesson, not a challenge, the code runs automatically.

But change it! Play with it! Click "Run" to see your changes.

Run
Run and Focus Canvas
Reset
Canvas (your drawing will display here)
Challenge1
 

Draw a Row of Windows on the Top Floor of a Building

To draw a row of windows, we are going to use a for loop. A for loop allows us to perform a set of actions over and over again.

In this example, we draw a line of French flags.

We start by saving the original drawing state and moving the coordinate system to (140, 20). This is where we will draw the first French flag.

context.save(); // Save the original drawing state
context.translate(140, 20); // Move the coordinate system to the top left corner of the first flag

Then, we start the for loop by assigning var i = 0. Everytime through the loop, we increase i by one using the statement i = i + 1.

for (i = 0; i < 6; i = i + 1) {

  // code block

}

So, the first time through the loop, i == 0. The second time, i == 1. The third time, i == 2. The loop keeps running as long as i < 6. This means the loop will run six times and end after i == 5.

Inside of the loop, we draw a French flag at the origin of the current coordinate system, and then move the origin of the coordinate system 50 pixels down using the context.translate() method.

for (i = 0; i < 6; i = i + 1) { // Repeat the code in the code block six times
  drawFrenchFlag();
  context.translate(0, 50); // Move the coordinate system to the top left corner of the next flag
}

Finally, we restore the drawing state so the origin of the coordinate system is back at the top left corner of the canvas.

context.restore(); // Restore the original drawing state

What would happen if, instead of using context.translate(30, 50) inside of the for loop, we used context.translate(0, 50)?

To learn more about for loops and translating the origin of the coordinate system, visit the For Loops and translate() lessons.

Quick Reference: For Loops save() / restore() translate()

Editor (write code below)
var canvas = document.getElementById('basic_cityscape_stage2_example2'); var context = canvas.getContext('2d'); context.save(); // Save the original drawing state context.translate(140, 20); // Move the coordinate system to the top left corner of the first flag for (i = 0; i < 6; i = i + 1) { // Repeat the code in the code block six times drawFrenchFlag(); context.translate(0, 50); // Move the coordinate system to the top left corner of the next flag } context.restore(); // Restore the original drawing state function drawFrenchFlag() { context.save(); context.fillStyle = '#0055A4'; context.fillRect(0, 0, 20, 40); context.fillStyle = '#FFFFFF'; context.fillRect(20, 0, 20, 40); context.fillStyle = '#EF4135'; context.fillRect(40, 0, 20, 40); context.restore(); }
Message Log
This is a lesson, not a challenge, the code runs automatically.

But change it! Play with it! Click "Run" to see your changes.

Run
Run and Focus Canvas
Reset
Canvas (your drawing will display here)

Challenge 2

Challenge2visual1
What your drawing should look like

Replace the code for drawing one window in your drawBuilding() function from Challenge 1 with a for loop to draw windows in all of the offices on a building's top floor.

Remember, the building has 4-pixel thick walls all around it, so the first top floor office is at (4, 4), and each office is 16 pixels wide.

Draw the same dark gray window (color '#666666') that is 8 pixels wide and 10 pixels tall at (4, 2) in each office on the top floor.

The number of times you run through the for loop will depend on the number of office units per floor in the building. Inside of the function, that number is stored in a parameter. We can use variables and parameters when setting up a for loop.

Use the drawBuilding() function to draw a building that is 12 office units across and 8 floors tall sitting on the ground at (50, 300).

If you need help using for loops and translating the origin of the coordinate system, visit the For Loops and translate() lessons.

Quick Reference: Variables Functions For Loops save() / restore() translate()

Previous Challenge: View your code from Stage 2 Challenge 1 to use on this challenge.

Code Missing: You have not yet entered any code in to the previous challenge: Stage 2 Challenge 1
Stage 2 Challenge 1
Editor (write code below)
var canvas = document.getElementById('basic_cityscape_stage2_challenge2'); var context = canvas.getContext('2d'); function drawBuilding(leftX, groundY, units, floors) { // PUT YOUR CODE FOR DRAWING A BUILDING HERE // AFTER MOVING THE COORDINATE SYSTEM TO (4, 4) INSIDE THE BUILDING // AND CHANGING THE FILLSTYLE TO #666666... // SET UP A FOR LOOP TO DRAW A WINDOW IN EACH OFFICE UNIT ON THE TOP FLOOR // INSIDE EACH OFFICE, DRAW A RECTANGLE AT (4, 2) THAT IS 8 PIXELS WIDE AND 10 PIXELS TALL // THEN MOVE THE COORDINATE SYSTEM TO THE NEXT OFFICE } // DRAW A BUILDING AT (50, 300) WITH 12 UNITS AND 8 FLOORS HERE drawGround(300); // Draws the ground at y = 300 function drawGround(y) { context.save(); context.fillStyle = 'Black'; context.fillRect(0, y, canvas.width, 2); context.restore(); }
Message Log
This is a lesson, not a challenge, the code runs automatically.

But change it! Play with it! Click "Run" to see your changes.

Run
Run and Focus Canvas
Reset
Canvas (your drawing will display here)
Challenge2
 

Draw Windows for Each Floor of a Building

To draw a building with rows of windows on each floor, we are going to use two for loops: one inside the other. The inner for loop draws a window in each office of a single floor. The outer for loop draws one floor at a time.

In this example, we draw a grid of French flags with 5 French flags in a row and four rows.

The inner for loop is using the variable i as its counter. Each time through the loop, it draws a French flag at the origin of the coordinate system and then moves the origin over to the right by 80 pixels using the context.translate() method.

for (i = 0; i < 5; i = i + 1) {
  drawFrenchFlag();
  context.translate(80, 0); // Move the coordinate system to the right to draw the next flag
}

The outer for loop is using the variable j as its counter. While the name of the variable isn't important, the two for loops can't use the same variable since one for loop is inside the other. Each time through the outer loop, it saves the drawing state, draws a row of flags using the inner loop, restores the drawing state back to its state at the start of the row, and then moves the coordinate system down 80 pixels using the context.translate() method.

for (j = 0; j < 4; j = j + 1) {
  context.save(); // Save the drawing state at the start of the row

  for (i = 0; i < 5; i = i + 1) {
    drawFrenchFlag();
    context.translate(80, 0); // Move the coordinate system to the right to draw the next flag
  }

  context.restore(); // Restore the drawing state back to the start of the row
  context.translate(0, 80); // Move the coordinate system down to draw the next row
}

Saving and restoring the drawing state in the outer for loop is important. Think about what would happen if we didn't do that. The next row of flags would start after the last flag in the previous row, not below the first flag. Each row would keep getting shifted over to the right. You can see what would happen, by commenting out the context.save() and context.restore() method calls in the outer for loop.

Quick Reference: For Loops save() / restore() translate()

Editor (write code below)
var canvas = document.getElementById('basic_cityscape_stage2_example3'); var context = canvas.getContext('2d'); context.save(); // Save the original drawing state context.translate(10, 10); // Move the coordinate system to the first flag for (j = 0; j < 4; j = j + 1) { context.save(); // Saves the drawing state at the start of the row for (i = 0; i < 5; i = i + 1) { drawFrenchFlag(); context.translate(80, 0); // Move the coordinate system to the right to draw the next flag } context.restore(); // Restore the drawing state back to the start of the row context.translate(0, 80); // Move the coordinate system down to draw the next row } context.restore(); // Restores the original drawing state function drawFrenchFlag() { context.save(); context.fillStyle = '#0055A4'; context.fillRect(0, 0, 20, 40); context.fillStyle = '#FFFFFF'; context.fillRect(20, 0, 20, 40); context.fillStyle = '#EF4135'; context.fillRect(40, 0, 20, 40); context.restore(); }
Message Log
This is a lesson, not a challenge, the code runs automatically.

But change it! Play with it! Click "Run" to see your changes.

Run
Run and Focus Canvas
Reset
Canvas (your drawing will display here)

Challenge 3

Challenge3visual1
What your drawing should look like

Replace the code for drawing a top row of windows in your drawBuilding() function from Challenge 2 with two for loops (one inside the other) to draw windows in all of the offices on all of the floors of a building.

Remember, the building has 4-pixel thick walls all around it, so the first top floor office is at (4, 4), and each office is 16 pixels wide and 16 pixels tall.

Draw the same dark gray window (color '#666666') that is 8 pixels wide and 10 pixels tall at (4, 2) in each office on every floor.

Use the drawBuilding() function to draw a building that is 7 office units across and 10 floors tall sitting on the ground at (80, 300).

If you need help using for loops and translating the origin of the coordinate system, visit the For Loops and translate() lessons.

Quick Reference: Variables Functions For Loops save() / restore() translate()

Previous Challenge: View your code from Stage 2 Challenge 2 to use on this challenge.

Code Missing: You have not yet entered any code in to the previous challenge: Stage 2 Challenge 2
Stage 2 Challenge 2
Editor (write code below)
var canvas = document.getElementById('basic_cityscape_stage2_challenge3'); var context = canvas.getContext('2d'); function drawBuilding(leftX, groundY, units, floors) { // PUT YOUR CODE FOR DRAWING A BUILDING HERE // AFTER MOVING THE COORDINATE SYSTEM TO (4, 4) INSIDE THE BUILDING // AND CHANGING THE FILLSTYLE TO #666666... // SET UP TWO FOR LOOPS TO DRAW A WINDOW IN EACH OFFICE UNIT // INSIDE EACH OFFICE, DRAW A RECTANGLE AT (4, 2) THAT IS 8 PIXELS WIDE AND 10 PIXELS TALL // MAKE SURE TO SAVE AND RESTORE THE DRAWING STATE BEFORE AND AFTER DRAWING A ROW OF WINDOWS } // DRAW A BUILDING AT (80, 300) WITH 7 UNITS AND 10 FLOORS HERE drawGround(300); // Draws the ground at y = 300 function drawGround(y) { context.save(); context.fillStyle = 'Black'; context.fillRect(0, y, canvas.width, 2); context.restore(); }
Message Log
This is a lesson, not a challenge, the code runs automatically.

But change it! Play with it! Click "Run" to see your changes.

Run
Run and Focus Canvas
Reset
Canvas (your drawing will display here)
Challenge3

Ready for the next lesson?

Next up, the "Stage: 3" lesson >