You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用P5.js实现分形树时遭遇递归异常问题求助

P5.js分形树异常问题修复

问题现象

用P5.js编写分形树程序时出现以下异常:

  • 仅第一个递归调用执行,draw循环会在非预期位置重启
  • 分支层数设置为3或更少时,绘制效果异常

原代码

function setup() {
  createCanvas(1920, 1080);
}

function draw() {
  background(10);
  x = 1920/2
  y = 1080/2
  fractalDraw(x, y, 5, 0)
}

function fractalDraw(nodeX, nodeY, numNodes, sideFlag){
  offset = 10 * numNodes
  leftNodeX = nodeX - offset
  rightNodeX = nodeX + offset
  topNodeY = nodeY - offset
  botNodeY = nodeY + offset
  
  if(sideFlag === -1){                                 //Leftside draw
    line(nodeX, nodeY, leftNodeX, topNodeY)
    stroke(255,255,255)
    line(nodeX, nodeY, leftNodeX, botNodeY)
    stroke(255,255,255);
  }
  else if(sideFlag === 1){                            //Rightside draw
    line(nodeX, nodeY, rightNodeX, topNodeY)
    stroke(255,255,255);
    line(nodeX, nodeY, rightNodeX, botNodeY)
    stroke(255,255,255);
  }
  else{                                              //Starting draw
    line(nodeX, nodeY, leftNodeX, topNodeY)
    stroke(255,255,255)
    line(nodeX, nodeY, leftNodeX, botNodeY)
    stroke(255,255,255);
    line(nodeX, nodeY, rightNodeX, topNodeY)
    stroke(255,255,255);
    line(nodeX, nodeY, rightNodeX, botNodeY)
    stroke(255,255,255);
  }
  
  if(numNodes === 1){                              //Recursion Base Case
    return 1
  }
  else{                                            //Recursive calls
    fractalDraw(leftNodeX, topNodeY, numNodes-1, -1)
    fractalDraw(leftNodeX, botNodeY, numNodes-1, -1)
    fractalDraw(rightNodeX, topNodeY, numNodes-1, 1)
    fractalDraw(rightNodeX, botNodeY, numNodes-1, 1)
  }
}

错误根源

  1. 全局变量污染:所有变量(x、y、offset、leftNodeX等)未用let/const声明,默认成为全局变量。递归过程中后续调用会覆盖这些变量值,导致递归逻辑混乱,出现仅第一个递归执行、绘制位置偏移的问题。
  2. stroke调用位置错误:stroke()放在line()之后,第一条线会使用默认黑色(未设置白色笔触),且重复调用冗余。
  3. 不必要的返回值:递归终止条件返回1无意义,直接return即可。
  4. offset计算不合理:10 * numNodes会让层数越少的分支长度越短,导致层数≤3时分支过于细碎,视觉上异常。

修复后的代码

function setup() {
  createCanvas(1920, 1080);
  stroke(255, 255, 255); // 统一设置白色笔触,避免重复调用
}

function draw() {
  background(10);
  const x = 1920 / 2;
  const y = 1080; // 把起始点移到画布底部,更符合树的形态(可选调整)
  fractalDraw(x, y, 5, 0);
}

function fractalDraw(nodeX, nodeY, numNodes, sideFlag) {
  const offset = 30 * numNodes; // 调整偏移量系数,让分支长度更合理
  const leftNodeX = nodeX - offset;
  const rightNodeX = nodeX + offset;
  const topNodeY = nodeY - offset;
  const botNodeY = nodeY + offset;

  if (sideFlag === -1) { // 左侧分支绘制
    line(nodeX, nodeY, leftNodeX, topNodeY);
    line(nodeX, nodeY, leftNodeX, botNodeY);
  } else if (sideFlag === 1) { // 右侧分支绘制
    line(nodeX, nodeY, rightNodeX, topNodeY);
    line(nodeX, nodeY, rightNodeX, botNodeY);
  } else { // 初始节点绘制
    line(nodeX, nodeY, leftNodeX, topNodeY);
    line(nodeX, nodeY, leftNodeX, botNodeY);
    line(nodeX, nodeY, rightNodeX, topNodeY);
    line(nodeX, nodeY, rightNodeX, botNodeY);
  }

  if (numNodes === 1) { // 递归终止条件
    return;
  }

  // 递归调用所有子节点
  fractalDraw(leftNodeX, topNodeY, numNodes - 1, -1);
  fractalDraw(leftNodeX, botNodeY, numNodes - 1, -1);
  fractalDraw(rightNodeX, topNodeY, numNodes - 1, 1);
  fractalDraw(rightNodeX, botNodeY, numNodes - 1, 1);
}

修复说明

  • 给所有局部变量添加const/let声明,隔离递归过程中的变量作用域,避免全局污染。
  • 将stroke()移到setup()中统一设置,减少冗余代码,确保所有线条都是白色。
  • 调整初始Y坐标到画布底部,让分形更像树的形态(可根据需求改回原位置)。
  • 增大offset的系数,让低层数的分支长度更合理,解决层数≤3时的异常问题。
  • 移除递归终止条件中不必要的返回值,逻辑更简洁。

内容的提问来源于stack exchange,提问作者Yonahel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 03:12:36