0 / 9
Chapter 6 of 6

Suburban Scene

Bring everything together — use functions, loops, and conditionals to build a complete neighbourhood from geometric components.

A Square

You have been drawing squares since Chapter 1. This final chapter brings every skill together to build a whole neighbourhood from reusable components. Let's start by recapping the foundation — a function that draws a square of any size.

Swift
func drawSquare(pen: Pen, size: Double) {
    for _ in 1...4 {
        pen.move(size)
        pen.turn(90)
    }
}

Notice that the pen is passed as a parameter. This is an important design pattern — it means drawSquare can work with any pen, in any colour, wherever the pen happens to be positioned. All the functions you write in this chapter should follow the same pattern.

You will also need to reposition the pen between components without drawing a line. Use penUp() and penDown() for this:

Swift
// Lift the pen, travel, lower it again
pen.penUp()
pen.move(50)   // no line is drawn
pen.penDown()
pen.move(50)   // drawing resumes

It is worth writing a small jump helper that you can use throughout this chapter:

Swift
func jump(pen: Pen, distance: Double) {
    pen.penUp()
    pen.move(distance)
    pen.penDown()
}

With these two building blocks — a drawing function and a movement helper — you are ready to assemble a neighbourhood.

📷 06-01-a-square.png Image coming soon
IM1 Curriculum Connection
StandardConnection
Geometry — Properties of ShapesA square has four equal sides and four right angles (90° turns). Encoding these constraints in a function ensures every drawn square satisfies both conditions automatically.

A Triangle

Every building in our scene needs a roof. Write a function drawRoof(pen:base:) that draws an equilateral triangle — this gives a clean, symmetric roof shape.

Remember from Chapter 3: for an equilateral triangle (three equal sides, three 60° angles), the pen needs to turn by the exterior angle at each vertex. What is the exterior angle of an equilateral triangle?

Hint: The sum of exterior angles of any polygon is 360°. For an equilateral triangle, all three exterior angles are equal.
Swift
// Draw an equilateral triangle roof.
// The pen starts at the bottom-left corner, facing right.
// After the call, the pen is at the bottom-right corner, facing right.
// (The base itself is NOT drawn — it is the top edge of the wall below.)
func drawRoof(pen: Pen, base: Double) {
    // your code here
}
Solution
// An equilateral triangle has exterior angles of 360° ÷ 3 = 120°.
// The pen turns left 60° to head up the left slope, reaches the apex,
// then turns right 120° to head down the right slope.
func drawRoof(pen: Pen, base: Double) {
    pen.turn(-60)   // face upper-right at 60° above horizontal
    pen.move(base)   // go to apex
    pen.turn(120)    // turn to face lower-right at 60° below horizontal
    pen.move(base)   // go to bottom-right corner
    pen.turn(-60)   // back to original heading (facing right)
}

// Test it
let pen = Pen()
pen.color = .red
drawRoof(pen: pen, base: 100)

The three turns sum to −60° + 120° + (−60°) = 0°, which means the pen ends up facing the same direction it started — a useful property when composing shapes.

📷 06-02-a-triangle.png Image coming soon
IM1 Curriculum Connection
StandardConnection
Geometry — TrianglesExterior angle theorem: the exterior angle at each vertex equals the supplement of the interior angle. For an equilateral triangle, each interior angle = 60°, so each exterior angle = 120°. The total of all exterior angles = 360°.

A Cross

Churches need a cross! Write drawCross(pen:size:) that draws a plus-sign cross as two perpendicular strokes of equal length. Use penUp() and penDown() to lift the pen between strokes so no unwanted lines appear.

The cross should be symmetric: each arm extends size / 2 from the centre point.

Swift
// Draw a plus-sign cross.
// The pen starts at the BOTTOM of the vertical stroke, facing right.
// After the call it returns to the same position and heading.
func drawCross(pen: Pen, size: Double) {
    let half = size / 2
    // Step 1: draw the vertical stroke (up through the centre to the top)
    // Step 2: penUp back to the centre, then left to the start of the horizontal stroke
    // Step 3: draw the horizontal stroke
    // Step 4: return pen to original position and heading
    // your code here
}
Solution
func drawCross(pen: Pen, size: Double) {
    let half = size / 2

    // ── Vertical stroke ──────────────────────────────────
    pen.turn(-90)        // face upward
    pen.move(size)        // draw from bottom to top

    // Lift pen, go back to centre
    pen.penUp()
    pen.turn(180)         // face downward
    pen.move(half)        // back to centre, no drawing
    pen.turn(-90)        // face left
    pen.move(half)        // go to left end of horizontal arm
    pen.turn(180)         // face right
    pen.penDown()

    // ── Horizontal stroke ─────────────────────────────────
    pen.move(size)         // draw from left end to right end

    // ── Return to start position, facing right ────────────
    pen.penUp()
    pen.turn(180)          // face left
    pen.move(half)         // back to centre
    pen.turn(90)           // face down
    pen.move(half)         // back to bottom
    pen.turn(-90)         // face right (original heading)
    pen.penDown()
}

// Test it
let pen = Pen()
pen.color = .white
drawCross(pen: pen, size: 40)
📷 06-03-a-cross.png Image coming soon
IM1 Curriculum Connection
StandardConnection
Geometry — Perpendicular LinesThe two strokes of the cross are perpendicular (intersecting at 90°). Using turn(-90) and turn(90) encodes perpendicularity directly in the code.

A Church

Now compose your three building-block functions into a complete church. Write drawChurch(pen:size:) that draws:

  • A square body (colour: .gray)
  • A triangular roof sitting on top (colour: .red)
  • A small cross at the apex of the roof (colour: .white)

This is the key skill of Chapter 6: carefully repositioning the pen between each component using penUp() and penDown().

Tip: After drawSquare the pen is back at its starting position, facing right. To reach the top-left corner of the square, turn left and move size upward. After drawRoof the pen is at the top-right corner of the roof base, facing right.
Swift
func drawChurch(pen: Pen, size: Double) {
    // 1. Draw the body
    pen.color = .gray
    drawSquare(pen: pen, size: size)

    // 2. Move to top-left corner of body, then draw roof
    // your code here

    // 3. Move to the apex of the roof, then draw cross
    // your code here
}
Solution
func drawChurch(pen: Pen, size: Double) {
    // ── 1. Body ──────────────────────────────────────────
    pen.color = .gray
    drawSquare(pen: pen, size: size)
    // Pen is back at bottom-left, facing right.

    // ── 2. Roof ───────────────────────────────────────────
    // Move to top-left corner (turn up, walk size, turn right)
    pen.penUp()
    pen.turn(-90)         // face up
    pen.move(size)         // arrive at top-left of body
    pen.turn(90)           // face right
    pen.penDown()
    pen.color = .red
    drawRoof(pen: pen, base: size)
    // Pen is now at top-right of roof base, facing right.

    // ── 3. Cross at the apex ──────────────────────────────
    // Apex is at base/2 to the left and base*sin(60°) ≈ base*0.87 up.
    // Simpler: move left to centre of roof base, then up to apex height.
    pen.penUp()
    pen.turn(180)          // face left
    pen.move(size / 2)    // reach horizontal centre
    pen.turn(-90)         // face up
    pen.move(size * 0.87) // move up approx to apex (sin 60° ≈ 0.87)
    pen.turn(90)           // face right
    pen.penDown()
    pen.color = .white
    drawCross(pen: pen, size: size * 0.2)
}

// Test
let pen = Pen()
drawChurch(pen: pen, size: 80)
📷 06-04-a-church.png Image coming soon
IM1 Curriculum Connection
StandardConnection
Geometry — Modelling with ShapesA church is modelled as a composite of three primitive shapes. Each component occupies a specific region — identifying those regions and computing their relative positions requires spatial reasoning and coordinate thinking.

Row of Churches

Draw a row of five churches, each 10 points larger than the last. The first church should have size: 40, the last size: 80. Leave a gap of 20 points between each church.

Because drawChurch always leaves the pen at the bottom-left of the church, facing right, moving to the start of the next church is straightforward.

Swift
let pen = Pen()
let gap = 20.0

for i in 0...4 {
    let size = 40.0 + Double(i) * 10
    // draw one church then move pen right to the next start position
    // your code here
}
Solution
let pen = Pen()
let gap = 20.0

