去做题

第六单元:绘制与函数 · 用代码画出图形

Unit 6: Drawing with Functions — Painting Shapes with Code
计算机科学 5 · 安息云峰 Cloudrip Mountain · 花的几何学

6.1 什么是代码绘图

What Is Drawing with Code

前面的单元里,你把编程用来指挥军队、寻路、找目标。这一单元画风一转——你要拿到花之戒指,把英雄当成一支魔法画笔,用代码在大地上画出圆、方,乃至任何你想要的图案!这是一次把数学、循环、函数融会贯通的创意实践,也让你真切体会到"编程不只是解题,更是创造"。

本单元主线:你将学会用 toggleFlowers 控制"落笔/提笔"、用 setFlowerColor 给花上色;用 while 循环配合三角函数 Math.cos / Math.sin,让角度不断递增、沿圆周描点画圆;用顺序移动到四个角画正方形;并理解这些绘图逻辑如何被封装成带"中心坐标"和"大小"参数的通用函数 drawCircle、drawSquare。

6.1.1 英雄就是一支画笔(The Hero Is a Pen)

6.1.1.1 走到哪,花开到哪 Flowers bloom where the hero walks

代码绘图的核心心智模型很简单:英雄走过的路径,就是画出来的线条。当"绘花"开启时,英雄每移动一步,脚下就绽放花朵,连成线;当"绘花"关闭时,英雄照样移动,但不留痕迹——相当于提起画笔在纸上空移。想画什么图形,本质就是算出图形轮廓上一连串的点,让英雄依次走过它们。

🖊️ 就像"连点成画"
小时候玩的连线游戏:按 1、2、3…的顺序把点连起来,一幅画就出现了。
代码绘图也一样:先算出图形轮廓上的点,再让英雄按顺序走过它们。圆是密密麻麻的一圈点,正方形是四个角。落笔时走,提笔时移,图形就成形了。

6.1.2 花之戒指的三件工具(Three Tools of the Flower Ring)

6.1.2.1 移动、开关、上色 Move, toggle, color

🚶 moveXY(x, y)

让英雄走到坐标 (x, y)。落笔状态下这一段路就会留下花线,是"画线"的动作。

🔘 toggleFlowers(开/关)

toggleFlowers(True) 落笔(走了留痕),toggleFlowers(False) 提笔(走了不留痕)。

🎨 setFlowerColor(颜色)

设置花的颜色,如 "red"、"purple"、"random",之后画的花都用这个色。

6.2 落笔与提笔

Pen Down and Pen Up

6.2.1 toggleFlowers 开关(The Drawing Toggle)

6.2.1.1 先移动到起点,再落笔 Move to the start, then start drawing

toggleFlowers(True/False) 就是画笔的"落/提"开关。这个开关有一个非常重要的用法:在移动到图形起点的途中,要先关掉绘花,免得从旧位置到起点的这段"空移"也画上多余的线;到了起点再打开绘花,正式开始画。观察 drawCircle 的开头就是这个套路。

# 提笔 → 走到起点 → 落笔,避免画出多余连线
hero.toggleFlowers(False)   # 提笔(空移不留痕)
hero.moveXY(startX, startY)   # 移动到图形起点
hero.toggleFlowers(True)    # 落笔,开始正式作画
提笔空移到起点,再落笔作画 toggleFlowers(False) 提笔 ✋ moveXY(起点) 空移,不留痕 toggleFlowers(True) 落笔 ✍️

6.2.2 setFlowerColor 上色(Setting the Color)

6.2.2.1 换一种颜色,画下一个图形 Change color before the next shape

hero.setFlowerColor("颜色") 决定接下来画的花是什么颜色,可选 "pink"、"red"、"blue"、"purple"、"yellow"、"white",还有随机的 "random"。设色是"持续生效"的——设一次,之后所有花都是这个色,直到你再次改它。所以画多个不同色的图形时,在每个图形开画前先设好颜色。

hero.setFlowerColor("red")      # 先设红色
drawCircle(redX.x, redX.y, 10)  # 这个圆是红的

hero.setFlowerColor("purple")   # 改成紫色
drawSquare(whiteX.x, whiteX.y, 10)  # 这个方是紫的

6.3 用 while 画圆

Drawing a Circle with while

6.3.1 圆是无数个点(A Circle Is Many Points)

6.3.1.1 用一圈密集的点近似圆 Approximating a circle with points

计算机画不出"真正光滑"的圆,它的办法是:沿着圆周取足够多的点,用短直线段把它们连起来——点够密,看起来就是圆滑的圆。这正是第五单元"极坐标转直角坐标"的直接应用:圆上任意一点,都由"圆心 + 半径 × (cos角度, sin角度)"确定。让角度从 0 转到一整圈(2π),描过的点就连成一个圆。

6.3.2 角度递增取点(Incrementing the Angle)

6.3.2.1 while 让角度一圈转到底 The while loop sweeps the angle

