如何找到 L-Systems 树上分支的 beginning/end 个顶点? (p5.js)
How do I find beginning/end vertices of branches on an L-Systems tree? (p5.js)
我正在尝试获取简单 L 系统树上分支端点的 x、y 坐标。这个想法是创建一个 p5.Vector(x, y)
并将其推送到数组。
现在,我可以通过将椭圆的原点设置为 (0, -len) 来绘制标记所需点的椭圆,但我遇到了问题。当我尝试将 (0, -len) 作为 new p5.Vector(x, y)
推入数组时,每个点的 x 坐标都为 0,尽管 y 坐标正确。
我知道这与将坐标转换回 (width/2, height) 有关,但我无法找出正确的计算方法。我什至试过 tan(angle) * (y1 - y2)
但不太正确。 TIA!
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();
}
据我所知,目前 p5.js 对 vector/matrix 操作和坐标 space 转换的支持还不完善。
理论上,您可以手动跟踪每个变换 (translate/rotate) 并手动计算它以获得变换后的位置,但是,在实践中,这可能容易出错且麻烦。
在 Processing 中,您可以依靠 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();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
作为进一步的探索,这里有几个有趣的 L 系统 JS 实现可供使用:
我正在尝试获取简单 L 系统树上分支端点的 x、y 坐标。这个想法是创建一个 p5.Vector(x, y)
并将其推送到数组。
现在,我可以通过将椭圆的原点设置为 (0, -len) 来绘制标记所需点的椭圆,但我遇到了问题。当我尝试将 (0, -len) 作为 new p5.Vector(x, y)
推入数组时,每个点的 x 坐标都为 0,尽管 y 坐标正确。
我知道这与将坐标转换回 (width/2, height) 有关,但我无法找出正确的计算方法。我什至试过 tan(angle) * (y1 - y2)
但不太正确。 TIA!
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();
}
据我所知,目前 p5.js 对 vector/matrix 操作和坐标 space 转换的支持还不完善。
理论上,您可以手动跟踪每个变换 (translate/rotate) 并手动计算它以获得变换后的位置,但是,在实践中,这可能容易出错且麻烦。
在 Processing 中,您可以依靠 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();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
作为进一步的探索,这里有几个有趣的 L 系统 JS 实现可供使用: