递归分形实战:基于 p5.js 的递归圆环绘制(Coding Challenge 77 深度解析)
教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载本文基于仓库中 Coding Challenge #77 Recursion 的挑战文档 及其配套源码展开它以 p5.js 在 HTML5 Canvas 上利用递归Recursion绘制分形图案是 Coding Train 系列中讲解「函数调用自身 基线条件」这一核心思想的经典一集。读完本文你将理解递归分形的两个基本要素能够逐行剖析并复现drawCircle递归圆环程序掌握递归深度与调用树的分析方法并顺藤摸瓜看到同一仓库中分形树等更多递归实战案例。挑战档案文档与配套资源该挑战文档的元信息front matter记录了以下关键内容字段值说明video_number77Coding Train 系列第 77 期挑战date2017-10-05发布与存档日期repositoryCC_077_Recursion对应源码目录名web_editorpseBrWaMm在线 p5.js 编辑器版本编号video_idjPsZwrV9ld0配套视频 ID仓库内仅作记录文档正文只有一句核心陈述In this coding challenge, I explore the concept of recursion to create fractal patterns in JavaScript HTML5 canvas with the p5.js library.—— 即用递归在 HTML5 Canvas 上创建分形图案。与之配套的源码就存放在 CodingChallenges/CC_077_Recursion 目录下包含p5.js 版本P5/sketch.js 与 P5/index.htmlProcessing 移植版本Processing/CC_077_Recursion/CC_077_Recursion.pde。文档还关联了「分形树递归」一期的视频内部链接指向 014-fractaltree以及 Benoit Mandelbrot 的著作The Fractal Geometry of Nature作为延伸阅读这提示了本挑战的理论背景递归是生成自相似self-similar分形结构最自然的编程手段。递归的本质函数调用自己在深入代码之前先厘清递归的两个核心要素基线条件base case决定递归何时停止的条件。缺少它函数会无限调用自身最终导致调用栈溢出stack overflow。递归调用recursive call函数在自身内部调用自己通常传入一个「规模更小」的参数使问题逐步逼近基线条件。本挑战的drawCircle正是这两要素的教科书级示范每层递归把圆的直径缩小为上一层的 1/4直到直径不超过 2 像素时停止。自相似的分形结构由此诞生——任意一层圆圈的排列方式都与整体一致。核心实现递归圆环 drawCircle 逐行解析完整的 p5.js 源码位于 P5/sketch.js// Daniel Shiffman // http://codingtra.in // http://patreon.com/codingtrain // Recursion // Edited Video: https://www.youtube.com/watch?vjPsZwrV9ld0 function setup() { createCanvas(600, 600); } function draw() { background(0); stroke(255); noFill(); drawCircle(300, 200, 600); noLoop(); } function drawCircle(x, y, d) { ellipse(x, y, d); if (d 2) { let newD d * 0.25; drawCircle(x newD, y, newD); drawCircle(x - newD, y, newD); //drawCircle(x, y d * 0.5, d * 0.5); } }主流程setup 与 drawcreateCanvas(600, 600)创建 600 × 600 的画布draw()中先background(0)将背景刷成黑色再设置stroke(255)白色描边与noFill()空心圆随后以drawCircle(300, 200, 600)启动递归——圆心在 (300, 200)直径 600关键一行noLoop()递归在draw内一次性完成绘制画面是静态的因此调用noLoop()让draw只执行一次避免每帧重复绘制。这是这类「一帧出图」程序的常用手法与后续章节中交互式分形树每帧重绘形成鲜明对比。递归函数drawCircle(x, y, d)函数签名drawCircle(x, y, d)的三个参数含义如下参数含义本次调用示例x圆心横坐标300y圆心纵坐标200d圆的直径600函数体分三步先绘制当前圆ellipse(x, y, d)画出一个空心圆判断基线条件if (d 2)—— 只有当直径大于 2 像素时才继续递归否则该分支是树叶节点递归调用计算newD d * 0.25然后向左右两侧各派生一个直径为newD的子圆圆心分别偏移到x newD与x - newD。第 3 步的几何意义值得展开子圆直径是父圆的 1/4圆心相对父圆心水平偏移一个子圆直径的长度。因为偏移量newD小于父圆半径d / 2所以所有后代圆都嵌套在父圆内部层层收缩形成「圆内有圆」的递归分形。源码中最后一行//drawCircle(x, y d * 0.5, d * 0.5);是被注释掉的第三个分支——若取消注释会在正下方再派生一个直径为 1/2 的子圆图案会变得更加稠密详见下文「变体实验」。运行与观察配套的 P5/index.html 通过 CDN 引入 p5.js 运行时仓库记录的是 1.4.1 版本再加载sketch.js!DOCTYPE html html head meta charsetUTF-8 meta http-equivX-UA-Compatible contentIEedge meta nameviewport contentwidthdevice-width, initial-scale1 titlerecursion/title script typetext/javascript srchttps://cdn.jsdelivr.net/npm/p51.4.1/lib/p5.min.js/script script typetext/javascript srchttps://cdn.jsdelivr.net/npm/p51.4.1/lib/addons/p5.sound.min.js/script script typetext/javascript srcsketch.js/script /head body /body /html运行方式很简单直接在浏览器中打开该index.html或在本地起一个静态文件服务器例如在P5/目录下执行python3 -m http.server后访问对应端口。页面加载后即一次性渲染出白色空心圆构成的递归分形。注意本挑战没有使用 p5.sound 相关功能HTML 中加载 sound 附加库属于模板遗留可以按需移除。递归深度与调用树分析从源码可以直接推导出递归的规模和形态。设初始直径d 600每层乘系数0.25当直径 ≤ 2 时停止层级 k直径 d 600 × 0.25ᵏ该层圆数量是否继续递归06001是600 211502是237.54是39.3758是42.3437516是2.34375 250.585937532否≤ 2仅绘制不派生因此画面上共绘制1 2 4 8 16 32 63 个圆递归深度约为 6 层。这个数字完全由源码中的参数初始直径 600、缩放系数 0.25、基线阈值 2决定修改任意一个都会改变分形的层数与稠密度。从计算复杂度看这是一棵满二叉树状的递归调用树每层调用数翻倍调用总数与深度呈2^深度的指数关系。这也解释了为什么递归分形必须设置基线条件——没有d 2的约束调用数将无限膨胀直至栈溢出。由于绘制按「先父后子、先左后右」的深度优先顺序执行所以渲染结果中内层的圆总是覆盖在外层圆之上描边互相叠加这也是分形视觉层次感的来源之一。Processing 移植版本对照同一挑战在仓库中还提供了 Processing 版本 Processing/CC_077_Recursion/CC_077_Recursion.pde逻辑与 p5.js 版一一对应// Daniel Shiffman // http://codingtra.in // http://patreon.com/codingtrain // Recursion // Edited Video: https://www.youtube.com/watch?vjPsZwrV9ld0 // Originally written using p5.js void setup() { size(600, 600); } void draw() { background(0); stroke(255); noFill(); drawCircle(300, 200, 600); noLoop(); } void drawCircle(float x, float y, float d) { ellipse(x, y, d, d); if (d 2) { float newD d * 0.25; drawCircle(x newD, y, newD); drawCircle(x - newD, y, newD); //drawCircle(x, y d * 0.5, d * 0.5); } }两个版本只有三处语法层面的差异算法结构完全一致p5.jsProcessing说明createCanvas(600, 600)size(600, 600)画布创建 API 不同ellipse(x, y, d)ellipse(x, y, d, d)p5.js 中直径 d 自动同时作用于宽高Processing 需显式传入两个直径动态类型let静态类型float语言类型系统差异这份「同构双版本」恰好说明递归思想与具体语言无关只要把「绘制当前圆 按条件派生子圆」的规则表达出来无论是 JavaScript 还是 Java 系语言都能得到同样的分形结果。变体实验改造递归参数源码的结构让变体实验变得极其简单以下是几个可立即验证的方向恢复第三个分支取消注释//drawCircle(x, y d * 0.5, d * 0.5);图案会在每个圆的垂直下方增加一支直径为 1/2 的子圆链整体更接近「雪花」式的稠密分形。改变缩放系数把d * 0.25换成d * 0.33或d * 0.5子圆更大、层数更少直径衰减慢更快达到或超过基线阈值图案更疏朗换成更小的系数如d * 0.15则层数更多、图案更细密。调整基线阈值把d 2改为d 50递归提前终止只保留少数几层改为d 1则会增加一层圆数量从 63 增至 127。加入第三个偏移方向将两个子调用扩展为三个例如分别偏移-newD、0、newD形成三叉分形。这些改动都不超过几行却能直观感受到「基线条件与缩放系数共同决定分形复杂度」这一核心规律是理解递归成本与视觉密度的最佳实验台。仓库中的递归应用从圆环到分形树本挑战文档在videos字段中明确关联了「Fractal Trees - Recursive」一期014-fractaltree.md其源码 CC_014_FractalTree/P5/sketch.js 是理解递归分形的第二个经典范例var angle 0; var slider; function setup() { createCanvas(400, 400); slider createSlider(0, TWO_PI, PI / 4, 0.01); } function draw() { background(51); angle slider.value(); stroke(255); translate(200, height); branch(100); } function branch(len) { line(0, 0, 0, -len); translate(0, -len); if (len 4) { push(); rotate(angle); branch(len * 0.67); pop(); push(); rotate(-angle); branch(len * 0.67); pop(); } }对比drawCircle与branch可以提炼出递归分形的通用模板每次调用先绘制一个基本单元圆环画ellipse树画line基线条件圆环是d 2树是len 4枝长小于等于 4 像素时停止递归缩放圆环乘 0.25树枝乘 0.67递归分支数圆环 2 个水平子圆树通过push()/pop()保护坐标系后左右各旋转angle派生出 2 条子枝。分形树版本还展示了递归与状态保存的配合push()记住当前坐标系rotate(angle)旋转后递归pop()恢复坐标系从而让左右两枝从同一个分叉点长出。交互上它通过滑杆实时调节角度配合draw()的每帧重绘让读者直接看到分支角度如何改变树的形态。仓库中更进阶的变体还包括 CC_015_FractalTreeArray/P5/sketch.js用Branch对象数组 p5.Vector计算分支端点鼠标点击逐步长出树枝是「递归生成 数组存储」的替代思路其分支几何在 branch.js 中实现以及 CC_129_Koch_Snowflake/P5/sketch.js科赫雪花采用「每一代由上一代所有线段派生、存入下一代数组」的迭代式生成与直接递归形成对照。这些案例共同说明递归/迭代只是手段分形的本质是用相同的规则反复作用于更小的尺度。社区贡献与延伸阅读挑战文档的contributions字段记录了社区围绕「递归分形」创作的一批作品例如Fractaloid - Interactive Fractal BranchesDiego de la Fuente Curaqueo可交互的分形树枝Sierpinski Cometfanbyprinciple谢尔宾斯基彗星#RecursiveTrainAlca、Recursive RemixJustin Riley对递归主题的再混排Recursive Squares与Recursive Koch CurveDonKarlssonSan递归正方形与科赫曲线Recursion PyramidTrevor Clarke递归金字塔Sierpinski TriangleChris Oliver、Recursion Geometric ShapeHussain Salih Mahdi谢尔宾斯基三角形与递归几何形状。这些作品与源码细节均记录在 077-recursion.md 的 front matter 中可作为继续探索递归分形表现力的灵感来源。理论背景方面文档推荐的 Benoit Mandelbrot《The Fractal Geometry of Nature》是理解分形数学的经典读物仓库内 Coding Challenges 索引 汇总了包括分形树014、科赫雪花129在内的全部挑战条目适合按主题串读。结语从 63 个空心圆开始本挑战用最短的代码讲透了递归分形的全部要点函数调用自身、基线条件防止无限递归、参数逐层收缩产生自相似结构。掌握drawCircle之后无论是转向分形树的分支递归还是把同样的思想用于四叉树分区如 CC_098.1_QuadTree 这类数据结构你都能快速识别出「递归 基线条件」的骨架。递归不是魔法而是一种「把问题缩小一点再交给同一个自己」的工程思维。赞分享教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载相关推荐用 p5.js 递归算法绘制分形树Coding Train 第 14 号挑战全解析用 p5.js 递归算法绘制分形树Coding Train 第 14 号挑战全解析 本篇技术指南围绕 Coding TrainCoding Rainbow教程文档示例工程2D 太阳系生成器递归行星轨道系统的 Processing 与 p5.js 实现Coding Challenge 72D 太阳系生成器递归行星轨道系统的 Processing 与 p5.js 实现Coding Challenge 7 本篇文章以 Coding Train教程文档示例工程p5.js 迷宫生成器第 3 部分递归回溯算法的拆墙与访问标记实战解析Coding Challenge 10.3p5.js 迷宫生成器第 3 部分递归回溯算法的拆墙与访问标记实战解析Coding Challenge 10.3 本文聚焦 Coding Train 的经教程文档示例工程上一篇MoviePilot媒体文件同步删除插件优化解析下一篇deepagents 上下文检索评估任务 cb-cloud-70 深度解析多实体比较题的构造、运行与 LLM 判题机制创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

用Python实现轻量级日志监控与告警系统

用Python实现轻量级日志监控与告警系统

1. 项目概述与核心需求1.1 为什么你需要自己写日志监控做运维或者后端开发的人,多多少少都经历过这样的场景:凌晨两点被电话吵醒,说线上服务挂了,你迷迷糊糊爬起来,连上服务器,翻了一大堆日志文件&#xff…

2026/10/12 3:37:09 阅读更多 →
REA 一键安装 Hopper 完整指南:macOS 与 Linux 双平台免费体验逆向工程

REA 一键安装 Hopper 完整指南:macOS 与 Linux 双平台免费体验逆向工程

REA 一键安装 Hopper 完整指南:macOS 与 Linux 双平台免费体验逆向工程 【免费下载链接】rea Reverse engineer anything with agents, from app behavior down to native binaries. 项目地址: https://gitcode.com/GitHub_Trending/rea2/rea REA&#xff08…

2026/10/12 3:37:09 阅读更多 →
Java常用API陷阱:Math、System、Runtime、Object与深浅拷贝实战解析

Java常用API陷阱:Math、System、Runtime、Object与深浅拷贝实战解析

在Java基础类库里,Math、System、Runtime、Object、对象克隆、Objects 这几个名字,凡是写过两年代码的人应该都能报出来。可也恰恰是这类“入门就见过”的API,在代码评审和线上排障里反复出问题。我见过有人用Math.round做金额舍入&#xff0…

2026/10/12 3:37:09 阅读更多 →

最新新闻

FreeRTOS CMSIS系列(9):中断管理详解

FreeRTOS CMSIS系列(9):中断管理详解

一、中断优先级任何中断的优先级都大于任务! 在我们的操作系统,中断同样是具有优先级的,并且我们也可以设置它的优先级,但是他的优先级并不是从0~15 ,默认情况下它是从 5~15 ,0~4 这 5 个中断优先级不是 Fr…

2026/10/12 4:25:38 阅读更多 →
FreeRTOS CMSIS系列(6):任务通知详解

FreeRTOS CMSIS系列(6):任务通知详解

目录 一、什么是任务通知 二、任务通知值的更新方式 三、任务通知的优势和劣势 3-1 任务通知的优势 3-2 任务通知的劣势 四、任务通知相关 API 函数 4-1 发送通知 4-2 等待通知 五、测试程序 一、什么是任务通知 FreeRTOS 从版本 V8.2.0 开始提供任务通知这个功能&…

2026/10/12 4:25:38 阅读更多 →
SVD揭秘推荐背后的兴趣密码

SVD揭秘推荐背后的兴趣密码

一、假设你刚刚刷了三条视频 你的行为是: FPS 枪法教学 → 看完,还点了赞 游戏装备评测 → 看完 蛋糕制作教程 → 很快划走现在系统要决定:下一条给你推荐什么?最直接的方法是:继续推荐游戏视频。 但系统怎么从海量观看…