drawCircle 的核心是一个 while 循环:让角度 angle 从 0 开始,每次循环加一点点(+= 0.2),直到转满一整圈(angle <= Math.PI * 2)。每一步都用 cos、sin 算出当前角度对应的圆周点坐标,再 moveXY 过去。角度像时针一样扫过一圈,英雄的足迹就描出了整个圆。

def drawCircle(x, y, size):
    angle = 0
    hero.toggleFlowers(False)   # 先提笔移到起点
    while angle <= Math.PI * 2:   # 转满一整圈(360°)
        newX = x + (size * Math.cos(angle))   # 当前角度的圆周点
        newY = y + (size * Math.sin(angle))
        hero.moveXY(newX, newY)            # 走过去,连成弧
        hero.toggleFlowers(True)          # 到第一个点后落笔
        angle += 0.2                        # 角度递增一步
angle 从 0 每次 +0.2,扫满 2π 描出整圆 圆心(x,y) angle=0

6.3.3 步长决定精细度(Step Size Controls Smoothness)

6.3.3.1 加得越小,圆越圆 Smaller steps, smoother curve

那句 angle += 0.2 里的 0.2 是步长,它决定了圆的精细度。步长越小,一圈取的点越多,线段越短,圆看起来越光滑;步长越大,点越稀疏,圆会变成带棱角的多边形。这是"用离散的点近似连续曲线"时永恒的权衡:更精细 vs 更省步数。

步长小 → 光滑
angle += 0.1
# 一圈约 63 个点
# 圆润顺滑
步长大 → 有棱角
angle += 1.0
# 一圈仅约 7 个点
# 更像多边形
别忘了让循环能结束:while 里每轮一定要有 angle += 0.2 这样的递增,让角度不断逼近 2π 的终止条件。如果忘了递增,angle 永远是 0、条件永远成立,就会陷入死循环,圆画不出来、程序也卡住。

6.4 顺序移动画方

Drawing a Square by Sequential Moves

6.4.1 四角坐标(The Four Corners)

6.4.1.1 用"中心 ± 半边长"算出四个角 Corners from center ± half-side

正方形不用循环——它只有四个角,直接顺序走过去连线即可。诀窍是先算半边长 cornerOffset = size / 2,再以中心 (x, y) 为基准,用"加减半边长"的四种组合得到四个角:左下 (x-半, y-半)、右下 (x+半, y-半)、右上 (x+半, y+半)、左上 (x-半, y+半)。

中心 (x,y) ± 半边长 = 四个角 (x,y) (x-半,y-半) (x+半,y-半) (x+半,y+半) (x-半,y+半)

6.4.2 依次连线成方(Connecting the Corners)

6.4.2.1 提笔到起点,落笔绕一圈回到起点 Pen up to start, then trace the loop

画法很直接:先提笔移到第一个角(左下),落笔,再依次 moveXY 到其余三个角,最后回到起点闭合。四条边就画好了。注意最后一定要走回第一个角,否则正方形会缺一条边、闭不上口。

def drawSquare(x, y, size):
    hero.toggleFlowers(False)
    cornerOffset = size / 2            # 半边长
    hero.moveXY(x - cornerOffset, y - cornerOffset)  # 提笔到左下角
    hero.toggleFlowers(True)             # 落笔
    hero.moveXY(x + cornerOffset, y - cornerOffset)  # → 右下
    hero.moveXY(x + cornerOffset, y + cornerOffset)  # → 右上
    hero.moveXY(x - cornerOffset, y + cornerOffset)  # → 左上
    hero.moveXY(x - cornerOffset, y - cornerOffset)  # → 回左下,闭合
圆用循环,方用顺序——为什么不同?圆有几十个点、且每个点靠公式算出,规律统一,最适合用 while 循环反复计算;正方形只有四个角、各不相同,写死四行 moveXY 反而更清楚直接。点多且有规律用循环,点少且各异用顺序——选对工具,代码才简洁。

6.5 参数化的绘图函数

Parameterized Drawing Functions

6.5.1 中心与大小(Center and Size)

6.5.1.1 一个函数,画到任意位置、任意大小 One function, any place and size

drawCircle(x, y, size) 和 drawSquare(x, y, size) 都带三个参数:x、y 是图形中心,size 是图形大小(圆的半径 / 方的边长)。这就是参数的威力——同一个函数,换不同的参数,就能在任意位置画出任意大小的图形,不必为每个图形重写一遍绘图逻辑。

drawCircle(28, 36, 10)   # 在(28,36)画半径10的圆
drawCircle(50, 50, 5)    # 同一函数,换个位置画个小圆
drawSquare(44, 36, 20)   # 画个大方块
🏗️ 绘图函数像"印章"
drawCircle 就像一枚可缩放的圆形印章:告诉它盖在哪(x, y)、盖多大(size),它就在那儿印出一个圆。写好一次,随处可用、想画几个画几个——这正是函数"封装 + 复用"思想在绘图中的体现。

6.6 实战关卡精讲

Level Strategy & Analysis

这一节聚焦关卡的策略、思路与算法设计,只展示用于说明的关键片段,完整通关程序请你自己动手完成。

关卡 · 花的几何学

🌷 花的几何学 —— 画圆、画方,随心创作

