【问题标题】:How do I find beginning/end vertices of branches on an L-Systems tree? (p5.js)如何在 L-Systems 树上找到分支的开始/结束顶点? (p5.js)
【发布时间】:2020-07-31 11:29:03
【问题描述】:

我试图在一个简单的 L-Systems 树上获取分支端点的 x、y 坐标。这个想法是创建一个p5.Vector(x, y) 并将其推送到一个数组中。

现在,我可以通过将它们的原点设置为 (0, -len) 来绘制标记所需点的椭圆,但我有一个问题。当我尝试将 (0, -len) 作为 new p5.Vector(x, y) 推送到数组时,每个点的 x 坐标都为 0,尽管 y 坐标正确。

我知道这与将坐标转换回(宽度/2,高度)有关,但我只是无法弄清楚正确的计算。我什至尝试过tan(angle) * (y1 - y2),但这并不完全正确。蒂亚!

var axiom = 'F';
var sentence = axiom;
var len = 300;

var count = 0;
var flowerArr = [];

var rules = [];
rules[0] = {
    a: 'F',
    b: 'G[+F][-F]GF'
};

rules[1] = {
    a: 'G',
    b: 'GG'
};


function setup() {
    createCanvas(window.innerWidth, window.innerHeight);
    stroke(10);
    smooth();
    turtle();
}


function turtle() {
    background(255);
    strokeWeight(1);
    angle = radians(Math.random() * (25 - 15) + 15);
    resetMatrix();
    translate(width / 2, height);

    for (var i = 0; i < sentence.length; i++) {
        var current = sentence.charAt(i);
        var randomSeed = 2;
        if (current == 'F' || current == 'G') {
            ellipse(0, -len, 5);
            line(0, 0, 0, -len);
            translate(0, -len);
        } else if (current == '+') {
            let positiveRotation = angle * Math.random() * randomSeed;
            rotate(positiveRotation);
        } else if (current == '-') {
            let negativeRotation = -angle * Math.random() * randomSeed;
            rotate(negativeRotation);
        } else if (current == '[') {
            push();
        } else if (current == ']') {
            pop();
            count++;
        }
    }
    if (i >= sentence.length) {
        finished = true;
        console.log("done", count);
    }
}

function generateStems(iterations) {
    for (i = iterations - 1; i > 0 ; i--) {
        branch();
    }
}

function branch() {
    len *= Math.random() * (.52 - .45) + .45;
    var nextSentence = '';
    for (var i = 0; i < sentence.length; i++) {
        var current = sentence.charAt(i);
        var found = false;
        for (var j = 0; j < rules.length; j++) {
            if (current == rules[j].a) {
                found = true;
                nextSentence += rules[j].b;
                break;
            }
        }
        if (!found) {
            nextSentence += current;
        }
    }
    sentence = nextSentence;
    turtle();
}

function draw() {
    generateStems(4);
    noLoop();
}

【问题讨论】:

  • 你打算用向量数组做什么? (也许还有另一种方法可以实现相同的目标而无需计算它们?)
  • @GeorgeProfenza 我想沿着分形创建一个点数组,这样我就可以指定一个顶点来生成一朵花。例如,我会编写一个类似“如果vertex.xPos != 0,或者如果vertex.y &gt;= 50% of the screen height,生成一朵花”的函数
  • 查看更新后的答案

标签: processing p5.js fractals procedural-generation l-systems


【解决方案1】:

据我所知,目前 p5.js 对vector/matrix 操作和坐标空间转换的支持还不完善。

理论上,您可以手动跟踪每个变换(平移/旋转)并手动计算它以获得变换后的位置,但实际上这可能容易出错且很麻烦。

在处理中,您可以依靠PMatrix's mult(PVector) 方法将一个点从一个坐标系转换到另一个坐标系,但目前在 p5.js 中不行。 screenX()/screenY() 之类的函数进一步简化了这一过程。

这是一个基本的例子(注意P3D的用法):

PVector v1 = new PVector();
float len = 100;

void setup(){
  size(300,300,P3D);
  noFill();
  strokeWeight(3);
}

