我一直在尝试使用 P5.js 创建一个简单的分形树程序,用于在网站上显示,但我似乎得到了意想不到的行为。附上代码和图片。
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)
}
}
我正在使用递归调用,似乎只有我的第一个递归调用正在运行,然后一旦第一个(并且只有第一个)递归调用完成,整个绘制循环就会在与预期不同的起始位置重新启动。当使用少量分支层(3 或更少)时,我也有一些奇怪的行为。
主要问题是您没有使用
var
、let
或 const
定义变量,因此所有变量都隐式声明为全局变量,这是造成严重破坏的原因。 leftNodeX
和类似变量的值会被递归调用修改,因此当这些递归调用返回时,这些变量不再具有其预期值,并且下一个递归调用将获得不再有意义的参数。
还有其他一些问题:
偏移量不应以常数减小。如果您从
numNodes
等于 5 开始,那么在不同的递归深度,您的 offset
将为 50,然后是 40、30、20……这并不理想。该序列应该是一个geometric序列,即offset
应该减少一个factor,而不是一个常数。为了实现这一点,将偏移量作为参数而不是节点数传递会更容易。
第一条线未绘制,因为未设置笔画。请注意,当调用
line()
时,就会绘制线条,并且您只需要调用 stroke
一次:它是对 line
(以及其他绘图函数)的任何后续调用的配置。因此,您可以在设置中移动该 stroke()
调用。
要支持其他屏幕尺寸,请勿对画布尺寸进行硬编码,而是使用浏览器的信息(与 CSS 一起),例如
clientWidth
和 clientHeight
。
p5将永远继续呼叫
draw
。正如您所定义的 fractalDraw
,没有必要这样做:一次调用就足够了。您可以通过添加对 p5 的 noLoop
的调用来表明这一点
更正:
function setup() {
// Use browser information to set size (see also CSS settings for body)
createCanvas(document.documentElement.clientWidth,
document.documentElement.clientHeight);
// Call stroke before drawing
stroke(255,255,255);
}
function draw() {
background(10);
// Define variables with const
const x = width >> 1; // Use integer division
const y = height >> 1;
// Pass the initial offset instead of the number of nodes
fractalDraw(x, y, width >> 2, 0);
noLoop(); // Avoid repeating calls to draw()
}
function fractalDraw(nodeX, nodeY, offset, sideFlag){
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(offset <= 1){
return 1;
}
else{
// The offset should decrease by a factor, not by a constant
offset >>= 1; // For example: integer divide by 2
fractalDraw(leftNodeX, topNodeY, offset, -1);
fractalDraw(leftNodeX, botNodeY, offset, -1);
fractalDraw(rightNodeX, topNodeY, offset, 1);
fractalDraw(rightNodeX, botNodeY, offset, 1);
}
}
html, body { margin: 0; height: 100%; overflow: hidden; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.min.js"></script>