扣哒世界 花的几何学关卡 - koudashijie.com
图2:花的几何学——手握花之戒指,在红 X 处画圆、白 X 处画方,尽情用花朵作画

策略:先按要求画两图,再自由发挥

本关是一个轻松的创意练习关,没有敌人。通关要求很明确:在 redX 坐标处画一个大小为 10 的圆,在 whiteX 坐标处画一个大小为 10 的方。关卡已经把 drawCircle 和 drawSquare 两个函数写好给你,你只需正确地设色并调用它们;完成后还可以随意画任何你喜欢的图案。

思路拆解:设色 + 调用

算法思路非常简单,就四步。第一,选颜色:hero.setFlowerColor("red")。第二,画圆:把 redX 的 x、y 和大小 10 传给 drawCircle(redX.x, redX.y, 10)。第三,换颜色:hero.setFlowerColor("purple")。第四,画方:drawSquare(whiteX.x, whiteX.y, 10)。关键点是记得在每个图形前先设好颜色,以及正确地从坐标对象里取出 .x、.y。

# 关键片段:设色 → 调用绘图函数
redX = {"x": 28, "y": 36}
whiteX = {"x": 44, "y": 36}

hero.setFlowerColor("red")          # 选红色
drawCircle(redX.x, redX.y, 10)       # 红 X 处画圆

hero.setFlowerColor("purple")       # 换紫色
drawSquare(whiteX.x, whiteX.y, 10)   # 白 X 处画方
进阶创作思路:通关后不妨试试组合玩法:用 while 循环+取余在一排画好几个不同颜色的圆;把 drawCircle 的 size 由小到大循环,画出同心圆;或在一个大圆的圆周上,每隔一段角度画一个小圆,拼出一朵"花"。关卡也建议你去 Mountain Flower Grove 继续练习绘画技巧。
把图形当积木:复杂图案都是简单图形的组合。想画一张笑脸?就是"一个大圆(脸)+ 两个小圆(眼)+ 一段圆弧(嘴)"。学会用 drawCircle、drawSquare 这些"积木"和参数控制位置大小,你就能搭出无穷无尽的图案——这就是用函数组合去构建复杂事物的编程思维。

6.7 常见错误

Common Mistakes

6.7.1 绘图与循环的典型坑(Pitfalls)

6.7.1.1 画图相关的常见问题 Typical issues with drawing & loops

扣哒世界 绘图常见错误速查 - koudashijie.com
图3:忘递增导致死循环、忘落笔画不出线、忘换色、坐标取值错,是本单元最常见的失误
易错点清单:
  • while 里忘了 angle += 0.2——角度不变、条件永真,陷入死循环,圆永远画不完。
  • 忘记落笔 toggleFlowers(True)——英雄走了一圈却不留花,什么都没画出来。
  • 移到起点时忘提笔——从旧位置到起点的空移也画上了线,图形多出一条"尾巴"。
  • 画多个图形忘换色——所有图形都是同一颜色,或忘了在每个图形前重新 setFlowerColor。
  • 正方形忘了回到起点——少走最后一条边,方形缺口、闭不上。
  • 坐标对象取值错——应写 redX.x、redX.y,别直接把整个 redX 当数字传进去。
  • 把半径当直径用——圆的 size 是半径,方的 size 是边长,含义不同别混淆。
  • 角度终止条件写错——要转满一圈是 <= Math.PI * 2,写成 Math.PI 只画半圆。
  • 步长过大——angle 每次加太多,圆会变成带棱角的多边形,不够圆润。

本单元小结 ✨

恭喜你解锁了花之戒指,把编程变成了画笔!这一单元你把前面学的循环、三角函数、函数封装全部串了起来:用 toggleFlowers 控制落笔/提笔、用 setFlowerColor 上色;理解了圆是"沿圆周描点近似"的,用 while 让角度递增、配合 cos/sin 画圆,并明白步长决定圆的精细度;用顺序移动到四个角画正方形;还看清了 drawCircle、drawSquare 如何用"中心 + 大小"参数变成可随处复用的通用工具。在"花的几何学"里,你设色、调用,画出了自己的第一幅代码画作。来回顾一下核心概念:

核心概念关键点对应关卡
toggleFlowers 开关True 落笔留痕,False 提笔空移花的几何学
setFlowerColor 上色持续生效,画前先设色花的几何学
while + 三角函数角度递增,cos/sin 描圆周点花的几何学
步长与精细度步长越小,圆越光滑花的几何学
顺序移动画方中心±半边长得四角,依次连线花的几何学
回到起点闭合正方形最后要走回第一个角花的几何学
参数化绘图中心 x,y + 大小 size,随处复用花的几何学
图形组合创作简单图形拼出复杂图案花的几何学

懂得用循环与三角函数把冰冷的坐标化作满地繁花的英雄,才是云峰之上真正兼具理性与浪漫的程序员。
继续向雪山之巅挺进吧,勇敢的程序员!🏔️🌷🎨

扣哒世界 编程知识 · 计算机科学 5(安息云峰):第六单元

扣哒世界教研团队