void draw(){
  background(255);
  // isolate coordinate system
  pushMatrix();
  // apply a set of transformations
  translate(width / 2, height);
  translate(0,-len);
  rotate(radians(45));
  // draw a blue rectangle from the corner to illustrate this transformed position
  stroke(0,0,192);
  rect(0,0,30,30);
  // further transform
  translate(90,0);
  // draw a rect rectangle
  stroke(192,0,0);
  rect(0,0,30,30);
  // use screenX/screenY to calculate the transformed coordinates
  v1.set(screenX(0,0,0),screenY(0,0,0)); 
  popMatrix();

  // simply draw a (green) circle on top at the same transformed coordinates, without being in that local coordinate system 
  stroke(0,192,0);
  ellipse(v1.x, v1.y, 30, 30);
}

目前,出于实际原因,如果必须计算转换后的位置,我建议将您的代码移植到 Processing。

更新根据您的评论,使用 L 系统为花卉引入新规则会更容易。

假设* 代表一朵花,您可以修改规则以将其包括在最后一条指令中:b: 'G[+F][-F]GF' 变为 b: 'G[+F][-F]GF*'

那么只要在你遍历当前句子时处理那个符号:

var axiom = 'F';
var sentence = axiom;
var len = 300;

var count = 0;
var flowerArr = [];

var rules = [];
rules[0] = {
    a: 'F',
    b: 'G[+F][-F]GF*'
};

rules[1] = {
    a: 'G',
    b: 'GG'
};


function setup() {
    createCanvas(630, 630);
    stroke(10);
    noFill();
    smooth();
    turtle();
}


function turtle() {
    background(255);
    strokeWeight(1);
    angle = radians(Math.random() * (25 - 15) + 15);
    resetMatrix();
    translate(width / 2, height);
    for (var i = 0; i < sentence.length; i++) {
        var current = sentence.charAt(i);
        var randomSeed = 2;
        if (current == 'F' || current == 'G') {
            ellipse(0, -len, 5);
            line(0, 0, 0, -len);
            translate(0, -len);
        // flower rule
        } else if (current == '*') {
            flower(6,len * 0.618033);
        } else if (current == '+') {
            let positiveRotation = angle * Math.random() * randomSeed;
            rotate(positiveRotation);
        } else if (current == '-') {
            let negativeRotation = -angle * Math.random() * randomSeed;
            rotate(negativeRotation);
        } else if (current == '[') {
            push();
        } else if (current == ']') {
            pop();
            count++;
        }
    }
    if (i >= sentence.length) {
        finished = true;
        // console.log("done", count);
    }
}

function flower(sides, sideLength){
  beginShape();
  let angleIncrement = TWO_PI / sides;
  for(let i = 0 ; i <= sides; i++){
    vertex(cos(angleIncrement * i) * sideLength,
           sin(angleIncrement * i) * sideLength);
  }
  endShape();
}

function generateStems(iterations) {
    for (i = iterations - 1; i > 0 ; i--) {
        branch();
    }
}

function branch() {
    len *= Math.random() * (.52 - .45) + .45;
    var nextSentence = '';
    for (var i = 0; i < sentence.length; i++) {
        var current = sentence.charAt(i);
        var found = false;
        for (var j = 0; j < rules.length; j++) {
          
            if (current == rules[j].a) {
              
                found = true;
                nextSentence += rules[j].b;
                break;
            }
        }
        if (!found) {
            nextSentence += current;
        }
    }
    sentence = nextSentence;
    turtle();
}

function draw() {
    generateStems(5);
    noLoop();
}
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"&gt;&lt;/script&gt;

作为进一步的探索,这里有几个有趣的 L-system JS 实现可供使用:

【讨论】:

  • 哦,哇,非常感谢。甚至没有考虑过这样做,即使这是一个 L-systems-y 答案。我真的很感谢你不遗余力地帮助我!
猜你喜欢
  • 1970-01-01
  • 2012-12-18
  • 1970-01-01
  • 2020-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-27
  • 1970-01-01
相关资源
最近更新 更多