Skip to main content
0 / 18 done
Chapter II of VI

Shape Properties with Variables

Store side lengths, colours, and coordinates in var and let, then reuse them to build diamonds, crosses, snowflakes, and rhombuses. Along the way you'll investigate symmetry, parallel sides, and other shape properties that define what makes a polygon a polygon.

Adding Pens

Discover how to use the addShape(pen:) call and what happens when you create multiple independent pens on the same canvas.

Rendering Shapes

In Chapter I you drew with a single pen. This chapter introduces the key idea of multiple independent pens — each one with its own colour, thickness, and position. To display a pen's path on the canvas you must call addShape(pen:) — think of it as "print this pen's drawing to the screen".

Swift
var p1 = Pen()
p1.addLine(distance: 100)
addShape(pen: p1)   // render p1's path

var p2 = Pen()
p2.turn(degrees: 90)
p2.addLine(distance: 100)
addShape(pen: p2)   // render p2's path
Why var p = Pen() and not let p = Pen()? Because a pen changes as you call methods on it — moving, turning, setting colour. Swift uses var for things that can change and let for things that stay the same. A pen has to be var; a fixed side length like let side = 100 should be let. You'll see this distinction again in the Variables tutorial (Section 10).
Each pen is an independent draft. When you create p2, it starts fresh at the origin (0, 0) facing right — not where p1 left off. Each pen tracks its own position and direction. This is what makes multiple pens powerful: you can draw unrelated shapes without them interfering with each other's state.
Nothing appears until you call addShape. The pen records its path quietly in memory until you hand it to addShape(pen:), which renders it onto the canvas. You can build up a complex path with many calls and then render it all at once, or render incrementally. Forgetting addShape is the single most common reason a student's code runs without errors but draws nothing!
Two Lines
Curriculum Connections
ConceptConnection
Coordinate geometryEach pen starts at the origin — independent paths share the same coordinate system
Line segmentsEach addLine call produces a directed segment with defined start and end points

Multiple Pens

Draw two separate squares side-by-side using two independent pens — one starting at the origin and the other offset to the right.

Your Task

Create two pens. Draw a 100-unit square with p1 starting at the origin. Then use p2 — start it at (150, 0) using move(distance:) or by setting its position — and draw a second 100-unit square.

Swift
var p1 = Pen()
// Draw first square with p1

var p2 = Pen()
// Position p2 and draw second square

addShape(pen: p1)
addShape(pen: p2)
Two Lines
Translation in geometry. Offsetting p2 by 150 units to the right is a rigid translation — every point of p2's square is shifted by the same vector (+150, 0) from p1's square. The two squares are congruent: same shape, same size, just at different positions. Translation is one of three rigid motions (along with rotation and reflection) that preserve distances and angles. You'll meet all three throughout this chapter.
Two pens, two origins. Both pens start at the canvas origin (0, 0) by default. Calling p2.move(distance: 150) slides p2 forward by 150 units without drawing — so when you then call addLine, the line starts at (150, 0). Think of move as lifting the pen off the paper, sliding it, then putting it back down ready to draw.
Swift
var p1 = Pen()
p1.addLine(distance: 100)
p1.turn(degrees: 90)
p1.addLine(distance: 100)
p1.turn(degrees: 90)
p1.addLine(distance: 100)
p1.turn(degrees: 90)
p1.addLine(distance: 100)
addShape(pen: p1)

var p2 = Pen()
p2.move(distance: 150)   // move right without drawing
p2.addLine(distance: 100)
p2.turn(degrees: 90)
p2.addLine(distance: 100)
p2.turn(degrees: 90)
p2.addLine(distance: 100)
p2.turn(degrees: 90)
p2.addLine(distance: 100)
addShape(pen: p2)
Curriculum Connections
ConceptConnection
TranslationsOffsetting p2 by 150 units is a translation — a rigid motion that shifts every point the same distance
CongruenceBoth squares are congruent — same shape, same size, different position

Turning Both Ways

Explore negative degree values — turning right (clockwise) instead of left — and understand how this unlocks a wider range of shapes.

Positive vs Negative Turns

A positive turn angle rotates the pen anti-clockwise (left). A negative angle rotates it clockwise (right). This matches the standard mathematical convention you'll see in trigonometry and coordinate geometry.