2026/10/12 4:25:38 阅读更多 →
602 蓝牙耳机使用WSDF5361锂保芯片进入船运模式实现方法

602 蓝牙耳机使用WSDF5361锂保芯片进入船运模式实现方法

耳机经过锂电池保护,电池供电,发24个脉冲进船运模式后,可以用充电5V脉冲激活才能启动。所以电路是经过锂电池保护的。为什么写了25个脉冲还是没有进入船运模式?什么原因?GPIO测量,可以产生高电平3V, 低电平…

2026/10/12 4:25:38 阅读更多 →
Xilem 构建压力测试指南:用 `cargo rustc` 与 `compile_stress_test` 度量 Rust UI 框架的编译性能

Xilem 构建压力测试指南:用 `cargo rustc` 与 `compile_stress_test` 度量 Rust UI 框架的编译性能

前端桌面应用 【免费下载链接】xilem An experimental Rust native UI framework 项目地址: https://gitcode.com/gh_mirrors/xil/xilem 点击查看 免费下载 Xilem 是一个高度依赖 Rust 泛型与类型系统组合的实验性原生 UI 框架,其声明式 view 树在编译期…

2026/10/12 4:25:38 阅读更多 →
React 18 服务器错误恢复机制深度解析:Suspense 兜底、水合回退与 onRecoverableError 完整指南

React 18 服务器错误恢复机制深度解析:Suspense 兜底、水合回退与 onRecoverableError 完整指南

前端 【免费下载链接】rfcs RFCs for changes to React 项目地址: https://gitcode.com/gh_mirrors/rfc/rfcs 点击查看 免费下载 React 18 引入了一套全新的服务器渲染错误恢复机制:当组件在服务端抛出异常时,React 不再让整个页面崩溃&…

2026/10/12 4:24:38 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →