如何优化递归实现的谢尔宾斯基地毯?消除异常粗线条
解决谢尔宾斯基地毯的粗线条问题与代码优化方案
嘿,很高兴看到你跟着Shiffman的递归教程探索分形图形!咱们一步步解决你遇到的两个问题:不该出现的粗线条,以及代码的优化空间。
一、消除粗线条的根本原因与修复方案
你看到的粗线条,本质是两个核心问题导致的:
- 递归坐标计算错误:你当前代码里用了
x+d/x-d作为偏移量,这完全偏离了谢尔宾斯基地毯的分形逻辑——当大正方形边长为d时,下一级小正方形的中心应该相对于大中心偏移d/3(而非d),因为小正方形的边长是d/3,中心间距刚好是d/3。 - 整数坐标的舍入误差:强制用
int()转换坐标会导致位置偏移,相邻矩形出现重叠或间隙,视觉上就呈现出了粗线条。
修复后的核心递归函数调整如下:
void Serpinski(float x, float y, float d) { rect(x, y, d, d); if (d > 1.5) { // 用更合理的终止阈值,避免绘制肉眼无法分辨的过小矩形 float newD = d / 3; float offset = d / 3; // 八个方向的递归调用 Serpinski(x + offset, y, newD); Serpinski(x - offset, y, newD); Serpinski(x, y + offset, newD); Serpinski(x, y - offset, newD); Serpinski(x + offset, y + offset, newD); Serpinski(x - offset, y - offset, newD); Serpinski(x + offset, y - offset, newD); Serpinski(x - offset, y + offset, newD); } }
同时调整主函数,去掉不必要的int转换,并把全局设置移到setup里:
void setup() { size(800, 800); rectMode(CENTER); // 全局只设置一次即可 noStroke(); fill(0); } void draw() { background(255); Serpinski(width/2, height/2, width/3); noLoop(); // 只绘制一次,避免draw循环重复执行浪费资源 }
二、代码优化方案
除了修复粗线条,还可以从这几个方面优化代码:
- 避免重复绘制:
draw()默认每秒执行60次,加入noLoop()让图形只绘制一次,节省性能开销。 - 提取重复变量:把
newD和offset单独提取出来,避免重复计算d/3,让代码更清晰易读。 - 合理终止递归:用
d > 1.5替代d > 1,当d小到肉眼无法分辨时提前终止递归,提升运行效率。 - 统一参数类型:全部用
float处理坐标和边长,彻底避免整数转换带来的精度损失。
修改后,谢尔宾斯基地毯的每个小矩形都会精准定位,不会再出现重叠的粗线条,同时递归逻辑更清晰,性能也有所提升。
内容的提问来源于stack exchange,提问作者jirm
相关产品推荐
相关产品推荐