for i in 0...4 {
    let size = 40.0 + Double(i) * 10
    drawChurch(pen: pen, size: size)
    // After drawChurch the pen is at bottom-left facing right.
    // Jump right by the church's width plus the gap.
    jump(pen: pen, distance: size + gap)
}
📷 06-05-row-of-churches.png Image coming soon
IM1 Curriculum Connection
StandardConnection
Functions & SequencesThe church sizes form an arithmetic sequence: 40, 50, 60, 70, 80 — a common difference of 10. The loop variable i acts as the index; size = 40 + 10i is a linear function of i.

A Pretty House

A house is more detailed than a church. Write drawHouse(pen:size:) that includes:

  • A rectangular body (width = size, height = size * 0.75)
  • A triangular roof on top (call drawRoof)
  • A rectangular door centred at the bottom (width = size * 0.25, height = size * 0.4)
  • Two square windows, one on each side of the door (side = size * 0.18)

Write helper functions drawRect and drawWindow first — it will make drawHouse much cleaner.

Swift
// Draw a rectangle (width × height), pen at bottom-left facing right.
func drawRect(pen: Pen, width: Double, height: Double) {
    // your code here
}

func drawHouse(pen: Pen, size: Double) {
    let bodyH = size * 0.75
    let doorW = size * 0.25
    let doorH = size * 0.4
    let winSide = size * 0.18

    // 1. Body
    // 2. Roof
    // 3. Door (centred horizontally at the bottom)
    // 4. Left window and right window
    // your code here
}
Solution
func drawRect(pen: Pen, width: Double, height: Double) {
    for _ in 1...2 {
        pen.move(width)
        pen.turn(90)
        pen.move(height)
        pen.turn(90)
    }
}

func drawHouse(pen: Pen, size: Double) {
    let bodyH = size * 0.75
    let doorW = size * 0.25
    let doorH = size * 0.4
    let winSide = size * 0.18

    // ── 1. Body ───────────────────────────────────────────
    pen.color = .yellow
    drawRect(pen: pen, width: size, height: bodyH)
    // Pen back at bottom-left, facing right.

    // ── 2. Roof ────────────────────────────────────────────
    pen.penUp()
    pen.turn(-90)
    pen.move(bodyH)
    pen.turn(90)
    pen.penDown()
    pen.color = .red
    drawRoof(pen: pen, base: size)
    // Pen at top-right of roof base, facing right.

    // ── 3. Door (centred at bottom of body) ───────────────
    // Return to bottom-left, move right by (size - doorW) / 2
    pen.penUp()
    pen.turn(180)
    pen.move(size)          // back to top-left of roof base
    pen.turn(-90)           // face down
    pen.move(bodyH)          // down to bottom-left of body
    pen.turn(-90)           // face right
    pen.move((size - doorW) / 2)
    pen.penDown()
    pen.color = .brown
    drawRect(pen: pen, width: doorW, height: doorH)
    // Pen at left side of door, facing right.

    // ── 4. Left window ────────────────────────────────────
    // Back to bottom-left, then position for left window
    pen.penUp()
    pen.turn(180)
    pen.move((size - doorW) / 2)  // back to bottom-left
    pen.turn(-90)
    pen.move(bodyH * 0.45)       // up to window height
    pen.turn(90)
    pen.move(size * 0.08)         // inset from left edge
    pen.penDown()
    pen.color = .cyan
    drawSquare(pen: pen, size: winSide)

    // ── 5. Right window ───────────────────────────────────
    pen.penUp()
    pen.move(size * 0.08 + winSide + doorW + size * 0.08)
    pen.penDown()
    pen.color = .cyan
    drawSquare(pen: pen, size: winSide)
}

// Test
let pen = Pen()
drawHouse(pen: pen, size: 120)
📷 06-06-a-pretty-house.png Image coming soon
IM1 Curriculum Connection
StandardConnection
Geometry — Proportional ReasoningAll sub-components are proportional to size: the door is 25% as wide, windows are 18%, the body is 75% as tall. Scaling the house means scaling every component uniformly — a real-world application of ratio and proportion.

A Fence Post

A classic picket fence post is a rectangle with a pointed top. Write drawFencePost(pen:width:height:) that draws the rectangular body and then calls drawRoof to add the pointed top.

