Stage 2

Overview

In the second stage of the Cityscape Challenge, we will 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.

Overview
 

Lesson: Draw a Window in an Office in a Building

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

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 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 context 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.

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

Editor (write code below)
var canvas = document.getElementById('granular_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

Challenge6visual1
Challenge1example
Challenge 1 Sample Solution

Copy the drawBuilding() function that you wrote in Stage 1 Challenge 8.

Next, we will edit the drawBuilding() function to draw a window in the top left corner office.

Inside the function, after moving the coordinate system to the top left corner of the building and drawing a gray rectangle for the building, move the coordinate system to (4, 4) in the building. This is the top left corner of the top left office. Remember, the building has 4 pixels of padding 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.

Go ahead and draw a building that is 6 office units across and 12 floors tall sitting on the ground at (100, 300). The corners of the building should cover the black x's and the window should cover the red x.

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

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

Code Missing: You have not yet entered any code in to the previous challenge: Stage 1 Challenge 8
Stage 1 Challenge 8
Editor (write code below)
var canvas = document.getElementById('granular_basic_cityscape_stage2_challenge1'); var context = canvas.getContext('2d'); function drawBuilding(leftX, groundY, units, floors) { // YOUR CODE FOR DRAWING A BUILDING HERE } // YOUR CODE FOR DRAWING THE BUILDING AT (100, 300) HERE
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

A Solution: Here's the code I wrote to complete this challenge. View One Possible Solution

var canvas = document.getElementById('flappy_square_stage2_challenge0'); var context = canvas.getContext('2d'); function drawBuilding(leftX, groundY, units, floors) { context.save(); var width = (units * 16) + (4 * 2); var height = (floors * 16) + (4 * 2); context.translate(leftX, groundY - height); context.fillStyle = '#999999'; context.fillRect(0, 0, width, height); context.save(); context.fillStyle = '#666666'; context.translate(4, 4); // Building padding offset context.translate(4, 4); // Window offset within office context.fillRect(0, 0, 8, 10); context.restore(); context.restore(); } drawBuilding(100, 300, 6, 12);
 

Lesson: 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 row of French flags.

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

We then start our loop by assigning i = 0. Everytime the loop runs, we will add 1 to i. 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 once 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 80 pixels to the right using context.translate(80, 0).

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

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

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

Editor (write code below)
var canvas = document.getElementById('granular_basic_cityscape_stage2_example2'); var context = canvas.getContext('2d'); 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(); } context.save(); context.translate(20, 120); for (i = 0; i < 6; i = i + 1) { drawFrenchFlag(); context.translate(80, 0); } 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

Challenge6visual1
Challenge2example
Challenge 2 Sample Solution

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

Remember, the building has 4 pixels of padding 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.

Hint: The number of times you will 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 variable (or parameter). We can use variables when setting up a for loop.

Then 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). The corners of the building should cover the black x's and the windows should cover the red x's.

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

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('granular_basic_cityscape_stage2_challenge2'); var context = canvas.getContext('2d'); function drawBuilding(leftX, groundY, units, floors) { // YOUR CODE FOR DRAWING A BUILDING WITH TOP FLOOR WINDOWS HERE } // YOUR CODE FOR DRAWING THE BUILDING AT (50, 300) HERE
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

A Solution: Here's the code I wrote to complete this challenge. View One Possible Solution

var canvas = document.getElementById('flappy_square_stage2_challenge1'); var context = canvas.getContext('2d'); function drawBuilding(leftX, groundY, units, floors) { context.save(); var width = (units * 16) + (4 * 2); var height = (floors * 16) + (4 * 2); context.translate(leftX, groundY - height); context.fillStyle = '#999999'; context.fillRect(0, 0, width, height); context.save(); context.fillStyle = '#666666'; context.translate(4, 4); // Wall padding context.translate(4, 4); // Window offset within office. for (var i=0; i < units; ++i) { context.fillRect(0, 0, 8, 10); context.translate(16, 0); } context.restore(); context.restore(); } drawBuilding(50, 300, 12, 8);
 

Lesson: 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. Just like in the previous example, there are 6 French flags in a row. But this time, there are four rows.

The inner for loop is using the variable i as its counter: the loop starts at i = 0 and ends when i = 5. 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 context.translate(80, 0).

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. The outer for loop starts at j = 0 and ends when j = 3. Each time through the loop, it saves the context, draws a row of flags using the inner loop, restores the context back to where it was before drawing the row of flags, and then moves the origin of the coordinate system down 90 pixels using context.translate(0, 90).

Saving and restoring the context 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. (Comment out the context.save() and context.restore() in the outer for loop if you want to see what would happen.)

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

Editor (write code below)
var canvas = document.getElementById('granular_basic_cityscape_stage2_example3'); var context = canvas.getContext('2d'); 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(); } context.save(); // saves the context at the start of the drawing context.translate(30, 10); for (j = 0; j < 4; j = j + 1) { context.save(); // saves the context at the start of the row for (i = 0; i < 6; i = i + 1) { drawFrenchFlag(); context.translate(80, 0); } context.restore(); // restores the context back to the start of the row context.translate(0, 90); } context.restore(); // restores the context back to the start of the drawing
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

Challenge3example
Challenge 3 Sample Solution

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) that will draw windows in all of the offices on all of the floors of a building.

Remember, the building has 4 pixels of padding 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.

Then 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). The corners of the building should cover the black x's and the windows should cover the red x's.

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

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('granular_basic_cityscape_stage2_challenge3'); var context = canvas.getContext('2d'); function drawBuilding(leftX, groundY, units, floors) { // YOUR CODE FOR DRAWING A BUILDING WITH WINDOWS HERE } // YOUR CODE FOR DRAWING THE BUILDING AT (80, 300) HERE
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

A Solution: Here's the code I wrote to complete this challenge. View One Possible Solution

var canvas = document.getElementById('flappy_square_stage2_challenge2'); var context = canvas.getContext('2d'); function drawBuilding(leftX, groundY, units, floors) { context.save(); var width = (units * 16) + (4 * 2); var height = (floors * 16) + (4 * 2); context.translate(leftX, groundY - height); context.fillStyle = '#999999'; context.fillRect(0, 0, width, height); context.save(); context.translate(4, 4); context.fillStyle = '#666666'; for (var j=0; j < floors; ++j) { context.save(); context.translate(4, 4); for (var i=0; i < units; ++i) { context.fillRect(0, 0, 8, 10); context.translate(16, 0); } context.restore(); context.translate(0, 16); } context.restore(); context.restore(); } drawBuilding(80, 300, 7, 10);

Ready for the next lesson?

Next up, the "Stage: 3" lesson >