Swift
p.turn(degrees:  90)   // rotate LEFT  90° (anti-clockwise)
p.turn(degrees: -90)   // rotate RIGHT 90° (clockwise)
Why "positive = anti-clockwise"? It feels backwards at first (clocks go clockwise, so shouldn't positive mean clockwise?). But mathematicians chose the opposite convention centuries ago, for a good reason: in standard (x, y) coordinate geometry, rotating the positive x-axis toward the positive y-axis goes counter-clockwise. Keeping that direction "positive" means angle formulas work cleanly without sign flips. Every maths textbook and programming language uses this convention — get comfortable with it now.
If you walked around a shape. When you drew a square in Chapter I by turning 90° four times, you were walking counter-clockwise around the square — because positive turns go left (anti-clockwise). That's why the square "grew upward and to the right" from your starting corner. Try drawing one with turn(degrees: -90) instead, and you'll see the square grow downward and to the right: you're walking clockwise now, tracing the same shape in the mirror direction.
Two Lines
Curriculum Connections
ConceptConnection
Directed anglesPositive = anti-clockwise, negative = clockwise — this is the convention used in trigonometry and coordinate geometry
RotationsTurning the pen is equivalent to a rotation of the direction vector around the pen's current position

Two Squares

Draw a large square (200 units) and a small square (100 units) that shares the same bottom-left corner — creating a nested effect.

Your Task

Use a single pen to draw both squares without lifting it (use move to reposition if needed), or use two separate pens.

Swift
var p = Pen()
// Draw large square (200 units)
// Draw small square (100 units)
addShape(pen: p)
Two squares
Similarity vs congruence. Two shapes are congruent if they have the same size and shape — one is a translated/rotated/reflected copy of the other. Two shapes are similar if they have the same shape but possibly different sizes — one is a scaled copy of the other. The two squares in this exercise are similar (same shape, different sizes) but not congruent (different sizes). The ratio of their side lengths, 200 : 100, is called the scale factor. The ratio of their areas is the scale factor squared: (200/100)² = 4 — the large square has four times the area of the small one.
Why the pen ends up back at the starting corner. After drawing each square, the pen's heading has rotated by 4 × 90° = 360° — a full circle. Combined with 4 equal-length sides, the pen returns to exactly its starting position. This is why you can "draw another square" by continuing from the same pen without repositioning — it's already back home.
Swift
var p = Pen()

// Large square: 200 units
p.addLine(distance: 200)
p.turn(degrees: 90)
p.addLine(distance: 200)
p.turn(degrees: 90)
p.addLine(distance: 200)
p.turn(degrees: 90)
p.addLine(distance: 200)
p.turn(degrees: 90)

// Small square: 100 units (starts from the same corner)
p.addLine(distance: 100)
p.turn(degrees: 90)
p.addLine(distance: 100)
p.turn(degrees: 90)
p.addLine(distance: 100)
p.turn(degrees: 90)
p.addLine(distance: 100)

addShape(pen: p)
Curriculum Connections
ConceptConnection
SimilarityThe two squares are similar — same shape but different sizes, with a scale factor of 2:1
PerimeterPerimeter of large square = 4 × 200 = 800; small = 4 × 100 = 400

Thicker Lines

Use the lineWidth property to draw a bold triangle — and explore how stroke width affects a shape's visual weight.

Your Task

Draw an equilateral triangle (3 sides, turning 120° at each corner) with a lineWidth of 6.

Swift
var p = Pen()
p.lineWidth = 6
// Draw equilateral triangle
addShape(pen: p)
Thicker lines
Why 120° for a triangle? Same reason as Chapter I: if you walked around the triangle, your total turning would be 360° (a full rotation), split equally across 3 corners — 360° ÷ 3 = 120° per turn. You'll see this "exterior angle = 360° ÷ n" formula again and again for every regular polygon.
Line width vs shape width. lineWidth controls the stroke thickness — how fat the drawn line is — not the size of the shape. A triangle with side 150 and lineWidth = 6 is the same size as one with lineWidth = 1, just drawn with a thicker pen. Think of it like choosing a marker vs a fine-tipped pen: same drawing, different visual weight.
Swift
var p = Pen()
p.lineWidth = 6
p.addLine(distance: 150)
p.turn(degrees: 120)
p.addLine(distance: 150)
p.turn(degrees: 120)
p.addLine(distance: 150)
addShape(pen: p)
Curriculum Connections
ConceptConnection
Equilateral triangleAll three sides equal; all interior angles = 60°; exterior turn = 120°
Exterior angle theorem360° ÷ 3 sides = 120° per turn; verifies sum of exterior angles = 360°

Coloured Triangle

Draw a triangle where each side is a different colour — green, red, and blue — by changing penColor between sides.

Your Task

Use three pens (or change the colour on one pen before each side) to produce a triangle with sides in three different colours.

Swift
var p = Pen()
p.penColor = .green
p.addLine(distance: 150)
p.turn(degrees: 120)
// Continue for red and blue sides…
addShape(pen: p)
Coloured triangle
Colour is a pen state. When you set p.penColor = .red, it's not drawing anything yet — it's just updating the pen's "current colour" setting. The next addLine you call will use that colour. This is the same pattern as position and heading: the pen keeps track of its current state, and each drawing command uses whatever state is active when you call it. Change the state between lines to get multi-coloured shapes.
Swift
var p = Pen()
p.penColor = .green
p.addLine(distance: 150)
p.turn(degrees: 120)
p.penColor = .red
p.addLine(distance: 150)
p.turn(degrees: 120)
p.penColor = .blue
p.addLine(distance: 150)
addShape(pen: p)
Curriculum Connections
ConceptConnection
Triangle classificationEquilateral — three sides equal, three angles equal (60° each)
Angle sumInterior angles of any triangle sum to 180°

Diamond

Draw a diamond (rhombus) with equal sides but non-right angles — using turn angles less than and greater than 90°.

Your Task

Draw a diamond with side length 150 and interior angles of 60° and 120°. Hint: the exterior turns will be 120° and 60° alternating.

Swift
var p = Pen()
// 4 equal sides, alternating turns
addShape(pen: p)
Diamond
Why two different turn angles? A rhombus has 4 equal sides but two different interior angles — 60° and 120° — that alternate around the shape. The exterior angle at each corner is 180° − interior, so the exterior angles alternate between 180° − 60° = 120° and 180° − 120° = 60°. Those are exactly the turn values in the solution below. Check the sanity: 120° + 60° + 120° + 60° = 360° — one full rotation, so the path closes. ✓
Adjacent angles are supplementary. In any parallelogram (including rhombuses), adjacent interior angles sum to 180°. So if one angle is 60°, the next one around must be 120°, and then back to 60°, and so on. This is a direct consequence of the parallel sides: imagine extending one side as a transversal — the co-interior angles with the next side must sum to 180°.
Swift
var p = Pen()
p.turn(degrees: 45)     // tilt the rhombus so it stands on a corner
p.addLine(distance: 150)
p.turn(degrees: 60)
p.addLine(distance: 150)
p.turn(degrees: 120)
p.addLine(distance: 150)
p.turn(degrees: 60)
p.addLine(distance: 150)
addShape(pen: p)
Curriculum Connections
ConceptConnection
Rhombus properties4 equal sides; opposite angles equal; diagonals bisect each other at right angles
Supplementary anglesAdjacent interior angles sum to 180°: 60° + 120° = 180°

Fill It In

Learn about the fillColor property to colour the interior of closed shapes.

Adding Fill

Setting fillColor on a pen before drawing will colour the enclosed area of a closed shape. The path must be closed (return to its starting point) for fill to work correctly.

Swift
var p = Pen()
p.fillColor = .yellow
p.penColor = .orange
p.addLine(distance: 100)
p.turn(degrees: 90)
p.addLine(distance: 100)
p.turn(degrees: 90)
p.addLine(distance: 100)
p.turn(degrees: 90)
p.addLine(distance: 100)
addShape(pen: p)
Fill vs stroke. fillColor paints the interior of a closed shape; penColor (also called "stroke") paints the boundary line. You can set both, neither, or just one — giving you a yellow square with an orange outline, a pure yellow fill with no border (set penColor to match fillColor), or just an outline with no fill. This is the same distinction every vector graphics tool uses.
Area vs perimeter. The fill occupies the area (100 × 100 = 10,000 square units for this square); the pen line traces the perimeter (4 × 100 = 400 units). These are the two fundamental measurements of a 2D shape. Perimeter is a length (measured in units); area is an area (measured in square units).
"Closed" matters. Fill only works if the path forms a closed loop — i.e., the last point connects back to the first. An unclosed path (three sides of a square with the fourth side missing) can't be filled because the rendering system doesn't know what counts as "inside". This is a property of plane topology: a closed curve divides the plane into exactly two regions (inside and outside), but an open curve doesn't.
Fill it in
Curriculum Connections
ConceptConnection
Area vs perimeterFill occupies the interior (area); the pen line defines the boundary (perimeter)
Closed figuresA shape must be topologically closed for fill to render — a key property in geometry

Coloured Squares

Draw three filled squares in a row — red, green, and blue — each 80 units wide with a 20-unit gap between them.

Your Task

Use three separate pens positioned at different x-coordinates. Give each pen a different fillColor.

Swift
// Three pens at x = 0, 100, 200
// Each draws an 80×80 filled square
Coloured squares
Three pens, three starting positions. Each Pen() begins at the origin — your job is to move it to the right starting position before drawing. For a row of squares at x = 0, 100, 200, the second pen needs move(distance: 100) and the third needs move(distance: 200). Note the 20-unit gap: square 1 ends at x = 80, square 2 starts at x = 100, giving a 20-unit space between them.
Notice the repetition. The three blocks are nearly identical except for the move distance and the fill colour. This is a perfect candidate for a loop (Chapter III!) and then a function (Chapter V!) — but with only variables and conditionals available so far, you have to write it out longhand. Feel the pain of the repetition; that pain is exactly what loops and functions solve.
Swift
var p1 = Pen()
p1.fillColor = .red
p1.addLine(distance: 80)
p1.turn(degrees: 90)
p1.addLine(distance: 80)
p1.turn(degrees: 90)
p1.addLine(distance: 80)
p1.turn(degrees: 90)
p1.addLine(distance: 80)
addShape(pen: p1)

var p2 = Pen()
p2.move(distance: 100)
p2.fillColor = .green
p2.addLine(distance: 80)
p2.turn(degrees: 90)
p2.addLine(distance: 80)
p2.turn(degrees: 90)
p2.addLine(distance: 80)
p2.turn(degrees: 90)
p2.addLine(distance: 80)
addShape(pen: p2)

var p3 = Pen()
p3.move(distance: 200)
p3.fillColor = .blue
p3.addLine(distance: 80)
p3.turn(degrees: 90)
p3.addLine(distance: 80)
p3.turn(degrees: 90)
p3.addLine(distance: 80)
p3.turn(degrees: 90)
p3.addLine(distance: 80)
addShape(pen: p3)
Curriculum Connections
ConceptConnection
AreaEach square: area = 80² = 6400 square units; total area = 3 × 6400 = 19,200
TranslationsEach square is a translated copy — same shape, size, and orientation at a different position

Variables

Store sizes in variables so you can change your shape's dimensions in one place and have the whole drawing update automatically.

Using Variables for Dimensions

A variable is a named container for a value. By storing a side length in a variable, changing it once updates the entire drawing — and the code becomes self-documenting because the name tells you what the number means.

Swift
let side = 120.0   // change this one value to resize everything
var p = Pen()
p.addLine(distance: side)
p.turn(degrees: 90)
p.addLine(distance: side)
p.turn(degrees: 90)
p.addLine(distance: side)
p.turn(degrees: 90)
p.addLine(distance: side)
addShape(pen: p)

Variables vs Constants

You may have noticed two different keywords in Swift: var and let. They both name a value — but behave differently.

KeywordNameCan it change?Use it for…
varVariableYesThings that change over time — like a Pen as it moves and turns
letConstantNoFixed values that should stay the same — like a side length you set once
Swift
let side = 100.0   // constant — the value is fixed
var p = Pen()      // variable — p changes as we call methods on it

Use let when a value should never change after you set it — Swift will warn you if you accidentally try to modify it. Use var when the value needs to change (or when Swift requires it, as with Pen).

Why use variables at all? Three reasons: (1) one place to change — update side once and every addLine(distance: side) uses the new value; (2) self-documenting codeside tells the reader what 120.0 means in context, whereas bare numbers don't; (3) expressions — you can compute derived values like let perimeter = 4 * side without manually recalculating.
Variables are like letters in algebra. In maths you write "let s be the side length" and then use s everywhere: perimeter = 4s, area = s². Swift's let side = 100.0 is literally the same idea — you're binding a name to a value and reasoning about it symbolically. This is the bridge from specific numbers (arithmetic) to general patterns (algebra).
Why default to let? When Swift sees let x = 10, it guarantees that x never changes. This guarantee means you (and anyone reading your code) can trust that every reference to x has the same value. Using var gives up that guarantee. Rule of thumb: start with let, and only switch to var if Swift tells you the variable needs to change. The fewer things that can change, the easier it is to reason about your program.
Types are inferred. When you write let side = 100.0, Swift figures out that side is a Double (because of the decimal point). If you wrote let side = 100 (no decimal), it would be an Int. This matters: addLine(distance:) expects a Double, so writing let side = 100 (Int) would be a type error. Writing 100.0 (Double) works directly. Small distinction, big difference!
Curriculum Connections
ConceptConnection
Variables & expressionsSide length s is a variable; perimeter = 4s and area = s² are expressions derived from it
GeneralisationWriting a formula in terms of a variable generalises from specific numbers to all cases

House

Combine a square base and a triangular roof to draw a simple house shape using two pens or careful repositioning.

Your Task

Draw a square (100 units) for the walls, then continue from the top-left corner to draw an isosceles triangle roof. The roof peak should be centred above the square.

Swift
var p = Pen()
// Square body + triangular roof
addShape(pen: p)
House
Composite figures. A house is a composite of two basic shapes: a square (the body) and a triangle (the roof) sitting on top. Breaking a complex shape into simple parts is a core problem-solving technique in geometry — you calculate area, perimeter, and properties of each part separately, then combine. Cities, circuit boards, molecules, and computer graphics are all built from composites of simpler parts.
The mysterious 57.74. This is the side length of an equilateral triangle with the same base as the square. An equilateral triangle with side s has a height of s × √3 / 2 ≈ 0.866 × s. If the square has side 100, an equilateral roof would have sides of 100 each — but the code uses 57.74, which is actually 100 / √3. This makes the roof an isosceles (not equilateral) triangle with a 120° apex and 30° base angles — a flatter roof. If you wanted an equilateral roof (60°-60°-60°, a steeper pitch), you'd use side length 100 with a 60° first turn.
Swift
var p = Pen()

// Square base (100 × 100)
p.addLine(distance: 100)
p.turn(degrees: 90)
p.addLine(distance: 100)
p.turn(degrees: 90)
p.addLine(distance: 100)
p.turn(degrees: 90)
p.addLine(distance: 100)
p.turn(degrees: 90)

// Move up to the top-left of the square (without drawing)
p.turn(degrees: 90)
p.move(distance: 100)
p.turn(degrees: -90)

// Roof: two sides at 30° from horizontal, meeting at a 120° apex
p.turn(degrees: 30)
p.addLine(distance: 57.74)
p.turn(degrees: 120)
p.addLine(distance: 57.74)
addShape(pen: p)
Curriculum Connections
ConceptConnection
Composite figuresTotal area = area of square + area of triangle — composites are decomposed into simpler shapes
Isosceles triangleRoof triangle has two equal sides and two equal base angles

Calculate

Use Swift arithmetic to compute side lengths and angles — let the code do the maths rather than calculating by hand.

Your Task

Draw a regular hexagon. Instead of calculating the turn angle yourself, store the number of sides in a constant and use 360.0 / 6 to let Swift compute it. Then write one addLine and one turn for each of the six sides.

Swift
let sides = 6
let turn = 360.0 / Double(sides)
var p = Pen()
// Draw each of the 6 sides using 'turn'
addShape(pen: p)
Calculate
Let the code do the maths. Instead of calculating 360 ÷ 6 = 60 in your head and hardcoding turn(degrees: 60), store the number of sides in a constant and compute turn = 360.0 / Double(sides). Now changing sides to 8 automatically updates the turn to 45°. This is a huge pedagogical leap: you're moving from "doing arithmetic with specific numbers" to "expressing a general formula algebraically" — exactly the step students take when moving from arithmetic to algebra in school maths.
Why 360.0 and not 360? Because Double(sides) produces a Double, Swift needs the other side of the division to also be a Double for the types to match. Writing 360.0 (with the decimal point) makes it a Double literal. If you wrote 360 (no decimal), Swift would treat it as an Int and the division wouldn't compile because Int / Double is a type mismatch. This is the strict-typing theme you'll see again in Chapter III's spiral exercise.
Swift
let sides = 6
let turn = 360.0 / Double(sides)
var p = Pen()
p.addLine(distance: 100)
p.turn(degrees: turn)
p.addLine(distance: 100)
p.turn(degrees: turn)
p.addLine(distance: 100)
p.turn(degrees: turn)
p.addLine(distance: 100)
p.turn(degrees: turn)
p.addLine(distance: 100)
p.turn(degrees: turn)
p.addLine(distance: 100)
p.turn(degrees: turn)
addShape(pen: p)
Curriculum Connections
ConceptConnection
Regular polygonsExterior angle = 360° ÷ n; interior angle = 180° − (360° ÷ n)
Algebraic reasoningExpressing the angle as 360/n generalises the formula for all regular polygons

Variable House

Refactor the House exercise using a variable for the house width, so you can resize the whole building by changing one number.

Your Task

Store the house width in a variable w. The roof height and angles should be derived from w using arithmetic, so the shape scales correctly.

Swift
let w = 120.0   // change this to resize the house
var p = Pen()
// Use w throughout
addShape(pen: p)
Proportional scaling. This is the whole point of variable-based design: everything that depends on the house's size should be derived from w, not hardcoded. Roof side length = w / sqrt(3) (or w for an equilateral roof). Window position = w * 0.3. Door width = w * 0.25. Change w and every piece scales together, producing a larger or smaller but geometrically similar house. This "proportional design" pattern is foundational for every flag, house, and vehicle you'll build in later chapters.
Angles don't scale. When you shrink a triangle, its lengths get smaller but its angles stay the same. That's what makes similar shapes similar. So your variable w multiplies every addLine(distance: ...), but the turn(degrees: ...) values stay constant (60°, 120°, 90° etc). This directly mirrors the mathematical definition of similar figures: lengths scale by a common factor, angles are preserved.
Variable house
Curriculum Connections
ConceptConnection
Proportional reasoningScaling all dimensions by the same factor produces a similar figure
Variables in geometryWriting perimeter/area formulas in terms of w models geometric relationships algebraically

Red Cross

Draw a filled red cross (plus sign) shape — a composite figure made from rectangles.

Your Task

A cross can be drawn as a 12-sided polygon. Start at the bottom-left of the lower arm and work around the perimeter, alternating between long (100) and short (40) sides, and alternating the direction of the turns (some are 90° left, some are 90° right).

Swift
var p = Pen()
p.fillColor = .red
p.penColor = .red
// 12 sides, 4 outer corners (90°), 8 inner corners (-90°)
addShape(pen: p)
Convex vs concave polygons. A convex polygon has all interior angles less than 180° — every triangle, square, regular polygon, etc. A concave (or "non-convex") polygon has at least one interior angle greater than 180° — the cross is the classic example. You can spot a concave polygon because the boundary "bends inward" at some corner. The sum of interior angles still equals (n − 2) × 180°, but individual angles can exceed 180° (reflex angles) as long as others compensate.
Sign of the turn. At the outer corners of the cross (the 4 points where the arms end), the pen turns 90° left — positive. At the inner corners (where an arm joins the centre square), it turns 90° right — negative. The total turning must still sum to 360° for the shape to close: 4 × 90° + 8 × (−90°) = 360° − 720° = −360°. Wait, that's −360°, not +360°! That's fine — −360° also brings the pen back to its original heading. A concave polygon traced clockwise ends with total turning of −360°; traced anti-clockwise, +360°. Either way, it closes.
Area via inclusion–exclusion. The cross is made of 3 overlapping rectangles: a horizontal bar (100 × 40), a vertical bar (40 × 100), and — wait, they overlap at a 40 × 40 square in the middle which would be counted twice. Total area = (100 × 40) + (100 × 40) − (40 × 40) = 4000 + 4000 − 1600 = 6400 square units. This "inclusion–exclusion" trick — add the parts, subtract the overlap — is one of the most useful formulas in combinatorics and set theory.
Red cross
Curriculum Connections
ConceptConnection
Area of composite figuresCross area = 3 rectangles overlapping; use inclusion-exclusion: 3(100×40) − 2(40×40)
Perimeter of irregular shapesCount each boundary segment: 4 long sides + 8 short sides

Simple Snowflake

Draw 6 arms radiating from the centre — each arm a straight line — to create a basic snowflake with 6-fold symmetry.

Your Task

Draw 6 arms of equal length, each rotated 60° from the last. After each arm, return to the centre by moving backwards (negative distance).

Swift
var p = Pen()

// Arm 1
p.addLine(distance: 100)
p.move(distance: -100)
p.turn(degrees: 60)

// Arm 2
p.addLine(distance: 100)
p.move(distance: -100)
p.turn(degrees: 60)

// Arm 3
p.addLine(distance: 100)
p.move(distance: -100)
p.turn(degrees: 60)

// Arm 4
p.addLine(distance: 100)
p.move(distance: -100)
p.turn(degrees: 60)

// Arm 5
p.addLine(distance: 100)
p.move(distance: -100)
p.turn(degrees: 60)

// Arm 6
p.addLine(distance: 100)
addShape(pen: p)
Negative distance = move backwards. Calling move(distance: -100) slides the pen backwards along its current heading by 100 units. It's equivalent to "turn 180°, move 100, turn 180° back again" but more concise. In this exercise, each arm draws 100 units forward, then uses move(-100) to return to the centre without leaving a visible line.
6-fold rotational symmetry. Dividing 360° by 6 gives 60° — the rotation needed to evenly space 6 arms around a full circle. The result has order-6 rotational symmetry: rotating the whole snowflake by 60° leaves it visually unchanged. Real snowflakes have this same symmetry because of how water molecules bond — the hydrogen bonding angles force 6-fold crystalline patterns. Nature is full of symmetry constraints like this.
The angles at the centre sum to 360°. Six arms, each offset by 60° from the previous one: 6 × 60° = 360°. The angles around any point in the plane always sum to one full rotation — this is a foundational axiom of Euclidean geometry. Try changing to 8 arms with 45° turns (8 × 45° = 360° ✓) or 5 arms with 72° (5 × 72° = 360° ✓) to see the rule generalise.
Simple snowflake
Curriculum Connections
ConceptConnection
Rotational symmetryA 6-arm snowflake has order-6 rotational symmetry — it maps onto itself after 60° rotations
Angles at a point6 arms × 60° = 360° — the angles around the centre sum to one full rotation

Parallel Lines

Draw four horizontal parallel lines — equally spaced — demonstrating lines that never meet.

Your Task

Draw 4 horizontal lines, each 200 units long, spaced 40 units apart vertically. Use 4 pens — start each one at a different height using turn and move.

Swift
// Line 1 — at y = 0
var p1 = Pen()
p1.addLine(distance: 200)
addShape(pen: p1)

// Line 2 — at y = 40
var p2 = Pen()
// Move p2 up 40 units, then face right again

// Line 3 and 4 — continue the pattern
Parallel lines
Parallel lines never meet. In Euclidean (flat) geometry, two lines are parallel if they're in the same plane and never intersect. Equivalently: they have the same direction (in coordinate terms, the same slope or gradient). Every pen in this solution has the same heading (facing right, slope = 0), so their lines are automatically parallel.
The parallel postulate. One of Euclid's five axioms says that through a point not on a line, there's exactly one parallel line. This sounds obvious, but in the 19th century mathematicians realised that dropping this axiom gives you non-Euclidean geometry — curved spaces like the surface of a sphere (where "parallel" lines can intersect) or a hyperbolic plane (where infinitely many parallels can exist). Einstein's general relativity uses non-Euclidean geometry to describe gravity.
Swift
// Line 1 — at y = 0
var p1 = Pen()
p1.addLine(distance: 200)
addShape(pen: p1)

// Line 2 — at y = 40
var p2 = Pen()
p2.turn(degrees: 90)
p2.move(distance: 40)
p2.turn(degrees: -90)
p2.addLine(distance: 200)
addShape(pen: p2)

// Line 3 — at y = 80
var p3 = Pen()
p3.turn(degrees: 90)
p3.move(distance: 80)
p3.turn(degrees: -90)
p3.addLine(distance: 200)
addShape(pen: p3)

// Line 4 — at y = 120
var p4 = Pen()
p4.turn(degrees: 90)
p4.move(distance: 120)
p4.turn(degrees: -90)
p4.addLine(distance: 200)
addShape(pen: p4)
Curriculum Connections
ConceptConnection
Parallel linesLines in the same direction that never intersect; have equal gradient (slope)
Coordinate geometryOffsetting the y-start corresponds to a vertical translation

Letter Z

Draw the letter Z using three line segments — a top horizontal, a diagonal, and a bottom horizontal — observing how the Z-angles relate to parallel lines.

Your Task

Draw: a horizontal line right 100, then turn down-left to draw the diagonal, then turn to draw the bottom horizontal. The diagonal creates Z-angles (alternate interior angles) with the horizontal lines.

Swift
var p = Pen()
p.addLine(distance: 100)    // top line
p.turn(degrees: -120)
p.addLine(distance: 115.5)  // diagonal
p.turn(degrees: -60)
p.addLine(distance: 100)    // bottom line
addShape(pen: p)
The Z angle theorem. When a diagonal line (called a transversal) crosses two parallel lines, it creates two "alternate interior angles" — the angles inside the Z-shape on opposite sides of the transversal. These angles are equal. In this exercise, the top horizontal meets the diagonal at 60° (interior); the bottom horizontal meets the diagonal at 60° on the other side. Mirror each other, equal angle. Some people remember this as "Z-angles are equal" because the shape literally forms a Z.
Why 115.5 for the diagonal? The diagonal has to connect the right end of the top line to the left end of the bottom line, covering a horizontal distance of 100 units (back the other way). If the diagonal makes a 60° angle with the horizontal, its length is 100 / cos(60°) = 100 / 0.5 = 200... wait, that's not right for this code. Let me rethink: with turn(degrees: -120) from facing right, the new heading is 60° below the horizontal (from facing right, clockwise 120° = facing lower-right at 60° below horizontal). The diagonal of length 115.5 ≈ 100 × 2/√3 covers the required horizontal and vertical distances to reach the bottom-left of where the bottom line starts.
Letter Z
Curriculum Connections
ConceptConnection
Alternate interior anglesThe Z-shape shows alternate angles — equal when lines are parallel — visually embedded in the letter
TransversalThe diagonal is a transversal crossing two parallel (horizontal) lines

Rhombus

Draw a filled rhombus with side length 120 and interior angles of 70° and 110°. Explore how opposite angles in a parallelogram are equal.

Your Task

A rhombus has 4 equal sides. Opposite angles are equal; adjacent angles are supplementary (sum to 180°). So if one angle is 70°, the next is 110°, then 70°, then 110°.

Swift
let interiorAngle = 70.0
let exterior = 180.0 - interiorAngle
var p = Pen()
p.fillColor = .purple
// Use interior/exterior angles to draw rhombus
addShape(pen: p)
Rhombus properties. A rhombus is defined by two things: (1) all four sides are equal in length, and (2) opposite sides are parallel. From these, a whole family of properties follow: opposite angles are equal, adjacent angles sum to 180°, diagonals bisect each other at 90°, diagonals bisect the vertex angles. Every square is a rhombus (special case where all angles are 90°); every rhombus is a parallelogram. You'll see these hierarchical relationships again in Chapter IV's quadrilateral classifier.
Storing derived values. The code computes let exterior = 180.0 - interiorAngle — an expression that derives the exterior angle from the interior angle. This is the core of algebraic modelling: you define one variable (the "input") and derive others from it. Change interiorAngle to 50° and exterior automatically becomes 130°. The relationship interior + exterior = 180° is hard-coded into the expression, never into individual numbers.
Curriculum Connections
ConceptConnection
Rhombus propertiesAll sides equal; opposite angles equal; diagonals bisect each other at 90°
Co-interior anglesAdjacent interior angles are supplementary — another transversal property applied to parallel sides

Parallelogram

Draw a parallelogram — two pairs of parallel sides — showing how it differs from a rhombus when sides have different lengths.

Your Task

Draw a parallelogram with long sides 160 units, short sides 80 units, and interior angles of 70° and 110°.

Swift
var p = Pen()
p.addLine(distance: 160)
p.turn(degrees: 110)
p.addLine(distance: 80)
p.turn(degrees: 70)
p.addLine(distance: 160)
p.turn(degrees: 110)
p.addLine(distance: 80)
addShape(pen: p)
Parallelogram vs rhombus. A parallelogram has opposite sides that are parallel and equal (like a rhombus), but unlike a rhombus, adjacent sides may have different lengths. A rhombus is the special case where all four sides are equal. So: every rhombus is a parallelogram, but not every parallelogram is a rhombus. In the exercise above, 160 ≠ 80, so this is a parallelogram but not a rhombus.
Parallelogram area formula. The area of a parallelogram is base × height, where "height" is the perpendicular distance between the two base sides — not the length of the slanted side. For this parallelogram with base 160 and a slanted side of 80 at 70° from horizontal, the perpendicular height is 80 × sin(70°) ≈ 75.2, so the area is about 160 × 75.2 ≈ 12,030 square units. Compare this to a rectangle with the same side lengths (area 160 × 80 = 12,800) — the parallelogram is slightly smaller because some of the "80" runs horizontally, not vertically.
Shear transformation. You can think of a parallelogram as a sheared rectangle — take a 160×80 rectangle and push the top edge horizontally while keeping the bottom edge still. The sides become slanted but the top and bottom stay parallel. This "shear" is one of the basic affine transformations in linear algebra and computer graphics.
Parallelogram
Curriculum Connections
ConceptConnection
Parallelogram propertiesOpposite sides parallel and equal; opposite angles equal; diagonals bisect each other
Area formulaArea = base × height = 160 × 80 × sin(70°) ≈ 12,030 sq units

Polygon Properties Explorer

Draw a selection of regular polygons — from a triangle to a decagon — and observe how the shape approaches a circle as the number of sides increases.

Your Task

Change sides to explore different regular polygons — try 3, 4, 5, 6, 8. Add or remove addLine/turn pairs so the number of lines matches the number of sides. Notice how the shape changes as sides increase.

Swift
// Change 'sides' and add/remove lines to match
let sides = 6
let turn = 360.0 / Double(sides)
var p = Pen()
p.addLine(distance: 80)
p.turn(degrees: turn)
p.addLine(distance: 80)
p.turn(degrees: turn)
p.addLine(distance: 80)
p.turn(degrees: turn)
p.addLine(distance: 80)
p.turn(degrees: turn)
p.addLine(distance: 80)
p.turn(degrees: turn)
p.addLine(distance: 80)
p.turn(degrees: turn)
addShape(pen: p)
A polygon is a polygon is a polygon… Notice that every pair of addLine/turn lines is identical. You're writing the same two statements over and over, just with a different number of repetitions depending on sides. This is crying out for a loop — and that's exactly what Chapter III introduces! In Chapter III you'll replace these 12 lines with a single for _ in 1...sides loop.
As n → ∞, the polygon becomes a circle. Try a 12-sided polygon (dodecagon), then 20 sides (icosagon), then 50 sides. You'll notice it becomes progressively harder to tell the shape apart from a perfect circle. This is a beautiful preview of the concept of a limit: as the number of sides grows without bound, the polygon approaches a circle, and in the limit, it becomes a circle. This is how ancient Greek mathematicians computed π — by inscribing polygons in circles and letting n get very large.
Interior angle formula. The interior angle of a regular n-gon is (n − 2) × 180° ÷ n. This comes from the fact that you can decompose any n-gon into n − 2 triangles (by drawing diagonals from one vertex), and each triangle contributes 180° to the total interior angle sum — so the total is (n − 2) × 180°, and each angle (in a regular polygon where they're all equal) is that divided by n.
Polygon explorer
Curriculum Connections
ConceptConnection
Regular polygon formulasInterior angle = (n − 2) × 180° ÷ n; exterior angle = 360° ÷ n
Limit concept (preview)As n → ∞, the polygon approaches a circle — an intuitive preview of limits

Angle Classification

Draw shapes whose interior angles represent each angle type — acute, right, obtuse, and reflex — then label them with your understanding.

Your Task

Draw at least four shapes: one with a reflex angle, one with all right angles (square), one with acute angles (equilateral triangle), and one with obtuse angles (regular hexagon). Try to draw them adjacent on the canvas.

Four angle categories. Every angle (measured between 0° and 360°) falls into exactly one of these categories:
  • Acute: less than 90° (e.g. 30°, 60°, 89°)
  • Right: exactly 90°
  • Obtuse: more than 90° but less than 180° (e.g. 100°, 135°, 170°)
  • Straight: exactly 180° (a straight line — the "angle" is zero bending)
  • Reflex: more than 180° but less than 360° (e.g. 200°, 270°, 355°)
You'll write code to classify these programmatically in Chapter IV's "Classify an Angle" exercise. For now, just draw shapes that contain each type and recognise them visually.
Regular polygon interior angles. Using the formula (n − 2) × 180° ÷ n:
  • Triangle (n = 3): 60° — acute
  • Square (n = 4): 90° — right
  • Pentagon (n = 5): 108° — obtuse
  • Hexagon (n = 6): 120° — obtuse
  • Octagon (n = 8): 135° — obtuse
  • Decagon (n = 10): 144° — obtuse
As n grows, the interior angles grow toward (but never reach) 180°. Only the triangle has acute interior angles; only the square has right angles; all regular polygons with 5 or more sides have obtuse angles.
Preview of Chapter IV. This is the end of Chapter II — you've now worked with variables, multiple pens, colour, fill, and composite shapes. Next up in Chapter III: loops will let you replace repetitive code with one concise statement, so drawing a 20-sided polygon doesn't require copying 40 lines of code. And Chapter IV will teach you how to write code that makes decisions — automatically classifying angles like the ones above, or picking different actions based on inputs. Each new chapter adds one more tool to your toolkit.

💡 Think About It

  • What is the interior angle of a regular pentagon? Is it acute, right, or obtuse?
  • Can a triangle have an obtuse angle? Can it have two?
  • What is the minimum number of sides a polygon needs to have a reflex angle?
Curriculum Connections
ConceptConnection
Angle classificationAcute <90°; right = 90°; obtuse 90°–180°; straight = 180°; reflex >180°
Interior angle formula(n − 2) × 180° ÷ n — determines whether a regular polygon has acute or obtuse angles