The total visual height of the post is height (body) + the equilateral triangle's height above it. The body should have colour .white.

Swift
func drawFencePost(pen: Pen, width: Double, height: Double) {
    // 1. Draw rectangular body (width × height)
    // 2. Move to top-left corner
    // 3. Call drawRoof(pen: pen, base: width)
    // 4. Return pen to original position at bottom-left, facing right
    // your code here
}
Solution
func drawFencePost(pen: Pen, width: Double, height: Double) {
    // ── 1. Rectangular body ──────────────────────────────
    pen.color = .white
    drawRect(pen: pen, width: width, height: height)
    // Pen at bottom-left, facing right.

    // ── 2. Pointed top ───────────────────────────────────
    pen.penUp()
    pen.turn(-90)
    pen.move(height)
    pen.turn(90)
    pen.penDown()
    drawRoof(pen: pen, base: width)
    // Pen at top-right of body, facing right.

    // ── 3. Return to bottom-left ─────────────────────────
    pen.penUp()
    pen.turn(90)
    pen.move(height)
    pen.turn(90)
    pen.move(width)
    pen.turn(180)
    pen.penDown()
}

// Test
let pen = Pen()
drawFencePost(pen: pen, width: 18, height: 60)
📷 06-07-a-fence-post.png Image coming soon
IM1 Curriculum Connection
StandardConnection
Geometry — Composite FiguresThe total area of the fence post = area of rectangle + area of equilateral triangle. If rectangle height = h and width = w, the triangle height = w·√3/2. Calculating the total area requires combining both formulas.

A Whole Fence

Use a loop to draw a fence of 8 posts. The posts should be 18 wide and 60 tall, with a 12-point gap between them.

After drawing all the posts, go back and draw a horizontal rail across the middle of the posts (at half the post height) to complete the fence.

Swift
let pen = Pen()
let postW: Double = 18
let postH: Double = 60
let gap: Double   = 12
let count         = 8

// 1. Draw all posts in a row
// your code here

// 2. Return to left, climb to half height, draw horizontal rail
// your code here
Solution
let pen = Pen()
let postW: Double = 18
let postH: Double = 60
let gap: Double   = 12
let count         = 8

// ── 1. Posts ─────────────────────────────────────────────
for _ in 1...count {
    drawFencePost(pen: pen, width: postW, height: postH)
    jump(pen: pen, distance: postW + gap)
}
// Pen is now to the right of the last post, facing right.

// ── 2. Rail ───────────────────────────────────────────────
let totalWidth = Double(count) * (postW + gap) - gap
pen.penUp()
pen.turn(180)                  // face left
pen.move(totalWidth + postW)    // all the way back to start
pen.turn(-90)                  // face up
pen.move(postH / 2)            // halfway up the post
pen.turn(90)                   // face right
pen.penDown()
pen.color = .white
pen.move(totalWidth)            // draw the rail
📷 06-08-a-whole-fence.png Image coming soon
IM1 Curriculum Connection
StandardConnection
Functions — Linear ExpressionsTotal fence width = n × (postW + gap) − gap. For 8 posts: 8 × 30 − 12 = 228. This is a linear function of n — the same expression that arises when summing fence sections or tiles in a pattern.

Broom Broom

Every neighbourhood needs cars! Write drawCar(pen:size:) that draws a simple side-on car using three parts:

  • A wide rectangular body (width = size, height = size * 0.3)
  • A narrower rectangular cabin on top (width = size * 0.55, height = size * 0.28, inset size * 0.18 from the left)
  • Two square wheels (side = size * 0.2) at the bottom, positioned size * 0.1 in from each end
