Quiz: Facial Anatomy & Layout Design
Test your understanding of how a face decomposes into independently parameterized parts, and how one draw_face() function assembles them all.
1. What does facial symmetry let a face designer avoid doing?
- Drawing eyebrows and a mouth in the same program
- Using the quadrant fill code for eyelids
- Working out the right eye's position as a separate problem from the left eye's
- Storing face parameters in a dictionary
Show Answer
The correct answer is C. Because most facial features mirror around a vertical centerline, you compute one side's offset from center and flip its sign for the other. One calculation, reflected, gives a balanced face for half the thinking. Symmetry is a default a neutral face follows, not a law — a wink or a single raised eyebrow deliberately breaks it.
Concept Tested: Facial Symmetry
2. What is a face state data structure?
- A dictionary bundling every current face parameter into one object representing a complete expression
- The frame buffer holding the currently displayed face image
- A list of every drawing method
draw_face()will call, in order - A record of which expressions the robot has shown most recently
Show Answer
The correct answer is A. The face state dictionary is a complete snapshot of one expression, frozen as data — keys like eye_size, eyebrow_angle, and mouth_curvature paired with plain numbers. Change one value and you have described a different emotion without touching any drawing code. The dictionary is the expression; the drawing functions just turn it into pixels.
Concept Tested: Face State Data Structure
3. Using mirror_x(x, center_x), which returns center_x + (center_x - x), what is the right eye's x-position if the left eye sits at x = 44 and the centerline is at x = 64?
- 20
- 108
- 64
- 84
Show Answer
The correct answer is D. The left eye sits 20 pixels left of center (64 − 44 = 20), so the right eye sits 20 pixels right of center: 64 + 20 = 84. Option A is the distance from center rather than a coordinate, and option B would place the eye 44 pixels past center, doubling the offset by mistake.
Concept Tested: Facial Symmetry
4. How does a lowered eyelid get drawn without introducing any new drawing method?
- By calling
fb.scroll()to shift the eye downward behind the face outline - By redrawing the eye's top half in the background color using a quadrant mask
- By reducing the eye's
yradiusuntil only a thin sliver remains - By blitting a pre-drawn eyelid sprite over the eye
Show Answer
The correct answer is B. Eyelid representation reuses Chapter 7's quadrant fill code directly: fb.ellipse(x, y, eye_size, eye_size, background_color, True, TOP_HALF) covers the upper two quadrants of an already-drawn eye. One extra call after draw_eyes() produces a half-closed, sleepy look — no new shape, no new method.
Concept Tested: Eyelid Representation
5. A face built for the 128x64 OLED looks tiny and clustered in one corner of the 240x240 round display. What is the right fix?
- Redraw every feature at twice its pixel size by hand
- Move the origin to the center of the round display
- Increase the frame buffer's bit depth to match the color display
- Compute each parameter as a fraction of the display's width and height instead of a fixed pixel count
Show Answer
The correct answer is D. Feature scaling for screen size means writing eye_size = height // 8 rather than eye_size = 8. Calling default_face_state(128, 64) then produces OLED-sized values and default_face_state(240, 240) produces proportionally larger ones from the identical function body — draw_face() itself never changes, only the width and height passed in.
Concept Tested: Feature Scaling For Screen Size
6. Why is an eyebrow_angle parameter better than hardcoding the exact point array for an angry eyebrow?
- A hardcoded array uses more memory than a computed one
fb.poly()rejects point arrays that were typed in by hand- A hardcoded array works once, but any variation or mirrored version must be recomputed from scratch by hand
- A hardcoded array cannot be filled, only outlined
Show Answer
The correct answer is C. Typing in "the angry eyebrow" you want right now works exactly once. The moment you need a slightly less furrowed version, or the mirrored shape for a wink, you would redo the coordinates by hand. One parameter you can change with a single number is worth the extra few lines of setup every time.
Concept Tested: Eyebrow Angle Parameter
See: Shaping a Mood: Eyebrow Shape and Eyebrow Angle Parameter
7. In what order does draw_face() draw a face's features?
- Smallest details first, then progressively larger shapes, then the background
- Background, face outline, eyebrows, eyes, then pupils and mouth
- Whatever order the keys appear in the face state dictionary
- Eyes first, since they carry the most emotional weight
Show Answer
The correct answer is B. Largest and farthest-back shapes come first; smallest and most detailed shapes come last. This is not arbitrary — it is the same draw call order optimization Chapter 6 taught, now applied to a full face, so no feature is buried under something drawn after it and overdraw stays low.
Concept Tested: Draw Face Function
8. What does a mouth_curvature of 0 produce, given that curvature becomes the mouth ellipse's yradius?
- A nearly flat line, reading as a calm, neutral mouth
- A deep frown, since 0 counts as negative curvature
- An error, because an ellipse radius cannot be zero
- A perfectly round mouth, since both radii would then match
Show Answer
The correct answer is A. With yradius at 0, the bottom-half curve flattens into something very close to the straight fb.hline() mouth from Chapter 6 — without needing a separate function or method. That single special case is why one draw_mouth() covers smiles, frowns, and neutral expressions alike.
Concept Tested: Mouth Curvature Parameter
See: Curving a Mouth
9. What practical benefit does feature independence give a face-drawing program?
- It guarantees every feature stays within the display's bounding box
- It reduces the total memory the frame buffer needs
- One feature can be redesigned, tested, or replaced without touching any other feature's code
- It removes the need to call
fb.show()after drawing
Show Answer
The correct answer is C. Because draw_eyebrow() knows nothing about how draw_mouth() computes its curve, each can be debugged on its own and swapped out freely. It also means new features — blush marks in Chapter 11, blinking in Chapter 12 — arrive as one new function rather than edits scattered across every existing one.
Concept Tested: Feature Independence
See: A Design Principle, Not Just a Mechanic: Feature Independence
10. Why are a nose and cheeks treated as optional on most faces in this book?
- They cannot be drawn with
fb.ellipse()orfb.poly() - They consume too much frame buffer memory to redraw each frame
- They break facial symmetry and so complicate the mirroring math
- Research shows eyes, eyebrows, and a mouth carry almost all of a face's readable emotion
Show Answer
The correct answer is D. A nose is usually a short vline() tick and cheeks a couple of pixels — enough to make a face look more face-shaped, but contributing little to which emotion a viewer reads. Chapters 10 and 11 explain the research behind that finding, which is why the emotional heavy lifting stays with the eyes, eyebrows, and mouth.
Concept Tested: Nose Representation