Swift
func drawCar(pen: Pen, size: Double) {
    let bodyH   = size * 0.3
    let cabinW  = size * 0.55
    let cabinH  = size * 0.28
    let cabinIn = size * 0.18   // inset from left
    let wheelS  = size * 0.2
    let wheelIn = size * 0.1    // wheel inset from each end

    // 1. Draw body
    // 2. Move to cabin start, draw cabin
    // 3. Draw left wheel, then right wheel
    // your code here
}
Solution
func drawCar(pen: Pen, size: Double) {
    let bodyH   = size * 0.3
    let cabinW  = size * 0.55
    let cabinH  = size * 0.28
    let cabinIn = size * 0.18
    let wheelS  = size * 0.2
    let wheelIn = size * 0.1

    // ── 1. Body ──────────────────────────────────────────
    pen.color = .blue
    drawRect(pen: pen, width: size, height: bodyH)
    // Pen at bottom-left, facing right.

    // ── 2. Cabin ─────────────────────────────────────────
    pen.penUp()
    pen.turn(-90)
    pen.move(bodyH)          // up to top of body
    pen.turn(90)
    pen.move(cabinIn)         // inset from left
    pen.penDown()
    pen.color = .cyan
    drawRect(pen: pen, width: cabinW, height: cabinH)
    // Pen at cabin bottom-left (= start of cabin), facing right.

    // ── 3. Wheels ─────────────────────────────────────────
    // Return to body bottom-left first
    pen.penUp()
    pen.turn(180)
    pen.move(cabinIn)         // back to body left edge
    pen.turn(-90)
    pen.move(bodyH)           // down to bottom of body
    pen.turn(-90)            // face right

    // Left wheel (inset from left end)
    pen.move(wheelIn)
    pen.penDown()
    pen.color = .black
    drawSquare(pen: pen, size: wheelS)
    // Pen at left-wheel bottom-left, facing right.

    // Right wheel
    pen.penUp()
    pen.move(size - 2 * wheelIn - wheelS)  // gap between wheels
    pen.penDown()
    drawSquare(pen: pen, size: wheelS)
}

// Test
let pen = Pen()
drawCar(pen: pen, size: 150)
📷 06-09-broom-broom.png Image coming soon
IM1 Curriculum Connection
StandardConnection
Geometry — Scale and ProportionEvery dimension of the car is a fixed proportion of size. Changing size scales all components simultaneously — a direct application of scaling figures and maintaining ratios. The gap between wheels = size − 2·wheelIn − wheelS is an algebraic expression derived from fitting shapes within a bounded space.

Neighbourhood Scene

The grand finale! Use every function you have written to draw a complete suburban neighbourhood. Your scene must include at least:

  • Two houses of different sizes
  • One church
  • A fence in front of one house
  • One car on the street

Plan your scene on paper first — sketch where each building goes, estimate the sizes, and work out how far apart they should be. Then translate that plan into code, one component at a time.

Tip: Use the jump helper to reposition the pen between buildings. Remember that after each draw… call the pen returns to the bottom-left of that component, facing right — so jumping right by the component's width gets you to the next gap.
Swift
let pen = Pen()

// ── Plan your scene here ─────────────────────────────────

// House 1
// ...

// Gap between buildings
jump(pen: pen, distance: 30)

// Church
// ...

// Fence, car, House 2 ...
Solution
// One possible arrangement — yours will be different!
let pen = Pen()

// ── House 1 ──────────────────────────────────────────────
drawHouse(pen: pen, size: 100)
jump(pen: pen, distance: 25)

// ── Church ────────────────────────────────────────────────
drawChurch(pen: pen, size: 80)
jump(pen: pen, distance: 25)

// ── House 2 (bigger) ─────────────────────────────────────
drawHouse(pen: pen, size: 130)

// ── Fence in front of House 2 ────────────────────────────
// Move pen below and to the left of House 2
pen.penUp()
pen.turn(180)
pen.move(130)           // back to left edge of House 2
pen.turn(-90)           // face down
pen.move(30)            // drop below house
pen.turn(90)            // face right
pen.penDown()

for _ in 1...7 {
    drawFencePost(pen: pen, width: 16, height: 40)
    jump(pen: pen, distance: 16 + 6)
}

// ── Car on the street ─────────────────────────────────────
pen.penUp()
pen.turn(180)
pen.move(7 * (16 + 6) + 130)  // far left past all buildings
pen.turn(-90)
pen.move(60)                     // down to street level
pen.turn(90)
pen.penDown()
drawCar(pen: pen, size: 140)
📷 06-10-neighbourhood-scene.png Image coming soon
IM1 Curriculum Connection
StandardConnection
Geometry — Mathematical ModellingDesigning a scene requires applying geometric methods to solve a real design problem: fitting buildings of different sizes within a bounded canvas, using spatial reasoning to plan positions, and using algebraic expressions to compute coordinates and gaps.